fix: 修复管理端前端类型校验问题

- 修正知识库与 Bot 设置页相关组件的类型定义和空值处理

- 补齐工作流与公开聊天页的前端类型约束和动态导入类型

- 收敛本次改动文件的局部格式与样式规范,确保 pnpm check:type 通过
This commit is contained in:
2026-04-05 20:36:25 +08:00
parent b5dd427920
commit bb72e19c84
15 changed files with 361 additions and 244 deletions

View File

@@ -30,7 +30,9 @@ type NodeLike = {
type UpdateNodeData = (
nodeId: string,
data: Record<string, any> | ((node: Record<string, any>) => Record<string, any>),
data:
| Record<string, any>
| ((node: Record<string, any>) => Record<string, any>),
) => void;
type FlowInstance = {
@@ -80,7 +82,9 @@ function getSourceKey(datasetRef?: DatasetRefPayload | null) {
return datasetRef?.sourceId == null ? '' : String(datasetRef.sourceId);
}
function createSourceOnlyDatasetRef(source: ManagedDatasetSourceOption): DatasetRefPayload {
function createSourceOnlyDatasetRef(
source: ManagedDatasetSourceOption,
): DatasetRefPayload {
return {
sourceId: source.sourceId,
catalogId: null,
@@ -337,7 +341,10 @@ function buildStyles() {
`;
}
function filterTableOptions(options: ManagedDatasetOption[], searchText: string) {
function filterTableOptions(
options: ManagedDatasetOption[],
searchText: string,
) {
const keyword = searchText.trim().toLowerCase();
if (!keyword) {
return options;
@@ -350,7 +357,11 @@ function ensureOptionsLoaded(
parent: HTMLElement,
node: NodeLike,
flowInstance: RenderContext,
rerender: (parent: HTMLElement, node: NodeLike, flowInstance?: RenderContext) => void,
rerender: (
parent: HTMLElement,
node: NodeLike,
flowInstance?: RenderContext,
) => void,
) {
if (state.loadingOptions || state.optionsLoaded) {
return;
@@ -394,7 +405,11 @@ function buildPickerListItem(
`;
}
function buildSourceList(options: ManagedDatasetSourceOption[], activeKey: string, emptyText: string) {
function buildSourceList(
options: ManagedDatasetSourceOption[],
activeKey: string,
emptyText: string,
) {
if (!options.length) {
return `<div class="dataset-node-empty">${emptyText}</div>`;
}
@@ -412,7 +427,11 @@ function buildSourceList(options: ManagedDatasetSourceOption[], activeKey: strin
.join('');
}
function buildTableList(options: ManagedDatasetOption[], activeKey: string, emptyText: string) {
function buildTableList(
options: ManagedDatasetOption[],
activeKey: string,
emptyText: string,
) {
if (!options.length) {
return `<div class="dataset-node-empty">${emptyText}</div>`;
}
@@ -443,20 +462,29 @@ function buildSearchSummary(currentSource?: ManagedDatasetSourceOption) {
}
function bindInteractiveElements(parent: HTMLElement) {
parent.querySelectorAll<HTMLElement>('button, input, select, textarea').forEach((element) => {
element.onpointerdown = (event) => event.stopPropagation();
element.onmousedown = (event) => event.stopPropagation();
});
parent
.querySelectorAll<HTMLElement>('button, input, select, textarea')
.forEach((element) => {
element.onpointerdown = (event) => event.stopPropagation();
element.onmousedown = (event) => event.stopPropagation();
});
}
export function rerenderSearchNode(parent: HTMLElement, node: NodeLike, flowInstance?: RenderContext) {
export function rerenderSearchNode(
parent: HTMLElement,
node: NodeLike,
flowInstance?: RenderContext,
) {
const state = getState(parent);
const updateNodeData = getUpdateNodeData(parent, flowInstance);
ensureOptionsLoaded(state, parent, node, flowInstance, rerenderSearchNode);
const datasetRef = (node.data?.datasetRef || null) as DatasetRefPayload | null;
const datasetRef = (node.data?.datasetRef ||
null) as DatasetRefPayload | null;
const sourceKey = getSourceKey(datasetRef);
const currentSource = state.sources.find((item) => String(item.sourceId) === sourceKey);
const currentSource = state.sources.find(
(item) => String(item.sourceId) === sourceKey,
);
parent.innerHTML = `
${buildStyles()}
@@ -469,61 +497,75 @@ export function rerenderSearchNode(parent: HTMLElement, node: NodeLike, flowInst
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M11.9999 13.1714L16.9497 8.22168L18.3639 9.63589L11.9999 15.9999L5.63599 9.63589L7.0502 8.22168L11.9999 13.1714Z"></path></svg>
</span>
</button>
${state.pickerOpen ? `
${
state.pickerOpen
? `
<div class="dataset-node-picker">
<div class="dataset-node-picker-body">
${state.loadingOptions ? '<div class="dataset-node-empty">正在加载连接服务...</div>' : buildSourceList(state.sources, sourceKey, '暂无可用连接服务')}
</div>
</div>
` : ''}
`
: ''
}
</div>
</div>
`;
bindInteractiveElements(parent);
parent.querySelector<HTMLElement>('[data-action="toggle-picker"]')?.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
state.pickerOpen = !state.pickerOpen;
rerenderSearchNode(parent, node, flowInstance);
});
parent.querySelectorAll<HTMLElement>('[data-action="select-source"]').forEach((element) => {
element.addEventListener('click', (event) => {
parent
.querySelector<HTMLElement>('[data-action="toggle-picker"]')
?.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
const sourceId = element.dataset.sourceId;
const source = state.sources.find((item) => String(item.sourceId) === String(sourceId));
if (!source) {
return;
}
state.pickerOpen = false;
const nextDatasetRef = createSourceOnlyDatasetRef(source);
node.data = {
...(node.data || {}),
datasetRef: nextDatasetRef,
sourceName: source.sourceName,
sourceType: source.sourceType,
};
updateNodeData?.(node.id, {
datasetRef: nextDatasetRef,
sourceName: source.sourceName,
sourceType: source.sourceType,
});
state.pickerOpen = !state.pickerOpen;
rerenderSearchNode(parent, node, flowInstance);
});
});
parent
.querySelectorAll<HTMLElement>('[data-action="select-source"]')
.forEach((element) => {
element.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
const sourceId = element.dataset.sourceId;
const source = state.sources.find(
(item) => String(item.sourceId) === String(sourceId),
);
if (!source) {
return;
}
state.pickerOpen = false;
const nextDatasetRef = createSourceOnlyDatasetRef(source);
node.data = {
...(node.data || {}),
datasetRef: nextDatasetRef,
sourceName: source.sourceName,
sourceType: source.sourceType,
};
updateNodeData?.(node.id, {
datasetRef: nextDatasetRef,
sourceName: source.sourceName,
sourceType: source.sourceType,
});
rerenderSearchNode(parent, node, flowInstance);
});
});
}
export function rerenderSaveNode(parent: HTMLElement, node: NodeLike, flowInstance?: RenderContext) {
export function rerenderSaveNode(
parent: HTMLElement,
node: NodeLike,
flowInstance?: RenderContext,
) {
const state = getState(parent);
const updateNodeData = getUpdateNodeData(parent, flowInstance);
ensureOptionsLoaded(state, parent, node, flowInstance, rerenderSaveNode);
const datasetRef = (node.data?.datasetRef || null) as DatasetRefPayload | null;
const datasetRef = (node.data?.datasetRef ||
null) as DatasetRefPayload | null;
const activeKey = getDatasetKey(datasetRef);
const currentOption = state.options.find((item) => String(item.datasetRef.tableId) === activeKey);
const filtered = filterTableOptions(state.options, state.tableSearchText);
parent.innerHTML = `
@@ -541,33 +583,40 @@ export function rerenderSaveNode(parent: HTMLElement, node: NodeLike, flowInstan
bindInteractiveElements(parent);
parent.querySelector<HTMLInputElement>('[data-role="table-search"]')?.addEventListener('input', (event) => {
event.stopPropagation();
state.tableSearchText = (event.currentTarget as HTMLInputElement).value || '';
rerenderSaveNode(parent, node, flowInstance);
});
parent.querySelectorAll<HTMLElement>('[data-action="select-dataset"]').forEach((element) => {
element.addEventListener('click', (event) => {
event.preventDefault();
parent
.querySelector<HTMLInputElement>('[data-role="table-search"]')
?.addEventListener('input', (event) => {
event.stopPropagation();
const tableId = element.dataset.tableId;
const option = state.options.find((item) => String(item.datasetRef.tableId) === String(tableId));
if (!option) {
return;
}
node.data = {
...(node.data || {}),
datasetRef: option.datasetRef,
sourceName: option.sourceName,
sourceType: option.sourceType,
};
updateNodeData?.(node.id, {
datasetRef: option.datasetRef,
sourceName: option.sourceName,
sourceType: option.sourceType,
});
state.tableSearchText =
(event.currentTarget as HTMLInputElement).value || '';
rerenderSaveNode(parent, node, flowInstance);
});
});
parent
.querySelectorAll<HTMLElement>('[data-action="select-dataset"]')
.forEach((element) => {
element.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
const tableId = element.dataset.tableId;
const option = state.options.find(
(item) => String(item.datasetRef.tableId) === String(tableId),
);
if (!option) {
return;
}
node.data = {
...(node.data || {}),
datasetRef: option.datasetRef,
sourceName: option.sourceName,
sourceType: option.sourceType,
};
updateNodeData?.(node.id, {
datasetRef: option.datasetRef,
sourceName: option.sourceName,
sourceType: option.sourceType,
});
rerenderSaveNode(parent, node, flowInstance);
});
});
}