DiscussionArea.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <template>
  2. <div class="discussion-area">
  3. <h1 class="sub-title vertical-center">
  4. 讨论区
  5. <c-button theme="green" :left="12" :icon="focus ? 'unfold_less' : 'edit'" text @click="handleFocusButtonClick">
  6. {{focus ? '隐藏编辑器' : '讨论|笔记'}}
  7. </c-button>
  8. <c-button :no-bold="personal" text :theme="personal ? 'blue' : ''"
  9. :icon="personal ? 'check_box' : 'check_box_outline_blank'"
  10. title="如果设置为私人笔记,则这条讨论仅自己可见"
  11. @click="handleSetPersonal">笔记模式
  12. </c-button>
  13. </h1>
  14. <div class="discussion-area-content">
  15. <show-transition>
  16. <write-new-comment :show="personal" v-show="focus" class="mb-8" ref="writeComment"
  17. @comment-submit="handleCommentSubmit"
  18. :placeholder="writingAreaPlaceholder"></write-new-comment>
  19. </show-transition>
  20. <div v-show="notes.length">
  21. <div class="line"><span>私人笔记</span></div>
  22. <template v-for="comment of notes">
  23. <c-comment :key="'note' + comment.id" :comment="comment" personal
  24. @need-refresh="updateComments"
  25. >
  26. </c-comment>
  27. </template>
  28. </div>
  29. <div class="line"><span>公共讨论区</span></div>
  30. <div class="no-content-warning" v-if="comments === null">
  31. <div class="no-content-warning-tip">加载中...</div>
  32. </div>
  33. <div class="no-content-warning" v-else-if="comments.length === 0 && wsComments.length === 0">
  34. 本页还没有讨论
  35. </div>
  36. <template v-for="comment of wsComments">
  37. <c-comment v-if="comment.pageNumber === currentPage" :key="comment.id" :comment="comment" is-new
  38. @need-refresh="updateComments"
  39. >
  40. </c-comment>
  41. </template>
  42. <template v-for="comment of comments">
  43. <c-comment :key="comment.id" :comment="comment"
  44. @need-refresh="updateComments"
  45. >
  46. </c-comment>
  47. </template>
  48. </div>
  49. </div>
  50. </template>
  51. <script>
  52. import CComment from '@/components/Discussion/CComment'
  53. import WriteNewComment from '@/components/Discussion/WriteNewComment'
  54. import { mapState } from 'vuex'
  55. import { createComment, getComments, getSelfCommentsBySlide } from '@/server/comment'
  56. import WebsocketPublisher from '@/server/websocket/ws'
  57. import { useCommentWsServer } from '@/config/server-info'
  58. import CButton from '@/components/Basic/CButton'
  59. import ShowTransition from '@/animations/ShowTransition'
  60. import { FOCUS_ON_INPUT, SET_PERSONAL, USE_FOCUS } from '@/store/types/pdf-types'
  61. export default {
  62. components: { ShowTransition, CButton, WriteNewComment, CComment },
  63. data () {
  64. return {
  65. comments: null,
  66. notes: [],
  67. wsComments: [],
  68. slideId: this.$route.params.slideId
  69. }
  70. },
  71. mounted () {
  72. this.initWs()
  73. this.updateComments()
  74. },
  75. computed: {
  76. ...mapState({
  77. userInfo: state => state.main.userInfo,
  78. currentPage: state => state.pdf.currentPage,
  79. focus: state => state.pdf.focus,
  80. personal: state => state.pdf.personal
  81. }),
  82. writingAreaPlaceholder () {
  83. return '主题'
  84. }
  85. },
  86. watch: {
  87. currentPage () {
  88. this.comments = null
  89. this.updateComments()
  90. }
  91. },
  92. beforeDestroy () {
  93. this._ws.close()
  94. },
  95. methods: {
  96. initWs () {
  97. this._ws = new WebsocketPublisher(useCommentWsServer('/slide/') + this.$route.params.slideId)
  98. this._ws.subscribe({
  99. onNext: data => {
  100. this.wsComments.unshift(data)
  101. },
  102. onError: err => {
  103. this.$setErrorMessage(err)
  104. },
  105. onClose: () => {}
  106. })
  107. },
  108. updateComments () {
  109. this.getComments(1000)
  110. this.getNote(1000)
  111. },
  112. getComments (size) {
  113. return getComments({
  114. slideId: this.slideId,
  115. size,
  116. pageNumber: this.currentPage,
  117. sort: ['topNumber,desc', 'createAt,desc']
  118. }).then(res => {
  119. this.wsComments = []
  120. this.comments = res.data
  121. }).catch(err => {
  122. this.$setErrorMessage('获取本页评论区的过程中:' + err.response.data.msg)
  123. })
  124. },
  125. getNote (size) {
  126. return getSelfCommentsBySlide({
  127. slideId: this.slideId,
  128. pageNumber: this.currentPage,
  129. show: false,
  130. size: 1000,
  131. sort: ['createAt,desc']
  132. })
  133. .then(res => {
  134. this.notes = []
  135. this.notes = res.data
  136. })
  137. .catch(err => this.$setErrorMessage(err.response.data.msg))
  138. },
  139. handleCommentSubmit (comment) {
  140. createComment({
  141. slideId: this.slideId,
  142. pageNumber: this.currentPage,
  143. title: comment.title,
  144. content: comment.content,
  145. show: !this.personal
  146. }).then(res => {
  147. this.$refs.writeComment.resetContent()
  148. this.$setSuccessMessage('您的讨论帖发表成功')
  149. if (this.personal) {
  150. this.getNote(1000)
  151. }
  152. }).catch(err => {
  153. this.$setErrorMessage(err.response.data.msg)
  154. })
  155. },
  156. handleFocusButtonClick () {
  157. this.focus ? this.$store.commit(USE_FOCUS) : this.$store.commit(FOCUS_ON_INPUT)
  158. },
  159. handleSetPersonal () {
  160. this.$store.commit(SET_PERSONAL, { personal: !this.personal })
  161. }
  162. }
  163. }
  164. </script>
  165. <style lang="scss" scoped>
  166. .discussion-area {
  167. max-width: 410px;
  168. padding: 12px 12px 0 12px;
  169. box-sizing: border-box;
  170. position: fixed;
  171. min-width: 405px;
  172. max-height: calc(100vh - 76px);
  173. overflow: auto;
  174. overflow-x: hidden;
  175. bottom: 0;
  176. right: 0;
  177. box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  178. background-color: white;
  179. border: 1px solid #e8e8e8;
  180. }
  181. .no-content-warning {
  182. height: 50px;
  183. }
  184. .c-comment:nth-child(2n-1) {
  185. background-color: #f9f9f9;
  186. }
  187. .c-comment:nth-child(2n) {
  188. background-color: white;
  189. }
  190. @media (max-width: 500px) {
  191. .discussion-area {
  192. min-width: unset;
  193. max-width: unset;
  194. left: 0;
  195. right: 0;
  196. background-color: rgba(255, 255, 255, 1);
  197. }
  198. }
  199. .line {
  200. position: relative;
  201. font-size: 12px;
  202. font-weight: normal;
  203. text-align: center;
  204. line-height: 0;
  205. margin: 4px 0;
  206. & > span {
  207. color: #5a6a7a;
  208. display: inline-block;
  209. background-color: white;
  210. padding: 4px 12px;
  211. line-height: 12px;
  212. }
  213. &:before {
  214. z-index: -1;
  215. content: '';
  216. width: 100%;
  217. position: absolute;
  218. left: 0;
  219. top: 6px;
  220. border-bottom: 1px solid #aabaca;
  221. }
  222. }
  223. </style>