329 lines
9.3 KiB
Vue
329 lines
9.3 KiB
Vue
<template>
|
|
<div :style="{ position: 'relative', height: allHeight + 'px' }">
|
|
<a-list class="jeecg-comment-list" header="" item-layout="horizontal" :data-source="dataList" :style="{ height: commentHeight + 'px' }">
|
|
<template #renderItem="{ item }">
|
|
<a-list-item style="padding-left: 10px; flex-direction: column" @click="handleClickItem">
|
|
<a-comment>
|
|
<template #avatar>
|
|
<a-avatar class="tx" :src="getAvatar(item)" :alt="getAvatarText(item)">{{ getAvatarText(item) }}</a-avatar>
|
|
</template>
|
|
|
|
<template #author>
|
|
<div class="comment-author">
|
|
<span>{{ item.fromUserId_dictText }}</span>
|
|
|
|
<template v-if="item.toUserId">
|
|
<span>回复</span>
|
|
<span>{{ item.toUserId_dictText }}</span>
|
|
<Tooltip class="comment-last-content" @visibleChange="(v)=>visibleChange(v, item)">
|
|
<template #title>
|
|
<div v-html="getHtml(item.commentId_dictText)"></div>
|
|
</template>
|
|
<message-outlined />
|
|
</Tooltip>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<template #datetime>
|
|
<div>
|
|
<Tooltip :title="item.createTime">
|
|
<span>{{ getDateDiff(item) }}</span>
|
|
</Tooltip>
|
|
</div>
|
|
</template>
|
|
|
|
<template #actions>
|
|
<span @click="showReply(item)">回复</span>
|
|
|
|
<Popconfirm title="确定删除吗?" @confirm="deleteComment(item)">
|
|
<span>删除</span>
|
|
</Popconfirm>
|
|
</template>
|
|
|
|
<template #content>
|
|
<div v-html="getHtml(item.commentContent)" style="font-size: 15px">
|
|
</div>
|
|
|
|
<div v-if="item.fileList && item.fileList.length > 0">
|
|
<!-- 历史文件 -->
|
|
<history-file-list :dataList="item.fileList" isComment></history-file-list>
|
|
</div>
|
|
</template>
|
|
</a-comment>
|
|
<div v-if="item.commentStatus" class="inner-comment">
|
|
<my-comment inner @cancel="item.commentStatus = false" @comment="(content, fileList) => replyComment(item, content, fileList)" :inputFocus="focusStatus"></my-comment>
|
|
</div>
|
|
</a-list-item>
|
|
</template>
|
|
</a-list>
|
|
|
|
<div style="position: absolute; bottom: 0; left: 0; width: 100%; background: #fff; border-top: 1px solid #eee">
|
|
<a-comment style="margin: 0 10px">
|
|
<template #avatar>
|
|
<a-avatar class="tx" :src="getMyAvatar()" :alt="getMyname()">{{ getMyname() }}</a-avatar>
|
|
</template>
|
|
<template #content>
|
|
<my-comment ref="bottomCommentRef" @comment="sendComment" :inputFocus="focusStatus"></my-comment>
|
|
</template>
|
|
</a-comment>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
/**
|
|
* 评论列表
|
|
*/
|
|
import { defineComponent, ref, onMounted, watch, watchEffect } from 'vue';
|
|
import { propTypes } from '/@/utils/propTypes';
|
|
import dayjs from 'dayjs';
|
|
import 'dayjs/locale/zh.js';
|
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
|
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
dayjs.locale('zh');
|
|
dayjs.extend(relativeTime);
|
|
dayjs.extend(customParseFormat);
|
|
import { MessageOutlined } from '@ant-design/icons-vue';
|
|
import { Comment, Tooltip } from 'ant-design-vue';
|
|
import { useUserStore } from '/@/store/modules/user';
|
|
import MyComment from './MyComment.vue';
|
|
import { list, saveOne, deleteOne, useCommentWithFile, useEmojiHtml, queryById } from './useComment';
|
|
import { useMessage } from '/@/hooks/web/useMessage';
|
|
import HistoryFileList from './HistoryFileList.vue';
|
|
import { Popconfirm } from 'ant-design-vue';
|
|
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
|
|
|
|
export default defineComponent({
|
|
name: 'CommentList',
|
|
components: {
|
|
MessageOutlined,
|
|
AComment: Comment,
|
|
Tooltip,
|
|
MyComment,
|
|
Popconfirm,
|
|
HistoryFileList,
|
|
},
|
|
props: {
|
|
tableName: propTypes.string.def(''),
|
|
dataId: propTypes.string.def(''),
|
|
datetime: propTypes.number.def(1)
|
|
},
|
|
setup(props) {
|
|
const { createMessage } = useMessage();
|
|
const dataList = ref([]);
|
|
const { userInfo } = useUserStore();
|
|
/**
|
|
* 获取当前用户名称
|
|
*/
|
|
function getMyname() {
|
|
if (userInfo.realname) {
|
|
return userInfo.realname.substr(0, 2);
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function getMyAvatar(){
|
|
return userInfo.avatar;
|
|
}
|
|
|
|
// 获取头像
|
|
function getAvatar(item) {
|
|
if (item.fromUserAvatar) {
|
|
return getFileAccessHttpUrl(item.fromUserAvatar)
|
|
}
|
|
return '';
|
|
}
|
|
|
|
// 头像没有获取 用户名前两位
|
|
function getAvatarText(item){
|
|
if (item.fromUserId_dictText) {
|
|
return item.fromUserId_dictText.substr(0, 2);
|
|
}
|
|
return '未知';
|
|
}
|
|
|
|
function getAuthor(item) {
|
|
if (item.toUser) {
|
|
return item.fromUserId_dictText + ' 回复 ' + item.fromUserId_dictText;
|
|
} else {
|
|
return item.fromUserId_dictText;
|
|
}
|
|
}
|
|
|
|
function getDateDiff(item) {
|
|
if (item.createTime) {
|
|
const temp = dayjs(item.createTime, 'YYYY-MM-DD hh:mm:ss');
|
|
return temp.fromNow();
|
|
}
|
|
return '';
|
|
}
|
|
const commentHeight = ref(300);
|
|
const allHeight = ref(300);
|
|
onMounted(() => {
|
|
commentHeight.value = window.innerHeight - 57 - 46 - 70 - 160;
|
|
allHeight.value = window.innerHeight - 57 - 46 - 53 -20;
|
|
});
|
|
|
|
/**
|
|
* 加载数据
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function loadData() {
|
|
const params = {
|
|
tableName: props.tableName,
|
|
tableDataId: props.dataId,
|
|
column: 'createTime',
|
|
order: 'desc',
|
|
};
|
|
const data = await list(params);
|
|
if (!data || !data.records || data.records.length == 0) {
|
|
dataList.value = [];
|
|
} else {
|
|
let array = data.records;
|
|
console.log(123, array);
|
|
dataList.value = array;
|
|
}
|
|
}
|
|
|
|
const { saveCommentAndFiles } = useCommentWithFile(props);
|
|
// 回复
|
|
async function replyComment(item, content, fileList) {
|
|
console.log(content, item);
|
|
let obj = {
|
|
fromUserId: userInfo.id,
|
|
toUserId: item.fromUserId,
|
|
commentId: item.id,
|
|
commentContent: content
|
|
}
|
|
await saveCommentAndFiles(obj, fileList)
|
|
await loadData();
|
|
}
|
|
|
|
//评论
|
|
async function sendComment(content, fileList) {
|
|
let obj = {
|
|
fromUserId: userInfo.id,
|
|
commentContent: content
|
|
}
|
|
await saveCommentAndFiles(obj, fileList)
|
|
await loadData();
|
|
focusStatus.value = false;
|
|
setTimeout(()=>{
|
|
focusStatus.value = true;
|
|
},100)
|
|
}
|
|
|
|
//删除
|
|
async function deleteComment(item) {
|
|
const params = { id: item.id };
|
|
await deleteOne(params);
|
|
await loadData();
|
|
}
|
|
|
|
/**
|
|
* 打开回复时触发
|
|
* @type {Ref<UnwrapRef<boolean>>}
|
|
*/
|
|
const focusStatus = ref(false);
|
|
function showReply(item) {
|
|
let arr = dataList.value;
|
|
for (let temp of arr) {
|
|
temp.commentStatus = false;
|
|
}
|
|
item.commentStatus = true;
|
|
focusStatus.value = false;
|
|
focusStatus.value = true;
|
|
}
|
|
|
|
// 表单改变 -重新加载评论列表
|
|
watchEffect(() => {
|
|
if(props.datetime){
|
|
if (props.tableName && props.dataId) {
|
|
loadData();
|
|
}
|
|
}
|
|
});
|
|
|
|
const { getHtml } = useEmojiHtml();
|
|
const bottomCommentRef = ref()
|
|
function handleClickItem(){
|
|
bottomCommentRef.value.changeActive()
|
|
}
|
|
|
|
|
|
/**
|
|
* 根据id查询评论信息
|
|
*/
|
|
async function visibleChange(v, item){
|
|
if(v==true){
|
|
if(!item.commentId_dictText){
|
|
const data = await queryById(item.commentId);
|
|
if(data.success == true){
|
|
item.commentId_dictText = data.result.commentContent
|
|
}else{
|
|
console.error(data.message)
|
|
item.commentId_dictText='该评论已被删除';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
dataList,
|
|
getAvatar,
|
|
getAvatarText,
|
|
getAuthor,
|
|
getDateDiff,
|
|
commentHeight,
|
|
allHeight,
|
|
replyComment,
|
|
sendComment,
|
|
getMyname,
|
|
getMyAvatar,
|
|
|
|
focusStatus,
|
|
showReply,
|
|
deleteComment,
|
|
getHtml,
|
|
handleClickItem,
|
|
bottomCommentRef,
|
|
visibleChange
|
|
};
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.jeecg-comment-list {
|
|
overflow: auto;
|
|
/* border-bottom: 1px solid #eee;*/
|
|
.inner-comment {
|
|
width: 100%;
|
|
padding: 0 10px;
|
|
}
|
|
.ant-comment {
|
|
width: 100%;
|
|
}
|
|
}
|
|
.comment-author {
|
|
span {
|
|
margin: 3px;
|
|
}
|
|
.comment-last-content {
|
|
margin-left: 5px;
|
|
&:hover{
|
|
color: #1890ff;
|
|
}
|
|
}
|
|
}
|
|
.ant-list-items{
|
|
.ant-list-item:last-child{
|
|
margin-bottom: 46px;
|
|
}
|
|
}
|
|
.tx{
|
|
margin-top: 4px;
|
|
}
|
|
</style>
|