mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-12 19:35:50 +00:00
perf: 优化AI创建任务
This commit is contained in:
parent
13222fbe9a
commit
cbd9e8a33c
@ -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("未找到可应用的规则");
|
||||
}
|
||||
|
||||
|
||||
@ -593,13 +593,13 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
}
|
||||
$before_text[] = <<<EOF
|
||||
如果你判断我想要添加任务,请将任务标题和描述添加到回复中,例如:
|
||||
```CreateTask
|
||||
::: create-task-list
|
||||
title: 任务标题1
|
||||
desc: 任务描述1
|
||||
|
||||
title: 任务标题2
|
||||
desc: 任务描述2
|
||||
```
|
||||
:::
|
||||
EOF;
|
||||
}
|
||||
break;
|
||||
@ -621,10 +621,10 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
}
|
||||
$before_text[] = <<<EOF
|
||||
如果你判断我想要添加子任务,请将子任务标题添加到回复中,例如:
|
||||
```CreateSubTask
|
||||
子任务标题1
|
||||
子任务标题2
|
||||
```
|
||||
::: create-subtask-list
|
||||
title: 子任务标题1
|
||||
title: 子任务标题2
|
||||
:::
|
||||
EOF;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -3220,16 +3220,38 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
async applyCreateTask(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 (this.dialogData.group_type !== 'project') {
|
||||
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('只有在项目中才能创建任务')
|
||||
$A.modalError('未知类型')
|
||||
return
|
||||
}
|
||||
|
||||
@ -3246,14 +3268,9 @@ export default {
|
||||
}
|
||||
if (!target) {
|
||||
currentTarget.classList.remove('applying')
|
||||
$A.modalError('未找到任务内容')
|
||||
$A.modalError('未找到内容')
|
||||
return
|
||||
}
|
||||
if (!this.dialogData.group_info) {
|
||||
currentTarget.classList.remove('applying')
|
||||
$A.modalError('项目不存在')
|
||||
return;
|
||||
}
|
||||
|
||||
const allTaskElements = el.querySelectorAll('.apply-create-task');
|
||||
const taskIndex = Array.from(allTaskElements).indexOf(target);
|
||||
@ -3268,6 +3285,12 @@ export default {
|
||||
.map(line => `<p>${line.trim()}</p>`)
|
||||
.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;
|
||||
}
|
||||
|
||||
|
||||
240
resources/assets/js/store/markdown.js
vendored
240
resources/assets/js/store/markdown.js
vendored
@ -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 = [
|
||||
'<div class="apply-create-task">',
|
||||
'<ul>'
|
||||
];
|
||||
|
||||
items.forEach((item, index) => {
|
||||
if (item.title) {
|
||||
html.push(`<li>`);
|
||||
html.push(`<div class="task-index">${index + 1}.</div>`);
|
||||
html.push(`<div class="task-item">`);
|
||||
html.push(`<div class="title">${this.escapeHtml(item.title)}</div>`);
|
||||
if (item.desc) {
|
||||
html.push(`<div class="desc">${this.escapeHtml(item.desc)}</div>`);
|
||||
}
|
||||
html.push('</div>');
|
||||
html.push('</li>');
|
||||
}
|
||||
});
|
||||
|
||||
html.push(
|
||||
'</ul>',
|
||||
`<div class="apply-button"><div class="apply-create-task-button ${status||''}">${this.escapeHtml($A.L(this.config.defaultTitle))}</div></div>`,
|
||||
'</div>'
|
||||
);
|
||||
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) => [
|
||||
'<li>',
|
||||
showIndex ? `<div class="task-index">${index + 1}.</div>` : '',
|
||||
'<div class="task-item">',
|
||||
`<div class="title">${this.escapeHtml(this.validateInput(task.title, this.config.maxTitleLength))}</div>`,
|
||||
task.desc && match[1] === 'create-task-list' ? `<div class="desc">${this.escapeHtml(this.validateInput(task.desc, this.config.maxDescLength))}</div>` : '',
|
||||
'</div>',
|
||||
'</li>'
|
||||
].join(''));
|
||||
|
||||
const htmls = [
|
||||
'<div class="apply-create-task">',
|
||||
'<ul>',
|
||||
taskItems.join(''),
|
||||
'</ul>',
|
||||
'<div class="apply-button">',
|
||||
`<div class="apply-create-${listType}-button${status ? ' ' + status : ''}">${$A.L(buttonTitle)}</div>`,
|
||||
'</div>',
|
||||
'</div>'
|
||||
];
|
||||
|
||||
// 添加token
|
||||
const token = state.push('html_block', '', 0);
|
||||
token.content = htmls.join('');
|
||||
token.map = [startLine, nextLine];
|
||||
|
||||
state.line = nextLine + 1;
|
||||
return true;
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -2118,7 +2118,7 @@
|
||||
}
|
||||
}
|
||||
&.applied {
|
||||
color: #c5c8ce;
|
||||
color: #a5a8ae;
|
||||
background-color: #f7f7f7;
|
||||
border-color: #dcdee2;
|
||||
&:before {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user