messages.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <script>
  2. import { hoursTimeFormat } from "utils";
  3. import { isString } from "utils/validate";
  4. import contextmenu from "../directives/contextmenu";
  5. export default {
  6. name: "LemonMessages",
  7. components: {},
  8. props: {
  9. //是否隐藏消息发送人昵称
  10. hideName: Boolean,
  11. //是否隐藏显示消息时间
  12. hideTime: Boolean,
  13. reverseUserId: [String, Number],
  14. timeRange: {
  15. type: Number,
  16. default: 1,
  17. },
  18. timeFormat: {
  19. type: Function,
  20. default(val) {
  21. return hoursTimeFormat(val);
  22. },
  23. },
  24. loadingText: {
  25. type: [String, Function],
  26. },
  27. loadendText: {
  28. type: [String, Function],
  29. default: "暂无更多消息",
  30. },
  31. messages: {
  32. type: Array,
  33. default: () => [],
  34. },
  35. },
  36. data() {
  37. this._lockScroll = false;
  38. return {
  39. _loading: false,
  40. _loadend: false,
  41. };
  42. },
  43. render() {
  44. return (
  45. <div class="lemon-messages" ref="wrap" on-scroll={this._handleScroll}>
  46. <div
  47. class={[
  48. "lemon-messages__load",
  49. `lemon-messages__load--${this._loadend ? "end" : "ing"}`,
  50. ]}
  51. >
  52. <span class="lemon-messages__loadend">
  53. {isString(this.loadendText) ? this.loadendText : this.loadendText()}
  54. </span>
  55. <span class="lemon-messages__loading">
  56. {this.loadingText ? (
  57. isString(this.loadingText) ? (
  58. this.loadingText
  59. ) : (
  60. this.loadingText()
  61. )
  62. ) : (
  63. <i class="lemon-icon-loading lemonani-spin" />
  64. )}
  65. </span>
  66. </div>
  67. {this.messages.map((message, index) => {
  68. const node = [];
  69. const tagName = `lemon-message-${message.type}`;
  70. const prev = this.messages[index - 1];
  71. if (
  72. prev &&
  73. this.msecRange &&
  74. message.sendTime - prev.sendTime > this.msecRange
  75. ) {
  76. node.push(
  77. <lemon-message-event
  78. attrs={{
  79. message: {
  80. id: "__time__",
  81. type: "event",
  82. content: hoursTimeFormat(message.sendTime),
  83. },
  84. }}
  85. />,
  86. );
  87. }
  88. let attrs;
  89. if (message.type == "event") {
  90. attrs = { message: message };
  91. } else {
  92. attrs = {
  93. timeFormat: this.timeFormat,
  94. message: message,
  95. reverse: this.reverseUserId == message.fromUser.id,
  96. hideTime: this.hideTime,
  97. hideName: this.hideName,
  98. };
  99. }
  100. node.push(<tagName ref="message" refInFor={true} attrs={attrs} />);
  101. return node;
  102. })}
  103. </div>
  104. );
  105. },
  106. computed: {
  107. msecRange() {
  108. return this.timeRange * 1000 * 60;
  109. },
  110. },
  111. watch: {},
  112. methods: {
  113. loaded() {
  114. this._loadend = true;
  115. this.$forceUpdate();
  116. },
  117. resetLoadState() {
  118. this._lockScroll = true;
  119. this._loading = false;
  120. this._loadend = false;
  121. setTimeout(() => {
  122. this._lockScroll = false;
  123. }, 200);
  124. },
  125. async _handleScroll(e) {
  126. if (this._lockScroll) return;
  127. const { target } = e;
  128. contextmenu.hide();
  129. if (
  130. target.scrollTop == 0 &&
  131. this._loading == false &&
  132. this._loadend == false
  133. ) {
  134. this._loading = true;
  135. await this.$nextTick();
  136. const hst = target.scrollHeight;
  137. this.$emit("reach-top", async isEnd => {
  138. await this.$nextTick();
  139. target.scrollTop = target.scrollHeight - hst;
  140. this._loading = false;
  141. this._loadend = !!isEnd;
  142. });
  143. }
  144. },
  145. async scrollToBottom() {
  146. await this.$nextTick();
  147. const { wrap } = this.$refs;
  148. if (wrap) {
  149. wrap.scrollTop = wrap.scrollHeight;
  150. }
  151. },
  152. },
  153. created() {},
  154. mounted() {},
  155. };
  156. </script>
  157. <style lang="stylus">
  158. @import '~styles/utils/index'
  159. +b(lemon-messages)
  160. height 400px
  161. overflow-x hidden
  162. overflow-y auto
  163. scrollbar-light()
  164. padding 10px 15px
  165. +e(time)
  166. text-align center
  167. font-size 12px
  168. +e(load)
  169. user-select none
  170. font-size 12px
  171. text-align center
  172. color #999
  173. line-height 30px
  174. .lemon-messages__loading
  175. .lemon-messages__loadend
  176. display none
  177. +m(ing)
  178. .lemon-icon-loading
  179. font-size 22px
  180. .lemon-messages__loading
  181. display block
  182. +m(end)
  183. .lemon-messages__loadend
  184. display block
  185. </style>