mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-11 18:42:54 +00:00
feat: 优化报告AI整理功能,优化报告编辑逻辑,移除冗余代码
This commit is contained in:
parent
0434bde16f
commit
0b6c478b4f
@ -125,6 +125,8 @@ export default {
|
|||||||
showModal: false,
|
showModal: false,
|
||||||
closing: false,
|
closing: false,
|
||||||
loadIng: 0,
|
loadIng: 0,
|
||||||
|
pendingAutoSubmit: false,
|
||||||
|
autoSubmitTimer: null,
|
||||||
|
|
||||||
// 输入配置
|
// 输入配置
|
||||||
inputValue: '',
|
inputValue: '',
|
||||||
@ -160,6 +162,7 @@ export default {
|
|||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
emitter.off('openAIAssistant', this.onOpenAIAssistant);
|
emitter.off('openAIAssistant', this.onOpenAIAssistant);
|
||||||
this.clearActiveSSEClients();
|
this.clearActiveSSEClients();
|
||||||
|
this.clearAutoSubmitTimer();
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
selectedModelOption({modelMap, inputModel}) {
|
selectedModelOption({modelMap, inputModel}) {
|
||||||
@ -179,19 +182,26 @@ export default {
|
|||||||
* 打开助手弹窗并应用参数
|
* 打开助手弹窗并应用参数
|
||||||
*/
|
*/
|
||||||
onOpenAIAssistant(params) {
|
onOpenAIAssistant(params) {
|
||||||
if ($A.isJson(params)) {
|
if (!$A.isJson(params)) {
|
||||||
this.inputValue = params.value || '';
|
params = {};
|
||||||
this.inputPlaceholder = params.placeholder || this.defaultPlaceholder || this.$L('请输入你的问题...');
|
|
||||||
this.inputRows = params.rows || this.defaultInputRows;
|
|
||||||
this.inputAutosize = params.autosize || this.defaultInputAutosize;
|
|
||||||
this.inputMaxlength = params.maxlength || this.defaultInputMaxlength;
|
|
||||||
this.applyHook = params.onApply || null;
|
|
||||||
this.beforeSendHook = params.onBeforeSend || null;
|
|
||||||
this.renderHook = params.onRender || null;
|
|
||||||
}
|
}
|
||||||
|
this.inputValue = params.value || '';
|
||||||
|
this.inputPlaceholder = params.placeholder || this.defaultPlaceholder || this.$L('请输入你的问题...');
|
||||||
|
this.inputRows = params.rows || this.defaultInputRows;
|
||||||
|
this.inputAutosize = params.autosize || this.defaultInputAutosize;
|
||||||
|
this.inputMaxlength = params.maxlength || this.defaultInputMaxlength;
|
||||||
|
this.applyHook = params.onApply || null;
|
||||||
|
this.beforeSendHook = params.onBeforeSend || null;
|
||||||
|
this.renderHook = params.onRender || null;
|
||||||
|
this.pendingAutoSubmit = !!params.autoSubmit;
|
||||||
|
//
|
||||||
this.responses = [];
|
this.responses = [];
|
||||||
this.showModal = true;
|
this.showModal = true;
|
||||||
this.clearActiveSSEClients();
|
this.clearActiveSSEClients();
|
||||||
|
this.clearAutoSubmitTimer();
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scheduleAutoSubmit();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -596,6 +606,49 @@ export default {
|
|||||||
this.activeSSEClients = [];
|
this.activeSSEClients = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除自动提交定时器
|
||||||
|
*/
|
||||||
|
clearAutoSubmitTimer() {
|
||||||
|
if (this.autoSubmitTimer) {
|
||||||
|
clearTimeout(this.autoSubmitTimer);
|
||||||
|
this.autoSubmitTimer = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调度自动提交
|
||||||
|
*/
|
||||||
|
scheduleAutoSubmit() {
|
||||||
|
if (!this.pendingAutoSubmit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const attemptSubmit = () => {
|
||||||
|
if (!this.pendingAutoSubmit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.canAutoSubmit()) {
|
||||||
|
this.pendingAutoSubmit = false;
|
||||||
|
this.clearAutoSubmitTimer();
|
||||||
|
this.onSubmit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.autoSubmitTimer = setTimeout(attemptSubmit, 200);
|
||||||
|
};
|
||||||
|
this.clearAutoSubmitTimer();
|
||||||
|
this.autoSubmitTimer = setTimeout(attemptSubmit, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否可以自动提交
|
||||||
|
*/
|
||||||
|
canAutoSubmit() {
|
||||||
|
return !this.modelsLoading
|
||||||
|
&& !!this.selectedModelOption
|
||||||
|
&& this.responses.length === 0
|
||||||
|
&& this.loadIng === 0;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新建响应卡片
|
* 新建响应卡片
|
||||||
*/
|
*/
|
||||||
@ -719,9 +772,11 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.closing = true;
|
this.closing = true;
|
||||||
|
this.pendingAutoSubmit = false;
|
||||||
|
this.clearAutoSubmitTimer();
|
||||||
|
this.clearActiveSSEClients();
|
||||||
this.showModal = false;
|
this.showModal = false;
|
||||||
this.responses = [];
|
this.responses = [];
|
||||||
this.clearActiveSSEClients();
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.closing = false;
|
this.closing = false;
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|||||||
@ -40,7 +40,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem :label="$L('汇报内容')" class="report-content-editor">
|
<FormItem :label="$L('汇报内容')" class="report-content-editor">
|
||||||
<TEditor v-model="reportData.content" height="100%"/>
|
<TEditor ref="reportEditor" v-model="reportData.content" height="100%"/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem class="report-foot">
|
<FormItem class="report-foot">
|
||||||
<div class="report-bottoms">
|
<div class="report-bottoms">
|
||||||
@ -48,7 +48,6 @@
|
|||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
class="report-bottom"
|
class="report-bottom"
|
||||||
:loading="aiOrganizeLoading"
|
|
||||||
@click="onOrganize">
|
@click="onOrganize">
|
||||||
<Icon type="md-construct" />
|
<Icon type="md-construct" />
|
||||||
{{ $L("AI 整理汇报") }}
|
{{ $L("AI 整理汇报") }}
|
||||||
@ -56,26 +55,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
<Modal
|
|
||||||
v-model="organizePreviewVisible"
|
|
||||||
:title="$L('整理结果预览')"
|
|
||||||
:mask-closable="false"
|
|
||||||
:styles="{
|
|
||||||
width: '90%',
|
|
||||||
maxWidth: '800px'
|
|
||||||
}">
|
|
||||||
<div class="report-content organize-preview user-select-auto" v-html="organizeResult.html"></div>
|
|
||||||
<div slot="footer" class="adaption">
|
|
||||||
<Button type="default" @click="closeOrganizePreview">{{ $L("取消") }}</Button>
|
|
||||||
<Button type="primary" @click="applyOrganize" :loading="aiOrganizeLoading">{{ $L("应用到汇报") }}</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import UserSelect from "../../../components/UserSelect.vue";
|
import UserSelect from "../../../components/UserSelect.vue";
|
||||||
import {mapState} from "vuex";
|
import {mapState} from "vuex";
|
||||||
|
import emitter from "../../../store/events";
|
||||||
|
import {MarkdownConver} from "../../../utils/markdown";
|
||||||
|
import {extractPlainText} from "../../../utils/text";
|
||||||
|
import {REPORT_AI_SYSTEM_PROMPT} from "../../../utils/ai";
|
||||||
|
|
||||||
const TEditor = () => import('../../../components/TEditor');
|
const TEditor = () => import('../../../components/TEditor');
|
||||||
export default {
|
export default {
|
||||||
@ -93,12 +82,6 @@ export default {
|
|||||||
return {
|
return {
|
||||||
loadIng: 0,
|
loadIng: 0,
|
||||||
receiveLoad: 0,
|
receiveLoad: 0,
|
||||||
aiOrganizeLoading: false,
|
|
||||||
organizePreviewVisible: false,
|
|
||||||
organizeResult: {
|
|
||||||
html: '',
|
|
||||||
model: '',
|
|
||||||
},
|
|
||||||
|
|
||||||
reportData: {
|
reportData: {
|
||||||
sign: "",
|
sign: "",
|
||||||
@ -281,44 +264,82 @@ export default {
|
|||||||
$A.messageWarning("请先填写汇报内容");
|
$A.messageWarning("请先填写汇报内容");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.aiOrganizeLoading) {
|
emitter.emit('openAIAssistant', {
|
||||||
return;
|
placeholder: this.$L('补充你想强调的重点或特殊说明,AI 将在此基础上整理汇报'),
|
||||||
}
|
onBeforeSend: this.handleReportAIBeforeSend,
|
||||||
this.aiOrganizeLoading = true;
|
onApply: this.handleReportAIApply,
|
||||||
this.$store.dispatch("call", {
|
autoSubmit: true,
|
||||||
url: 'report/ai_organize',
|
|
||||||
method: 'post',
|
|
||||||
data: {
|
|
||||||
content: this.reportData.content,
|
|
||||||
title: this.reportData.title,
|
|
||||||
type: this.reportData.type,
|
|
||||||
},
|
|
||||||
timeout: 60 * 1000,
|
|
||||||
}).then(({data}) => {
|
|
||||||
this.organizeResult = data || {html: '', model: ''};
|
|
||||||
if (!this.organizeResult.html) {
|
|
||||||
$A.messageWarning("AI 未返回整理内容");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.organizePreviewVisible = true;
|
|
||||||
}).catch(({msg}) => {
|
|
||||||
$A.messageError(msg);
|
|
||||||
}).finally(() => {
|
|
||||||
this.aiOrganizeLoading = false;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
closeOrganizePreview() {
|
buildReportAIContextData() {
|
||||||
this.organizePreviewVisible = false;
|
const sections = [];
|
||||||
|
const meta = [];
|
||||||
|
const title = (this.reportData.title || '').trim();
|
||||||
|
if (title) {
|
||||||
|
meta.push(`标题:${title}`);
|
||||||
|
}
|
||||||
|
if (this.reportData.sign) {
|
||||||
|
meta.push(`周期:${this.reportData.sign}`);
|
||||||
|
}
|
||||||
|
if (this.reportData.type) {
|
||||||
|
const typeMap = {weekly: this.$L('周报'), daily: this.$L('日报')};
|
||||||
|
meta.push(`类型:${typeMap[this.reportData.type] || this.reportData.type}`);
|
||||||
|
}
|
||||||
|
if (meta.length > 0) {
|
||||||
|
sections.push('## 汇报信息');
|
||||||
|
sections.push(...meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
const plain = extractPlainText(this.reportData.content || '');
|
||||||
|
if (plain) {
|
||||||
|
const limit = 3200;
|
||||||
|
const slice = plain.slice(0, limit);
|
||||||
|
sections.push('## 当前汇报正文');
|
||||||
|
sections.push(slice + (plain.length > limit ? '...' : ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sections.join('\n').trim();
|
||||||
},
|
},
|
||||||
|
|
||||||
applyOrganize() {
|
handleReportAIBeforeSend(context = []) {
|
||||||
if (!this.organizeResult.html) {
|
const prepared = [
|
||||||
$A.messageWarning("没有可应用的内容");
|
['system', REPORT_AI_SYSTEM_PROMPT]
|
||||||
|
];
|
||||||
|
const contextPrompt = this.buildReportAIContextData();
|
||||||
|
if (contextPrompt) {
|
||||||
|
let assistantContext = [
|
||||||
|
'以下是当前汇报草稿,请在此基础上整理结构、补充要点:',
|
||||||
|
contextPrompt,
|
||||||
|
].join('\n');
|
||||||
|
if ($A.getObject(context, [0,0]) === 'human') {
|
||||||
|
assistantContext += "\n----\n请根据以上背景再结合用户输入给出结果:++++";
|
||||||
|
}
|
||||||
|
prepared.push(['human', assistantContext]);
|
||||||
|
}
|
||||||
|
if (context.length > 0) {
|
||||||
|
prepared.push(...context);
|
||||||
|
}
|
||||||
|
return prepared;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleReportAIApply({rawOutput}) {
|
||||||
|
if (!rawOutput) {
|
||||||
|
$A.messageWarning("AI 未生成内容");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.reportData.content = this.organizeResult.html;
|
const html = MarkdownConver(rawOutput).trim();
|
||||||
this.organizePreviewVisible = false;
|
if (!html) {
|
||||||
|
$A.modalError("AI 内容解析失败,请重试");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.reportData.content = html;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const editor = this.$refs.reportEditor;
|
||||||
|
if (editor && typeof editor.focus === 'function') {
|
||||||
|
editor.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
$A.messageSuccess("已应用整理结果");
|
$A.messageSuccess("已应用整理结果");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
resources/assets/js/utils/ai.js
vendored
15
resources/assets/js/utils/ai.js
vendored
@ -322,6 +322,20 @@ const PROJECT_AI_SYSTEM_PROMPT = `你是一名资深的项目规划顾问,帮
|
|||||||
- 列表名称应当互不重复且语义明确
|
- 列表名称应当互不重复且语义明确
|
||||||
- 若上下文包含已有名称或列表,请在此基础上迭代优化`;
|
- 若上下文包含已有名称或列表,请在此基础上迭代优化`;
|
||||||
|
|
||||||
|
const REPORT_AI_SYSTEM_PROMPT = `你是一名资深团队管理教练,需要根据提供的周报/日报草稿进行整理。
|
||||||
|
|
||||||
|
工作目标:
|
||||||
|
1. 提取并归纳已完成事项的成果、影响和量化数据
|
||||||
|
2. 梳理下周期/次日的计划,确保每条计划都是可执行动作
|
||||||
|
3. 暴露存在的风险、阻塞以及需要管理者协助的事项
|
||||||
|
4. 若上下文提到关注重点或特殊受众,需在描述中明确回应
|
||||||
|
|
||||||
|
输出要求:
|
||||||
|
- 使用 Markdown 编写,至少包含以下一级标题:## 本周期完成、## 下周期计划、## 风险与支持
|
||||||
|
- 每个章节使用有序或无序列表,保持语句简洁、可度量
|
||||||
|
- 若原文包含数据或里程碑,保留并突出这些数字
|
||||||
|
- 若某一章节没有信息,请输出“暂无”而非留空`;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
AIModelNames,
|
AIModelNames,
|
||||||
AINormalizeJsonContent,
|
AINormalizeJsonContent,
|
||||||
@ -330,4 +344,5 @@ export {
|
|||||||
MESSAGE_AI_SYSTEM_PROMPT,
|
MESSAGE_AI_SYSTEM_PROMPT,
|
||||||
TASK_AI_SYSTEM_PROMPT,
|
TASK_AI_SYSTEM_PROMPT,
|
||||||
PROJECT_AI_SYSTEM_PROMPT,
|
PROJECT_AI_SYSTEM_PROMPT,
|
||||||
|
REPORT_AI_SYSTEM_PROMPT,
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user