From 5c25bdfa91c29cb8fc618aee57facff01b265531 Mon Sep 17 00:00:00 2001 From: kuaifan Date: Tue, 22 Nov 2022 17:40:25 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E8=81=8A=E5=A4=A9=E4=BD=BF=E7=94=A8~?= =?UTF-8?q?=E7=AC=A6=E5=8F=B7=E5=88=86=E4=BA=AB=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Api/FileController.php | 31 ++++----------- app/Models/FileLink.php | 33 ++++++++++++++++ app/Models/WebSocketDialogMsg.php | 39 +++++++++++++++++-- .../manage/components/ChatInput/index.vue | 33 +++++++++++++++- .../pages/manage/components/DialogWrapper.vue | 3 +- .../sass/pages/components/dialog-wrapper.scss | 4 ++ 6 files changed, 113 insertions(+), 30 deletions(-) diff --git a/app/Http/Controllers/Api/FileController.php b/app/Http/Controllers/Api/FileController.php index 11a5506b1..257ac5632 100755 --- a/app/Http/Controllers/Api/FileController.php +++ b/app/Http/Controllers/Api/FileController.php @@ -171,15 +171,15 @@ class FileController extends AbstractController $user = User::auth(); // $key = trim(Request::input('key')); - if (empty($key)) { - return Base::retError('请输入关键词'); - } // 搜索自己的 - $builder = File::whereUserid($user->userid)->where("name", "like", "%{$key}%"); + $builder = File::whereUserid($user->userid); + if ($key) { + $builder->where("name", "like", "%{$key}%"); + } $array = $builder->take(50)->get()->toArray(); // 搜索共享的 $take = 50 - count($array); - if ($take > 0) { + if ($take > 0 && $key) { $list = File::where("name", "like", "%{$key}%") ->whereIn('pshare', function ($queryA) use ($user) { $queryA->select('files.id') @@ -1102,24 +1102,7 @@ class FileController extends AbstractController return Base::retError('文件夹暂不支持此功能'); } // - $fileLink = FileLink::whereFileId($file->id)->whereUserid($user->userid)->first(); - if (empty($fileLink)) { - $fileLink = FileLink::createInstance([ - 'file_id' => $file->id, - 'userid' => $user->userid, - 'code' => Base::generatePassword(64), - ]); - $fileLink->save(); - } else { - if ($refresh == 'yes') { - $fileLink->code = Base::generatePassword(64); - $fileLink->save(); - } - } - return Base::retSuccess('success', [ - 'id' => $file->id, - 'url' => Base::fillUrl('single/file/' . $fileLink->code), - 'num' => $fileLink->num - ]); + $data = FileLink::generateLink($file->id, $user->userid, $refresh == 'yes'); + return Base::retSuccess('success', $data); } } diff --git a/app/Models/FileLink.php b/app/Models/FileLink.php index 27a56e1f1..cc2bdb8e9 100644 --- a/app/Models/FileLink.php +++ b/app/Models/FileLink.php @@ -2,6 +2,8 @@ namespace App\Models; +use App\Module\Base; + /** * App\Models\FileLink * @@ -34,4 +36,35 @@ class FileLink extends AbstractModel { return $this->hasOne(File::class, 'id', 'file_id'); } + + /** + * 生成链接 + * @param $fileId + * @param $userid + * @param $refresh + * @return array + */ + public static function generateLink($fileId, $userid, $refresh = false) + { + $fileLink = FileLink::whereFileId($fileId)->whereUserid($userid)->first(); + if (empty($fileLink)) { + $fileLink = FileLink::createInstance([ + 'file_id' => $fileId, + 'userid' => $userid, + 'code' => Base::generatePassword(64), + ]); + $fileLink->save(); + } else { + if ($refresh == 'yes') { + $fileLink->code = Base::generatePassword(64); + $fileLink->save(); + } + } + return [ + 'id' => $fileId, + 'url' => Base::fillUrl('single/file/' . $fileLink->code), + 'code' => $fileLink->code, + 'num' => $fileLink->num + ]; + } } diff --git a/app/Models/WebSocketDialogMsg.php b/app/Models/WebSocketDialogMsg.php index e7bc96a4f..8c526eaf1 100644 --- a/app/Models/WebSocketDialogMsg.php +++ b/app/Models/WebSocketDialogMsg.php @@ -644,15 +644,47 @@ class WebSocketDialogMsg extends AbstractModel } } } - // @成员、#任务 + // @成员、#任务、~文件 preg_match_all("/.*?<\/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); + $keyId = $matchId[1]; + if ($matchChar[1] === "~") { + if (Base::isNumber($keyId)) { + $file = File::permissionFind($keyId); + if ($file->type == 'folder') { + throw new ApiException('文件夹不支持分享'); + } + $fileLink = FileLink::generateLink($file->id, User::userid()); + $keyId = $fileLink['code']; + } else { + preg_match("/\/single\/file\/(.*?)$/i", $keyId, $match); + if ($match && strlen($match[1]) >= 32) { + $keyId = $match[1]; + } else { + throw new ApiException('文件分享错误'); + } + } + } + $text = str_replace($matchs[0][$key], "[:{$matchChar[1]}:{$keyId}:{$matchValye[1]}:]", $text); } - // 处理链接 + // 文件分享链接 + preg_match_all("/(https*:\/\/)((\w|=|\?|\.|\/|&|-|:|\+|%|;|#)+)/i", $text, $matchs); + if ($matchs) { + foreach ($matchs[0] as $str) { + preg_match("/\/single\/file\/(.*?)$/i", $str, $match); + if ($match && strlen($match[1]) >= 32) { + $file = File::select(['files.id', 'files.name', 'files.ext'])->join('file_links as L', 'files.id', '=', 'L.file_id')->where('L.code', $match[1])->first(); + if ($file && $file->name) { + $name = $file->ext ? "{$file->name}.{$file->ext}" : $file->name; + $text = str_replace($str, "[:~:{$match[1]}:{$name}:]", $text); + } + } + } + } + // 处理链接标签 preg_match_all("/]*?href=([\"'])(.*?)\\1[^>]*?>([^<]*?)<\/a>/is", $text, $matchs); foreach ($matchs[2] as $key => $str) { $herf = $matchs[2][$key]; @@ -665,6 +697,7 @@ class WebSocketDialogMsg extends AbstractModel $text = preg_replace("/\[:IMAGE:(.*?):(.*?):(.*?):(.*?):(.*?):\]/i", "\"$5\"/", $text); $text = preg_replace("/\[:@:(.*?):(.*?):\]/i", "@$2", $text); $text = preg_replace("/\[:#:(.*?):(.*?):\]/i", "#$2", $text); + $text = preg_replace("/\[:~:(.*?):(.*?):\]/i", "~$2", $text); return preg_replace("/^(

<\/p>)+|(

<\/p>)+$/i", "", $text); } diff --git a/resources/assets/js/pages/manage/components/ChatInput/index.vue b/resources/assets/js/pages/manage/components/ChatInput/index.vue index bbd4ccb64..61d83600c 100755 --- a/resources/assets/js/pages/manage/components/ChatInput/index.vue +++ b/resources/assets/js/pages/manage/components/ChatInput/index.vue @@ -225,6 +225,7 @@ export default { userList: null, userCache: null, taskList: null, + fileList: {}, showMore: false, showEmoji: false, @@ -414,12 +415,14 @@ export default { this.userList = null; this.userCache = null; this.taskList = null; + this.fileList = {}; this.$emit('input', this.getInputCache()) }, taskId() { this.userList = null; this.userCache = null; this.taskList = null; + this.fileList = {}; this.$emit('input', this.getInputCache()) }, @@ -548,7 +551,7 @@ export default { }, mention: { allowedChars: /^\S*$/, - mentionDenotationChars: ["@", "#"], + mentionDenotationChars: ["@", "#", "~"], defaultMenuOrientation: this.defaultMenuOrientation, isolateCharacter: true, positioningStrategy: 'fixed', @@ -568,11 +571,12 @@ export default { return "Loading..."; }, source: (searchTerm, renderList, mentionChar) => { - const mentionName = mentionChar == "@" ? 'user-mention' : 'task-mention'; + const mentionName = mentionChar == "@" ? 'user-mention' : (mentionChar == "#" ? 'task-mention' : 'file-mention'); const containers = document.getElementsByClassName("ql-mention-list-container"); for (let i = 0; i < containers.length; i++) { containers[i].classList.remove("user-mention"); containers[i].classList.remove("task-mention"); + containers[i].classList.remove("file-mention"); containers[i].classList.add(mentionName); $A.scrollPreventThrough(containers[i]); } @@ -1223,6 +1227,31 @@ export default { taskCallback([]) break; + case "~": // ~文件 + this.mentionMode = "file-mention"; + if ($A.isArray(this.fileList[searchTerm])) { + resultCallback(this.fileList[searchTerm]) + return; + } + this.fileTimer && clearTimeout(this.fileTimer) + this.fileTimer = setTimeout(_ => { + this.$store.dispatch("searchFiles", searchTerm).then(({data}) => { + this.fileList[searchTerm] = [{ + label: [{id: 0, value: this.$L('文件分享查看'), disabled: true}], + list: data.filter(item => item.type !== "folder").map(item => { + return { + id: item.id, + value: item.ext ? `${item.name}.${item.ext}` : item.name + } + }) + }]; + resultCallback(this.fileList[searchTerm]) + }).catch(() => { + resultCallback([]) + }) + }, 300) + break; + default: resultCallback([]) break; diff --git a/resources/assets/js/pages/manage/components/DialogWrapper.vue b/resources/assets/js/pages/manage/components/DialogWrapper.vue index f592d63bb..96906e42b 100644 --- a/resources/assets/js/pages/manage/components/DialogWrapper.vue +++ b/resources/assets/js/pages/manage/components/DialogWrapper.vue @@ -1867,7 +1867,8 @@ export default { this.replyActiveUpdate = true let {text} = this.operateItem.msg if (text.indexOf("mention") > -1) { - text = text.replace(/([@#])(.*?)<\/span>/g, '$3$4') + text = text.replace(/]*)>~([^>]*)<\/a>/g, '~$3') + text = text.replace(/([@#])([^>]*)<\/span>/g, '$3$4') } this.msgText = $A.formatMsgBasic(text) } diff --git a/resources/assets/sass/pages/components/dialog-wrapper.scss b/resources/assets/sass/pages/components/dialog-wrapper.scss index f229bb35e..73f4f7d4e 100644 --- a/resources/assets/sass/pages/components/dialog-wrapper.scss +++ b/resources/assets/sass/pages/components/dialog-wrapper.scss @@ -587,6 +587,10 @@ cursor: pointer; } + &.file { + cursor: pointer; + } + &.me { font-size: 13px; font-weight: 600;