jeecgboot3.4.2版本发布,新功能(表单右侧评论功能)
This commit is contained in:
parent
78d182ba0c
commit
df0441c8f5
|
@ -0,0 +1,164 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-alert type="info" class="jeecg-comment-files">
|
||||||
|
<template #message>
|
||||||
|
<span class="j-icon">
|
||||||
|
<a-upload multiple v-model:file-list="selectFileList" :showUploadList="false" :before-upload="beforeUpload">
|
||||||
|
<span class="inner-button"><upload-outlined />上传</span>
|
||||||
|
</a-upload>
|
||||||
|
</span>
|
||||||
|
<span class="j-icon">
|
||||||
|
<span class="inner-button"><folder-outlined />从文件库选择?</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
|
||||||
|
<!-- 正在上传的文件 -->
|
||||||
|
<div class="selected-file-warp" v-if="selectFileList && selectFileList.length > 0">
|
||||||
|
<div class="selected-file-list">
|
||||||
|
<div class="item" v-for="item in selectFileList">
|
||||||
|
<div class="complex">
|
||||||
|
<div class="content" >
|
||||||
|
<!-- 图片 -->
|
||||||
|
<div v-if="isImage(item)" class="content-top" style="height: 100%">
|
||||||
|
<div class="content-image" :style="getImageAsBackground(item)">
|
||||||
|
<!-- <img style="height: 100%;" :src="getImageSrc(item)">-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 文件 -->
|
||||||
|
<template v-else>
|
||||||
|
<div class="content-top">
|
||||||
|
<div class="content-icon" :style="{ background: 'url(' + getBackground(item) + ') no-repeat' }"></div>
|
||||||
|
</div>
|
||||||
|
<div class="content-bottom" :title="item.name">
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="layer" :class="{'layer-image':isImage(item)}">
|
||||||
|
<div class="next" @click="viewImage(item)"><div class="text">{{ item.name }} </div></div>
|
||||||
|
<div class="buttons">
|
||||||
|
<div class="opt-icon">
|
||||||
|
<Tooltip title="删除">
|
||||||
|
<delete-outlined @click="handleRemove(item)" />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item empty"></div><div class="item empty"></div><div class="item empty"></div> <div class="item empty"></div><div class="item empty"></div><div class="item empty"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 24px; margin-top: 18px; text-align: right">
|
||||||
|
<a-button @click="quxiao">取消</a-button>
|
||||||
|
<a-button type="primary" style="margin-left: 10px" @click="queding" :loading="buttonLoading">确定</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 历史文件 -->
|
||||||
|
<history-file-list :dataList="dataList"></history-file-list>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { UploadOutlined, FolderOutlined, DownloadOutlined, PaperClipOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
|
import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue';
|
||||||
|
import { uploadFileUrl } from './useComment';
|
||||||
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
|
import { computed, watchEffect, unref, ref } from 'vue';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import { fileList } from './useComment';
|
||||||
|
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
|
||||||
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
import { saveOne, useCommentWithFile, useFileList } from './useComment';
|
||||||
|
|
||||||
|
import { Tooltip } from 'ant-design-vue';
|
||||||
|
import HistoryFileList from './HistoryFileList.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CommentFiles',
|
||||||
|
components: {
|
||||||
|
UploadOutlined,
|
||||||
|
FolderOutlined,
|
||||||
|
JUpload,
|
||||||
|
DownloadOutlined,
|
||||||
|
PaperClipOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
Tooltip,
|
||||||
|
HistoryFileList,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tableName: propTypes.string.def(''),
|
||||||
|
dataId: propTypes.string.def(''),
|
||||||
|
datetime: propTypes.number.def(1)
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
// const { createMessage } = useMessage();
|
||||||
|
const { userInfo } = useUserStore();
|
||||||
|
const dataList = ref([]);
|
||||||
|
const commentId = ref('');
|
||||||
|
|
||||||
|
async function loadFileList() {
|
||||||
|
const params = {
|
||||||
|
tableName: props.tableName,
|
||||||
|
tableDataId: props.dataId,
|
||||||
|
};
|
||||||
|
const data = await fileList(params);
|
||||||
|
console.log('1111', data)
|
||||||
|
if (!data || !data.records || data.records.length == 0) {
|
||||||
|
dataList.value = [];
|
||||||
|
} else {
|
||||||
|
let array = data.records;
|
||||||
|
console.log(123, array);
|
||||||
|
dataList.value = array;
|
||||||
|
}
|
||||||
|
commentId.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
// 每次切换tab都会刷新文件列表--- VUEN-1884 评论里上传的图片未在文件中显示
|
||||||
|
if(props.datetime){
|
||||||
|
if (props.tableName && props.dataId) {
|
||||||
|
loadFileList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { saveCommentAndFiles, buttonLoading } = useCommentWithFile(props);
|
||||||
|
const { selectFileList, beforeUpload, handleRemove, getBackground, isImage, getImageAsBackground, viewImage } = useFileList();
|
||||||
|
|
||||||
|
function quxiao() {
|
||||||
|
selectFileList.value = [];
|
||||||
|
}
|
||||||
|
async function queding() {
|
||||||
|
let obj = {
|
||||||
|
fromUserId: userInfo.id,
|
||||||
|
commentContent: '上传了附件'
|
||||||
|
}
|
||||||
|
await saveCommentAndFiles(obj, selectFileList.value)
|
||||||
|
selectFileList.value = [];
|
||||||
|
await loadFileList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectFileList,
|
||||||
|
beforeUpload,
|
||||||
|
handleRemove,
|
||||||
|
getBackground,
|
||||||
|
isImage,
|
||||||
|
dataList,
|
||||||
|
uploadFileUrl,
|
||||||
|
quxiao,
|
||||||
|
queding,
|
||||||
|
buttonLoading,
|
||||||
|
getImageAsBackground,
|
||||||
|
viewImage
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import 'comment.less';
|
||||||
|
</style>
|
|
@ -0,0 +1,328 @@
|
||||||
|
<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>
|
|
@ -0,0 +1,93 @@
|
||||||
|
<template>
|
||||||
|
<div class="comment-tabs-warp" v-if="showStatus">
|
||||||
|
<a-tabs @change="handleChange" :animated="false">
|
||||||
|
<a-tab-pane tab="评论" key="comment" class="comment-list-tab">
|
||||||
|
<comment-list :tableName="tableName" :dataId="dataId" :datetime="datetime1"></comment-list>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane tab="文件" key="file">
|
||||||
|
<comment-files :tableName="tableName" :dataId="dataId" :datetime="datetime2"></comment-files>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane tab="日志" key="log">
|
||||||
|
<data-log-list :tableName="tableName" :dataId="dataId" :datetime="datetime3"></data-log-list>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
<a-empty v-else description="新增页面不支持评论" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 评论区域
|
||||||
|
*/
|
||||||
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import CommentList from './CommentList.vue';
|
||||||
|
import CommentFiles from './CommentFiles.vue';
|
||||||
|
import DataLogList from './DataLogList.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CommentPanel',
|
||||||
|
components: {
|
||||||
|
CommentList,
|
||||||
|
CommentFiles,
|
||||||
|
DataLogList,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tableName: propTypes.string.def(''),
|
||||||
|
dataId: propTypes.string.def(''),
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const showStatus = computed(() => {
|
||||||
|
if (props.dataId && props.tableName) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const datetime1 = ref(1);
|
||||||
|
const datetime2 = ref(1);
|
||||||
|
const datetime3 = ref(1);
|
||||||
|
function handleChange(e) {
|
||||||
|
let temp = new Date().getTime();
|
||||||
|
if (e == 'comment') {
|
||||||
|
datetime1.value = temp;
|
||||||
|
} else if (e == 'file') {
|
||||||
|
datetime2.value = temp;
|
||||||
|
} else {
|
||||||
|
datetime3.value = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VUEN-1978【bug】online关联记录和他表字段存在问题 20 修改完数据,再次打开不切换tab的时候,修改日志没有变化
|
||||||
|
function reload() {
|
||||||
|
let temp = new Date().getTime();
|
||||||
|
datetime1.value = temp;
|
||||||
|
datetime2.value = temp;
|
||||||
|
datetime3.value = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
showStatus,
|
||||||
|
handleChange,
|
||||||
|
datetime1,
|
||||||
|
datetime2,
|
||||||
|
datetime3,
|
||||||
|
reload
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.comment-tabs-warp {
|
||||||
|
height: 100%;
|
||||||
|
overflow: visible;
|
||||||
|
> .ant-tabs {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//antd3升级后,表单右侧讨论样式调整
|
||||||
|
::v-deep(.ant-tabs-top .ant-tabs-nav, .ant-tabs-bottom .ant-tabs-nav, .ant-tabs-top div .ant-tabs-nav, .ant-tabs-bottom div .ant-tabs-nav) {
|
||||||
|
margin: 0 16px 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,177 @@
|
||||||
|
<template>
|
||||||
|
<div class="data-log-scroll" :style="{'height': height+'px'}">
|
||||||
|
<div class="data-log-content">
|
||||||
|
<div class="logbox">
|
||||||
|
|
||||||
|
<div class="log-item" v-for="(item, index) in dataList">
|
||||||
|
<span class="log-item-icon">
|
||||||
|
<plus-outlined v-if="lastIndex == index" style="margin-top:3px"/>
|
||||||
|
<edit-outlined v-else/>
|
||||||
|
</span>
|
||||||
|
<span class="log-item-content">
|
||||||
|
<a @click="handleClickPerson">@{{item.createBy}}</a>
|
||||||
|
{{ item.dataContent }}
|
||||||
|
</span>
|
||||||
|
<div class="log-item-date">
|
||||||
|
<Tooltip :title="item.createTime">
|
||||||
|
<span>{{ getDateDiff(item) }}</span>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { PlusOutlined, EditOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { getModalHeight, getLogList } from './useComment'
|
||||||
|
import {ref, watchEffect} from 'vue'
|
||||||
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
|
import { Tooltip } from 'ant-design-vue';
|
||||||
|
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);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "DataLogList",
|
||||||
|
components:{
|
||||||
|
PlusOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
Tooltip
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tableName: propTypes.string.def(''),
|
||||||
|
dataId: propTypes.string.def(''),
|
||||||
|
datetime: propTypes.number.def(1),
|
||||||
|
},
|
||||||
|
setup(props){
|
||||||
|
const winHeight = getModalHeight();
|
||||||
|
const height = ref(300);
|
||||||
|
height.value = winHeight - 46 - 57 -53 - 30;
|
||||||
|
|
||||||
|
const dataList = ref([]);
|
||||||
|
const lastIndex = ref(0);
|
||||||
|
/**
|
||||||
|
* 加载数据
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async function loadData() {
|
||||||
|
const params = {
|
||||||
|
dataTable: props.tableName,
|
||||||
|
dataId: props.dataId,
|
||||||
|
type: 'comment'
|
||||||
|
};
|
||||||
|
const res = await getLogList(params);
|
||||||
|
if (!res || !res.result || res.result.length == 0) {
|
||||||
|
dataList.value = [];
|
||||||
|
lastIndex.value = -1;
|
||||||
|
} else {
|
||||||
|
let arr = res.result;
|
||||||
|
lastIndex.value = arr.length-1;
|
||||||
|
console.log('log-list', arr);
|
||||||
|
dataList.value = arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if(props.datetime){
|
||||||
|
if (props.tableName && props.dataId) {
|
||||||
|
console.log(props.tableName, props.dataId)
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function getDateDiff(item) {
|
||||||
|
if (item.createTime) {
|
||||||
|
const temp = dayjs(item.createTime, 'YYYY-MM-DD hh:mm:ss');
|
||||||
|
return temp.fromNow();
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClickPerson() {
|
||||||
|
console.log('此功能未开放')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
height,
|
||||||
|
lastIndex,
|
||||||
|
dataList,
|
||||||
|
getDateDiff,
|
||||||
|
handleClickPerson
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.data-log-scroll{
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
overflow-y: auto;
|
||||||
|
.data-log-content{
|
||||||
|
/* right: -10px;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
overflow: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;*/
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
.logbox{
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-left: 16px;
|
||||||
|
.log-item{
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #9e9e9e;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 25px;
|
||||||
|
position: relative;
|
||||||
|
.log-item-icon{
|
||||||
|
left: 0;
|
||||||
|
line-height: 16px;
|
||||||
|
position: absolute;
|
||||||
|
top: 3px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.log-item-content{
|
||||||
|
word-wrap: break-word;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 13px;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 100%;
|
||||||
|
word-break: break-word;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.log-item-date{
|
||||||
|
word-wrap: break-word;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 13px;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 100%;
|
||||||
|
word-break: break-word;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,88 @@
|
||||||
|
<template>
|
||||||
|
<div class="comment-file-his-list" :class="isComment === true ? 'in-comment' : ''">
|
||||||
|
<div class="selected-file-list">
|
||||||
|
<div class="item" v-for="item in dataList">
|
||||||
|
<div class="complex">
|
||||||
|
<div class="content">
|
||||||
|
<!-- 图片 -->
|
||||||
|
<div v-if="isImage(item)" class="content-top" style="height: 100%">
|
||||||
|
<div class="content-image" :style="getImageAsBackground(item)">
|
||||||
|
<!--<img style="height: 100%;" :src="getImageSrc(item)"/>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 文件 -->
|
||||||
|
<template v-else>
|
||||||
|
<div class="content-top">
|
||||||
|
<div class="content-icon" :style="{ background: 'url(' + getBackground(item) + ') no-repeat' }"></div>
|
||||||
|
</div>
|
||||||
|
<div class="content-bottom" :title="item.name">
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="layer" :class="{'layer-image':isImage(item)}">
|
||||||
|
<div class="next" @click="viewImage(item)">
|
||||||
|
<div class="text">
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
|
<div class="text">
|
||||||
|
{{ getFileSize(item) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<div class="opt-icon">
|
||||||
|
<Tooltip title="下载">
|
||||||
|
<download-outlined @click="downLoad(item)" />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item empty"></div><div class="item empty"></div><div class="item empty"></div> <div class="item empty"></div><div class="item empty"></div><div class="item empty"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Tooltip } from 'ant-design-vue';
|
||||||
|
import { UploadOutlined, FolderOutlined, DownloadOutlined, PaperClipOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { useFileList } from './useComment';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HistoryFileList',
|
||||||
|
props: {
|
||||||
|
dataList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
isComment: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
UploadOutlined,
|
||||||
|
FolderOutlined,
|
||||||
|
DownloadOutlined,
|
||||||
|
PaperClipOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
Tooltip,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const { getBackground, getFileSize, downLoad, isImage, getImageAsBackground, viewImage } = useFileList();
|
||||||
|
return {
|
||||||
|
getBackground,
|
||||||
|
downLoad,
|
||||||
|
getFileSize,
|
||||||
|
isImage,
|
||||||
|
getImageAsBackground,
|
||||||
|
viewImage
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import 'comment.less';
|
||||||
|
</style>
|
|
@ -0,0 +1,383 @@
|
||||||
|
<template>
|
||||||
|
<div :class="{'comment-active': commentActive}" style="border: 1px solid #eee; margin: 0; position: relative" @click="handleClickBlank">
|
||||||
|
<textarea ref="commentRef" v-model="myComment" @input="handleCommentChange" @blur="handleBlur" class="comment-content" :rows="3" placeholder="请输入你的评论,可以@成员" />
|
||||||
|
<div class="comment-content comment-html-shower" :class="{'no-content':noConent, 'top-div': showHtml, 'bottom-div': showHtml == false }" v-html="commentHtml" @click="handleClickHtmlShower"></div>
|
||||||
|
<div class="comment-buttons" v-if="commentActive">
|
||||||
|
<div style="cursor: pointer">
|
||||||
|
<Tooltip title="选择@用户">
|
||||||
|
<user-add-outlined @click="openSelectUser" />
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip title="上传附件">
|
||||||
|
<PaperClipOutlined @click="uploadVisible = !uploadVisible" />
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<span title="表情" style="display: inline-block">
|
||||||
|
<SmileOutlined ref="emojiButton" @click="handleShowEmoji" />
|
||||||
|
<div style="position: relative" v-show=""> </div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="commentActive">
|
||||||
|
<a-button v-if="inner" @click="noComment" style="margin-right: 10px">取消</a-button>
|
||||||
|
<a-button type="primary" @click="sendComment" :loading="buttonLoading" :disabled="disabledButton">发 送</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<upload-chunk ref="uploadRef" :visible="uploadVisible" @select="selectFirstFile"></upload-chunk>
|
||||||
|
</div>
|
||||||
|
<UserSelectModal labelKey="realname" rowKey="username" @register="registerModal" @getSelectResult="setValue" isRadioSelection></UserSelectModal>
|
||||||
|
<a-modal v-model:visible="visibleEmoji" :footer="null" wrapClassName="emoji-modal" :closable="false" :width="490">
|
||||||
|
<template #title>
|
||||||
|
<span></span>
|
||||||
|
</template>
|
||||||
|
<Picker
|
||||||
|
:pickerStyles="pickerStyles"
|
||||||
|
:i18n="optionsName"
|
||||||
|
:data="emojiIndex"
|
||||||
|
emoji="grinning"
|
||||||
|
:showPreview="false"
|
||||||
|
:infiniteScroll="false"
|
||||||
|
:showSearch="false"
|
||||||
|
:showSkinTones="false"
|
||||||
|
set="apple"
|
||||||
|
@select="showEmoji">
|
||||||
|
</Picker>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { ref, watch, computed } from 'vue';
|
||||||
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
|
import { UserAddOutlined, PaperClipOutlined, SmileOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { Tooltip } from 'ant-design-vue';
|
||||||
|
import UserSelectModal from '/@/components/Form/src/jeecg/components/modal/UserSelectModal.vue';
|
||||||
|
import { useModal } from '/@/components/Modal';
|
||||||
|
import UploadChunk from './UploadChunk.vue';
|
||||||
|
import { Picker } from 'emoji-mart-vue-fast/src';
|
||||||
|
import 'emoji-mart-vue-fast/css/emoji-mart.css';
|
||||||
|
import { useEmojiHtml } from './useComment';
|
||||||
|
|
||||||
|
const optionsName = {
|
||||||
|
categories: {
|
||||||
|
recent: '最常用的',
|
||||||
|
smileys: '表情选择',
|
||||||
|
people: '人物&身体',
|
||||||
|
nature: '动物&自然',
|
||||||
|
foods: '食物&饮料',
|
||||||
|
activity: '活动',
|
||||||
|
places: '旅行&地点',
|
||||||
|
objects: '物品',
|
||||||
|
symbols: '符号',
|
||||||
|
flags: '旗帜',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default {
|
||||||
|
name: 'MyComment',
|
||||||
|
components: {
|
||||||
|
UserAddOutlined,
|
||||||
|
Tooltip,
|
||||||
|
UserSelectModal,
|
||||||
|
PaperClipOutlined,
|
||||||
|
UploadChunk,
|
||||||
|
SmileOutlined,
|
||||||
|
Picker,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
inner: propTypes.bool.def(false),
|
||||||
|
inputFocus: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['cancel', 'comment'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const uploadVisible = ref(false);
|
||||||
|
const uploadRef = ref();
|
||||||
|
//注册model
|
||||||
|
const [registerModal, { openModal }] = useModal();
|
||||||
|
const buttonLoading = ref(false);
|
||||||
|
const myComment = ref<string>('');
|
||||||
|
function sendComment() {
|
||||||
|
console.log(myComment.value);
|
||||||
|
let content = myComment.value;
|
||||||
|
if (!content && content !== '0') {
|
||||||
|
disabledButton.value = true;
|
||||||
|
} else {
|
||||||
|
buttonLoading.value = true;
|
||||||
|
let fileList = [];
|
||||||
|
if (uploadVisible.value == true) {
|
||||||
|
fileList = uploadRef.value.getUploadFileList();
|
||||||
|
}
|
||||||
|
emit('comment', content, fileList);
|
||||||
|
setTimeout(() => {
|
||||||
|
buttonLoading.value = false;
|
||||||
|
}, 350);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const disabledButton = ref(false);
|
||||||
|
watch(myComment, () => {
|
||||||
|
let content = myComment.value;
|
||||||
|
if (!content && content !== '0') {
|
||||||
|
disabledButton.value = true;
|
||||||
|
} else {
|
||||||
|
disabledButton.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function noComment() {
|
||||||
|
emit('cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
const commentRef = ref();
|
||||||
|
watch(
|
||||||
|
() => props.inputFocus,
|
||||||
|
(val) => {
|
||||||
|
if (val == true) {
|
||||||
|
// commentRef.value.focus()
|
||||||
|
myComment.value = '';
|
||||||
|
if (uploadVisible.value == true) {
|
||||||
|
uploadRef.value.clear();
|
||||||
|
uploadVisible.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
function openSelectUser() {
|
||||||
|
openModal(true, {
|
||||||
|
isUpdate: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function setValue(options) {
|
||||||
|
console.log('setValue', options);
|
||||||
|
if (options && options.length > 0) {
|
||||||
|
const { label, value } = options[0];
|
||||||
|
if (label && value) {
|
||||||
|
let str = `${label}[${value}]`;
|
||||||
|
let temp = myComment.value;
|
||||||
|
if (!temp) {
|
||||||
|
myComment.value = '@' + str;
|
||||||
|
} else {
|
||||||
|
if (temp.endsWith('@')) {
|
||||||
|
myComment.value = temp + str;
|
||||||
|
} else {
|
||||||
|
myComment.value = '@' + str + ' ' + temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCommentChange() {
|
||||||
|
//console.log(1,e)
|
||||||
|
}
|
||||||
|
watch(
|
||||||
|
() => myComment.value,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.endsWith('@')) {
|
||||||
|
openSelectUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emojiButton = ref();
|
||||||
|
function onSelectEmoji(emoji) {
|
||||||
|
let temp = myComment.value || '';
|
||||||
|
temp += emoji;
|
||||||
|
myComment.value = temp;
|
||||||
|
emojiButton.value.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
const visibleEmoji = ref(false);
|
||||||
|
function showEmoji(e) {
|
||||||
|
let temp = myComment.value || '';
|
||||||
|
let str = e.colons;
|
||||||
|
if (str.indexOf('::') > 0) {
|
||||||
|
str = str.substring(0, str.indexOf(':') + 1);
|
||||||
|
}
|
||||||
|
myComment.value = temp + str;
|
||||||
|
visibleEmoji.value = false;
|
||||||
|
handleBlur();
|
||||||
|
}
|
||||||
|
|
||||||
|
const pickerStyles = {
|
||||||
|
width: '490px'
|
||||||
|
/* height: '350px',
|
||||||
|
top: '0px',
|
||||||
|
left: '-75px',
|
||||||
|
position: 'absolute',
|
||||||
|
'z-index': 9999*/
|
||||||
|
};
|
||||||
|
function handleClickBlank(e) {
|
||||||
|
console.log('handleClickBlank');
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
visibleEmoji.value = false;
|
||||||
|
commentActive.value = true;
|
||||||
|
}
|
||||||
|
function handleShowEmoji(e) {
|
||||||
|
console.log('handleShowEmoji');
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
visibleEmoji.value = !visibleEmoji.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { emojiIndex, getHtml } = useEmojiHtml();
|
||||||
|
|
||||||
|
const commentHtml = computed(() => {
|
||||||
|
let temp = myComment.value;
|
||||||
|
if (!temp) {
|
||||||
|
return '请输入你的评论,可以@成员';
|
||||||
|
}
|
||||||
|
return getHtml(temp);
|
||||||
|
});
|
||||||
|
|
||||||
|
const showHtml = ref(false);
|
||||||
|
function handleClickHtmlShower(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
showHtml.value = false;
|
||||||
|
commentRef.value.focus();
|
||||||
|
console.log(234);
|
||||||
|
commentActive.value = true;
|
||||||
|
}
|
||||||
|
function handleBlur() {
|
||||||
|
showHtml.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const commentActive = ref(false);
|
||||||
|
const noConent = computed(()=>{
|
||||||
|
if(myComment.value.length>0){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
function changeActive(){
|
||||||
|
if(myComment.value.length==0){
|
||||||
|
commentActive.value = false
|
||||||
|
uploadVisible.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectFirstFile(fileName){
|
||||||
|
if(myComment.value.length==0){
|
||||||
|
myComment.value = fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
myComment,
|
||||||
|
sendComment,
|
||||||
|
noComment,
|
||||||
|
disabledButton,
|
||||||
|
buttonLoading,
|
||||||
|
commentRef,
|
||||||
|
registerModal,
|
||||||
|
openSelectUser,
|
||||||
|
setValue,
|
||||||
|
handleCommentChange,
|
||||||
|
uploadRef,
|
||||||
|
uploadVisible,
|
||||||
|
onSelectEmoji,
|
||||||
|
optionsName,
|
||||||
|
emojiButton,
|
||||||
|
emojiIndex,
|
||||||
|
showEmoji,
|
||||||
|
pickerStyles,
|
||||||
|
visibleEmoji,
|
||||||
|
handleClickBlank,
|
||||||
|
handleShowEmoji,
|
||||||
|
commentHtml,
|
||||||
|
showHtml,
|
||||||
|
handleClickHtmlShower,
|
||||||
|
handleBlur,
|
||||||
|
commentActive,
|
||||||
|
noConent,
|
||||||
|
changeActive,
|
||||||
|
selectFirstFile
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.comment-content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-variant: tabular-nums;
|
||||||
|
list-style: none;
|
||||||
|
font-feature-settings: tnum;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 4px 11px;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.5715;
|
||||||
|
background-color: #fff;
|
||||||
|
background-image: none;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
width: 100%;
|
||||||
|
border: solid 0px;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
.emoji-item {
|
||||||
|
display: inline-block !important;
|
||||||
|
width: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.comment-buttons {
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-top: 1px solid #d9d9d9;
|
||||||
|
.anticon {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.comment-html-shower {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 70px;
|
||||||
|
&.bottom-div {
|
||||||
|
z-index: -99;
|
||||||
|
}
|
||||||
|
&.top-div {
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-modal {
|
||||||
|
> .ant-modal{
|
||||||
|
right: 25% !important;
|
||||||
|
margin-right: 16px !important;
|
||||||
|
}
|
||||||
|
.ant-modal-header{
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.emoji-mart-bar{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
h3.emoji-mart-category-label{
|
||||||
|
/* display: none;*/
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-active{
|
||||||
|
border-color: #1e88e5 !important;
|
||||||
|
box-shadow: 0 1px 1px 0 #90caf9, 0 1px 6px 0 #90caf9;
|
||||||
|
}
|
||||||
|
.no-content{
|
||||||
|
color: #a1a1a1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**聊天表情本地化*/
|
||||||
|
.emoji-type-image.emoji-set-apple {
|
||||||
|
background-image: url("./image/emoji.png");
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,119 @@
|
||||||
|
<template>
|
||||||
|
<div v-if="visible">
|
||||||
|
<a-alert type="info" class="jeecg-comment-files" style="margin: 0">
|
||||||
|
<template #message>
|
||||||
|
<span class="j-icon">
|
||||||
|
<a-upload multiple v-model:file-list="selectFileList" :showUploadList="false" :before-upload="beforeUpload">
|
||||||
|
<span class="inner-button"><upload-outlined />上传</span>
|
||||||
|
</a-upload>
|
||||||
|
</span>
|
||||||
|
<span class="j-icon">
|
||||||
|
<span class="inner-button"><folder-outlined />从文件库选择?</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
|
||||||
|
<!-- 正在上传的文件 -->
|
||||||
|
<div class="selected-file-warp" v-if="selectFileList && selectFileList.length > 0">
|
||||||
|
<div class="selected-file-list">
|
||||||
|
<div class="item" v-for="item in selectFileList">
|
||||||
|
<div class="complex">
|
||||||
|
<div class="content">
|
||||||
|
<!-- 图片 -->
|
||||||
|
<div v-if="isImage(item)" class="content-top" style="height: 100%">
|
||||||
|
<div class="content-image" :style="{'height':'100%', 'backgroundImage': 'url('+getImageSrc(item)+')'}">
|
||||||
|
<!-- <img style="height: 100%;" :src="getImageSrc(item)">-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 文件 -->
|
||||||
|
<template v-else>
|
||||||
|
<div class="content-top">
|
||||||
|
<div class="content-icon" :style="{ background: 'url(' + getBackground(item) + ') no-repeat' }"></div>
|
||||||
|
</div>
|
||||||
|
<div class="content-bottom" :title="item.name">
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="layer" :class="{'layer-image':isImage(item)}">
|
||||||
|
<div class="next" @click="viewImage(item)">
|
||||||
|
<div class="text">{{ item.name }} </div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<div class="opt-icon">
|
||||||
|
<Tooltip title="删除">
|
||||||
|
<delete-outlined @click="handleRemove(item)" />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item empty"></div><div class="item empty"></div><div class="item empty"></div> <div class="item empty"></div><div class="item empty"></div><div class="item empty"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { toRaw, watch } from 'vue';
|
||||||
|
import { useFileList } from './useComment';
|
||||||
|
import { Tooltip } from 'ant-design-vue';
|
||||||
|
import { UploadOutlined, FolderOutlined, DownloadOutlined, PaperClipOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
|
export default {
|
||||||
|
name: 'UploadChunk',
|
||||||
|
components: {
|
||||||
|
Tooltip,
|
||||||
|
UploadOutlined,
|
||||||
|
FolderOutlined,
|
||||||
|
DownloadOutlined,
|
||||||
|
PaperClipOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits:['select'],
|
||||||
|
setup(_p, {emit}) {
|
||||||
|
const { selectFileList, beforeUpload, handleRemove, getBackground, isImage, getImageSrc, viewImage } = useFileList();
|
||||||
|
|
||||||
|
function getUploadFileList() {
|
||||||
|
let list = toRaw(selectFileList.value);
|
||||||
|
console.log(list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear(){
|
||||||
|
selectFileList.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(()=>selectFileList.value, (arr)=>{
|
||||||
|
if(arr && arr.length>0){
|
||||||
|
let name = arr[0].name;
|
||||||
|
if(name){
|
||||||
|
emit('select', name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectFileList,
|
||||||
|
beforeUpload,
|
||||||
|
handleRemove,
|
||||||
|
getBackground,
|
||||||
|
getUploadFileList,
|
||||||
|
clear,
|
||||||
|
isImage,
|
||||||
|
getImageSrc,
|
||||||
|
viewImage
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import 'comment.less';
|
||||||
|
</style>
|
|
@ -0,0 +1,234 @@
|
||||||
|
/*文件上传列表-begin*/
|
||||||
|
.selected-file-warp,
|
||||||
|
.comment-file-his-list {
|
||||||
|
margin: 10px 20px;
|
||||||
|
&.in-comment{
|
||||||
|
margin: 10px 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.selected-file-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-right: -6px;
|
||||||
|
.item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
flex: 1 1 0%;
|
||||||
|
height: 118px;
|
||||||
|
margin: 0 6px 6px 0;
|
||||||
|
min-width: 140px;
|
||||||
|
max-width: 200px;
|
||||||
|
width: 150px;
|
||||||
|
&.empty {
|
||||||
|
height: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.complex {
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
.content-top {
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 0%;
|
||||||
|
justify-content: center;
|
||||||
|
.content-icon {
|
||||||
|
background-position: 50%;
|
||||||
|
background-size: contain !important;
|
||||||
|
height: 55px;
|
||||||
|
width: 40px;
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: left;
|
||||||
|
text-indent: -9999px;
|
||||||
|
}
|
||||||
|
.content-image{
|
||||||
|
background-position: 50%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content-bottom {
|
||||||
|
align-items: center;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-basis: 30px;
|
||||||
|
font-size: 13px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 0 10px;
|
||||||
|
span {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.layer {
|
||||||
|
opacity: 0;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
width: 100%;
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.next {
|
||||||
|
height: 75px;
|
||||||
|
padding: 5px;
|
||||||
|
.text {
|
||||||
|
color: #1e88e5 !important;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-basis: 30px;
|
||||||
|
font-size: 12px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 3px 7px 4px;
|
||||||
|
word-break: break-all;
|
||||||
|
display: -webkit-box;
|
||||||
|
line-height: 14px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttons {
|
||||||
|
flex-basis: 32px;
|
||||||
|
text-align: right;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
padding-right: 5px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
.opt-icon {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 24px;
|
||||||
|
width: 32px;
|
||||||
|
margin: 5px;
|
||||||
|
text-align: center;
|
||||||
|
.anticon-delete:hover {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.anticon-download:hover{
|
||||||
|
color: #1e88e5 !important
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.layer-image{
|
||||||
|
background: #000;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
.next{
|
||||||
|
.text{
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.opt-icon{
|
||||||
|
color: #000 !important;
|
||||||
|
.anticon-delete:hover {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.jeecg-comment-files {
|
||||||
|
margin: 0 20px;
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
&.ant-alert-info{
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border: 1px solid #f5f5f5;
|
||||||
|
}
|
||||||
|
.j-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid #e6f7ff;
|
||||||
|
padding: 2px 7px;
|
||||||
|
margin: 0 10px;
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
border-color: #fff;
|
||||||
|
color: #096dd9;
|
||||||
|
}
|
||||||
|
.inner-button {
|
||||||
|
display: inline-block;
|
||||||
|
color:#9e9e9e;
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
/*border-color: #fff;*/
|
||||||
|
/* color: #096dd9;*/
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
span{
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-file-list {
|
||||||
|
.detail-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
line-height: 24px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.item-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
min-width: 100px;
|
||||||
|
width: 20%;
|
||||||
|
max-width: 220px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
border-right: 1px solid #f0f0f0;
|
||||||
|
/* border-left: 1px solid #f0f0f0;*/
|
||||||
|
padding: 10px 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-content {
|
||||||
|
border-right: 1px solid #f0f0f0;
|
||||||
|
flex-grow: 1;
|
||||||
|
padding-left: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
.anticon {
|
||||||
|
&:hover {
|
||||||
|
color: #40a9ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 839 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.2 MiB |
|
@ -0,0 +1,416 @@
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useGlobSetting } from '/@/hooks/setting';
|
||||||
|
const globSetting = useGlobSetting();
|
||||||
|
const baseUploadUrl = globSetting.uploadUrl;
|
||||||
|
import { ref, toRaw, unref, reactive } from 'vue';
|
||||||
|
import { uploadMyFile } from '/@/api/common/api';
|
||||||
|
|
||||||
|
import excel from '/@/assets/svg/fileType/excel.svg';
|
||||||
|
import other from '/@/assets/svg/fileType/other.svg';
|
||||||
|
import pdf from '/@/assets/svg/fileType/pdf.svg';
|
||||||
|
import txt from '/@/assets/svg/fileType/txt.svg';
|
||||||
|
import word from '/@/assets/svg/fileType/word.svg';
|
||||||
|
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
|
||||||
|
import { createImgPreview } from '/@/components/Preview';
|
||||||
|
import {EmojiIndex} from "emoji-mart-vue-fast/src";
|
||||||
|
import data from "emoji-mart-vue-fast/data/apple.json";
|
||||||
|
|
||||||
|
enum Api {
|
||||||
|
list = '/sys/comment/listByForm',
|
||||||
|
addText = '/sys/comment/addText',
|
||||||
|
deleteOne = '/sys/comment/deleteOne',
|
||||||
|
fileList = '/sys/comment/fileList',
|
||||||
|
logList = '/sys/dataLog/queryDataVerList',
|
||||||
|
queryById = '/sys/comment/queryById',
|
||||||
|
getFileViewDomain = '/sys/comment/getFileViewDomain',
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件预览地址的domain 在后台配置的
|
||||||
|
let onlinePreviewDomain = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件预览的domain
|
||||||
|
*/
|
||||||
|
const getViewFileDomain = () => defHttp.get({ url: Api.getFileViewDomain });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表接口
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询单条记录
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const queryById = (id) => {
|
||||||
|
let params = { id: id };
|
||||||
|
return defHttp.get({ url: Api.queryById, params },{ isTransformResponse: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件列表接口
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const fileList = (params) => defHttp.get({ url: Api.fileList, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除单个
|
||||||
|
*/
|
||||||
|
export const deleteOne = (params) => {
|
||||||
|
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const saveOne = (params) => {
|
||||||
|
let url = Api.addText;
|
||||||
|
return defHttp.post({ url: url, params }, { isTransformResponse: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据日志列表接口
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getLogList = (params) => defHttp.get({ url: Api.logList, params }, {isTransformResponse: false});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传接口
|
||||||
|
*/
|
||||||
|
export const uploadFileUrl = `${baseUploadUrl}/sys/comment/addFile`;
|
||||||
|
|
||||||
|
export function useCommentWithFile(props) {
|
||||||
|
let uploadData = {
|
||||||
|
biz: 'comment',
|
||||||
|
commentId: '',
|
||||||
|
};
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const buttonLoading = ref(false);
|
||||||
|
|
||||||
|
//确定按钮触发
|
||||||
|
async function saveCommentAndFiles(obj, fileList) {
|
||||||
|
buttonLoading.value = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
buttonLoading.value = false;
|
||||||
|
}, 500);
|
||||||
|
await saveComment(obj);
|
||||||
|
await uploadFiles(fileList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存评论
|
||||||
|
*/
|
||||||
|
async function saveComment(obj) {
|
||||||
|
const {fromUserId, toUserId, commentId, commentContent} = obj;
|
||||||
|
let commentData = {
|
||||||
|
tableName: props.tableName,
|
||||||
|
tableDataId: props.dataId,
|
||||||
|
fromUserId,
|
||||||
|
commentContent,
|
||||||
|
toUserId: '',
|
||||||
|
commentId: ''
|
||||||
|
};
|
||||||
|
if(toUserId){
|
||||||
|
commentData.toUserId = toUserId;
|
||||||
|
}
|
||||||
|
if(commentId){
|
||||||
|
commentData.commentId = commentId;
|
||||||
|
}
|
||||||
|
uploadData.commentId = '';
|
||||||
|
const res = await saveOne(commentData);
|
||||||
|
if (res.success) {
|
||||||
|
uploadData.commentId = res.result;
|
||||||
|
} else {
|
||||||
|
createMessage.warning(res.message);
|
||||||
|
return Promise.reject('保存评论失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadOne(file) {
|
||||||
|
let url = uploadFileUrl;
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
formData.append('tableName', props.tableName);
|
||||||
|
formData.append('tableDataId', props.dataId);
|
||||||
|
Object.keys(uploadData).map((k) => {
|
||||||
|
formData.append(k, uploadData[k]);
|
||||||
|
});
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uploadMyFile(url, formData).then((res: any) => {
|
||||||
|
console.log('uploadMyFile', res);
|
||||||
|
if (res && res.data) {
|
||||||
|
if (res.data.result == 'success') {
|
||||||
|
resolve(1);
|
||||||
|
} else {
|
||||||
|
createMessage.warning(res.data.message);
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadFiles(fileList) {
|
||||||
|
if (fileList && fileList.length > 0) {
|
||||||
|
for (let i = 0; i < fileList.length; i++) {
|
||||||
|
let file = toRaw(fileList[i]);
|
||||||
|
await uploadOne(file.originFileObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
saveCommentAndFiles,
|
||||||
|
buttonLoading,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function uploadMu(fileList) {
|
||||||
|
const formData = new FormData();
|
||||||
|
// let arr = []
|
||||||
|
for(let file of fileList){
|
||||||
|
formData.append('files[]', file.originFileObj);
|
||||||
|
}
|
||||||
|
console.log(formData)
|
||||||
|
let url = `${baseUploadUrl}/sys/comment/addFile2`;
|
||||||
|
uploadMyFile(url, formData).then((res: any) => {
|
||||||
|
console.log('uploadMyFile', res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示文件列表
|
||||||
|
*/
|
||||||
|
export function useFileList() {
|
||||||
|
const imageSrcMap = reactive({});
|
||||||
|
const typeMap = {
|
||||||
|
xls: excel,
|
||||||
|
xlsx: excel,
|
||||||
|
pdf: pdf,
|
||||||
|
txt: txt,
|
||||||
|
docx: word,
|
||||||
|
doc: word,
|
||||||
|
};
|
||||||
|
function getBackground(item) {
|
||||||
|
console.log('获取文件背景图', item);
|
||||||
|
if (isImage(item)) {
|
||||||
|
return 'none'
|
||||||
|
} else {
|
||||||
|
const name = item.name;
|
||||||
|
if(!name){
|
||||||
|
return 'none';
|
||||||
|
}
|
||||||
|
const suffix = name.substring(name.lastIndexOf('.') + 1);
|
||||||
|
console.log('suffix', suffix)
|
||||||
|
let bg = typeMap[suffix];
|
||||||
|
if (!bg) {
|
||||||
|
bg = other;
|
||||||
|
}
|
||||||
|
return bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBase64(file, id){
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
//声明js的文件流
|
||||||
|
let reader = new FileReader();
|
||||||
|
if(file){
|
||||||
|
//通过文件流将文件转换成Base64字符串
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
//转换成功后
|
||||||
|
reader.onload = function () {
|
||||||
|
let base = reader.result;
|
||||||
|
console.log('base', base)
|
||||||
|
imageSrcMap[id] = base;
|
||||||
|
console.log('imageSrcMap', imageSrcMap)
|
||||||
|
resolve(base)
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function handleImageSrc(file){
|
||||||
|
if(isImage(file)){
|
||||||
|
let id = file.uid;
|
||||||
|
getBase64(file, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function downLoad(file) {
|
||||||
|
let url = getFileAccessHttpUrl(file.url);
|
||||||
|
if (url) {
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileSize(item) {
|
||||||
|
let size = item.fileSize;
|
||||||
|
if (!size) {
|
||||||
|
return '0B';
|
||||||
|
}
|
||||||
|
let temp = Math.round(size / 1024);
|
||||||
|
return temp + ' KB';
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectFileList = ref<any[]>([]);
|
||||||
|
function beforeUpload(file) {
|
||||||
|
handleImageSrc(file);
|
||||||
|
selectFileList.value = [...selectFileList.value, file];
|
||||||
|
console.log('selectFileList', unref(selectFileList));
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemove(file) {
|
||||||
|
const index = selectFileList.value.indexOf(file);
|
||||||
|
const newFileList = selectFileList.value.slice();
|
||||||
|
newFileList.splice(index, 1);
|
||||||
|
selectFileList.value = newFileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isImage(item){
|
||||||
|
const type = item.type||'';
|
||||||
|
if (type.indexOf('image') >= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getImageSrc(file){
|
||||||
|
if(isImage(file)){
|
||||||
|
let id = file.uid;
|
||||||
|
if(id){
|
||||||
|
if(imageSrcMap[id]){
|
||||||
|
return imageSrcMap[id];
|
||||||
|
}
|
||||||
|
}else if(file.url){
|
||||||
|
//数据库中地址
|
||||||
|
let url = getFileAccessHttpUrl(file.url);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示图片
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
function getImageAsBackground(item){
|
||||||
|
let url = getImageSrc(item);
|
||||||
|
if(url){
|
||||||
|
return {
|
||||||
|
"backgroundImage": "url('"+url+"')"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预览列表 cell 图片
|
||||||
|
* @param text
|
||||||
|
*/
|
||||||
|
async function viewImage(file) {
|
||||||
|
if(isImage(file)){
|
||||||
|
let text = getImageSrc(file)
|
||||||
|
if (text) {
|
||||||
|
let imgList = [text];
|
||||||
|
createImgPreview({ imageList: imgList });
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(file.url){
|
||||||
|
//数据库中地址
|
||||||
|
let url = getFileAccessHttpUrl(file.url);
|
||||||
|
await initViewDomain();
|
||||||
|
//本地测试需要将文件地址的localhost/127.0.0.1替换成IP, 或是直接修改全局domain
|
||||||
|
//url = url.replace('localhost', '192.168.1.100')
|
||||||
|
//如果集成的KkFileview-v3.3.0+ 需要对url再做一层base64编码 encodeURIComponent(encryptByBase64(url))
|
||||||
|
window.open(onlinePreviewDomain+'?officePreviewType=pdf&url='+encodeURIComponent(url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化domain
|
||||||
|
*/
|
||||||
|
async function initViewDomain(){
|
||||||
|
if(!onlinePreviewDomain){
|
||||||
|
onlinePreviewDomain = await getViewFileDomain();
|
||||||
|
}
|
||||||
|
if(!onlinePreviewDomain.startsWith('http')){
|
||||||
|
onlinePreviewDomain = 'http://'+ onlinePreviewDomain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectFileList,
|
||||||
|
getBackground,
|
||||||
|
getFileSize,
|
||||||
|
downLoad,
|
||||||
|
beforeUpload,
|
||||||
|
handleRemove,
|
||||||
|
isImage,
|
||||||
|
getImageSrc,
|
||||||
|
getImageAsBackground,
|
||||||
|
viewImage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于emoji渲染
|
||||||
|
*/
|
||||||
|
export function useEmojiHtml(){
|
||||||
|
const COLONS_REGEX = new RegExp('([^:]+)?(:[a-zA-Z0-9-_+]+:(:skin-tone-[2-6]:)?)','g');
|
||||||
|
let emojisToShowFilter = function() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let emojiIndex = new EmojiIndex(data, {
|
||||||
|
emojisToShowFilter,
|
||||||
|
exclude:['recent','people','nature','foods','activity','places','objects','symbols','flags']
|
||||||
|
});
|
||||||
|
|
||||||
|
function getHtml(text) {
|
||||||
|
if(!text){
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return text.replace(COLONS_REGEX, function (match, p1, p2) {
|
||||||
|
const before = p1 || ''
|
||||||
|
if (endsWith(before, 'alt="') || endsWith(before, 'data-text="')) {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
let emoji = emojiIndex.findEmoji(p2)
|
||||||
|
if (!emoji) {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
return before + emoji2Html(emoji)
|
||||||
|
})
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function endsWith(str, temp){
|
||||||
|
return str.endsWith(temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
function emoji2Html(emoji) {
|
||||||
|
let style = `position: absolute;top: -3px;left: 3px;width: 18px; height: 18px;background-position: ${emoji.getPosition()}`
|
||||||
|
return `<span style="width: 24px" class="emoji-mart-emoji"><span class="my-emoji-icon emoji-set-apple emoji-type-image" style="${style}"> </span> </span>`
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
emojiIndex,
|
||||||
|
getHtml
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取modal窗体高度
|
||||||
|
*/
|
||||||
|
export function getModalHeight(){
|
||||||
|
return window.innerHeight;
|
||||||
|
}
|
Loading…
Reference in New Issue