no message

This commit is contained in:
kuaifan 2022-04-15 21:12:26 +08:00
parent 5c3d8727e0
commit 1de42f5c72
13 changed files with 156 additions and 34 deletions

View File

@ -15,3 +15,10 @@ if (!function_exists('seeders_at')) {
return date("Y-m-d H:i:s", $time);
}
}
if (!function_exists('md5s')) {
function md5s($val, $len = 16)
{
return substr(md5($val), 32 - $len);
}
}

View File

@ -214,7 +214,7 @@ class DialogController extends AbstractController
*/
public function msg__sendtext()
{
Base::checkClientVersion('0.8.1');
Base::checkClientVersion('0.12.95');
$user = User::auth();
//
$chat_nickname = Base::settingFind('system', 'chat_nickname');
@ -228,14 +228,14 @@ class DialogController extends AbstractController
$dialog_id = Base::getPostInt('dialog_id');
$text = trim(Base::getPostValue('text'));
//
WebSocketDialog::checkDialog($dialog_id);
//
$text = WebSocketDialogMsg::formatMsg($text, $dialog_id);
if (mb_strlen($text) < 1) {
return Base::retError('消息内容不能为空');
} elseif (mb_strlen($text) > 20000) {
return Base::retError('消息内容最大不能超过20000字');
}
//
WebSocketDialog::checkDialog($dialog_id);
//
if (mb_strlen($text) > 2000) {
$array = mb_str_split($text, 2000);
} else {

View File

@ -198,6 +198,40 @@ class WebSocketDialogMsg extends AbstractModel
});
}
/**
* 处理文本消息内容,用于发送前
* @param $text
* @param $dialog_id
* @return mixed|string|string[]
*/
public static function formatMsg($text, $dialog_id)
{
// 图片
preg_match_all("/<img\s*src=\"data:image\/(png|jpg|jpeg);base64,(.*?)\"(.*?)>(<\/img>)*/s", $text, $matchs);
foreach ($matchs[2] as $key => $base64) {
$tmpPath = "uploads/chat/" . date("Ym") . "/" . $dialog_id . "/";
Base::makeDir(public_path($tmpPath));
$tmpPath .= md5s($base64) . "." . $matchs[1][$key];
if (file_put_contents(public_path($tmpPath), base64_decode($base64))) {
$text = str_replace($matchs[0][$key], "[:IMG:{$tmpPath}:]", $text);
}
}
// @成员 #任务
preg_match_all("/<span class=\"mention\"(.*?)>.*?<\/span>.*?<\/span>.*?<\/span>/s", $text, $matchs);
foreach ($matchs[1] as $key => $str) {
preg_match("/data-denotation-char=\"(.*?)\"/", $str, $matchChar);
preg_match("/data-id=\"(.*?)\"/", $str, $matchId);
preg_match("/data-value=\"(.*?)\"/", $str, $matchValye);
$text = str_replace($matchs[0][$key], "[:{$matchChar[1]}:{$matchId[1]}:{$matchValye[1]}:]", $text);
}
// 过滤标签
$text = strip_tags($text, '<p>');
$text = preg_replace("/\<p.*?\>/i", "<p>", $text);
$text = preg_replace("/\[:IMG:(.*?):\]/i", "<img src=\"{{RemoteURL}}$1\"/>", $text);
$text = preg_replace("/\[:@:(.*?):(.*?):\]/i", "<span class=\"mention user\" data-id=\"$1\">@$2</span>", $text);
return preg_replace("/\[:#:(.*?):(.*?):\]/i", "<span class=\"mention task\" data-id=\"$1\">#$2</span>", $text);
}
/**
* 发送消息
* @param int $dialog_id 会话ID 聊天室ID

View File

@ -10,7 +10,7 @@
<div class="dialog-content">
<!--文本-->
<div v-if="msgData.type === 'text'" class="content-text">
<pre class="no-dark-mode">{{textMsg(msgData.msg.text)}}</pre>
<pre class="no-dark-mode" @click="viewText" v-html="textMsg(msgData.msg.text)"></pre>
</div>
<!--文件-->
<div v-else-if="msgData.type === 'file'" :class="`content-file ${msgData.msg.type}`">
@ -173,6 +173,7 @@ export default {
return ""
}
text = text.trim().replace(/(\n\x20*){3,}/g, "\n\n");
text = text.replace(/\{\{RemoteURL\}\}/g, $A.apiUrl('../'))
return text;
},
@ -223,22 +224,18 @@ export default {
});
},
viewText({target}) {
if (target.nodeName === "IMG") {
this.viewPicture(target.currentSrc);
} else if (target.classList.contains('mention') && target.classList.contains('task')) {
this.$store.dispatch("openTask", $A.runNum(target.getAttribute("data-id")));
}
},
viewFile() {
const {id, dialog_id, msg} = this.msgData;
const {msg} = this.msgData;
if (['jpg', 'jpeg', 'gif', 'png'].includes(msg.ext)) {
const list = $A.cloneJSON(this.dialogMsgs.filter(item => {
return item.dialog_id === dialog_id && item.type === 'file' && ['jpg', 'jpeg', 'gif', 'png'].includes(item.msg.ext);
})).sort((a, b) => {
return a.id - b.id;
});
const index = list.findIndex(item => item.id === id);
if (index > -1) {
this.$store.state.previewImageIndex = index;
this.$store.state.previewImageList = list.map(({msg}) => msg.path);
} else {
this.$store.state.previewImageIndex = 0;
this.$store.state.previewImageList = [msg.path];
}
this.viewPicture(msg.path);
return
}
if (this.$Electron) {
@ -260,6 +257,43 @@ export default {
}
},
viewPicture(currentUrl) {
const {dialog_id} = this.msgData;
const data = $A.cloneJSON(this.dialogMsgs.filter(item => {
if (item.dialog_id === dialog_id) {
if (item.type === 'file') {
return ['jpg', 'jpeg', 'gif', 'png'].includes(item.msg.ext);
} else if (item.type === 'text') {
return item.msg.text.match(/<img src="(.*?)"\/>/);
}
}
return false;
})).sort((a, b) => {
return a.id - b.id;
});
//
let list = [];
data.some(({type, msg}) => {
if (type === 'file') {
list.push(msg.path)
} else if (type === 'text') {
const array = msg.text.match(/<img src="(.*?)"\/>/g);
array.some(res => {
list.push(res.match(/<img src="(.*?)"\/>/)[1].replace(/\{\{RemoteURL\}\}/g, $A.apiUrl('../')))
})
}
})
//
let index = list.findIndex(item => item === currentUrl);
if (index > -1) {
this.$store.state.previewImageIndex = index;
this.$store.state.previewImageList = list;
} else {
this.$store.state.previewImageIndex = 0;
this.$store.state.previewImageList = [currentUrl];
}
},
downFile() {
$A.modalConfirm({
title: '下载文件',

View File

@ -162,7 +162,7 @@ import {Store} from "le5le-store";
import UserInput from "../../../components/UserInput";
import DrawerOverlay from "../../../components/DrawerOverlay";
import DialogGroupInfo from "./DialogGroupInfo";
import ChatInput from "../../../components/ChatInput";
import ChatInput from "./ChatInput";
export default {
name: "DialogWrapper",
@ -324,6 +324,8 @@ export default {
if (this.msgText == '') {
return;
}
this.msgText = this.msgText.replace(/<\/span> <\/p>$/, "</span></p>")
//
let tempId = $A.randomString(16);
this.tempMsgs.push({
id: tempId,

View File

@ -434,7 +434,7 @@ import DialogWrapper from "./DialogWrapper";
import ProjectLog from "./ProjectLog";
import {Store} from "le5le-store";
import TaskMenu from "./TaskMenu";
import ChatInput from "../../../components/ChatInput";
import ChatInput from "./ChatInput";
export default {
name: "TaskDetail",

View File

@ -397,14 +397,17 @@ export default {
if ($A.isJson(data)) {
switch (data.type) {
case 'text':
return data.msg.text
let text = data.msg.text;
text = text.replace(/<img src=".*?"\/>/g, `[${this.$L('图片')}]`)
text = text.replace(/<[^>]+>/g,"")
return text
case 'file':
if (data.msg.type == 'img') {
return '[' + this.$L('图片') + ']'
return `[${this.$L('图片')}]`
}
return '[' + this.$L('文件') + '] ' + data.msg.name
return `[${this.$L('文件')}] ${data.msg.name}`
default:
return '[' + this.$L('未知的消息') + ']'
return `[${this.$L('未知的消息')}]`
}
}
return '';

View File

@ -1,5 +1,4 @@
@import "auto-tip";
@import "chat-input";
@import "circle";
@import "drawer-overlay";
@import "img-update";

View File

@ -171,17 +171,35 @@ body.dark-mode-reverse {
> ul {
> li {
.dialog-view {
.dialog-content {
background-color: #e1e1e1;
.content-text {
color: #ffffff;
.dialog-head {
.dialog-content {
background-color: #e1e1e1;
.content-text {
color: #ffffff;
.content-text {
> pre {
.mention {
color: #000000;
}
}
}
}
}
}
}
&.self {
.dialog-view {
.dialog-content {
background-color: #8bcf70;
.dialog-head {
.dialog-content {
background-color: #8bcf70;
.content-text {
> pre {
.mention {
color: #000000;
}
}
}
}
}
}
}

View File

@ -1,3 +1,4 @@
@import "chat-input";
@import "dialog-group-info";
@import "dialog-wrapper";
@import "file-content";

View File

@ -221,12 +221,14 @@
background-color: #F4F5F7;
padding: 8px;
min-width: 32px;
border-radius: 6px 6px 6px 0;
border-radius: 0 6px 6px 6px;
display: flex;
align-items: flex-start;
.content-text {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
color: #333333;
> pre {
display: block;
margin: 0;
@ -235,6 +237,22 @@
white-space: pre-wrap;
word-wrap: break-word;
word-break: break-word;
img {
max-width: 220px;
max-height: 220px;
}
.mention {
color: $flow-status-end-color;
background-color: transparent;
user-select: auto;
margin-right: 0;
> span {
margin: 0;
}
&.task {
cursor: pointer;
}
}
}
}
@ -461,10 +479,16 @@
.dialog-content {
background-color: $primary-color;
border-radius: 6px 6px 0 6px;
border-radius: 6px 0 6px 6px;
.content-text {
color: #ffffff;
> pre {
.mention {
color: #333333;
}
}
}
.content-file {