|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
|
- <div class="chatArea" v-loading="loading" :element-loading-text="loadingText">
|
|
|
+ <div class="chatArea" v-loading="loading">
|
|
|
<div class="m-operation">
|
|
|
<div>实时转录</div>
|
|
|
<div>{{ createTime }}</div>
|
|
@@ -60,7 +60,7 @@
|
|
|
@change="changeEditor"
|
|
|
>
|
|
|
<el-button
|
|
|
- style="position: absolute; bottom: 70px; right: 20px; z-index: 10002"
|
|
|
+ style="position: absolute; bottom: 20px; right: 100px; z-index: 10002"
|
|
|
type="primary"
|
|
|
@click.stop="generateActionTypesMap"
|
|
|
>自动编码</el-button
|
|
@@ -378,6 +378,42 @@
|
|
|
</div>
|
|
|
<!-- <el-progress style="width:80%" :text-inside="true" :stroke-width="24" :percentage="progressData.value"></el-progress> -->
|
|
|
</div>
|
|
|
+ <div class="chatAreaLoading" v-else-if="jobContext">
|
|
|
+ <div class="cal_box" :style="{ height: 'fit-content' }">
|
|
|
+ <el-progress
|
|
|
+ type="circle"
|
|
|
+ :percentage="jobContext.progress.percentage"
|
|
|
+ :color="jobContext.status === 'paused' ? '#ff4949' : '#20a0ff'"
|
|
|
+ :format="() => `${jobContext.progress.current}/${jobContext.progress.total}`"
|
|
|
+ ></el-progress>
|
|
|
+ <span v-if="jobContext.status === 'paused' && jobContext.error">{{
|
|
|
+ jobContext.error
|
|
|
+ }}</span>
|
|
|
+ <div :style="{ display: 'flex' }">
|
|
|
+ <el-button
|
|
|
+ v-if="jobContext.status === 'paused'"
|
|
|
+ @click="() => jobContext.restart()"
|
|
|
+ icon="el-icon-video-play"
|
|
|
+ type="primary"
|
|
|
+ >
|
|
|
+ 继续
|
|
|
+ </el-button>
|
|
|
+ <!-- <el-button
|
|
|
+ v-if="jobContext.status === 'running'"
|
|
|
+ icon="el-icon-video-pause"
|
|
|
+ @click="() => jobContext.pause()"
|
|
|
+ >暂停</el-button
|
|
|
+ > -->
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ icon="el-icon-switch-button"
|
|
|
+ @click="() => jobContext.stop()"
|
|
|
+ >终止</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- <el-progress style="width:80%" :text-inside="true" :stroke-width="24" :percentage="progressData.value"></el-progress> -->
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
@@ -408,6 +444,23 @@ import _ from "lodash";
|
|
|
// console.log('当前录音的总数据([DataView, DataView...])', params.data);
|
|
|
// console.log('--------------END---------------')
|
|
|
// };
|
|
|
+const OPTIONS_GROUP = {
|
|
|
+ default: [
|
|
|
+ "老师讲课",
|
|
|
+ "老师提问或点名",
|
|
|
+ "老师板书或操作",
|
|
|
+ "老师评价或反馈",
|
|
|
+ "老师其他",
|
|
|
+ "学生发言",
|
|
|
+ "学生小组活动",
|
|
|
+ "学生自主学习",
|
|
|
+ "学生汇报分享",
|
|
|
+ "学生其他",
|
|
|
+ ],
|
|
|
+};
|
|
|
+const ATTENTION_GROUP = {
|
|
|
+ default: "先根据说话人角色判断,再在对应角色的选项中选择选项",
|
|
|
+};
|
|
|
|
|
|
// 自定义指令,用于处理点击外部区域的事件
|
|
|
const clickOutside = {
|
|
@@ -503,7 +556,6 @@ export default {
|
|
|
languageRadio: 2, //设置选择语言
|
|
|
languageShow: false, //控制显示
|
|
|
loading: false,
|
|
|
- loadingText: undefined,
|
|
|
chatLoading: false,
|
|
|
transcriptionLoading: false,
|
|
|
uploadFileLoading: false,
|
|
@@ -576,6 +628,7 @@ export default {
|
|
|
},
|
|
|
chatList: [],
|
|
|
actionTypesMap: {},
|
|
|
+ jobContext: null,
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
@@ -598,16 +651,14 @@ export default {
|
|
|
},
|
|
|
tid: {
|
|
|
async handler(tid) {
|
|
|
- console.log("tid handle: ", tid);
|
|
|
if (!tid) {
|
|
|
this.actionTypesMap = undefined;
|
|
|
return;
|
|
|
}
|
|
|
this.actionTypesMap = await this.loadActionTypesMap();
|
|
|
- console.log("result: ", this.actionTypesMap);
|
|
|
if (!this.actionTypesMap) {
|
|
|
const res = await this.insertActionTypes();
|
|
|
- console.log("res: ", res);
|
|
|
+ this.actionTypesMap = await this.loadActionTypesMap();
|
|
|
}
|
|
|
},
|
|
|
immediate: true,
|
|
@@ -2136,7 +2187,7 @@ ${JSON.stringify(_list)}
|
|
|
</table>
|
|
|
`;
|
|
|
}
|
|
|
- console.log(_content);
|
|
|
+ // console.log(_content);
|
|
|
this.editorBarData = { ..._editorBarData, content: _content };
|
|
|
},
|
|
|
// 获取对话记录
|
|
@@ -2271,18 +2322,8 @@ ${JSON.stringify(_list)}
|
|
|
const key = "default";
|
|
|
const appToken = "app-zOMxBqyEKoJSvW10e5SS0kgj";
|
|
|
// the default options
|
|
|
- const options = [
|
|
|
- "老师讲课",
|
|
|
- "老师提问或点名",
|
|
|
- "老师板书或操作",
|
|
|
- "老师评价或反馈",
|
|
|
- "老师其他",
|
|
|
- "学生发言",
|
|
|
- "学生小组活动",
|
|
|
- "学生自主学习",
|
|
|
- "学生汇报分享",
|
|
|
- "学生其他",
|
|
|
- ];
|
|
|
+ const options = OPTIONS_GROUP[key];
|
|
|
+ const attention = ATTENTION_GROUP[key];
|
|
|
const config = {
|
|
|
headers: {
|
|
|
Authorization: `Bearer ${appToken}`,
|
|
@@ -2293,31 +2334,51 @@ ${JSON.stringify(_list)}
|
|
|
const div = document.createElement("div");
|
|
|
div.innerHTML = content;
|
|
|
const tableRows = _.slice(div.querySelectorAll(`table tbody tr`), 1);
|
|
|
- if (!tableRows.length) {
|
|
|
+ if (!tableRows.length || tableRows[0].cells.length < 7) {
|
|
|
this.$notify.info("没有可编码的内容");
|
|
|
return;
|
|
|
}
|
|
|
- tableRows.forEach((i) => {
|
|
|
- while (i.cells.length >= 7) {
|
|
|
- i.removeChild(i.lastElementChild);
|
|
|
- }
|
|
|
- });
|
|
|
- this.loading = true;
|
|
|
- this.loadingText = `0/${tableRows.length}`;
|
|
|
- try {
|
|
|
- const chunkSize = 200;
|
|
|
- this.actionTypesMap.jsonData[key] = Array.from({ length: tableRows.length });
|
|
|
- // action type fetch by every { chunkSize } rows
|
|
|
- for (const [index, rows] of _.chunk(tableRows, chunkSize).entries()) {
|
|
|
+ const ctrl = new AbortController();
|
|
|
+ this.jobContext = {
|
|
|
+ ctrl,
|
|
|
+ status: "running",
|
|
|
+ restart: () => {},
|
|
|
+ pause: () => {},
|
|
|
+ stop: () => {
|
|
|
+ const err = new Error();
|
|
|
+ err.name = "StopError";
|
|
|
+ this.jobContext.ctrl.abort(err);
|
|
|
+ },
|
|
|
+ progress: {
|
|
|
+ current: 0,
|
|
|
+ currentSize: 0,
|
|
|
+ total: tableRows.length,
|
|
|
+ percentage: 0,
|
|
|
+ },
|
|
|
+ };
|
|
|
+ // large chunk size will cause token limit and slower
|
|
|
+ const chunkSize = 30;
|
|
|
+ this.actionTypesMap.jsonData[key] = Array.from({ length: tableRows.length }).fill(
|
|
|
+ ""
|
|
|
+ );
|
|
|
+
|
|
|
+ const jobs = _.chunk(tableRows, chunkSize).map((rows, index) => {
|
|
|
+ return async (pauseSignal) => {
|
|
|
+ this.jobContext.progress.current = index * chunkSize;
|
|
|
+ this.jobContext.progress.currentSize = rows.length;
|
|
|
+ this.jobContext.progress.percentage =
|
|
|
+ ((index * chunkSize) / tableRows.length) * 100;
|
|
|
const res = await fetch("https://dify.cocorobo.cn/v1/workflows/run", {
|
|
|
+ signal: AbortSignal.any([this.jobContext.ctrl.signal, pauseSignal]),
|
|
|
method: "POST",
|
|
|
body: JSON.stringify({
|
|
|
inputs: {
|
|
|
// PERF better to just include `role` and `content` to minimize token cost
|
|
|
- rows: rows
|
|
|
- .map((r) => `${r.cells[3].textContent}\t${r.cells[5].textContent}`)
|
|
|
- .join("\n"),
|
|
|
+ rows: JSON.stringify(
|
|
|
+ rows.map((r) => [r.cells[3].textContent, r.cells[5].textContent])
|
|
|
+ ),
|
|
|
options: options.join(","),
|
|
|
+ attention,
|
|
|
},
|
|
|
response_mode: "blocking",
|
|
|
user: this.userId,
|
|
@@ -2325,9 +2386,12 @@ ${JSON.stringify(_list)}
|
|
|
...config,
|
|
|
}).then((res) => res.json());
|
|
|
const error = _.get(res, ["data", "error"], null);
|
|
|
+ // test
|
|
|
+ // throw new Error('mock error');
|
|
|
if (error) {
|
|
|
- this.$notify.error(error);
|
|
|
- return;
|
|
|
+ const err = new Error(error);
|
|
|
+ err.name = "DifyError";
|
|
|
+ throw err;
|
|
|
}
|
|
|
const chunkResult = _.get(res, ["data", "outputs", "result"], []);
|
|
|
this.actionTypesMap.jsonData[key].splice(
|
|
@@ -2339,15 +2403,46 @@ ${JSON.stringify(_list)}
|
|
|
transcriptionData: this.transcriptionData.content,
|
|
|
editorBarData: this.editorBarData,
|
|
|
});
|
|
|
- this.loadingText = `${index * chunkSize + rows.length}/${tableRows.length}`;
|
|
|
+ };
|
|
|
+ });
|
|
|
+ while (!this.jobContext.ctrl.signal.aborted && jobs.length > 0) {
|
|
|
+ const job = jobs.shift();
|
|
|
+ while (!this.jobContext.ctrl.signal.aborted) {
|
|
|
+ const pauseCtrl = new AbortController();
|
|
|
+ this.jobContext.pause = () => {
|
|
|
+ const err = new Error();
|
|
|
+ err.name = "PauseError";
|
|
|
+ pauseCtrl.abort(err);
|
|
|
+ };
|
|
|
+ try {
|
|
|
+ await job(pauseCtrl.signal);
|
|
|
+ break;
|
|
|
+ } catch (err) {
|
|
|
+ // console.error(err);
|
|
|
+ if (err.name === "StopError") {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ this.jobContext.status = "paused";
|
|
|
+ if (err.name === "PauseError") {
|
|
|
+ this.jobContext.error = `用户暂停`;
|
|
|
+ } else {
|
|
|
+ this.jobContext.error = `部分生成失败。点击按钮可继续尝试生成`;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ const restartPromise = new Promise((resolve, reject) => {
|
|
|
+ this.jobContext.restart = resolve;
|
|
|
+ this.jobContext.ctrl.signal.addEventListener("abort", reject);
|
|
|
+ });
|
|
|
+ await restartPromise;
|
|
|
+ this.jobContext.status = "running";
|
|
|
+ this.jobContext.error = null;
|
|
|
+ } catch (_err) {
|
|
|
+ // nothing to do, just continue
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- } catch (err) {
|
|
|
- console.error(err);
|
|
|
- this.$notify.error(err);
|
|
|
- } finally {
|
|
|
- this.loading = false;
|
|
|
- this.loadingText = undefined;
|
|
|
}
|
|
|
+ this.jobContext = null;
|
|
|
},
|
|
|
},
|
|
|
mounted() {},
|