diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php index 6dac8e8e0..fe8b3ec3e 100755 --- a/app/Http/Controllers/Api/DialogController.php +++ b/app/Http/Controllers/Api/DialogController.php @@ -2893,8 +2893,6 @@ class DialogController extends AbstractController * @apiGroup dialog * @apiName msg__applied * - * @apiParam {String} type 类型 - * - CreateTask: 创建任务 * @apiParam {Number} index 索引 * @apiParam {Number} msg_id 消息ID * @@ -2907,7 +2905,6 @@ class DialogController extends AbstractController User::auth(); // $msg_id = intval(Request::input('msg_id')); - $type = trim(Request::input('type')); $index = intval(Request::input('index')); // $msg = WebSocketDialogMsg::whereId($msg_id)->first(); @@ -2917,17 +2914,17 @@ class DialogController extends AbstractController WebSocketDialog::checkDialog($msg->dialog_id); // $originalMsg = $msg->getRawOriginal('msg'); - $pattern = '/```\s*' . preg_quote($type, '/') . '\s*(applying|applied)?\s*(\n|\\\\n)/'; + $pattern = '/:::\s*(create-task-list|create-subtask-list)(?:\s+(\S+))?/'; $count = -1; - $updatedMsg = preg_replace_callback($pattern, function($matches) use (&$count, $index, $type) { + $updatedMsg = preg_replace_callback($pattern, function($matches) use (&$count, $index) { $count++; if ($count === $index || ($index === 0 && $count === 1)) { - return "```{$type} applied{$matches[2]}"; + return "::: {$matches[1]} applied"; } return $matches[0]; }, $originalMsg); - if ($count === 0) { + if ($count === -1) { return Base::retError("未找到可应用的规则"); } diff --git a/app/Tasks/BotReceiveMsgTask.php b/app/Tasks/BotReceiveMsgTask.php index 0be9ae407..0d8e99772 100644 --- a/app/Tasks/BotReceiveMsgTask.php +++ b/app/Tasks/BotReceiveMsgTask.php @@ -593,13 +593,13 @@ class BotReceiveMsgTask extends AbstractTask } $before_text[] = << `

${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, @@ -3276,29 +3299,40 @@ export default { }) .filter(Boolean); + const typeCall = type === 'subtask' ? 'taskAddSub' : 'taskAdd'; + const typeLabel = type === 'subtask' ? '子任务' : '任务'; const results = await Promise.all(taskList.map(item => - this.$store.dispatch("taskAdd", item).then( + this.$store.dispatch(typeCall, item).then( success => ({ success: true, data: success }), error => ({ 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) { - $A.modalError(`成功创建 ${successTasks.length} 个任务,${failedTasks.length} 个任务创建失败`); - } else { - $A.messageSuccess(`成功创建 ${successTasks.length} 个任务`); + 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: { - type: 'CreateTask', + msg_id: this.operateItem.id, index: taskIndex, - msg_id: this.operateItem.id }, }); }, @@ -3411,7 +3445,14 @@ export default { // 创建任务 if (target.classList.contains('apply-create-task-button')) { this.operateItem = this.findMsgByElement(el) - this.applyCreateTask(event, el) + this.applyCreateTask('task', event, el) + return; + } + + // 创建子任务 + if (target.classList.contains('apply-create-subtask-button')) { + this.operateItem = this.findMsgByElement(el) + this.applyCreateTask('subtask', event, el) return; } diff --git a/resources/assets/js/store/markdown.js b/resources/assets/js/store/markdown.js index 69f3a9065..ef595515a 100644 --- a/resources/assets/js/store/markdown.js +++ b/resources/assets/js/store/markdown.js @@ -39,10 +39,13 @@ const MarkdownUtils = { const MarkdownPluginUtils = { // 配置选项 config: { + maxItems: 200, maxTitleLength: 200, maxDescLength: 1000, - maxItems: 200, - defaultTitle: '创建任务' + buttonLabels: { + task: '创建任务', + subtask: '创建子任务' + } }, // HTML转义函数 @@ -65,137 +68,118 @@ const MarkdownPluginUtils = { return value; }, - // 解析任务项 - parseTaskItems(content) { - const items = []; - let currentItem = {}; - let itemCount = 0; - - content.forEach(line => { - line = line.trim(); - if (!line) { - if (Object.keys(currentItem).length > 0) { - items.push(currentItem); - currentItem = {}; - itemCount++; - } - return; - } - - if (itemCount >= this.config.maxItems) { - return; - } - - const [key, ...valueParts] = line.split(':'); - const value = valueParts.join(':').trim(); - - if (key === 'title' && value) { - if (Object.keys(currentItem).length > 0) { - items.push(currentItem); - currentItem = {}; - itemCount++; - } - currentItem.title = this.validateInput(value, this.config.maxTitleLength); - } else if (key === 'desc' && value) { - currentItem.desc = this.validateInput(value, this.config.maxDescLength); - } - }); - - if (Object.keys(currentItem).length > 0 && itemCount < this.config.maxItems) { - items.push(currentItem); - } - - return items; - }, - - // 生成HTML - generateTaskHtml(items, status = null) { - if (!Array.isArray(items) || items.length === 0) { - return ''; - } - - const html = [ - '
', - '', - `
${this.escapeHtml($A.L(this.config.defaultTitle))}
`, - '
' - ); - return html.join('\n'); - }, - // 修改初始化插件函数 initCreateTaskPlugin(md) { - md.block.ruler.before('fence', 'create_task', (state, startLine, endLine, silent) => { - try { - const start = state.bMarks[startLine] + state.tShift[startLine]; - const max = state.eMarks[startLine]; + 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(); - const match = state.src.slice(start, max).trim().match(/^```\s*CreateTask\s*(applying|applied)?$/); - if (!match) { - return false; - } - - if (silent) { - return true; - } - - let nextLine = startLine + 1; - let content = []; - let found = false; - - while (nextLine < endLine) { - const line = state.src.slice(state.bMarks[nextLine], state.eMarks[nextLine]).trim(); - if (line === '```') { - found = true; - break; - } - content.push(line); - nextLine++; - } - - if (!found) { - return false; - } - - // 创建 token 并设置为空字符串内容 - const token = state.push('html_block', '', 0); - - // 如果有内容,则解析并生成HTML - if (content.length > 0) { - const items = this.parseTaskItems(content); - const html = this.generateTaskHtml(items, match[1]); - token.content = html || ''; - } else { - token.content = ''; // 空内容直接返回空字符串 - } - - token.map = [startLine, nextLine + 1]; - state.line = nextLine + 1; - return true; - - } catch (error) { - console.error('Error in create_task parser:', error); + // 检查开始标记,并获取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 = [ + '
    ', + '', + '
    ', + `
    ${$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; + }) } }; diff --git a/resources/assets/sass/pages/components/dialog-wrapper.scss b/resources/assets/sass/pages/components/dialog-wrapper.scss index 02b9fad6d..04b0ce482 100644 --- a/resources/assets/sass/pages/components/dialog-wrapper.scss +++ b/resources/assets/sass/pages/components/dialog-wrapper.scss @@ -2118,7 +2118,7 @@ } } &.applied { - color: #c5c8ce; + color: #a5a8ae; background-color: #f7f7f7; border-color: #dcdee2; &:before {