feat: 长文本组件的对接调试

This commit is contained in:
2026-05-20 15:26:52 +08:00
parent dd7b41d1ad
commit d087ee6b35
6 changed files with 316 additions and 63 deletions

View File

@@ -1,28 +1,34 @@
// 简单的流式数据管理器:开启流、更新流、订阅流、关闭流
const streams = {};
function openStream(id, initial = '', finished = false) {
if (!id) return;
streams[id] = streams[id] || { text: '', finished: false, subs: new Set() };
streams[id].text = initial || '';
streams[id].finished = !!finished;
// notify existing subscribers
streams[id].subs.forEach((cb) => cb(streams[id].text, streams[id].finished));
function notify(stream) {
stream.subs.forEach((cb) => cb(stream.text, stream.finished, stream.payload));
}
function updateStream(id, text, finished = false) {
function openStream(id, initial = '', finished = false, payload = null) {
if (!id) return;
streams[id] = streams[id] || { text: '', finished: false, payload: null, subs: new Set() };
streams[id].text = initial || '';
streams[id].finished = !!finished;
streams[id].payload = payload || null;
// notify existing subscribers
notify(streams[id]);
}
function updateStream(id, text, finished = false, payload = null) {
if (!id || !streams[id]) return;
streams[id].text = text || '';
streams[id].finished = !!finished;
streams[id].subs.forEach((cb) => cb(streams[id].text, streams[id].finished));
streams[id].payload = payload || null;
notify(streams[id]);
}
function subscribe(id, cb) {
if (!id) return () => {};
streams[id] = streams[id] || { text: '', finished: false, subs: new Set() };
streams[id] = streams[id] || { text: '', finished: false, payload: null, subs: new Set() };
streams[id].subs.add(cb);
// send current snapshot immediately
cb(streams[id].text, streams[id].finished);
cb(streams[id].text, streams[id].finished, streams[id].payload);
return () => {
streams[id] && streams[id].subs.delete(cb);
// 移除空流
@@ -34,13 +40,13 @@ function subscribe(id, cb) {
function closeStream(id) {
if (!id || !streams[id]) return;
streams[id].subs.forEach((cb) => cb(streams[id].text, true));
streams[id].subs.forEach((cb) => cb(streams[id].text, true, streams[id].payload));
delete streams[id];
}
function getSnapshot(id) {
if (!id || !streams[id]) return { text: '', finished: false };
return { text: streams[id].text, finished: streams[id].finished };
if (!id || !streams[id]) return { text: '', finished: false, payload: null };
return { text: streams[id].text, finished: streams[id].finished, payload: streams[id].payload };
}
export default {

115
src/utils/longTextCard.js Normal file
View File

@@ -0,0 +1,115 @@
export const LONG_TEXT_KEYS = {
tag: "tag",
title: "title",
content: "content",
checklist: "checklist",
suggest: "suggest",
commodity: "commodity",
actionZone: "action_zone",
};
export const LONG_TEXT_FIELD_CONFIG = [
{ key: LONG_TEXT_KEYS.tag },
{ key: LONG_TEXT_KEYS.title },
{ key: LONG_TEXT_KEYS.content },
{ key: LONG_TEXT_KEYS.checklist },
{ key: LONG_TEXT_KEYS.suggest },
{ key: LONG_TEXT_KEYS.commodity },
{ key: LONG_TEXT_KEYS.actionZone },
];
export const LONG_TEXT_PREVIEW_KEYS = [
LONG_TEXT_KEYS.content,
LONG_TEXT_KEYS.title,
LONG_TEXT_KEYS.tag,
];
const CONFIGURED_KEYS = LONG_TEXT_FIELD_CONFIG.map((item) => item.key);
export const createLongTextData = () => ({
values: {},
parsedValues: {},
});
const toText = (value) => {
if (value === undefined || value === null) return "";
return typeof value === "string" ? value : String(value);
};
const shouldParseJSON = (raw) => {
if (!raw || typeof raw !== "string") return false;
return /^[\s]*[\[{]/.test(raw);
};
const tryParseJSON = (raw) => {
if (!shouldParseJSON(raw)) {
return { ok: false, value: null };
}
try {
return { ok: true, value: JSON.parse(raw) };
} catch (e) {
return { ok: false, value: null };
}
};
export const appendLongTextChunk = (target, chunk = {}) => {
if (!target || !chunk.contentKey) return target;
const key = String(chunk.contentKey);
const value = toText(chunk.contentValue);
if (!target.values) target.values = {};
if (!target.parsedValues) target.parsedValues = {};
target.values[key] = (target.values[key] || "") + value;
const parsed = tryParseJSON(target.values[key]);
if (parsed.ok) {
target.parsedValues[key] = parsed.value;
} else {
delete target.parsedValues[key];
}
return target;
};
export const getLongTextValue = (data, key) => {
if (!data || !data.values || !key) return "";
return data.values[key] || "";
};
export const getLongTextParsedValue = (data, key, fallback = undefined) => {
if (!data || !data.parsedValues || !key) return fallback;
return Object.prototype.hasOwnProperty.call(data.parsedValues, key)
? data.parsedValues[key]
: fallback;
};
export const getLongTextPreviewText = (data, keys = LONG_TEXT_PREVIEW_KEYS) => {
for (const key of keys) {
const value = getLongTextValue(data, key);
if (value) return value;
}
return "";
};
export const hasLongTextExtraSections = (data, previewKeys = LONG_TEXT_PREVIEW_KEYS) => {
if (!data || !data.values) return false;
return Object.keys(data.values).some((key) => !previewKeys.includes(key));
};
export const getLongTextSections = (data) => {
if (!data || !data.values) return [];
const extraKeys = Object.keys(data.values).filter(
(key) => !CONFIGURED_KEYS.includes(key),
);
return [...CONFIGURED_KEYS, ...extraKeys]
.filter((key) => Object.prototype.hasOwnProperty.call(data.values, key))
.map((key) => ({
contentKey: key,
contentValue: getLongTextValue(data, key),
parsedValue: getLongTextParsedValue(data, key, null),
}));
};