From cf78766a3797157deba08021efb3a2100a75db72 Mon Sep 17 00:00:00 2001 From: kuaifan Date: Tue, 11 Nov 2025 05:42:02 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=A7=BB=E9=99=A4=E6=9C=AA=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E6=B6=88=E6=81=AF=E5=A4=84=E7=90=86=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E5=92=8CMarkdown=E6=8F=92=E4=BB=B6=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Api/DialogController.php | 49 ------ app/Models/WebSocketDialogMsg.php | 31 ---- .../pages/manage/components/DialogWrapper.vue | 143 ------------------ resources/assets/js/utils/markdown.js | 127 ---------------- 4 files changed, 350 deletions(-) diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php index 0fe03d296..6a1c63d86 100755 --- a/app/Http/Controllers/Api/DialogController.php +++ b/app/Http/Controllers/Api/DialogController.php @@ -3245,55 +3245,6 @@ class DialogController extends AbstractController return Base::retSuccess('success', $topMsg); } - /** - * @api {get} api/dialog/msg/applied 标记消息已应用 - * - * @apiDescription 需要token身份 - * @apiVersion 1.0.0 - * @apiGroup dialog - * @apiName msg__applied - * - * @apiParam {Number} index 索引 - * @apiParam {Number} msg_id 消息ID - * - * @apiSuccess {Number} ret 返回状态码(1正确、0错误) - * @apiSuccess {String} msg 返回信息(错误描述) - * @apiSuccess {Object} data 返回数据 - */ - public function msg__applied() - { - User::auth(); - // - $msg_id = intval(Request::input('msg_id')); - $index = intval(Request::input('index')); - // - $msg = WebSocketDialogMsg::whereId($msg_id)->first(); - if (empty($msg)) { - return Base::retError("消息不存在或已被删除"); - } - WebSocketDialog::checkDialog($msg->dialog_id); - // - $originalMsg = $msg->getRawOriginal('msg'); - $pattern = '/:::\s*(create-task-list|create-subtask-list)(?:\s+(\S+))?/'; - $count = -1; - $updatedMsg = preg_replace_callback($pattern, function($matches) use (&$count, $index) { - $count++; - if ($count === $index || ($index === 0 && $count === 1)) { - return "::: {$matches[1]} applied"; - } - return $matches[0]; - }, $originalMsg); - - if ($count === -1) { - return Base::retError("未找到可应用的规则"); - } - - $msg->msg = $updatedMsg; - $msg->save(); - // - return Base::retSuccess("success"); - } - /** * @api {get} api/dialog/sticker/search 搜索在线表情 * diff --git a/app/Models/WebSocketDialogMsg.php b/app/Models/WebSocketDialogMsg.php index 420ba511e..a497dcfa4 100644 --- a/app/Models/WebSocketDialogMsg.php +++ b/app/Models/WebSocketDialogMsg.php @@ -695,7 +695,6 @@ class WebSocketDialogMsg extends AbstractModel $text = $title; } else { $text = Base::markdown2html($text); - $text = self::previewConvertTaskList($text); } } $text = preg_replace("/]*?alt=\"(\S+)\"[^>]*?>/", "[$1]", $text); @@ -711,36 +710,6 @@ class WebSocketDialogMsg extends AbstractModel return $text; } - /** - * 转换任务列表 - * @param $text - * @return array|string|string[]|null - */ - private static function previewConvertTaskList($text) { - $pattern = '/:::\s*(create-task-list|create-subtask-list)(.*?):::/s'; - $replacement = function($matches) { - $content = $matches[2]; - $lines = explode("\n", trim($content)); - $result = []; - $currentTitle = ''; - foreach ($lines as $line) { - $line = trim($line); - if (empty($line)) continue; - - if (preg_match('/^title:\s*(.+)$/', $line, $titleMatch)) { - $currentTitle = $titleMatch[1]; - $result[] = $currentTitle; - } elseif (preg_match('/^desc:\s*(.+)$/', $line, $descMatch)) { - if (!empty($currentTitle)) { - $result[] = $descMatch[1]; - } - } - } - return implode("\n", $result); - }; - return preg_replace_callback($pattern, $replacement, $text); - } - /** * 预览文件消息 * @param $msg diff --git a/resources/assets/js/pages/manage/components/DialogWrapper.vue b/resources/assets/js/pages/manage/components/DialogWrapper.vue index ba7ae1942..e7da4b850 100644 --- a/resources/assets/js/pages/manage/components/DialogWrapper.vue +++ b/resources/assets/js/pages/manage/components/DialogWrapper.vue @@ -3499,135 +3499,6 @@ export default { }); }, - applyCreateBefore(type, event, el) { - $A.modalConfirm({ - content: `你确定要创建${type === 'task' ? '任务' : '子任务'}吗?`, - onOk: () => { - this.applyCreateTask(type, event, el); - } - }); - }, - - async applyCreateTask(type, event, el) { - const currentTarget = event.target; - if (currentTarget.classList.contains('applying') || currentTarget.classList.contains('applied')) { - return; - } - currentTarget.classList.add('applying') - - if (type === 'task') { - if (this.dialogData.group_type !== 'project') { - currentTarget.classList.remove('applying') - $A.modalError('只有在项目中才能创建任务') - return - } - if (!this.dialogData.group_info) { - currentTarget.classList.remove('applying') - $A.modalError('项目不存在') - return; - } - } else if (type === 'subtask') { - if (this.dialogData.group_type !== 'task') { - currentTarget.classList.remove('applying') - $A.modalError('只有在任务中才能创建子任务') - return - } - if (!this.dialogData.group_info) { - currentTarget.classList.remove('applying') - $A.modalError('任务不存在') - return; - } - } else { - currentTarget.classList.remove('applying') - $A.modalError('未知类型') - return - } - - let target = event.target; - while (target) { - if (target.classList.contains('apply-create-task')) { - break; - } - if (target.classList.contains('dialog-scroller')) { - target = null; - break; - } - target = target.parentElement; - } - if (!target) { - currentTarget.classList.remove('applying') - $A.modalError('未找到内容') - return - } - - const allTaskElements = el.querySelectorAll('.apply-create-task'); - const taskIndex = Array.from(allTaskElements).indexOf(target); - const taskList = Array.from(target.querySelectorAll('li')) - .map(item => { - const title = item.querySelector('.title')?.innerText?.trim(); - if (!title) return null; - - const desc = item.querySelector('.desc')?.innerText?.trim() || ''; - const content = desc ? desc.split('\n') - .filter(Boolean) - .map(line => `

${line.trim()}

`) - .join('') : ''; - - if (type === 'subtask') { - return { - task_id: this.dialogData.group_info.id, - name: title, - }; - } - return { - project_id: this.dialogData.group_info.id, - name: title, - content - }; - }) - .filter(Boolean); - - const typeCall = type === 'subtask' ? 'taskAddSub' : 'taskAdd'; - const typeLabel = type === 'subtask' ? '子任务' : '任务'; - const results = []; - for (const item of taskList) { - try { - const success = await this.$store.dispatch(typeCall, item); - results.push({ success: true, data: success }); - } catch (error) { - results.push({ success: false, error: error }); - } - } - const successTasks = results.filter(r => r.success).map(r => r.data); - const failedTasks = results.filter(r => !r.success).map(r => r.error); - let notice = `${this.$store.state.userInfo.nickname} 成功创建 ${successTasks.length} 个${typeLabel}`; - if (failedTasks.length > 0) { - notice += `,${failedTasks.length} 个${typeLabel}创建失败`; - } - - currentTarget.classList.remove('applying') - currentTarget.classList.add('applied') - - const {data} = await this.$store.dispatch("call", { - url: 'dialog/msg/sendnotice', - data: { - dialog_id: this.dialogId, - source: 'ai', - notice, - }, - }); - this.sendSuccess(data) - - - await this.$store.dispatch("call", { - url: 'dialog/msg/applied', - data: { - msg_id: this.operateItem.id, - index: taskIndex, - }, - }); - }, - openTranslationMenu(event) { const list = Object.keys(languageList).map(item => ({ label: languageList[item], @@ -3761,20 +3632,6 @@ export default { return } - // 创建任务 - if (target.classList.contains('apply-create-task-button')) { - this.operateItem = this.findMsgByElement(el) - this.applyCreateBefore('task', event, el) - return; - } - - // 创建子任务 - if (target.classList.contains('apply-create-subtask-button')) { - this.operateItem = this.findMsgByElement(el) - this.applyCreateBefore('subtask', event, el) - return; - } - // 点击切换翻译 if (target.classList.contains('translation-label')) { this.operateItem = this.findMsgByElement(el) diff --git a/resources/assets/js/utils/markdown.js b/resources/assets/js/utils/markdown.js index 8baaa1ab5..c8dea2090 100644 --- a/resources/assets/js/utils/markdown.js +++ b/resources/assets/js/utils/markdown.js @@ -175,17 +175,6 @@ const MarkdownUtils = { } const MarkdownPluginUtils = { - // 配置选项 - config: { - maxItems: 200, - maxTitleLength: 200, - maxDescLength: 1000, - buttonLabels: { - task: '创建任务', - subtask: '创建子任务' - } - }, - // HTML转义函数 escapeHtml(unsafe) { return unsafe @@ -274,120 +263,6 @@ const MarkdownPluginUtils = { state.line = nextLine + 1; return true; }); - }, - - // 修改初始化插件函数(创建任务) - initCreateTaskPlugin(md) { - md.block.ruler.before('fence', 'create-task', (state, startLine, endLine, silent) => { - const start = state.bMarks[startLine] + state.tShift[startLine]; - const max = state.eMarks[startLine]; - const firstLine = state.src.slice(start, max).trim(); - - // 检查开始标记,并获取status值 - const match = firstLine.match(/^:::\s*(create-task-list|create-subtask-list)(?:\s+(\S+))?$/); - if (!match) { - return false; - } - - if (silent) { - return true; - } - - // 获取按钮标题和状态 - const listType = match[1] === 'create-task-list' ? 'task' : 'subtask'; - const buttonTitle = this.config.buttonLabels[listType] || ''; - const status = match[2] || ''; - - let nextLine = startLine + 1; - let content = []; - - // 查找结束标记 - while (nextLine < endLine) { - const lineStart = state.bMarks[nextLine] + state.tShift[nextLine]; - const lineMax = state.eMarks[nextLine]; - const line = state.src.slice(lineStart, lineMax); - - if (line.trim() === ':::') { - break; - } - - content.push(line); - nextLine++; - } - - // 解析任务 - const tasks = []; - let currentTask = null; - let isCollectingDesc = false; - let descLines = []; - - content.forEach(line => { - const titleMatch = line.trim().match(/^title:\s*(.+)$/); - const descMatch = line.trim().match(/^desc:\s*(.*)$/); - - if (titleMatch) { - // 如果已经有一个任务在处理中,保存它 - if (currentTask) { - if (descLines.length > 0) { - currentTask.desc = descLines.join('\n'); - } - tasks.push(currentTask); - } - - // 开始新的任务 - currentTask = {title: titleMatch[1]}; - isCollectingDesc = false; - descLines = []; - } else if (descMatch) { - isCollectingDesc = true; - if (descMatch[1]) { - descLines.push(descMatch[1]); - } - } else if (isCollectingDesc && line.trim() && !line.trim().startsWith('title:')) { - // 收集多行描述,但不包括空行和新的title行 - descLines.push(line.trim()); - } - }); - - // 处理最后一个任务 - if (currentTask) { - if (descLines.length > 0) { - currentTask.desc = descLines.join('\n'); - } - tasks.push(currentTask); - } - - // 生成HTML - const showIndex = tasks.length > 1; - const taskItems = tasks.slice(0, this.config.maxItems).map((task, index) => [ - '
  • ', - showIndex ? `
    ${index + 1}.
    ` : '', - '
    ', - `
    ${this.escapeHtml(this.validateInput(task.title, this.config.maxTitleLength))}
    `, - task.desc && match[1] === 'create-task-list' ? `
    ${this.escapeHtml(this.validateInput(task.desc, this.config.maxDescLength))}
    ` : '', - '
    ', - '
  • ' - ].join('')); - - const htmls = [ - '
    ', - '
      ', - taskItems.join(''), - '
    ', - '
    ', - `
    ${$A.L(buttonTitle)}
    `, - '
    ', - '
    ' - ]; - - // 添加token - const token = state.push('html_block', '', 0); - token.content = htmls.join(''); - token.map = [startLine, nextLine]; - - state.line = nextLine + 1; - return true; - }) } }; @@ -415,7 +290,6 @@ export function MarkdownConver(text) { MarkdownUtils.mdi.use(mila, {attrs: {target: '_blank', rel: 'noopener noreferrer'}}) MarkdownUtils.mdi.use(mdKatex, {blockClass: 'katexmath-block rounded-md p-[10px]', errorColor: ' #cc0000'}) MarkdownPluginUtils.initReasoningPlugin(MarkdownUtils.mdi); - MarkdownPluginUtils.initCreateTaskPlugin(MarkdownUtils.mdi); } text = MarkdownPluginUtils.clearEmptyReasoning(text); text = MarkdownUtils.mdi.render(text); @@ -426,7 +300,6 @@ export function MarkdownPreview(text) { if (MarkdownUtils.mds === null) { MarkdownUtils.mds = MarkdownIt() MarkdownPluginUtils.initReasoningPlugin(MarkdownUtils.mds); - MarkdownPluginUtils.initCreateTaskPlugin(MarkdownUtils.mds); } text = MarkdownPluginUtils.clearEmptyReasoning(text); return MarkdownUtils.mds.render(text)