|
|
@@ -110,12 +110,12 @@
|
|
|
>
|
|
|
</el-popover>
|
|
|
<el-popover placement="top" trigger="hover">
|
|
|
- <el-button size="small" @click.stop="startContinuousJobs('role')">
|
|
|
+ <el-button size="small" @click.stop="startContinuousJobs2('role')">
|
|
|
说话人编码
|
|
|
</el-button>
|
|
|
<el-button
|
|
|
size="small"
|
|
|
- @click.stop="startContinuousJobs('actionType')"
|
|
|
+ @click.stop="startContinuousJobs2('actionType')"
|
|
|
>
|
|
|
行为编码
|
|
|
</el-button>
|
|
|
@@ -2931,6 +2931,352 @@ ${JSON.stringify(_list)}
|
|
|
});
|
|
|
this.jobContext = null;
|
|
|
},
|
|
|
+ async startContinuousJobs2(type /* role, actionType */) {
|
|
|
+ const key = "default";
|
|
|
+ const { appToken, options, attention } = {
|
|
|
+ role: {
|
|
|
+ appToken: "app-TonzLPv7rPG0EtnFKszOWjwt",
|
|
|
+ options: ROLE_OPTIONS_GROUP[key],
|
|
|
+ attention: undefined
|
|
|
+ },
|
|
|
+ actionType: {
|
|
|
+ appToken: "app-zOMxBqyEKoJSvW10e5SS0kgj",
|
|
|
+ options: OPTIONS_GROUP[key],
|
|
|
+ attention: ATTENTION_GROUP[key]
|
|
|
+ }
|
|
|
+ }[type];
|
|
|
+ const config = {
|
|
|
+ headers: {
|
|
|
+ Authorization: `Bearer ${appToken}`,
|
|
|
+ "Content-Type": "application/json"
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const content = this.editorBarData.content;
|
|
|
+ const div = document.createElement("div");
|
|
|
+ div.innerHTML = content;
|
|
|
+ const tableRows = _.slice(div.querySelectorAll(`table tbody tr`), 1);
|
|
|
+ if (!tableRows.length || tableRows[0].cells.length < 7) {
|
|
|
+ this.$notify.info("没有可编码的内容");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 角色编码优化
|
|
|
+ if (type === "role") {
|
|
|
+ // 1. 收集所有唯一的Guest-XX,及空白role
|
|
|
+ // Build: guestRoles: { "Guest-1":[row1,row2...], ... }, emptyRoleRows:[row]
|
|
|
+ const guestRoleMap = {};
|
|
|
+ const emptyRoleRows = [];
|
|
|
+ tableRows.forEach((row, i) => {
|
|
|
+ const roleVal = row.cells[5].textContent.trim();
|
|
|
+ if (/^Guest-\d+$/.test(roleVal)) {
|
|
|
+ if (!guestRoleMap[roleVal]) guestRoleMap[roleVal] = [];
|
|
|
+ guestRoleMap[roleVal].push(row);
|
|
|
+ } else if (!roleVal) {
|
|
|
+ emptyRoleRows.push(row);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log("guestRoleMap", guestRoleMap);
|
|
|
+ console.log("emptyRoleRows", emptyRoleRows);
|
|
|
+
|
|
|
+ // 用于追踪已完成编码的条数字段
|
|
|
+ let completedCount = 0;
|
|
|
+
|
|
|
+ // 2. 为每个Guest-XX处理
|
|
|
+ for (const guest in guestRoleMap) {
|
|
|
+ // 不分批,直接全部guest一起处理
|
|
|
+ const rowsBatch = guestRoleMap[guest];
|
|
|
+ this.jobContext.progress.currentSize = rowsBatch.length;
|
|
|
+ // 进度 = (已完成条数) / 总数 * 100
|
|
|
+ this.jobContext.progress.current = completedCount;
|
|
|
+ this.jobContext.progress.percentage = (completedCount / tableRows.length) * 100;
|
|
|
+ // 取rows的content
|
|
|
+ const reqRows = rowsBatch.map(r => ({
|
|
|
+ content: r.cells[3].textContent,
|
|
|
+ role: guest
|
|
|
+ }));
|
|
|
+ let chunkResult = [];
|
|
|
+ let pauseCtrl = new AbortController();
|
|
|
+ this.jobContext.pause = () => {
|
|
|
+ const err = new Error();
|
|
|
+ err.name = "PauseError";
|
|
|
+ pauseCtrl.abort(err);
|
|
|
+ };
|
|
|
+ while (true) {
|
|
|
+ try {
|
|
|
+ const res = await fetch(
|
|
|
+ "https://dify.cocorobo.cn/v1/workflows/run",
|
|
|
+ {
|
|
|
+ signal: AbortSignal.any([
|
|
|
+ this.jobContext.ctrl.signal,
|
|
|
+ pauseCtrl.signal
|
|
|
+ ]),
|
|
|
+ method: "POST",
|
|
|
+ body: JSON.stringify({
|
|
|
+ inputs: {
|
|
|
+ rows: JSON.stringify(reqRows),
|
|
|
+ options: options.join(","),
|
|
|
+ attention
|
|
|
+ },
|
|
|
+ response_mode: "blocking",
|
|
|
+ user: this.userId
|
|
|
+ }),
|
|
|
+ ...config
|
|
|
+ }
|
|
|
+ ).then(res => res.json());
|
|
|
+ const error = _.get(res, ["data", "error"], null);
|
|
|
+ if (error) {
|
|
|
+ const err = new Error(error);
|
|
|
+ err.name = "DifyError";
|
|
|
+ throw err;
|
|
|
+ }
|
|
|
+ chunkResult = _.get(res, ["data", "outputs", "result"], []);
|
|
|
+ break; // 正常
|
|
|
+ } catch (err) {
|
|
|
+ if (err.name === "StopError") return;
|
|
|
+ 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;
|
|
|
+ pauseCtrl = new AbortController();
|
|
|
+ this.jobContext.pause = () => {
|
|
|
+ const err2 = new Error();
|
|
|
+ err2.name = "PauseError";
|
|
|
+ pauseCtrl.abort(err2);
|
|
|
+ };
|
|
|
+ } catch (err) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 统计chunkResult里老师/学生数量
|
|
|
+ const teacherCount = chunkResult.filter(x => x === "老师").length;
|
|
|
+ const studentCount = chunkResult.filter(x => x === "学生").length;
|
|
|
+ const finalRole =
|
|
|
+ teacherCount > studentCount
|
|
|
+ ? "老师"
|
|
|
+ : studentCount > teacherCount
|
|
|
+ ? "学生"
|
|
|
+ : "学生"; // 平局默认学生
|
|
|
+ // 替换所有行的role为finalRole
|
|
|
+ for (const row of rowsBatch) {
|
|
|
+ row.cells[5].textContent = finalRole;
|
|
|
+ }
|
|
|
+ // 更新进度
|
|
|
+ completedCount += rowsBatch.length;
|
|
|
+ this.jobContext.progress.current = completedCount;
|
|
|
+ this.jobContext.progress.percentage = (completedCount / tableRows.length) * 100;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 处理空白行,不分批,全部一起处理
|
|
|
+ if (emptyRoleRows.length > 0) {
|
|
|
+ const rowsBatch = emptyRoleRows;
|
|
|
+ // 进度
|
|
|
+ this.jobContext.progress.currentSize = rowsBatch.length;
|
|
|
+ this.jobContext.progress.current = completedCount;
|
|
|
+ this.jobContext.progress.percentage = (completedCount / tableRows.length) * 100;
|
|
|
+ // 取rows的content
|
|
|
+ const reqRows = rowsBatch.map(r => ({
|
|
|
+ content: r.cells[3].textContent,
|
|
|
+ role: "" // 空
|
|
|
+ }));
|
|
|
+ let chunkResult = [];
|
|
|
+ let pauseCtrl = new AbortController();
|
|
|
+ this.jobContext.pause = () => {
|
|
|
+ const err = new Error();
|
|
|
+ err.name = "PauseError";
|
|
|
+ pauseCtrl.abort(err);
|
|
|
+ };
|
|
|
+ while (true) {
|
|
|
+ try {
|
|
|
+ const res = await fetch(
|
|
|
+ "https://dify.cocorobo.cn/v1/workflows/run",
|
|
|
+ {
|
|
|
+ signal: AbortSignal.any([
|
|
|
+ this.jobContext.ctrl.signal,
|
|
|
+ pauseCtrl.signal
|
|
|
+ ]),
|
|
|
+ method: "POST",
|
|
|
+ body: JSON.stringify({
|
|
|
+ inputs: {
|
|
|
+ rows: JSON.stringify(reqRows),
|
|
|
+ options: options.join(","),
|
|
|
+ attention
|
|
|
+ },
|
|
|
+ response_mode: "blocking",
|
|
|
+ user: this.userId
|
|
|
+ }),
|
|
|
+ ...config
|
|
|
+ }
|
|
|
+ ).then(res => res.json());
|
|
|
+ const error = _.get(res, ["data", "error"], null);
|
|
|
+ if (error) {
|
|
|
+ const err = new Error(error);
|
|
|
+ err.name = "DifyError";
|
|
|
+ throw err;
|
|
|
+ }
|
|
|
+ chunkResult = _.get(res, ["data", "outputs", "result"], []);
|
|
|
+ break; // 正常
|
|
|
+ } catch (err) {
|
|
|
+ if (err.name === "StopError") return;
|
|
|
+ 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;
|
|
|
+ pauseCtrl = new AbortController();
|
|
|
+ this.jobContext.pause = () => {
|
|
|
+ const err2 = new Error();
|
|
|
+ err2.name = "PauseError";
|
|
|
+ pauseCtrl.abort(err2);
|
|
|
+ };
|
|
|
+ } catch (err) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ chunkResult.forEach((role, idx) => {
|
|
|
+ rowsBatch[idx].cells[5].textContent = role;
|
|
|
+ });
|
|
|
+ // 更新进度
|
|
|
+ completedCount += rowsBatch.length;
|
|
|
+ this.jobContext.progress.current = completedCount;
|
|
|
+ this.jobContext.progress.percentage = (completedCount / tableRows.length) * 100;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重新整理actionType (第7列) 留给后续流程处理
|
|
|
+ // 组装回去并保存
|
|
|
+ // 生成actionTypes数组
|
|
|
+ const _actionTypes = tableRows.map(row =>
|
|
|
+ _.get(row, ["cells", 6, "textContent"], "")
|
|
|
+ );
|
|
|
+ this.actionTypesMap.jsonData[key] = _actionTypes;
|
|
|
+ // 保存回HTML
|
|
|
+ const _table = div.querySelector("table");
|
|
|
+ this.editorBarData.content = _table.outerHTML;
|
|
|
+ } else if (type === "actionType") {
|
|
|
+ // 行为编码不进行分批,直接全部处理
|
|
|
+ this.actionTypesMap.jsonData[key] = Array.from({
|
|
|
+ length: tableRows.length
|
|
|
+ }).fill("");
|
|
|
+ let pauseCtrl = new AbortController();
|
|
|
+ this.jobContext.pause = () => {
|
|
|
+ const err = new Error();
|
|
|
+ err.name = "PauseError";
|
|
|
+ pauseCtrl.abort(err);
|
|
|
+ };
|
|
|
+ while (!this.jobContext.ctrl.signal.aborted) {
|
|
|
+ try {
|
|
|
+ this.jobContext.progress.current = 0;
|
|
|
+ this.jobContext.progress.currentSize = tableRows.length;
|
|
|
+ this.jobContext.progress.percentage = 0;
|
|
|
+
|
|
|
+ const res = await fetch("https://dify.cocorobo.cn/v1/workflows/run", {
|
|
|
+ signal: AbortSignal.any([this.jobContext.ctrl.signal, pauseCtrl.signal]),
|
|
|
+ method: "POST",
|
|
|
+ body: JSON.stringify({
|
|
|
+ inputs: {
|
|
|
+ rows: JSON.stringify(
|
|
|
+ tableRows.map(r => ({
|
|
|
+ content: r.cells[3].textContent,
|
|
|
+ role: r.cells[5].textContent
|
|
|
+ }))
|
|
|
+ ),
|
|
|
+ options: options.join(","),
|
|
|
+ attention
|
|
|
+ },
|
|
|
+ response_mode: "blocking",
|
|
|
+ user: this.userId
|
|
|
+ }),
|
|
|
+ ...config
|
|
|
+ }).then(res => res.json());
|
|
|
+ const error = _.get(res, ["data", "error"], null);
|
|
|
+ if (error) {
|
|
|
+ const err = new Error(error);
|
|
|
+ err.name = "DifyError";
|
|
|
+ throw err;
|
|
|
+ }
|
|
|
+ const result = _.get(res, ["data", "outputs", "result"], []);
|
|
|
+ this.actionTypesMap.jsonData[key] = Object.assign(
|
|
|
+ new Array(tableRows.length),
|
|
|
+ result.slice(0, tableRows.length)
|
|
|
+ );
|
|
|
+ break;
|
|
|
+ } catch (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;
|
|
|
+ pauseCtrl = new AbortController();
|
|
|
+ this.jobContext.pause = () => {
|
|
|
+ const err2 = new Error();
|
|
|
+ err2.name = "PauseError";
|
|
|
+ pauseCtrl.abort(err2);
|
|
|
+ };
|
|
|
+ } catch (_err) {
|
|
|
+ // user aborted
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.changeEditorBar({
|
|
|
+ transcriptionData: this.transcriptionData.content,
|
|
|
+ editorBarData: this.editorBarData
|
|
|
+ });
|
|
|
+ this.jobContext = null;
|
|
|
+ },
|
|
|
onEdit(value) {
|
|
|
const div = document.createElement("div");
|
|
|
div.innerHTML = value;
|