diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php index 6f5b0f451..513dcb2d8 100755 --- a/app/Http/Controllers/Api/DialogController.php +++ b/app/Http/Controllers/Api/DialogController.php @@ -14,6 +14,7 @@ use App\Module\Base; use App\Module\Timer; use App\Module\Extranet; use App\Module\TimeRange; +use App\Module\MsgTool; use App\Module\Table\OnlineData; use App\Models\FileContent; use App\Models\AbstractModel; @@ -1096,21 +1097,29 @@ class DialogController extends AbstractController return Base::retError('消息发送保存失败'); } $ext = $markdown ? 'md' : 'htm'; - $fileData = [ - 'name' => "LongText-{$strlen}.{$ext}", - 'size' => $size, - 'file' => $file, - 'path' => $path, - 'url' => Base::fillUrl($path), - 'thumb' => '', - 'width' => -1, - 'height' => -1, - 'ext' => $ext, + $text = MsgTool::truncateText($text, 500, $ext); + $desc = strip_tags($markdown ? Base::markdown2html($text) : $text); + $desc = mb_substr(WebSocketDialogMsg::filterEscape($desc), 0, 200); + $msgData = [ + 'desc' => $desc, // 描述内容 + 'text' => $text, // 简要内容 + 'type' => $ext, // 内容类型 + 'file' => [ + 'name' => "LongText-{$strlen}.{$ext}", + 'size' => $size, + 'file' => $file, + 'path' => $path, + 'url' => Base::fillUrl($path), + 'thumb' => '', + 'width' => -1, + 'height' => -1, + 'ext' => $ext, + ], ]; if (empty($key)) { - $key = mb_substr(strip_tags($text), 0, 200); + $key = $desc; } - $result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'file', $fileData, $user->userid, false, false, $silence, $key); + $result = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'longtext', $msgData, $user->userid, false, false, $silence, $key); } else { $msgData = ['text' => $text]; if ($markdown) { @@ -1638,6 +1647,18 @@ class DialogController extends AbstractController $msg = File::formatFileData($msg); $data['content'] = $msg['content']; $data['file_mode'] = $msg['file_mode']; + } elseif ($data['type'] == 'longtext') { + $data['content'] = [ + 'type' => 'htm', + 'content' => Doo::translate("内容不存在") + ]; + if (isset($data['msg']['file']['path'])) { + $filePath = public_path($data['msg']['file']['path']); + if (file_exists($filePath)) { + $data['content']['type'] = $data['msg']['type']; + $data['content']['content'] = file_get_contents($filePath); + } + } } // return Base::retSuccess('success', $data); diff --git a/app/Models/WebSocketDialogMsg.php b/app/Models/WebSocketDialogMsg.php index beb8de4f0..ba14d310f 100644 --- a/app/Models/WebSocketDialogMsg.php +++ b/app/Models/WebSocketDialogMsg.php @@ -592,6 +592,9 @@ class WebSocketDialogMsg extends AbstractModel case 'text': return self::previewTextMsg($data['msg'], $preserveHtml); + case 'longtext': + return $data['msg']['desc'] ? Base::cutStr($data['msg']['desc'], 50) : ("[" . Doo::translate("长文本") . "]"); + case 'vote': $action = Doo::translate("投票"); return "[{$action}] " . self::previewTextMsg($data['msg'], $preserveHtml); @@ -774,14 +777,24 @@ class WebSocketDialogMsg extends AbstractModel break; } } - $key = str_replace([""", "&", "<", ">"], "", $key); - $key = str_replace(["\r", "\n", "\t", " "], " ", $key); - $key = preg_replace("/^\/[A-Za-z]+/", " ", $key); - $key = preg_replace("/\s+/", " ", $key); - $this->key = trim($key); + $this->key = self::filterEscape($key); $this->save(); } + /** + * 过滤转义 + * @param $content + * @return string + */ + public static function filterEscape($content) + { + $content = str_replace([""", "&", "<", ">"], "", $content); + $content = str_replace(["\r", "\n", "\t", " "], " ", $content); + $content = preg_replace("/^\/[A-Za-z]+/", " ", $content); + $content = preg_replace("/\s+/", " ", $content); + return trim($content); + } + /** * 返回引用消息(如果是文本只取预览) * @return array|mixed diff --git a/app/Module/MsgTool.php b/app/Module/MsgTool.php new file mode 100644 index 000000000..b60b6234e --- /dev/null +++ b/app/Module/MsgTool.php @@ -0,0 +1,108 @@ +convert($text); + } catch (CommonMarkException) { + return ""; + } + } + + // 创建DOM文档 + $dom = new DOMDocument('1.0', 'UTF-8'); + libxml_use_internal_errors(true); + $dom->loadHTML(mb_convert_encoding($text, 'HTML-ENTITIES', 'UTF-8')); + libxml_clear_errors(); + + // 获取body元素 + $body = $dom->getElementsByTagName('body')->item(0); + $truncatedHtml = ''; + $currentLength = 0; + + // 递归函数来遍历节点并截取内容 + self::traverseNodes($body, $currentLength, $length, $truncatedHtml); + + // 如果是Markdown,转换回Markdown + if ($isMd) { + $converter = new HtmlConverter(); + $truncatedHtml = $converter->convert($truncatedHtml); + } + + return $truncatedHtml; + } + + /** + * 递归遍历节点 + * @param $node + * @param $currentLength + * @param $length + * @param $truncatedHtml + * @return void + */ + private static function traverseNodes($node, &$currentLength, $length, &$truncatedHtml) + { + foreach ($node->childNodes as $child) { + if ($currentLength >= $length) { + break; + } + + if ($child->nodeType === XML_TEXT_NODE) { + $textContent = $child->textContent; + $remainingLength = $length - $currentLength; + + if (mb_strlen($textContent) > $remainingLength) { + $truncatedHtml .= htmlspecialchars(mb_substr($textContent, 0, $remainingLength) . '...'); + $currentLength += $remainingLength; + } else { + $truncatedHtml .= htmlspecialchars($textContent); + $currentLength += mb_strlen($textContent); + } + } elseif ($child->nodeType === XML_ELEMENT_NODE) { + $truncatedHtml .= '<' . $child->nodeName; + + // 添加属性 + if ($child->hasAttributes()) { + foreach ($child->attributes as $attr) { + $truncatedHtml .= ' ' . $attr->nodeName . '="' . htmlspecialchars($attr->nodeValue) . '"'; + } + } + + $truncatedHtml .= '>'; + + self::traverseNodes($child, $currentLength, $length, $truncatedHtml); + + if ($currentLength < $length || $child->firstChild) { + $truncatedHtml .= 'nodeName . '>'; + } + } + } + } +} diff --git a/composer.json b/composer.json index 6d24cee3c..280cc7dba 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ "require": { "php": "^8.0", "ext-curl": "*", + "ext-dom": "*", "ext-gd": "*", "ext-imagick": "*", "ext-json": "*", diff --git a/language/original-api.txt b/language/original-api.txt index 84003e7bc..6e148d7d1 100644 --- a/language/original-api.txt +++ b/language/original-api.txt @@ -802,3 +802,6 @@ webhook地址最长仅支持255个字符。 更新子任务标签 AI机器人不存在 + +内容不存在 +长文本 diff --git a/language/original-web.txt b/language/original-web.txt index 05d16ceff..2075b3d2d 100644 --- a/language/original-web.txt +++ b/language/original-web.txt @@ -1903,3 +1903,5 @@ WiFi签到延迟时长为±1分钟。 请选择示例标签 全部保存成功 +消息详情 +长文本 diff --git a/resources/assets/js/functions/web.js b/resources/assets/js/functions/web.js index 1cbd55b77..0a3413c1c 100755 --- a/resources/assets/js/functions/web.js +++ b/resources/assets/js/functions/web.js @@ -401,6 +401,8 @@ import {convertLocalResourcePath} from "../components/Replace/utils"; switch (data.type) { case 'text': return $A.getMsgTextPreview(data.msg, imgClassName) + case 'longtext': + return data.msg.desc ? $A.cutString(data.msg.desc, 50) : ("[" + $A.L('长文本') + "]") case 'vote': return `[${$A.L('投票')}]` + $A.getMsgTextPreview(data.msg, imgClassName) case 'word-chain': diff --git a/resources/assets/js/pages/manage/components/DialogView/index.vue b/resources/assets/js/pages/manage/components/DialogView/index.vue index 96148d191..023f40c11 100644 --- a/resources/assets/js/pages/manage/components/DialogView/index.vue +++ b/resources/assets/js/pages/manage/components/DialogView/index.vue @@ -27,6 +27,8 @@
+ + @@ -177,6 +179,7 @@ import {mapGetters, mapState} from "vuex"; import longpress from "../../../../directives/longpress"; import TextMsg from "./text.vue"; +import LongTextMsg from "./longtext.vue"; import FileMsg from "./file.vue"; import RecordMsg from "./record.vue"; import LocationMsg from "./location.vue"; @@ -199,6 +202,7 @@ export default { MeetingMsg, LocationMsg, RecordMsg, + LongTextMsg, TextMsg, FileMsg, WCircle diff --git a/resources/assets/js/pages/manage/components/DialogView/longtext.vue b/resources/assets/js/pages/manage/components/DialogView/longtext.vue new file mode 100644 index 000000000..78b45ca50 --- /dev/null +++ b/resources/assets/js/pages/manage/components/DialogView/longtext.vue @@ -0,0 +1,30 @@ + + + diff --git a/resources/assets/js/pages/manage/components/DialogWrapper.vue b/resources/assets/js/pages/manage/components/DialogWrapper.vue index bc291eec3..44a6fb0c9 100644 --- a/resources/assets/js/pages/manage/components/DialogWrapper.vue +++ b/resources/assets/js/pages/manage/components/DialogWrapper.vue @@ -3644,6 +3644,10 @@ export default { if (!$A.isJson(data)) { data = this.operateItem } + if (data.type === 'longtext') { + this.onViewFile(data) + return; + } $A.modalConfirm({ language: false, title: this.$L('下载文件'), diff --git a/resources/assets/js/pages/single/fileMsg.vue b/resources/assets/js/pages/single/fileMsg.vue index 7026dbbc9..5eb0c2759 100644 --- a/resources/assets/js/pages/single/fileMsg.vue +++ b/resources/assets/js/pages/single/fileMsg.vue @@ -7,6 +7,10 @@ +