diff --git a/app/Models/ProjectTask.php b/app/Models/ProjectTask.php
index 6ca01eb96..09edffe81 100644
--- a/app/Models/ProjectTask.php
+++ b/app/Models/ProjectTask.php
@@ -368,7 +368,7 @@ class ProjectTask extends AbstractModel
}
}, $matches[0]);
}, $content);
- return Base::cutStr(strip_tags($content), 100, 0, "...");
+ return Base::cutStr(strip_tags($content), 100);
}
/**
diff --git a/app/Models/WebSocketDialog.php b/app/Models/WebSocketDialog.php
index b67d5c98b..77ff247bb 100644
--- a/app/Models/WebSocketDialog.php
+++ b/app/Models/WebSocketDialog.php
@@ -251,13 +251,12 @@ class WebSocketDialog extends AbstractModel
}
// 最后消息处理
- if ($data['last_msg']) {
- foreach ($data['last_msg']['emoji'] as &$value) {
- unset($value['userids']);
- }
- if ($data['last_msg']['type'] === 'text') {
- $data['last_msg']['msg']['text'] = WebSocketDialogMsg::previewTextMsg($data['last_msg']['msg']);
- }
+ if ($data['last_msg'] && $data['last_msg']['type'] != 'preview') {
+ $msgData = $data['last_msg'];
+ $msgData['emoji'] = Base::array_only_recursive($msgData['emoji'], ['symbol']);
+ $msgData['msg'] = ['preview' => WebSocketDialogMsg::previewMsg($msgData)];
+ $msgData['type'] = 'preview';
+ $data['last_msg'] = array_intersect_key($msgData, array_flip(['id', 'type', 'msg', 'userid', 'percentage', 'emoji', 'created_at']));
}
// 对方信息
diff --git a/app/Models/WebSocketDialogMsg.php b/app/Models/WebSocketDialogMsg.php
index 444fe5930..49c75a267 100644
--- a/app/Models/WebSocketDialogMsg.php
+++ b/app/Models/WebSocketDialogMsg.php
@@ -575,7 +575,7 @@ class WebSocketDialogMsg extends AbstractModel
case 'meeting':
$action = Doo::translate("会议");
- return "[{$action}] ${$data['msg']['name']}";
+ return "[{$action}] " . Base::cutStr($data['msg']['name'], 30);
case 'file':
return self::previewFileMsg($data['msg']);
@@ -593,17 +593,45 @@ class WebSocketDialogMsg extends AbstractModel
return "[{$action}] " . self::previewMsg($data['msg']['data']);
case 'notice':
- return Doo::translate($data['msg']['notice']);
+ return Base::cutStr(Doo::translate($data['msg']['notice']), 30);
case 'template':
return self::previewTemplateMsg($data['msg']);
+ case 'preview':
+ return $data['msg']['preview'];
+
default:
$action = Doo::translate("未知的消息");
return "[{$action}]";
}
}
+ /**
+ * 返回文本预览消息
+ * @param array $msgData
+ * @param bool $preserveHtml 保留html格式
+ * @return string|string[]|null
+ */
+ private static function previewTextMsg($msgData, $preserveHtml = false)
+ {
+ $text = $msgData['text'] ?? '';
+ if (!$text) return '';
+ if ($msgData['type'] === 'md') {
+ $text = Base::markdown2html($text);
+ }
+ $text = preg_replace("/
]*?alt=\"(\S+)\"[^>]*?>/", "[$1]", $text);
+ $text = preg_replace("/
]*?>/", "[动画表情]", $text);
+ $text = preg_replace("/
]*?>/", "[图片]", $text);
+ if (!$preserveHtml) {
+ $text = strip_tags($text);
+ $text = str_replace([" ", "&", "<", ">"], [" ", "&", "<", ">"], $text);
+ $text = preg_replace("/\s+/", " ", $text);
+ $text = Base::cutStr($text, 30);
+ }
+ return $text;
+ }
+
/**
* 预览文件消息
* @param $msg
@@ -619,7 +647,7 @@ class WebSocketDialogMsg extends AbstractModel
return "[{$action}]";
}
$action = Doo::translate("文件");
- return "[{$action}] {$msg['name']}";
+ return "[{$action}] " . Base::cutStr($msg['name'], 30);
}
/**
@@ -633,16 +661,15 @@ class WebSocketDialogMsg extends AbstractModel
return $msg['title_raw'];
}
if ($msg['type'] === 'task_list' && count($msg['list']) === 1) {
- return Doo::translate($msg['title']) . ": " . $msg['list'][0]['name'];
+ return Doo::translate($msg['title']) . ": " . Base::cutStr($msg['list'][0]['name'], 30);
}
if (!empty($msg['title'])) {
return Doo::translate($msg['title']);
}
if ($msg['type'] === 'content' && is_string($msg['content']) && $msg['content'] !== '') {
- return Doo::translate($msg['content']);
+ return Base::cutStr(Doo::translate($msg['content']), 30);
}
return Doo::translate('未知的消息');
-
}
/**
@@ -698,33 +725,6 @@ class WebSocketDialogMsg extends AbstractModel
return $msg;
}
- /**
- * 返回文本预览消息
- * @param array $msgData
- * @param bool $preserveHtml 保留html格式
- * @return string|string[]|null
- */
- public static function previewTextMsg($msgData, $preserveHtml = false)
- {
- $text = $msgData['text'] ?? '';
- if (!$text) return '';
- if ($msgData['type'] === 'md') {
- $text = Base::markdown2html($text);
- }
- $text = preg_replace("/
]*?alt=\"(\S+)\"[^>]*?>/", "[$1]", $text);
- $text = preg_replace("/
]*?>/", "[动画表情]", $text);
- $text = preg_replace("/
]*?>/", "[图片]", $text);
- if (!$preserveHtml) {
- $text = strip_tags($text);
- $text = str_replace([" ", "&", "<", ">"], [" ", "&", "<", ">"], $text);
- $text = preg_replace("/\s+/", " ", $text);
- if (mb_strlen($text) > 30) {
- $text = mb_substr($text, 0, 30) . "...";
- }
- }
- return $text;
- }
-
/**
* 处理文本消息内容,用于发送前
* @param $text
diff --git a/app/Module/Base.php b/app/Module/Base.php
index 4ca24d9f3..b26a9026f 100755
--- a/app/Module/Base.php
+++ b/app/Module/Base.php
@@ -375,79 +375,33 @@ class Base
/**
*
* 截取字符串
- * @param string $string 字符串
+ * @param string $str 字符串
* @param int $length 截取长度
* @param int $start 何处开始
- * @param string $dot 超出尾部添加
- * @param string $charset 默认编码
+ * @param string $suffix 后缀(超出长度显示,默认:...)
* @return string
*/
- public static function cutStr($string, $length, $start = 0, $dot = '', $charset = 'utf-8')
+ public static function cutStr(string $str, int $length, int $start = 0, string $suffix = '...')
{
- if (strtolower($charset) == 'utf-8') {
- if (Base::getStrlen($string) <= $length) return $string;
- $strcut = Base::utf8Substr($string, $length, $start);
- return $strcut . $dot;
- } else {
- $length = $length * 2;
- if (strlen($string) <= $length) return $string;
- $strcut = '';
- for ($i = 0; $i < $length; $i++) {
- $strcut .= ord($string[$i]) > 127 ? $string[$i] . $string[++$i] : $string[$i];
- }
+ $strLen = mb_strlen($str);
+ // 处理负数长度
+ if ($length < 0) {
+ $length = max($strLen + $length, 0);
}
- return $strcut . $dot;
- }
-
- /**
- * PHP获取字符串中英文混合长度
- * @param string $str 字符串
- * @param string $charset 编码
- * @return float 返回长度,1中文=1位,2英文=1位
- */
- public static function getStrlen($str, $charset = 'utf-8')
- {
- if (strtolower($charset) == 'utf-8') {
- $str = iconv('utf-8', 'GBK//IGNORE', $str);
+ // 处理负数起始位置
+ if ($start < 0) {
+ $start = max($strLen + $start, 0);
}
- $num = strlen($str);
- $cnNum = 0;
- for ($i = 0; $i < $num; $i++) {
- if (ord(substr($str, $i + 1, 1)) > 127) {
- $cnNum++;
- $i++;
- }
+ // 处理边界情况
+ if ($length === 0 || $start >= $strLen) {
+ return '';
}
- $enNum = $num - ($cnNum * 2);
- $number = ($enNum / 2) + $cnNum;
- return ceil($number);
- }
-
- /**
- * PHP截取UTF-8字符串,解决半字符问题。
- * @param string $str 源字符串
- * @param int $len 左边的子串的长度
- * @param int $start 何处开始
- * @return string 取出的字符串, 当$len小于等于0时, 会返回整个字符串
- */
- public static function utf8Substr($str, $len, $start = 0)
- {
- $len = $len * 2;
- $new_str = [];
- for ($i = 0; $i < $len; $i++) {
- $temp_str = substr($str, 0, 1);
- if (ord($temp_str) > 127) {
- $i++;
- if ($i < $len) {
- $new_str[] = substr($str, 0, 3);
- $str = substr($str, 3);
- }
- } else {
- $new_str[] = substr($str, 0, 1);
- $str = substr($str, 1);
- }
+ $result = mb_substr($str, $start, $length);
+ // 只有当实际截取的长度小于原字符串长度时才添加后缀
+ if ($start + $length < $strLen) {
+ return $result . $suffix;
}
- return join(array_slice($new_str, $start));
+ return $result;
}
/**
@@ -2025,6 +1979,20 @@ class Base
return $array;
}
+ /**
+ * 多维数组只保留指定键值
+ * @param $array
+ * @param $keys
+ * @return array
+ */
+ public static function array_only_recursive($array, $keys) {
+ return array_map(function ($item) use ($keys) {
+ return is_array($item)
+ ? array_intersect_key($item, array_flip($keys))
+ : $item;
+ }, $array);
+ }
+
/**
* 是否微信
* @return bool
diff --git a/resources/assets/js/functions/common.js b/resources/assets/js/functions/common.js
index aa106676d..2c7bb9754 100755
--- a/resources/assets/js/functions/common.js
+++ b/resources/assets/js/functions/common.js
@@ -86,7 +86,6 @@ const timezone = require("dayjs/plugin/timezone");
return str;
},
-
/**
* 字符串是否包含
* @param string
@@ -1226,6 +1225,36 @@ const timezone = require("dayjs/plugin/timezone");
extractImageParameterAll(html) {
const imgTags = html.match(/
]*?>/g) || [];
return imgTags.map(imgTag => this.extractImageParameter(imgTag));
+ },
+
+ /**
+ * 增强版的字符串截取
+ * @param str
+ * @param length
+ * @param start
+ * @param suffix
+ * @returns {string}
+ */
+ cutString(str, length, start = 0, suffix = '...') {
+ const chars = [...str];
+ // 如果长度为负数,则从末尾开始计数
+ if (length < 0) {
+ length = Math.max(chars.length + length, 0);
+ }
+ // 如果起始位置为负数,则从末尾开始计数
+ if (start < 0) {
+ start = Math.max(chars.length + start, 0);
+ }
+ // 如果截取长度为0或起始位置超出字符串长度,返回空字符串
+ if (length === 0 || start >= chars.length) {
+ return '';
+ }
+ const sliced = chars.slice(start, start + length);
+ // 只有当实际截取的长度小于原字符串长度时才添加后缀
+ if (start + length < chars.length) {
+ return sliced.join('') + suffix;
+ }
+ return sliced.join('');
}
});
diff --git a/resources/assets/js/functions/web.js b/resources/assets/js/functions/web.js
index 523f0a8e4..746ec40fd 100755
--- a/resources/assets/js/functions/web.js
+++ b/resources/assets/js/functions/web.js
@@ -281,10 +281,7 @@ import {MarkdownPreview} from "../store/markdown";
text = text.replace(/\[image:(.*?)\]/g, `
`)
text = text.replace(/\{\{RemoteURL\}\}/g, this.apiUrl('../'))
} else {
- const tmpText = text.substring(0, 30)
- if (tmpText.length < text.length) {
- text = tmpText + '...'
- }
+ text = $A.cutString(text, 30)
}
return text
},
@@ -409,7 +406,7 @@ import {MarkdownPreview} from "../store/markdown";
case 'record':
return `[${$A.L('语音')}]`
case 'meeting':
- return `[${$A.L('会议')}] ${data.msg.name}`
+ return `[${$A.L('会议')}] ${$A.cutString(data.msg.name, 30)}`
case 'file':
return $A.fileMsgSimpleDesc(data.msg, imgClassName)
case 'tag':
@@ -419,9 +416,11 @@ import {MarkdownPreview} from "../store/markdown";
case 'todo':
return `[${$A.L(data.msg.action === 'remove' ? '取消待办' : (data.msg.action === 'done' ? '完成' : '设待办'))}] ${$A.getMsgSimpleDesc(data.msg.data)}`
case 'notice':
- return $A.L(data.msg.notice)
+ return $A.cutString($A.L(data.msg.notice), 30)
case 'template':
return $A.templateMsgSimpleDesc(data.msg)
+ case 'preview':
+ return data.msg.preview
default:
return `[${$A.L('未知的消息')}]`
}
@@ -450,7 +449,7 @@ import {MarkdownPreview} from "../store/markdown";
} else if (msg.ext == 'mp4') {
return `[${$A.L('视频')}]`
}
- return `[${$A.L('文件')}] ${msg.name}`
+ return `[${$A.L('文件')}] ${$A.cutString(msg.name, 30)}`
},
/**
@@ -463,13 +462,13 @@ import {MarkdownPreview} from "../store/markdown";
return msg.title_raw
}
if (msg.type === 'task_list' && $A.arrayLength(msg.list) === 1) {
- return $A.L(msg.title) + ": " + msg.list[0].name
+ return $A.L(msg.title) + ": " + $A.cutString(msg.list[0].name, 30)
}
if (msg.title) {
return $A.L(msg.title)
}
if (msg.type === 'content' && typeof msg.content === 'string' && msg.content !== '') {
- return $A.L(msg.content)
+ return $A.cutString($A.L(msg.content), 30)
}
return $A.L('未知的消息')
},
diff --git a/resources/assets/js/pages/manage/messenger.vue b/resources/assets/js/pages/manage/messenger.vue
index 2dde7ea9d..eb18df809 100644
--- a/resources/assets/js/pages/manage/messenger.vue
+++ b/resources/assets/js/pages/manage/messenger.vue
@@ -412,6 +412,9 @@ export default {
case 'file':
searchString += ` ${last_msg.msg.name}`
break
+ case 'preview':
+ searchString += ` ${last_msg.msg.preview}`
+ break
}
}
if (!$A.strExists(searchString, dialogSearchKey)) {
diff --git a/resources/assets/js/store/actions.js b/resources/assets/js/store/actions.js
index 9b412f72c..9c2c85938 100644
--- a/resources/assets/js/store/actions.js
+++ b/resources/assets/js/store/actions.js
@@ -3634,8 +3634,8 @@ export default {
// 更新最后消息
const newData = {
id: dialog_id,
- last_at: data.last_msg && data.last_msg.created_at,
last_msg: data.last_msg,
+ last_at: data.last_msg ? data.last_msg.created_at : $A.daytz().format("YYYY-MM-DD HH:mm:ss"),
}
if (data.update_read) {
// 更新未读数量