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
|
* @apiGroup dialog
|
||||||
* @apiName msg__applied
|
* @apiName msg__applied
|
||||||
*
|
*
|
||||||
* @apiParam {String} type 类型
|
|
||||||
* - CreateTask: 创建任务
|
|
||||||
* @apiParam {Number} index 索引
|
* @apiParam {Number} index 索引
|
||||||
* @apiParam {Number} msg_id 消息ID
|
* @apiParam {Number} msg_id 消息ID
|
||||||
*
|
*
|
||||||
@ -2907,7 +2905,6 @@ class DialogController extends AbstractController
|
|||||||
User::auth();
|
User::auth();
|
||||||
//
|
//
|
||||||
$msg_id = intval(Request::input('msg_id'));
|
$msg_id = intval(Request::input('msg_id'));
|
||||||
$type = trim(Request::input('type'));
|
|
||||||
$index = intval(Request::input('index'));
|
$index = intval(Request::input('index'));
|
||||||
//
|
//
|
||||||
$msg = WebSocketDialogMsg::whereId($msg_id)->first();
|
$msg = WebSocketDialogMsg::whereId($msg_id)->first();
|
||||||
@ -2917,17 +2914,17 @@ class DialogController extends AbstractController
|
|||||||
WebSocketDialog::checkDialog($msg->dialog_id);
|
WebSocketDialog::checkDialog($msg->dialog_id);
|
||||||
//
|
//
|
||||||
$originalMsg = $msg->getRawOriginal('msg');
|
$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;
|
$count = -1;
|
||||||
$updatedMsg = preg_replace_callback($pattern, function($matches) use (&$count, $index, $type) {
|
$updatedMsg = preg_replace_callback($pattern, function($matches) use (&$count, $index) {
|
||||||
$count++;
|
$count++;
|
||||||
if ($count === $index || ($index === 0 && $count === 1)) {
|
if ($count === $index || ($index === 0 && $count === 1)) {
|
||||||
return "```{$type} applied{$matches[2]}";
|
return "::: {$matches[1]} applied";
|
||||||
}
|
}
|
||||||
return $matches[0];
|
return $matches[0];
|
||||||
}, $originalMsg);
|
}, $originalMsg);
|
||||||
|
|
||||||
if ($count === 0) {
|
if ($count === -1) {
|
||||||
return Base::retError("未找到可应用的规则");
|
return Base::retError("未找到可应用的规则");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -593,13 +593,13 @@ class BotReceiveMsgTask extends AbstractTask
|
|||||||
}
|
}
|
||||||
$before_text[] = <<<EOF
|
$before_text[] = <<<EOF
|
||||||
如果你判断我想要添加任务,请将任务标题和描述添加到回复中,例如:
|
如果你判断我想要添加任务,请将任务标题和描述添加到回复中,例如:
|
||||||
```CreateTask
|
::: create-task-list
|
||||||
title: 任务标题1
|
title: 任务标题1
|
||||||
desc: 任务描述1
|
desc: 任务描述1
|
||||||
|
|
||||||
title: 任务标题2
|
title: 任务标题2
|
||||||
desc: 任务描述2
|
desc: 任务描述2
|
||||||
```
|
:::
|
||||||
EOF;
|
EOF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -621,10 +621,10 @@ class BotReceiveMsgTask extends AbstractTask
|
|||||||
}
|
}
|
||||||
$before_text[] = <<<EOF
|
$before_text[] = <<<EOF
|
||||||
如果你判断我想要添加子任务,请将子任务标题添加到回复中,例如:
|
如果你判断我想要添加子任务,请将子任务标题添加到回复中,例如:
|
||||||
```CreateSubTask
|
::: create-subtask-list
|
||||||
子任务标题1
|
title: 子任务标题1
|
||||||
子任务标题2
|
title: 子任务标题2
|
||||||
```
|
:::
|
||||||
EOF;
|
EOF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -3220,18 +3220,40 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async applyCreateTask(event, el) {
|
async applyCreateTask(type, event, el) {
|
||||||
const currentTarget = event.target;
|
const currentTarget = event.target;
|
||||||
if (currentTarget.classList.contains('applying') || currentTarget.classList.contains('applied')) {
|
if (currentTarget.classList.contains('applying') || currentTarget.classList.contains('applied')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentTarget.classList.add('applying')
|
currentTarget.classList.add('applying')
|
||||||
|
|
||||||
|
if (type === 'task') {
|
||||||
if (this.dialogData.group_type !== 'project') {
|
if (this.dialogData.group_type !== 'project') {
|
||||||
currentTarget.classList.remove('applying')
|
currentTarget.classList.remove('applying')
|
||||||
$A.modalError('只有在项目中才能创建任务')
|
$A.modalError('只有在项目中才能创建任务')
|
||||||
return
|
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;
|
let target = event.target;
|
||||||
while (target) {
|
while (target) {
|
||||||
@ -3246,14 +3268,9 @@ export default {
|
|||||||
}
|
}
|
||||||
if (!target) {
|
if (!target) {
|
||||||
currentTarget.classList.remove('applying')
|
currentTarget.classList.remove('applying')
|
||||||
$A.modalError('未找到任务内容')
|
$A.modalError('未找到内容')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!this.dialogData.group_info) {
|
|
||||||
currentTarget.classList.remove('applying')
|
|
||||||
$A.modalError('项目不存在')
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allTaskElements = el.querySelectorAll('.apply-create-task');
|
const allTaskElements = el.querySelectorAll('.apply-create-task');
|
||||||
const taskIndex = Array.from(allTaskElements).indexOf(target);
|
const taskIndex = Array.from(allTaskElements).indexOf(target);
|
||||||
@ -3268,6 +3285,12 @@ export default {
|
|||||||
.map(line => `<p>${line.trim()}</p>`)
|
.map(line => `<p>${line.trim()}</p>`)
|
||||||
.join('') : '';
|
.join('') : '';
|
||||||
|
|
||||||
|
if (type === 'subtask') {
|
||||||
|
return {
|
||||||
|
task_id: this.dialogData.group_info.id,
|
||||||
|
name: title,
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
project_id: this.dialogData.group_info.id,
|
project_id: this.dialogData.group_info.id,
|
||||||
name: title,
|
name: title,
|
||||||
@ -3276,29 +3299,40 @@ export default {
|
|||||||
})
|
})
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
|
const typeCall = type === 'subtask' ? 'taskAddSub' : 'taskAdd';
|
||||||
|
const typeLabel = type === 'subtask' ? '子任务' : '任务';
|
||||||
const results = await Promise.all(taskList.map(item =>
|
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 }),
|
success => ({ success: true, data: success }),
|
||||||
error => ({ success: false, error: error })
|
error => ({ success: false, error: error })
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
const successTasks = results.filter(r => r.success).map(r => r.data);
|
const successTasks = results.filter(r => r.success).map(r => r.data);
|
||||||
const failedTasks = results.filter(r => !r.success).map(r => r.error);
|
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) {
|
if (failedTasks.length > 0) {
|
||||||
$A.modalError(`成功创建 ${successTasks.length} 个任务,${failedTasks.length} 个任务创建失败`);
|
notice += `,${failedTasks.length} 个${typeLabel}创建失败`;
|
||||||
} else {
|
|
||||||
$A.messageSuccess(`成功创建 ${successTasks.length} 个任务`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTarget.classList.remove('applying')
|
currentTarget.classList.remove('applying')
|
||||||
currentTarget.classList.add('applied')
|
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", {
|
await this.$store.dispatch("call", {
|
||||||
url: 'dialog/msg/applied',
|
url: 'dialog/msg/applied',
|
||||||
data: {
|
data: {
|
||||||
type: 'CreateTask',
|
msg_id: this.operateItem.id,
|
||||||
index: taskIndex,
|
index: taskIndex,
|
||||||
msg_id: this.operateItem.id
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -3411,7 +3445,14 @@ export default {
|
|||||||
// 创建任务
|
// 创建任务
|
||||||
if (target.classList.contains('apply-create-task-button')) {
|
if (target.classList.contains('apply-create-task-button')) {
|
||||||
this.operateItem = this.findMsgByElement(el)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
194
resources/assets/js/store/markdown.js
vendored
194
resources/assets/js/store/markdown.js
vendored
@ -39,10 +39,13 @@ const MarkdownUtils = {
|
|||||||
const MarkdownPluginUtils = {
|
const MarkdownPluginUtils = {
|
||||||
// 配置选项
|
// 配置选项
|
||||||
config: {
|
config: {
|
||||||
|
maxItems: 200,
|
||||||
maxTitleLength: 200,
|
maxTitleLength: 200,
|
||||||
maxDescLength: 1000,
|
maxDescLength: 1000,
|
||||||
maxItems: 200,
|
buttonLabels: {
|
||||||
defaultTitle: '创建任务'
|
task: '创建任务',
|
||||||
|
subtask: '创建子任务'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// HTML转义函数
|
// HTML转义函数
|
||||||
@ -65,90 +68,15 @@ const MarkdownPluginUtils = {
|
|||||||
return value;
|
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) {
|
initCreateTaskPlugin(md) {
|
||||||
md.block.ruler.before('fence', 'create_task', (state, startLine, endLine, silent) => {
|
md.block.ruler.before('fence', 'create-task', (state, startLine, endLine, silent) => {
|
||||||
try {
|
|
||||||
const start = state.bMarks[startLine] + state.tShift[startLine];
|
const start = state.bMarks[startLine] + state.tShift[startLine];
|
||||||
const max = state.eMarks[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)?$/);
|
// 检查开始标记,并获取status值
|
||||||
|
const match = firstLine.match(/^:::\s*(create-task-list|create-subtask-list)(?:\s+(\S+))?$/);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -157,45 +85,101 @@ const MarkdownPluginUtils = {
|
|||||||
return true;
|
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 nextLine = startLine + 1;
|
||||||
let content = [];
|
let content = [];
|
||||||
let found = false;
|
|
||||||
|
|
||||||
|
// 查找结束标记
|
||||||
while (nextLine < endLine) {
|
while (nextLine < endLine) {
|
||||||
const line = state.src.slice(state.bMarks[nextLine], state.eMarks[nextLine]).trim();
|
const lineStart = state.bMarks[nextLine] + state.tShift[nextLine];
|
||||||
if (line === '```') {
|
const lineMax = state.eMarks[nextLine];
|
||||||
found = true;
|
const line = state.src.slice(lineStart, lineMax);
|
||||||
|
|
||||||
|
if (line.trim() === ':::') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
content.push(line);
|
content.push(line);
|
||||||
nextLine++;
|
nextLine++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
// 解析任务
|
||||||
return false;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 token 并设置为空字符串内容
|
// 开始新的任务
|
||||||
const token = state.push('html_block', '', 0);
|
currentTask = {title: titleMatch[1]};
|
||||||
|
isCollectingDesc = false;
|
||||||
// 如果有内容,则解析并生成HTML
|
descLines = [];
|
||||||
if (content.length > 0) {
|
} else if (descMatch) {
|
||||||
const items = this.parseTaskItems(content);
|
isCollectingDesc = true;
|
||||||
const html = this.generateTaskHtml(items, match[1]);
|
if (descMatch[1]) {
|
||||||
token.content = html || '';
|
descLines.push(descMatch[1]);
|
||||||
} else {
|
|
||||||
token.content = ''; // 空内容直接返回空字符串
|
|
||||||
}
|
}
|
||||||
|
} else if (isCollectingDesc && line.trim() && !line.trim().startsWith('title:')) {
|
||||||
token.map = [startLine, nextLine + 1];
|
// 收集多行描述,但不包括空行和新的title行
|
||||||
state.line = nextLine + 1;
|
descLines.push(line.trim());
|
||||||
return true;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error in create_task parser:', error);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 处理最后一个任务
|
||||||
|
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 {
|
&.applied {
|
||||||
color: #c5c8ce;
|
color: #a5a8ae;
|
||||||
background-color: #f7f7f7;
|
background-color: #f7f7f7;
|
||||||
border-color: #dcdee2;
|
border-color: #dcdee2;
|
||||||
&:before {
|
&:before {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user