CommentList.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <template>
  2. <div :style="{ position: 'relative', height: allHeight + 'px' }">
  3. <a-list class="jeecg-comment-list" header="" item-layout="horizontal" :data-source="dataList" :style="{ height: commentHeight + 'px' }">
  4. <template #renderItem="{ item }">
  5. <a-list-item style="padding-left: 10px; flex-direction: column" @click="handleClickItem">
  6. <a-comment>
  7. <template #avatar>
  8. <a-avatar class="tx" :src="getAvatar(item)" :alt="getAvatarText(item)">{{ getAvatarText(item) }}</a-avatar>
  9. </template>
  10. <template #author>
  11. <div class="comment-author">
  12. <span>{{ item.fromUserId_dictText }}</span>
  13. <template v-if="item.toUserId">
  14. <span>回复</span>
  15. <span>{{ item.toUserId_dictText }}</span>
  16. <Tooltip class="comment-last-content" @visibleChange="(v)=>visibleChange(v, item)">
  17. <template #title>
  18. <div v-html="getHtml(item.commentId_dictText)"></div>
  19. </template>
  20. <message-outlined />
  21. </Tooltip>
  22. </template>
  23. </div>
  24. </template>
  25. <template #datetime>
  26. <div>
  27. <Tooltip :title="item.createTime">
  28. <span>{{ getDateDiff(item) }}</span>
  29. </Tooltip>
  30. </div>
  31. </template>
  32. <template #actions>
  33. <span @click="showReply(item)">回复</span>
  34. <Popconfirm title="确定删除吗?" @confirm="deleteComment(item)">
  35. <span>删除</span>
  36. </Popconfirm>
  37. </template>
  38. <template #content>
  39. <div v-html="getHtml(item.commentContent)" style="font-size: 15px">
  40. </div>
  41. <div v-if="item.fileList && item.fileList.length > 0">
  42. <!-- 历史文件 -->
  43. <history-file-list :dataList="item.fileList" isComment></history-file-list>
  44. </div>
  45. </template>
  46. </a-comment>
  47. <div v-if="item.commentStatus" class="inner-comment">
  48. <my-comment inner @cancel="item.commentStatus = false" @comment="(content, fileList) => replyComment(item, content, fileList)" :inputFocus="focusStatus"></my-comment>
  49. </div>
  50. </a-list-item>
  51. </template>
  52. </a-list>
  53. <div style="position: absolute; bottom: 0; left: 0; width: 100%; background: #fff; border-top: 1px solid #eee">
  54. <a-comment style="margin: 0 10px">
  55. <template #avatar>
  56. <a-avatar class="tx" :src="getMyAvatar()" :alt="getMyname()">{{ getMyname() }}</a-avatar>
  57. </template>
  58. <template #content>
  59. <my-comment ref="bottomCommentRef" @comment="sendComment" :inputFocus="focusStatus"></my-comment>
  60. </template>
  61. </a-comment>
  62. </div>
  63. </div>
  64. </template>
  65. <script>
  66. /**
  67. * 评论列表
  68. */
  69. import { defineComponent, ref, onMounted, watch, watchEffect ,inject } from 'vue';
  70. import { propTypes } from '/@/utils/propTypes';
  71. // import dayjs from 'dayjs';
  72. // import relativeTime from 'dayjs/plugin/relativeTime';
  73. // import customParseFormat from 'dayjs/plugin/customParseFormat';
  74. // dayjs.locale('zh');
  75. // dayjs.extend(relativeTime);
  76. // dayjs.extend(customParseFormat);
  77. import { MessageOutlined } from '@ant-design/icons-vue';
  78. import { Comment, Tooltip } from 'ant-design-vue';
  79. import { useUserStore } from '/@/store/modules/user';
  80. import MyComment from './MyComment.vue';
  81. import { list, saveOne, deleteOne, useCommentWithFile, useEmojiHtml, queryById, getGloablEmojiIndex } from './useComment';
  82. import { useMessage } from '/@/hooks/web/useMessage';
  83. import HistoryFileList from './HistoryFileList.vue';
  84. import { Popconfirm } from 'ant-design-vue';
  85. import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
  86. export default defineComponent({
  87. name: 'CommentList',
  88. components: {
  89. MessageOutlined,
  90. AComment: Comment,
  91. Tooltip,
  92. MyComment,
  93. Popconfirm,
  94. HistoryFileList,
  95. },
  96. props: {
  97. tableName: propTypes.string.def(''),
  98. dataId: propTypes.string.def(''),
  99. datetime: propTypes.number.def(1),
  100. // 其他需要减去的高度
  101. otherHeight: propTypes.number.def(0),
  102. },
  103. setup(props) {
  104. const { createMessage } = useMessage();
  105. const dataList = ref([]);
  106. const { userInfo } = useUserStore();
  107. const dayjs = inject('$dayjs')
  108. /**
  109. * 获取当前用户名称
  110. */
  111. function getMyname() {
  112. if (userInfo.realname) {
  113. return userInfo.realname.substr(0, 2);
  114. }
  115. return '';
  116. }
  117. function getMyAvatar(){
  118. return userInfo.avatar;
  119. }
  120. // 获取头像
  121. function getAvatar(item) {
  122. if (item.fromUserAvatar) {
  123. return getFileAccessHttpUrl(item.fromUserAvatar)
  124. }
  125. return '';
  126. }
  127. // 头像没有获取 用户名前两位
  128. function getAvatarText(item){
  129. if (item.fromUserId_dictText) {
  130. return item.fromUserId_dictText.substr(0, 2);
  131. }
  132. return '未知';
  133. }
  134. function getAuthor(item) {
  135. if (item.toUser) {
  136. return item.fromUserId_dictText + ' 回复 ' + item.fromUserId_dictText;
  137. } else {
  138. return item.fromUserId_dictText;
  139. }
  140. }
  141. function getDateDiff(item) {
  142. if (item.createTime) {
  143. const temp = dayjs(item.createTime, 'YYYY-MM-DD hh:mm:ss');
  144. return temp.fromNow();
  145. }
  146. return '';
  147. }
  148. const commentHeight = ref(300);
  149. const allHeight = ref(300);
  150. onMounted(() => {
  151. let otherHeight = props.otherHeight || 0;
  152. commentHeight.value = window.innerHeight - 57 - 46 - 70 - 160 - otherHeight;
  153. allHeight.value = window.innerHeight - 57 - 46 - 53 -20 - otherHeight;
  154. });
  155. /**
  156. * 加载数据
  157. * @returns {Promise<void>}
  158. */
  159. async function loadData() {
  160. const params = {
  161. tableName: props.tableName,
  162. tableDataId: props.dataId,
  163. column: 'createTime',
  164. order: 'desc',
  165. };
  166. const data = await list(params);
  167. if (!data || !data.records || data.records.length == 0) {
  168. dataList.value = [];
  169. } else {
  170. let array = data.records;
  171. console.log(123, array);
  172. dataList.value = array;
  173. }
  174. }
  175. const { saveCommentAndFiles } = useCommentWithFile(props);
  176. // 回复
  177. async function replyComment(item, content, fileList) {
  178. console.log(content, item);
  179. let obj = {
  180. fromUserId: userInfo.id,
  181. toUserId: item.fromUserId,
  182. commentId: item.id,
  183. commentContent: content
  184. }
  185. await saveCommentAndFiles(obj, fileList)
  186. await loadData();
  187. }
  188. //评论
  189. async function sendComment(content, fileList) {
  190. let obj = {
  191. fromUserId: userInfo.id,
  192. commentContent: content
  193. }
  194. await saveCommentAndFiles(obj, fileList)
  195. await loadData();
  196. focusStatus.value = false;
  197. setTimeout(()=>{
  198. focusStatus.value = true;
  199. },100)
  200. }
  201. //删除
  202. async function deleteComment(item) {
  203. const params = { id: item.id };
  204. await deleteOne(params);
  205. await loadData();
  206. }
  207. /**
  208. * 打开回复时触发
  209. * @type {Ref<UnwrapRef<boolean>>}
  210. */
  211. const focusStatus = ref(false);
  212. function showReply(item) {
  213. let arr = dataList.value;
  214. for (let temp of arr) {
  215. temp.commentStatus = false;
  216. }
  217. item.commentStatus = true;
  218. focusStatus.value = false;
  219. focusStatus.value = true;
  220. }
  221. // 表单改变 -重新加载评论列表
  222. watchEffect(() => {
  223. if(props.datetime){
  224. if (props.tableName && props.dataId) {
  225. loadData();
  226. }
  227. }
  228. });
  229. //const storageEmojiIndex = inject('$globalEmojiIndex')
  230. const storageEmojiIndex = getGloablEmojiIndex()
  231. const { getHtml } = useEmojiHtml(storageEmojiIndex);
  232. const bottomCommentRef = ref()
  233. function handleClickItem(){
  234. bottomCommentRef.value.changeActive()
  235. }
  236. /**
  237. * 根据id查询评论信息
  238. */
  239. async function visibleChange(v, item){
  240. if(v==true){
  241. if(!item.commentId_dictText){
  242. const data = await queryById(item.commentId);
  243. if(data.success == true){
  244. item.commentId_dictText = data.result.commentContent
  245. }else{
  246. console.error(data.message)
  247. item.commentId_dictText='该评论已被删除';
  248. }
  249. }
  250. }
  251. }
  252. return {
  253. dataList,
  254. getAvatar,
  255. getAvatarText,
  256. getAuthor,
  257. getDateDiff,
  258. commentHeight,
  259. allHeight,
  260. replyComment,
  261. sendComment,
  262. getMyname,
  263. getMyAvatar,
  264. focusStatus,
  265. showReply,
  266. deleteComment,
  267. getHtml,
  268. handleClickItem,
  269. bottomCommentRef,
  270. visibleChange
  271. };
  272. },
  273. });
  274. </script>
  275. <style lang="less" scoped>
  276. @ventSpace: zxm;
  277. .jeecg-comment-list {
  278. overflow: auto;
  279. /* border-bottom: 1px solid #eee;*/
  280. .inner-comment {
  281. width: 100%;
  282. padding: 0 10px;
  283. }
  284. .@{ventSpace}-comment {
  285. width: 100%;
  286. }
  287. }
  288. .comment-author {
  289. span {
  290. margin: 3px;
  291. }
  292. .comment-last-content {
  293. margin-left: 5px;
  294. &:hover{
  295. color: #1890ff;
  296. }
  297. }
  298. }
  299. .@{ventSpace}-list-items{
  300. .@{ventSpace}-list-item:last-child{
  301. margin-bottom: 46px;
  302. }
  303. }
  304. .tx{
  305. margin-top: 4px;
  306. }
  307. </style>