feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { nanoid } from 'nanoid';
|
||||
import { transSliceContentOutput } from '@coze-data/knowledge-modal-base';
|
||||
import {
|
||||
DocumentEditor,
|
||||
useInitEditor,
|
||||
EditorToolbar,
|
||||
type Chunk,
|
||||
} from '@coze-data/knowledge-common-components/text-knowledge-editor';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Form, Input, Modal, Toast } from '@coze-arch/coze-design';
|
||||
import { MemoryApi } from '@coze-arch/bot-api';
|
||||
|
||||
import { useGetWebInfo } from './hooks/get-web-info';
|
||||
import { editorToolbarActionRegistry } from './editor-toolbar-actions-contributes';
|
||||
import { editorContextActionRegistry } from './editor-context-actions-contributes';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
const MAX_DOC_NAME_LEN = 100;
|
||||
export interface UseBrowseDetailModalReturnValue {
|
||||
open: () => void;
|
||||
node: JSX.Element;
|
||||
}
|
||||
|
||||
export interface ViewOnlinePageDetailProps {
|
||||
id?: string;
|
||||
url?: string;
|
||||
content?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface BrowseUrlModalProps {
|
||||
name: string;
|
||||
webID?: string;
|
||||
onSubmit: (name: string, content?: string) => void;
|
||||
onCancel?: () => void;
|
||||
}
|
||||
|
||||
export const BrowseUrlModal = ({
|
||||
name,
|
||||
webID,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}: BrowseUrlModalProps) => {
|
||||
const [docName, setDocName] = useState<string>(name);
|
||||
const { data: pageList, runAsync, mutate } = useGetWebInfo();
|
||||
|
||||
const [initChunk, setInitChunk] = useState<Chunk>({
|
||||
text_knowledge_editor_chunk_uuid: nanoid(),
|
||||
content: '',
|
||||
});
|
||||
|
||||
const { editor } = useInitEditor({
|
||||
chunk: initChunk,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: 'h-[360px] overflow-y-auto',
|
||||
},
|
||||
},
|
||||
onChange: v => {
|
||||
mutate(
|
||||
([] as ViewOnlinePageDetailProps[]).concat({
|
||||
...pageList?.[0],
|
||||
content: v.content ?? '',
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setDocName(name);
|
||||
}, [name]);
|
||||
|
||||
useEffect(() => {
|
||||
if (webID) {
|
||||
runAsync(webID).then(data => {
|
||||
setInitChunk({
|
||||
text_knowledge_editor_chunk_uuid: nanoid(),
|
||||
content: data[0].content ?? '',
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [webID, runAsync, editor]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={I18n.t('knowledge_insert_img_001')}
|
||||
width={792}
|
||||
visible
|
||||
cancelText={I18n.t('Cancel')}
|
||||
okText={I18n.t('datasets_segment_detailModel_save')}
|
||||
maskClosable={false}
|
||||
onOk={async () => {
|
||||
const pageInfo = pageList?.[0];
|
||||
const content = transSliceContentOutput(pageInfo.content as string);
|
||||
await MemoryApi.SubmitWebContentV2({
|
||||
web_id: pageInfo.id,
|
||||
content,
|
||||
});
|
||||
Toast.success({
|
||||
content: I18n.t('datasets_url_saveSuccess'),
|
||||
showClose: false,
|
||||
});
|
||||
onSubmit?.(docName, content);
|
||||
onCancel?.();
|
||||
}}
|
||||
onCancel={() => {
|
||||
onCancel?.();
|
||||
}}
|
||||
>
|
||||
<Form<Record<string, unknown>> layout="vertical" showValidateIcon={false}>
|
||||
<Form.Slot
|
||||
label={{ text: I18n.t('knowledge_upload_text_custom_doc_name') }}
|
||||
>
|
||||
<Input
|
||||
className={styles['doc-name-input']}
|
||||
value={docName}
|
||||
onChange={(v: string) => setDocName(v)}
|
||||
maxLength={MAX_DOC_NAME_LEN}
|
||||
placeholder={I18n.t('knowledge_upload_text_custom_doc_name_tips')}
|
||||
/>
|
||||
</Form.Slot>
|
||||
<Form.Slot
|
||||
className={styles['form-segment-content']}
|
||||
label={{ text: I18n.t('knowledge_upload_text_custom_doc_content') }}
|
||||
>
|
||||
<DocumentEditor
|
||||
editor={editor}
|
||||
placeholder={I18n.t(
|
||||
'knowledge_upload_text_custom_doc_content_tips',
|
||||
)}
|
||||
editorContextMenuItemsRegistry={editorContextActionRegistry}
|
||||
editorBottomSlot={
|
||||
<EditorToolbar
|
||||
editor={editor}
|
||||
actionRegistry={editorToolbarActionRegistry}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{pageList?.[0]?.url ? (
|
||||
<div className={styles['browse-source-url']}>
|
||||
{I18n.t('knowledge_insert_img_003', {
|
||||
url: pageList[0]?.url,
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
</Form.Slot>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export const useBrowseUrlModal = (props: BrowseUrlModalProps) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
return {
|
||||
open: () => setVisible(true),
|
||||
close: () => setVisible(false),
|
||||
node: visible ? (
|
||||
<BrowseUrlModal {...props} onCancel={() => setVisible(false)} />
|
||||
) : null,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { UploadImageMenu } from '@coze-data/knowledge-common-components/text-knowledge-editor/features/editor-actions/upload-image';
|
||||
import {
|
||||
createEditorActionFeatureRegistry,
|
||||
type EditorActionRegistry,
|
||||
} from '@coze-data/knowledge-common-components/text-knowledge-editor/features/editor-actions';
|
||||
export const editorContextActionRegistry: EditorActionRegistry = (() => {
|
||||
const editorContextActionFeatureRegistry = createEditorActionFeatureRegistry(
|
||||
'editor-context-actions',
|
||||
);
|
||||
editorContextActionFeatureRegistry.registerSome([
|
||||
{
|
||||
type: 'upload-image',
|
||||
module: {
|
||||
Component: UploadImageMenu,
|
||||
},
|
||||
},
|
||||
]);
|
||||
return editorContextActionFeatureRegistry;
|
||||
})();
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { UploadImageButton } from '@coze-data/knowledge-common-components/text-knowledge-editor/features/editor-actions/upload-image';
|
||||
import {
|
||||
createEditorActionFeatureRegistry,
|
||||
type EditorActionRegistry,
|
||||
} from '@coze-data/knowledge-common-components/text-knowledge-editor/features/editor-actions';
|
||||
export const editorToolbarActionRegistry: EditorActionRegistry = (() => {
|
||||
const editorToolbarActionFeatureRegistry = createEditorActionFeatureRegistry(
|
||||
'editor-toolbar-actions',
|
||||
);
|
||||
editorToolbarActionFeatureRegistry.registerSome([
|
||||
{
|
||||
type: 'upload-image',
|
||||
module: {
|
||||
Component: UploadImageButton,
|
||||
},
|
||||
},
|
||||
]);
|
||||
return editorToolbarActionFeatureRegistry;
|
||||
})();
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useRequest } from 'ahooks';
|
||||
import { KnowledgeApi } from '@coze-arch/bot-api';
|
||||
|
||||
import { type ViewOnlinePageDetailProps } from '@/types';
|
||||
|
||||
/**
|
||||
* 将API返回的网页信息转换为视图数据
|
||||
*/
|
||||
const transformWebInfoToViewData = (webInfo: {
|
||||
id?: string;
|
||||
url?: string;
|
||||
title?: string;
|
||||
content?: string;
|
||||
}): ViewOnlinePageDetailProps => ({
|
||||
id: webInfo?.id,
|
||||
url: webInfo?.url,
|
||||
title: webInfo?.title,
|
||||
content: webInfo?.content,
|
||||
});
|
||||
|
||||
export const useGetWebInfo = (): {
|
||||
data: ViewOnlinePageDetailProps[];
|
||||
loading: boolean;
|
||||
runAsync: (webID: string) => Promise<ViewOnlinePageDetailProps[]>;
|
||||
mutate: (data: ViewOnlinePageDetailProps[]) => void;
|
||||
} => {
|
||||
const { data, mutate, loading, runAsync } = useRequest(
|
||||
async (webID: string) => {
|
||||
const { data: responseData } = await KnowledgeApi.GetWebInfo({
|
||||
web_ids: [webID],
|
||||
include_content: true,
|
||||
});
|
||||
|
||||
// 如果没有数据,返回空数组
|
||||
if (!responseData?.[webID]?.web_info) {
|
||||
return [] as ViewOnlinePageDetailProps[];
|
||||
}
|
||||
|
||||
const webInfo = responseData[webID].web_info;
|
||||
const mainPageData = transformWebInfoToViewData(webInfo);
|
||||
const result = [mainPageData];
|
||||
|
||||
// 处理子页面数据
|
||||
if (webInfo?.subpages?.length) {
|
||||
const subpagesData = webInfo.subpages.map(transformWebInfoToViewData);
|
||||
result.push(...subpagesData);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
loading,
|
||||
runAsync,
|
||||
mutate,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
/* stylelint-disable declaration-no-important */
|
||||
.browse-detail-modal {
|
||||
padding-bottom: 40px;
|
||||
|
||||
&-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin-bottom: 16px;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
color: rgb(28 31 35 / 100%);
|
||||
|
||||
&-image {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 7px;
|
||||
|
||||
background-color: rgb(152 205 253 / 100%);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
&-title {
|
||||
max-width: 40%;
|
||||
}
|
||||
|
||||
&-url {
|
||||
max-width: 40%;
|
||||
margin-left: 7px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
color: rgb(28 31 35 / 35%);
|
||||
}
|
||||
}
|
||||
|
||||
&-collapse {
|
||||
overflow-y: auto;
|
||||
max-height: 400px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
|
||||
:global {
|
||||
.semi-collapse-item {
|
||||
border-color: #efefef !important;
|
||||
}
|
||||
|
||||
.semi-input-textarea-wrapper {
|
||||
background-color: rgb(0 0 0 / 0%) !important;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.browse-modal-header {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
padding: 24px 0;
|
||||
|
||||
&-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal.upgrade-level {
|
||||
:global {
|
||||
.semi-modal-body {
|
||||
height: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.semi-modal-content {
|
||||
background-color: var(--semi-color-tertiary-light-default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.browse-detail-tooltip {
|
||||
width: 200px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.browse-source-url {
|
||||
margin-top: 12px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 16px;
|
||||
color: rgb(29 28 36 / 35%)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { useBrowseUrlModal } from './browser-url-modal';
|
||||
@@ -0,0 +1,37 @@
|
||||
.card-radio-group {
|
||||
gap: 4px;
|
||||
|
||||
:global {
|
||||
.semi-radio {
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--coz-mg-secondary-hovered);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--coz-mg-secondary-pressed);
|
||||
}
|
||||
|
||||
&.semi-radio-checked {
|
||||
padding: 11.5px;
|
||||
background-color: var(--coz-mg-card);
|
||||
border-width: 1.5px;
|
||||
}
|
||||
}
|
||||
|
||||
.semi-radio-content {
|
||||
flex-grow: 1;
|
||||
|
||||
.semi-radio-addon {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.semi-radio-extra {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type PropsWithChildren } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { RadioGroup, type RadioGroupProps } from '@coze-arch/coze-design';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export type CardRadioGroupProps<T = unknown> = PropsWithChildren<
|
||||
Pick<RadioGroupProps, 'value' | 'className'>
|
||||
> & {
|
||||
onChange?: (value: T) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* 始终使用卡片风格,并符合 UI 设计样式的 {@link RadioGroup}
|
||||
*/
|
||||
export function CardRadioGroup<T = unknown>({
|
||||
value,
|
||||
onChange,
|
||||
className,
|
||||
children,
|
||||
}: CardRadioGroupProps<T>) {
|
||||
return (
|
||||
<RadioGroup
|
||||
type="pureCard"
|
||||
direction="vertical"
|
||||
value={value}
|
||||
onChange={e => {
|
||||
onChange?.(e.target.value as T);
|
||||
}}
|
||||
className={classNames(styles['card-radio-group'], className)}
|
||||
>
|
||||
{children}
|
||||
</RadioGroup>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useState, type PropsWithChildren } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { IconCozArrowRight } from '@coze-arch/coze-design/icons';
|
||||
import { Collapsible, Typography } from '@coze-arch/coze-design';
|
||||
|
||||
export interface CollapsePanelProps extends PropsWithChildren {
|
||||
header: React.ReactNode;
|
||||
keepDOM?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用 Collapsible 封装的更符合 UI 设计的折叠面板
|
||||
*/
|
||||
export function CollapsePanel({
|
||||
header,
|
||||
keepDOM,
|
||||
children,
|
||||
}: CollapsePanelProps) {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
||||
return (
|
||||
<div className="mb-[4px]">
|
||||
<div
|
||||
className={classNames(
|
||||
'h-[40px] flex items-center gap-[4px] shrink-0 rounded',
|
||||
'cursor-pointer hover:coz-mg-secondary-hovered active:coz-mg-secondary-pressed',
|
||||
)}
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
<IconCozArrowRight
|
||||
className={classNames('coz-fg-secondary text-[14px] m-[4px]', {
|
||||
'rotate-90': open,
|
||||
})}
|
||||
/>
|
||||
<Typography.Text fontSize="14px" weight={400}>
|
||||
{header}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<Collapsible
|
||||
className="ml-[26px] [&>div]:pt-[4px]"
|
||||
isOpen={open}
|
||||
keepDOM={keepDOM}
|
||||
>
|
||||
{children}
|
||||
</Collapsible>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Banner } from '@coze-arch/coze-design';
|
||||
|
||||
export const ConfigurationBanner = () => (
|
||||
<Banner
|
||||
style={{ marginTop: '10px' }}
|
||||
type="warning"
|
||||
description={I18n.t('knowledge_limit_20')}
|
||||
/>
|
||||
);
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { IllustrationNoResult } from '@douyinfe/semi-illustrations';
|
||||
import { UIEmpty } from '@coze-arch/bot-semi';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
interface ConfigurationErrorProps {
|
||||
fetchTableInfo: () => void;
|
||||
}
|
||||
export const ConfigurationError = (props: ConfigurationErrorProps) => {
|
||||
const { fetchTableInfo } = props;
|
||||
return (
|
||||
<UIEmpty
|
||||
className={styles['load-failure']}
|
||||
empty={{
|
||||
title: 'Read failure',
|
||||
description: '',
|
||||
icon: <IllustrationNoResult></IllustrationNoResult>,
|
||||
btnText: 'Retry',
|
||||
btnOnClick: fetchTableInfo,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
/* stylelint-disable font-family-no-missing-generic-family-keyword */
|
||||
.load-failure {
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100%;
|
||||
min-height: 400px;
|
||||
|
||||
&-content {
|
||||
margin-left: 8px;
|
||||
|
||||
font-family: "SF Pro Display";
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
color: rgb(29 28 35 / 35%);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Spin } from '@coze-arch/coze-design';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const ConfigurationLoading = () => (
|
||||
<div className={styles.loading}>
|
||||
<Spin spinning></Spin>
|
||||
<div className={styles['loading-content']}>
|
||||
{I18n.t('knowledge_1221_03')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import cls from 'classnames';
|
||||
import { Typography } from '@coze-arch/coze-design';
|
||||
|
||||
interface IDocumentItemProps {
|
||||
id: string;
|
||||
onClick: (id: string) => void;
|
||||
selected: boolean;
|
||||
title: string;
|
||||
status?: 'pending' | 'finished' | 'failed';
|
||||
addonAfter?: ReactNode;
|
||||
}
|
||||
|
||||
export const DocumentItem: React.FC<IDocumentItemProps> = props => {
|
||||
const { id, onClick, title, selected, addonAfter } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cls(
|
||||
'w-full h-8 px-2 py-[6px] rounded-[8px] hover:coz-mg-primary cursor-pointer flex flex-nowrap',
|
||||
selected && 'coz-mg-primary',
|
||||
)}
|
||||
onClick={() => onClick(id)}
|
||||
>
|
||||
<Typography.Text
|
||||
className="w-full coz-fg-primary text-[14px] leading-[20px] grow truncate"
|
||||
ellipsis
|
||||
>
|
||||
{title}
|
||||
</Typography.Text>
|
||||
{addonAfter}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DocumentItem;
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Tag, Tooltip } from '@coze-arch/coze-design';
|
||||
|
||||
import {
|
||||
getFilterPagesString,
|
||||
getSortedFilterPages,
|
||||
} from '../../utils/render-document-filter-value';
|
||||
import { DocumentItem } from './document-item';
|
||||
|
||||
interface FilterPageConfig {
|
||||
pageIndex: number;
|
||||
isFilter: boolean;
|
||||
}
|
||||
|
||||
interface Document {
|
||||
// TODO: 扩充
|
||||
id: string;
|
||||
title: string;
|
||||
/** 是否存在过滤内容 */
|
||||
filterPageConfigList: FilterPageConfig[];
|
||||
}
|
||||
|
||||
interface IDocumentListProps {
|
||||
documents: Document[];
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const DocumentList = (props: IDocumentListProps) => (
|
||||
<div className={classNames('flex flex-col gap-1 h-full', props.className)}>
|
||||
<div className="w-full pl-2 h-6 items-center flex">
|
||||
<div className="coz-fg-secondary text-[12px] font-[400] leading-4 shrink-0">
|
||||
{I18n.t('kl_write_105')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 h-[150px] !overflow-scroll shrink-0">
|
||||
{props.documents.map(document => {
|
||||
if (!document.id) {
|
||||
return null;
|
||||
}
|
||||
const filterPages = getSortedFilterPages(document.filterPageConfigList);
|
||||
const isFiltered = Boolean(filterPages.length);
|
||||
const filterPagesString = getFilterPagesString(filterPages);
|
||||
|
||||
return (
|
||||
<DocumentItem
|
||||
id={document.id}
|
||||
selected={document.id === props.value}
|
||||
onClick={id => {
|
||||
props.onChange(id);
|
||||
}}
|
||||
title={document.title}
|
||||
addonAfter={
|
||||
isFiltered ? (
|
||||
<Tooltip
|
||||
content={I18n.t('data_filter_values', {
|
||||
filterPages: filterPagesString,
|
||||
})}
|
||||
>
|
||||
<Tag color="primary" className="flex-shrink-0">
|
||||
{I18n.t('knowledge_new_002')}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,41 @@
|
||||
.auth-empty-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: calc(100% - 112px);
|
||||
|
||||
.auth-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.auth-empty-image {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
.auth-empty-description {
|
||||
margin-top: 16px;
|
||||
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
color: var(--coz-fg-primary);
|
||||
}
|
||||
|
||||
.auth-empty-second-desc {
|
||||
margin-top: 4px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--coz-fg-secondary);
|
||||
}
|
||||
|
||||
.auth-empty-button {
|
||||
margin-top: 21px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { Button } from '@coze-arch/coze-design';
|
||||
|
||||
import style from './index.module.less';
|
||||
|
||||
export interface CommonEmptyAuthProps {
|
||||
illustrationIcon: JSX.Element;
|
||||
description: string;
|
||||
authUrl?: string;
|
||||
btnText?: string;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
secondDesc?: string;
|
||||
}
|
||||
export const AuthEmpty = (props: CommonEmptyAuthProps) => {
|
||||
const {
|
||||
description,
|
||||
authUrl,
|
||||
onClick,
|
||||
btnText,
|
||||
illustrationIcon,
|
||||
className,
|
||||
secondDesc,
|
||||
} = props;
|
||||
const handleClick = () => {
|
||||
if (authUrl) {
|
||||
window.open(authUrl, '_self');
|
||||
} else {
|
||||
onClick?.();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classNames(style['auth-empty-wrapper'], className)}>
|
||||
<div className={style['auth-empty']}>
|
||||
<div className={style['auth-empty-image']}>{illustrationIcon}</div>
|
||||
<div className={style['auth-empty-description']}>{description}</div>
|
||||
{secondDesc ? (
|
||||
<div className={style['auth-empty-second-desc']}>{secondDesc}</div>
|
||||
) : null}
|
||||
{btnText ? (
|
||||
<Button
|
||||
color="highlight"
|
||||
className={style['auth-empty-button']}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{btnText}
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { AuthEmpty } from './common-empty-auth';
|
||||
export type { CommonEmptyAuthProps } from './common-empty-auth';
|
||||
@@ -0,0 +1,41 @@
|
||||
.auth-empty-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: calc(100% - 112px);
|
||||
|
||||
.auth-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.auth-empty-image {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
.auth-empty-description {
|
||||
margin-top: 16px;
|
||||
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
color: var(--Light-usage-text---color-text-0, #1D1C23);
|
||||
}
|
||||
|
||||
.auth-empty-second-desc {
|
||||
margin-top: 4px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--Light-usage-text---color-text-2, rgba(29, 28, 35, 60%));
|
||||
}
|
||||
|
||||
.auth-empty-button {
|
||||
margin-top: 21px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { UIButton } from '@coze-arch/bot-semi';
|
||||
|
||||
import style from './index.module.less';
|
||||
|
||||
interface Props {
|
||||
illustrationIcon: JSX.Element;
|
||||
description: string;
|
||||
btnText?: string;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
secondDesc?: string;
|
||||
}
|
||||
export const Empty = (props: Props) => {
|
||||
const {
|
||||
description,
|
||||
onClick,
|
||||
btnText,
|
||||
illustrationIcon,
|
||||
className,
|
||||
secondDesc,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className={classNames(style['auth-empty-wrapper'], className)}>
|
||||
<div className={style['auth-empty']}>
|
||||
<div className={style['auth-empty-image']}>{illustrationIcon}</div>
|
||||
<div className={style['auth-empty-description']}>{description}</div>
|
||||
{secondDesc ? (
|
||||
<div className={style['auth-empty-second-desc']}>{secondDesc}</div>
|
||||
) : null}
|
||||
{btnText ? (
|
||||
<UIButton
|
||||
type="tertiary"
|
||||
className={style['auth-empty-button']}
|
||||
onClick={onClick}
|
||||
>
|
||||
{btnText}
|
||||
</UIButton>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
.frequency-form-item {
|
||||
.title {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 8px;
|
||||
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-secondary);
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Select, Typography } from '@coze-arch/coze-design';
|
||||
|
||||
import { getFrequencyMap } from '../../utils';
|
||||
import { FrequencyDay } from '../../constants';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
interface Props {
|
||||
value: number;
|
||||
onChange: (v: number) => void;
|
||||
}
|
||||
|
||||
export const FrequencyFormItem: FC<Props> = ({ value, onChange }) => (
|
||||
<div
|
||||
className={styles['frequency-form-item']}
|
||||
data-testid={KnowledgeE2e.OnlineUploadModalFrequencySelect}
|
||||
>
|
||||
<Typography.Text className={styles.title}>
|
||||
{I18n.t('datasets_frequencyModal_frequency')}
|
||||
</Typography.Text>
|
||||
<div className={styles.content}>
|
||||
<Select
|
||||
placeholder={I18n.t('datasets_frequencyModal_frequency')}
|
||||
style={{ width: '100%' }}
|
||||
value={value}
|
||||
onChange={v => onChange(v as number)}
|
||||
>
|
||||
{[
|
||||
FrequencyDay.ZERO,
|
||||
FrequencyDay.ONE,
|
||||
FrequencyDay.THREE,
|
||||
FrequencyDay.SEVEN,
|
||||
FrequencyDay.THIRTY,
|
||||
].map(frequency => (
|
||||
<Select.Option key={frequency} value={frequency}>
|
||||
{getFrequencyMap(frequency)}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { UploadFooter } from './upload-footer';
|
||||
export { useBrowseUrlModal } from './browser-url-modal';
|
||||
export {
|
||||
TableSettingBar,
|
||||
type TableSettingBarProps,
|
||||
type TablePreviewProps,
|
||||
TablePreview,
|
||||
type TableStructureProps,
|
||||
TableStructure,
|
||||
TableStructureTitle,
|
||||
} from './table-format';
|
||||
export { UnitProgress } from './unit-progress';
|
||||
export { UploadUnitFile } from './upload-unit-file';
|
||||
export { UploadUnitTable } from './upload-unit-table';
|
||||
export { ConfigurationError } from './configuration/error';
|
||||
export { ConfigurationLoading } from './configuration/loading';
|
||||
export { ConfigurationBanner } from './configuration/banner';
|
||||
export { AuthEmpty } from './empty-auth';
|
||||
export { Empty } from './empty';
|
||||
export { FrequencyFormItem } from './frequency-form-item';
|
||||
export { CollapsePanel } from './collapse-panel';
|
||||
export { CardRadioGroup } from './card-radio-group';
|
||||
export { getProcessStatus } from './upload-unit-table/utils';
|
||||
export {
|
||||
type ColumnInfo,
|
||||
type UploadUnitTableProps,
|
||||
type RenderColumnsProps,
|
||||
} from './upload-unit-table/types';
|
||||
export {
|
||||
ActionRenderByDelete,
|
||||
ActionRenderByEditName,
|
||||
ActionRenderByRetry,
|
||||
} from './upload-unit-table';
|
||||
@@ -0,0 +1,117 @@
|
||||
/* stylelint-disable declaration-no-important */
|
||||
/* stylelint-disable no-duplicate-selectors */
|
||||
.progress-wrap {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
|
||||
padding: 8px 10px;
|
||||
|
||||
background: var(--coz-mg-card);
|
||||
border:1px solid var(--coz-stroke-primary);
|
||||
border-radius: var(--default, 8px);
|
||||
|
||||
|
||||
|
||||
.content {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.progress {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
background: linear-gradient(180deg, rgba(148, 152, 247, 44%), rgba(255, 255, 255, 0%));
|
||||
}
|
||||
|
||||
.info {
|
||||
.main-text {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--coz-fg-primary);
|
||||
}
|
||||
|
||||
.sub-text {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-secondary);
|
||||
}
|
||||
|
||||
.desc {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tip-desc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:global {
|
||||
.semi-image {
|
||||
margin-right: 0!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.info {
|
||||
.desc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tip-desc {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&.processing-failed {
|
||||
border: 1px solid var(--coz-stroke-hglt-red);
|
||||
|
||||
.sub-text {
|
||||
color: var(--coz-fg-hglt-red)!important;
|
||||
}
|
||||
}
|
||||
|
||||
&.processing {
|
||||
&:hover {
|
||||
.info {
|
||||
.desc {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tip-desc {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.percent {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
color: var(--coz-fg-primary);
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.actions {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { ProcessProgressItem } from './process-progress-item';
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Fragment } from 'react';
|
||||
|
||||
import cls from 'classnames';
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { Typography, Space } from '@coze-arch/coze-design';
|
||||
|
||||
import { ProcessStatus, type ProcessProgressItemProps } from '../../types';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const ProcessProgressItem: React.FC<ProcessProgressItemProps> = ({
|
||||
className,
|
||||
style,
|
||||
mainText,
|
||||
subText,
|
||||
percent = 10,
|
||||
percentFormat,
|
||||
avatar,
|
||||
status,
|
||||
actions,
|
||||
tipText = '',
|
||||
}) => {
|
||||
const renderProgress = () => {
|
||||
if (status === ProcessStatus.Processing) {
|
||||
return (
|
||||
<div className={styles.progress} style={{ width: `${percent}%` }}></div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderActions = () => {
|
||||
if (status === ProcessStatus.Processing) {
|
||||
return (
|
||||
<span className={styles.percent}>
|
||||
{percentFormat ? percentFormat : `${percent}%`}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cls(styles.actions, 'process-progress-item-actions')}>
|
||||
{Array.isArray(actions) ? (
|
||||
<Space spacing="tight">
|
||||
{actions.map((action, idx) => (
|
||||
<Fragment key={idx}>{action}</Fragment>
|
||||
))}
|
||||
</Space>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={mainText}
|
||||
className={cls(
|
||||
styles['progress-wrap'],
|
||||
'flex justify-between relative mb-[8px]',
|
||||
status === ProcessStatus.Failed ? styles['processing-failed'] : '',
|
||||
status === ProcessStatus.Processing ? styles.processing : '',
|
||||
className,
|
||||
)}
|
||||
style={style}
|
||||
>
|
||||
<div
|
||||
className={cls(
|
||||
styles.content,
|
||||
'process-progress-item-content',
|
||||
'max-w-[calc(100%-100px)]',
|
||||
)}
|
||||
>
|
||||
<div className={cls('flex items-center', styles.info)}>
|
||||
{avatar}
|
||||
<div className={cls('pl-[10px] max-w-full')}>
|
||||
<div className={styles['main-text']}>
|
||||
<Typography.Text
|
||||
data-dtestid={`${KnowledgeE2e.CreateUnitListProgressName}.${mainText}`}
|
||||
className={'coz-fg-primary text-14px'}
|
||||
ellipsis={{
|
||||
showTooltip: {
|
||||
opts: { content: mainText },
|
||||
},
|
||||
}}
|
||||
>
|
||||
{mainText}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<div className={styles['sub-text']}>
|
||||
<div className={styles.desc}>{subText}</div>
|
||||
{tipText ? (
|
||||
<div className={styles['tip-desc']}>{tipText}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cls(styles.right, 'process-progress-item-right')}>
|
||||
{renderActions()}
|
||||
</div>
|
||||
{renderProgress()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,164 @@
|
||||
/* stylelint-disable no-descending-specificity */
|
||||
/* stylelint-disable max-nesting-depth */
|
||||
/* stylelint-disable font-family-no-missing-generic-family-keyword */
|
||||
/* stylelint-disable declaration-no-important */
|
||||
.table-preview {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
|
||||
&-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
height: 32px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
color: var(--coz-fg-plus);
|
||||
}
|
||||
|
||||
// .table-view-wrapper {
|
||||
// border: 1px solid #1D1C231F;
|
||||
// border-radius: 8px;
|
||||
|
||||
// :global {
|
||||
// .semi-table-wrapper {
|
||||
// margin-top: 0;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
.semantic-tag,
|
||||
.column-type {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.preview-tips {
|
||||
margin-top: 8px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
color: var(--coz-fg-dim);
|
||||
text-align: left;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.no-result {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100%;
|
||||
height: 448px;
|
||||
|
||||
&-tips {
|
||||
margin-top: 16px;
|
||||
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
line-height: 22px;
|
||||
color: var(--coz-fg-plus);
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
// flex: 1;
|
||||
overflow: auto;
|
||||
|
||||
:global {
|
||||
th,tr {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.semi-table-wrapper {
|
||||
height: 100%;
|
||||
margin-top: 0;
|
||||
|
||||
.coz-tag {
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.coz-table-wrapper {
|
||||
.coz-table-list-hover .semi-table-row:hover>.semi-table-row-cell::before {
|
||||
background-color: transparent
|
||||
}
|
||||
|
||||
.coz-table-list-hover .semi-table-row:hover>.semi-table-row-cell:first-child {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.coz-table-list {
|
||||
.semi-table-thead>.semi-table-row>.semi-table-row-head {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--coz-fg-secondary);
|
||||
}
|
||||
|
||||
.semi-table-tbody>.semi-table-row>.semi-table-row-cell {
|
||||
height: 56px;
|
||||
text-align: unset;
|
||||
|
||||
.semi-typography {
|
||||
color: var(--coz-fg-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.semi-table-container>.semi-table-body {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.semi-table-tbody>.semi-table-row:last-child>.semi-table-row-cell {
|
||||
border-bottom: 1px solid var(--coz-stroke-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.semi-table-header {
|
||||
position: sticky;
|
||||
z-index: 99;
|
||||
overflow-y: hidden !important;
|
||||
// border-top-left-radius: 8px;
|
||||
// border-top-right-radius: 8px;
|
||||
}
|
||||
|
||||
|
||||
.semi-table-body {
|
||||
// max-height: calc(100% - 40px) !important;
|
||||
// max-height: 460px !important;
|
||||
// border-bottom-right-radius: 8px;
|
||||
// border-bottom-left-radius: 8px;
|
||||
}
|
||||
|
||||
.semi-table-colgroup .semi-table-col {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.semi-table-tbody>.semi-table-row>.semi-table-row-cell {
|
||||
min-height: 40px;
|
||||
padding: 9px 16px !important;
|
||||
}
|
||||
|
||||
/** 去掉hover行样式 */
|
||||
.semi-table-tbody .semi-table-row:hover>.semi-table-row-cell {
|
||||
background-color: transparent !important;
|
||||
background-image: none !important;
|
||||
border-bottom: 1px solid var(--coz-stroke-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.td-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { IllustrationNoResult } from '@douyinfe/semi-illustrations';
|
||||
import { getDataTypeText } from '@coze-data/utils';
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { ImageRender, type TableViewColumns } from '@coze-common/table-view';
|
||||
import { ColumnType } from '@coze-arch/idl/knowledge';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { type ColumnProps } from '@coze-arch/bot-semi/Table';
|
||||
import { Table, Tag, Typography } from '@coze-arch/coze-design';
|
||||
|
||||
import { getSrcFromImg } from '@/utils/table';
|
||||
import { type TableInfo, type TableSettings } from '@/types';
|
||||
import { TableSettingFormFields } from '@/constants';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export interface TablePreviewProps {
|
||||
data: TableInfo;
|
||||
settings: TableSettings;
|
||||
}
|
||||
|
||||
interface PreviewColumn {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
const ColumnTypeComp = (props: { columnType: ColumnType }) => (
|
||||
<Tag color="primary" className={styles['column-type']} size="mini">
|
||||
{getDataTypeText(props.columnType)}
|
||||
</Tag>
|
||||
);
|
||||
|
||||
const baseClassName = 'table-preview';
|
||||
const maxDataLen = 10;
|
||||
export const TablePreview: React.FC<TablePreviewProps> = ({
|
||||
data,
|
||||
settings,
|
||||
}) => {
|
||||
const { sheet_list = [], table_meta = {}, preview_data = {} } = data;
|
||||
const startRow = Number(settings[TableSettingFormFields.DATA_START_ROW]) || 0;
|
||||
// 选中的表id
|
||||
const sheetId = useMemo(
|
||||
() => settings[TableSettingFormFields.SHEET] || 0,
|
||||
[settings],
|
||||
);
|
||||
// 选中的表名
|
||||
const sheetName = useMemo(
|
||||
() => (sheet_list || []).find(sheet => sheet?.id === sheetId)?.sheet_name,
|
||||
[sheet_list, sheetId],
|
||||
);
|
||||
// 选中的表的数据量
|
||||
const total = useMemo(
|
||||
() =>
|
||||
Number(
|
||||
(sheet_list || []).find(sheet => sheet?.id === sheetId)?.total_row || 0,
|
||||
) - startRow || 0,
|
||||
[sheet_list, sheetId],
|
||||
);
|
||||
const newColumns: ColumnProps<PreviewColumn | TableViewColumns>[] = (
|
||||
table_meta[sheetId] || []
|
||||
).map(meta => {
|
||||
const {
|
||||
sequence,
|
||||
column_name,
|
||||
is_semantic,
|
||||
column_type = ColumnType.Unknown,
|
||||
} = meta;
|
||||
if (column_type === ColumnType.Image) {
|
||||
return {
|
||||
title: (
|
||||
<div className={styles['td-title']}>
|
||||
<div>{column_name}</div>
|
||||
<ColumnTypeComp columnType={column_type}></ColumnTypeComp>
|
||||
</div>
|
||||
),
|
||||
dataIndex: sequence,
|
||||
render: (text, _record) => {
|
||||
const srcList = getSrcFromImg(text);
|
||||
return <ImageRender srcList={srcList} editable={false} />;
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
title: is_semantic ? (
|
||||
<div className={styles['td-title']}>
|
||||
{column_name}
|
||||
{is_semantic ? (
|
||||
<Tag
|
||||
size="mini"
|
||||
color="green"
|
||||
className={styles['semantic-tag']}
|
||||
data-testid={KnowledgeE2e.TableLocalPreviewSemantic}
|
||||
>
|
||||
{I18n.t('knowledge_1226_001')}
|
||||
</Tag>
|
||||
) : null}
|
||||
<ColumnTypeComp columnType={column_type}></ColumnTypeComp>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles['td-title']}>
|
||||
{column_name}
|
||||
<ColumnTypeComp columnType={column_type}></ColumnTypeComp>
|
||||
</div>
|
||||
),
|
||||
width: 180,
|
||||
dataIndex: sequence,
|
||||
ellipsis: { showTitle: false },
|
||||
render: text => (
|
||||
<Typography.Text ellipsis={{ showTooltip: true }}>
|
||||
{text}
|
||||
</Typography.Text>
|
||||
),
|
||||
};
|
||||
});
|
||||
const dataList = useMemo(() => {
|
||||
const previewData = preview_data[sheetId] || [];
|
||||
return previewData
|
||||
.slice(0, maxDataLen)
|
||||
.sort((a, b) =>
|
||||
(JSON.stringify(a) as unknown as number) >
|
||||
(JSON.stringify(b) as unknown as number)
|
||||
? 1
|
||||
: -1,
|
||||
);
|
||||
}, [preview_data, sheetId]);
|
||||
return (
|
||||
<div className={styles[baseClassName]}>
|
||||
{dataList.length ? (
|
||||
<>
|
||||
<div
|
||||
className={styles[`${baseClassName}-title`]}
|
||||
data-testid={KnowledgeE2e.TableLocalPreviewTitle}
|
||||
>
|
||||
{sheetName}
|
||||
</div>
|
||||
<div className={styles[`${baseClassName}-content`]}>
|
||||
<Table
|
||||
tableProps={{
|
||||
dataSource: dataList,
|
||||
columns: newColumns,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={styles['preview-tips']}
|
||||
data-testid={KnowledgeE2e.TableLocalPreviewFooterTotal}
|
||||
>
|
||||
{I18n.t('datasets_unit_tableformat_tips1', {
|
||||
TotalRows: total,
|
||||
ShowRows: Number(total) > maxDataLen ? maxDataLen : total,
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className={styles['no-result']}>
|
||||
<IllustrationNoResult></IllustrationNoResult>
|
||||
<div className={styles['no-result-tips']}>
|
||||
{I18n.t('knowledge_1221_02')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
/* stylelint-disable font-family-no-missing-generic-family-keyword */
|
||||
.table-setting-bar {
|
||||
width: 100%;
|
||||
margin-bottom: 30px;
|
||||
|
||||
:global {
|
||||
.semi-form-horizontal .semi-form-field {
|
||||
flex: 1;
|
||||
|
||||
.semi-select {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.semi-form-field-label-text {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
line-height: 24px;
|
||||
color: var(--coz-fg-plus);
|
||||
}
|
||||
|
||||
.semi-form-field-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.semi-form-horizontal .semi-form-field:last-child {
|
||||
margin-right: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
|
||||
import { get, isNumber } from 'lodash-es';
|
||||
import classNames from 'classnames';
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { type FormApi } from '@coze-arch/bot-semi/Form';
|
||||
import { Form } from '@coze-arch/bot-semi';
|
||||
import { type GetDocumentTableInfoResponse } from '@coze-arch/bot-api/memory';
|
||||
import { FormSelect } from '@coze-arch/coze-design';
|
||||
|
||||
import { type TableSettings } from '@/types';
|
||||
import { TableSettingFormFields } from '@/constants';
|
||||
|
||||
import { initIndexOptions } from './utils';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export interface TableSettingBarProps {
|
||||
className?: string;
|
||||
data: GetDocumentTableInfoResponse;
|
||||
tableSettings: TableSettings;
|
||||
setTableSettings: (values: TableSettings) => void;
|
||||
}
|
||||
|
||||
const minOptLen = 2;
|
||||
export const TableSettingBar: React.FC<TableSettingBarProps> = ({
|
||||
className = '',
|
||||
data = {},
|
||||
tableSettings,
|
||||
setTableSettings,
|
||||
}) => {
|
||||
const { preview_data, sheet_list } = data;
|
||||
if (!preview_data || !sheet_list || !sheet_list.length) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks -- linter-disable-autofix
|
||||
const formApi = useRef<FormApi<TableSettings>>();
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks -- linter-disable-autofix
|
||||
const sheet = useMemo(
|
||||
() => get(sheet_list, tableSettings[TableSettingFormFields.SHEET]),
|
||||
[sheet_list, tableSettings],
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks -- linter-disable-autofix
|
||||
const initValues = useMemo(() => tableSettings, [sheet_list]);
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks -- linter-disable-autofix
|
||||
const settings = useMemo(() => {
|
||||
const options =
|
||||
!sheet.id && sheet.id !== 0
|
||||
? []
|
||||
: initIndexOptions(
|
||||
Number(sheet?.total_row) > 1 ? Number(sheet.total_row) : minOptLen,
|
||||
0,
|
||||
);
|
||||
return [
|
||||
{
|
||||
e2e: KnowledgeE2e.TableLocalTableConfigurationDataSheet,
|
||||
field: TableSettingFormFields.SHEET,
|
||||
label: I18n.t('datasets_createFileModel_tab_DataSheet'),
|
||||
options: sheet_list.map(s => ({
|
||||
value: s.id,
|
||||
label: s.sheet_name,
|
||||
})),
|
||||
},
|
||||
{
|
||||
e2e: KnowledgeE2e.TableLocalTableConfigurationSheetHeader,
|
||||
field: TableSettingFormFields.KEY_START_ROW,
|
||||
label: I18n.t('datasets_createFileModel_tab_header'),
|
||||
options: options.slice(0, options.length - 1),
|
||||
},
|
||||
{
|
||||
e2e: KnowledgeE2e.TableLocalTableConfigurationStarRow,
|
||||
field: TableSettingFormFields.DATA_START_ROW,
|
||||
label: I18n.t('datasets_createFileModel_tab_dataStarRow'),
|
||||
// 数据起始行需大于表头行
|
||||
options: options.slice(
|
||||
Number(tableSettings[TableSettingFormFields.KEY_START_ROW]) + 1,
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [data, sheet]);
|
||||
|
||||
const handleFormChange = (
|
||||
values: TableSettings,
|
||||
changedValue: Partial<TableSettings>,
|
||||
) => {
|
||||
if (setTableSettings) {
|
||||
setTableSettings({ ...values });
|
||||
}
|
||||
|
||||
const curSheet = get(changedValue, TableSettingFormFields.SHEET);
|
||||
const curKeyStartRow = get(
|
||||
changedValue,
|
||||
TableSettingFormFields.KEY_START_ROW,
|
||||
);
|
||||
if (isNumber(curSheet) && formApi.current) {
|
||||
// 修改sheet,初始化表头行和数据行
|
||||
formApi.current.setValue(TableSettingFormFields.KEY_START_ROW, 0);
|
||||
formApi.current.setValue(TableSettingFormFields.DATA_START_ROW, 1);
|
||||
}
|
||||
if (curKeyStartRow && formApi.current) {
|
||||
const dataStartRow = get(values, TableSettingFormFields.DATA_START_ROW);
|
||||
if (!(curKeyStartRow < dataStartRow)) {
|
||||
formApi.current.setValue(
|
||||
TableSettingFormFields.DATA_START_ROW,
|
||||
curKeyStartRow + 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classNames(styles['table-setting-bar'], className)}>
|
||||
<Form<typeof initValues>
|
||||
layout="horizontal"
|
||||
initValues={initValues}
|
||||
getFormApi={api => (formApi.current = api)}
|
||||
onValueChange={(values, changedValue) => {
|
||||
handleFormChange(values, changedValue);
|
||||
}}
|
||||
>
|
||||
{settings.map(setting => {
|
||||
const { options, ...selectProps } = setting;
|
||||
return (
|
||||
<FormSelect
|
||||
data-testid={setting.e2e}
|
||||
key={setting.field}
|
||||
optionList={options}
|
||||
{...selectProps}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onChange={(v?: string | number | any[] | Record<string, any>) => {
|
||||
if (!v) {
|
||||
return;
|
||||
}
|
||||
handleFormChange(
|
||||
{ ...tableSettings, [setting.field]: v as unknown as number },
|
||||
{ [setting.field]: v as unknown as number },
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
export const initIndexOptions = (length: number, start: number) => {
|
||||
const MAX_VALUE = 50;
|
||||
const limit = length > MAX_VALUE ? MAX_VALUE : length;
|
||||
const res: Array<{
|
||||
label: string;
|
||||
value: number;
|
||||
}> = [];
|
||||
for (let i = start; i < limit; i++) {
|
||||
res.push({
|
||||
label: I18n.t('datasets_createFileModel_tab_dataStarRow_value', {
|
||||
LineNumber: i + 1,
|
||||
}),
|
||||
value: i,
|
||||
});
|
||||
}
|
||||
return res;
|
||||
};
|
||||
@@ -0,0 +1,234 @@
|
||||
/* stylelint-disable no-descending-specificity */
|
||||
/* stylelint-disable no-duplicate-selectors */
|
||||
/* stylelint-disable declaration-no-important */
|
||||
/* stylelint-disable max-nesting-depth */
|
||||
.table-structure-wrapper {
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.structure-wrapper {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
}
|
||||
|
||||
.drag-table {
|
||||
:global {
|
||||
.semi-table-container {
|
||||
.semi-table-tbody {
|
||||
.semi-table-row {
|
||||
.semi-table-row-cell {
|
||||
&:first-child {
|
||||
padding-left: 16px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/** 暂时注释,后续还要用,待讨论 */
|
||||
// &:hover {
|
||||
// background: var(--coz-mg-secondary-hovered) !important;
|
||||
|
||||
// .semi-table-row-cell:first-child {
|
||||
// border-top-left-radius: 8px !important;
|
||||
// border-bottom-left-radius: 8px !important;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.table-structure {
|
||||
overflow: hidden;
|
||||
margin-top: 4px !important;
|
||||
border-radius: 8px;
|
||||
|
||||
:global {
|
||||
.semi-spin, .semi-spin-children, .semi-table-fixed-header,.semi-table-container {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&-required-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-col-required {
|
||||
margin-top: 4px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-hglt-red);
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.input-suffix {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
font-size: 12px;
|
||||
// color: rgb(28 29 35 / 35%);
|
||||
}
|
||||
|
||||
.input-error-msg {
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
|
||||
margin-top: 4px;
|
||||
margin-bottom: -20px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-hglt-red);
|
||||
}
|
||||
|
||||
.semantic-radio {
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
height: 100%;
|
||||
// padding-top: 3px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.column-item-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
height: 100%;
|
||||
padding-top: 3px;
|
||||
padding-left: 7px;
|
||||
|
||||
&-delete {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.column-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.column-item-value {
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
color: var(--coz-fg-secondary);
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
:global {
|
||||
.structure-table-drag-icon {
|
||||
position: absolute;
|
||||
left: -16px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.semi-table-row {
|
||||
&:hover,
|
||||
&:focus {
|
||||
.structure-table-drag-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.semi-table-thead>.semi-table-row>.semi-table-row-head {
|
||||
font-size: 12px !important;
|
||||
color: var(--coz-fg-secondary) !important;
|
||||
}
|
||||
|
||||
.semi-table-container {
|
||||
.semi-table-body {
|
||||
height: calc(100% - 39px) !important;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.select-error-text {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.singleline-select-error-content {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.semi-table-thead>.semi-table-row>.semi-table-row-head {
|
||||
&:first-child {
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/** table去掉背景色 */
|
||||
.semi-table-thead>.semi-table-row>.semi-table-row-head, .semi-table-tbody>.semi-table-row, .semi-table-tbody>.semi-table-row>.semi-table-cell-fixed-left,.semi-table-thead>.semi-table-row>.semi-table-row-head.semi-table-cell-fixed-left::before {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.semi-table-tbody {
|
||||
padding: 5px 0;
|
||||
|
||||
.semi-table-row {
|
||||
.semi-table-row-cell {
|
||||
height: 56px !important;
|
||||
padding: 12px 8px !important;
|
||||
border-bottom: 0;
|
||||
|
||||
// 此处不需要 UITable 默认的 border-radius
|
||||
&:first-child {
|
||||
padding-right: 28px !important;
|
||||
padding-left: 8px !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: auto;
|
||||
background: var(--coz-mg-secondary-hovered);
|
||||
|
||||
&>.semi-table-row-cell {
|
||||
background: transparent !important;
|
||||
border-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.table-header-tooltip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.table-structure-bar-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
height: 32px;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
color: var(--coz-fg-plus);
|
||||
|
||||
.icon {
|
||||
margin-top: 3px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,630 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-lines */
|
||||
/* eslint-disable max-lines-per-function */
|
||||
import React, {
|
||||
useCallback,
|
||||
useMemo,
|
||||
useState,
|
||||
type FC,
|
||||
type CSSProperties,
|
||||
} from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { CSS as cssDndKit } from '@dnd-kit/utilities';
|
||||
import {
|
||||
SortableContext,
|
||||
arrayMove,
|
||||
useSortable,
|
||||
verticalListSortingStrategy,
|
||||
} from '@dnd-kit/sortable';
|
||||
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
||||
import {
|
||||
DndContext,
|
||||
PointerSensor,
|
||||
useSensors,
|
||||
useSensor,
|
||||
type DragEndEvent,
|
||||
} from '@dnd-kit/core';
|
||||
import {
|
||||
DataTypeSelect,
|
||||
getDataTypeOptions,
|
||||
getDataTypeText,
|
||||
} from '@coze-data/utils';
|
||||
import { OptType } from '@coze-data/knowledge-resource-processor-core';
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
IconCozInfoCircle,
|
||||
IconCozTrashCan,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
import {
|
||||
Input,
|
||||
Table,
|
||||
Tooltip,
|
||||
Checkbox,
|
||||
type CheckboxProps,
|
||||
} from '@coze-arch/coze-design';
|
||||
import { type ColumnProps, type TableProps } from '@coze-arch/bot-semi/Table';
|
||||
import { UIButton, Typography } from '@coze-arch/bot-semi';
|
||||
import { IconDragOutlined } from '@coze-arch/bot-icons';
|
||||
import { type DocTableColumn } from '@coze-arch/bot-api/memory';
|
||||
import { ColumnType } from '@coze-arch/bot-api/knowledge';
|
||||
|
||||
import { type IValidateRes, validateField, useOptFromQuery } from '@/utils';
|
||||
import { type TableItem } from '@/types/table';
|
||||
import { type SemanticValidateItem } from '@/types';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
type Align = 'center' | 'left' | 'right' | undefined;
|
||||
enum ValidateStatus {
|
||||
Error = 'error',
|
||||
Default = 'default',
|
||||
}
|
||||
|
||||
export interface TableStructureProps extends TableProps {
|
||||
data: Array<
|
||||
DocTableColumn & {
|
||||
key?: string;
|
||||
autofocus?: boolean;
|
||||
errMsg?: string;
|
||||
}
|
||||
>;
|
||||
setData: (v: Array<DocTableColumn>) => void;
|
||||
initValid?: boolean;
|
||||
isBlurValid?: boolean;
|
||||
isPreview?: boolean;
|
||||
verifyMap?: SemanticValidateItem;
|
||||
baseKey?: string;
|
||||
showTitle?: boolean;
|
||||
tipsNode?: React.ReactNode;
|
||||
isDragTable?: boolean;
|
||||
}
|
||||
const NAME_MAX_STR_LEN = 30;
|
||||
const DESC_MAX_STR_LEN = 2000;
|
||||
const baseClassName = 'table-structure';
|
||||
const RequiredColRender = ({
|
||||
children,
|
||||
tooltip,
|
||||
dataTestId,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
tooltip?: string | React.ReactNode;
|
||||
dataTestId?: string;
|
||||
}) => (
|
||||
<div
|
||||
className={styles[`${baseClassName}-required-container`]}
|
||||
data-testid={dataTestId}
|
||||
>
|
||||
{children}
|
||||
<span className={styles[`${baseClassName}-col-required`]}> *</span>
|
||||
{tooltip}
|
||||
</div>
|
||||
);
|
||||
|
||||
interface InputRenderProps {
|
||||
onChange: (v: string) => void;
|
||||
validate?: (v: string, error: string) => IValidateRes;
|
||||
record: TableItem & { errMsg?: string };
|
||||
value: string;
|
||||
autofocus: boolean;
|
||||
initValid: boolean;
|
||||
isBlurValid: boolean;
|
||||
errorMsg?: string;
|
||||
isPreview?: boolean;
|
||||
placeholder?: string;
|
||||
maxStrLen: number;
|
||||
}
|
||||
const InputRender = ({
|
||||
onChange,
|
||||
record,
|
||||
value,
|
||||
autofocus = false,
|
||||
isBlurValid = false,
|
||||
initValid,
|
||||
validate,
|
||||
isPreview,
|
||||
placeholder,
|
||||
maxStrLen,
|
||||
}: InputRenderProps) => {
|
||||
const ValidateResult = (v: string) => {
|
||||
const validRes = validate?.(
|
||||
v,
|
||||
I18n.t('datasets_segment_tableStructure_field_errEmpty'),
|
||||
);
|
||||
return {
|
||||
valid: initValid ? !!(validate ? validRes?.valid : v && v !== '') : true,
|
||||
errorMsg:
|
||||
validRes?.errorMsg ||
|
||||
I18n.t('datasets_segment_tableStructure_field_errEmpty'),
|
||||
};
|
||||
};
|
||||
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
const [validObj, setValidObj] = useState(() => {
|
||||
const res = ValidateResult(value);
|
||||
return {
|
||||
valid: isBlurValid ? true : res.valid,
|
||||
errorMsg: res.errorMsg,
|
||||
};
|
||||
});
|
||||
|
||||
const validateValue = (v: string) => {
|
||||
const validRes = ValidateResult(v);
|
||||
setValidObj(validRes);
|
||||
};
|
||||
|
||||
const hasDisable = isPreview;
|
||||
const apiErrorMsg = record?.errMsg || '';
|
||||
|
||||
const validateStatus = useMemo(() => {
|
||||
if (apiErrorMsg) {
|
||||
return ValidateStatus.Error;
|
||||
}
|
||||
return !validObj.valid ? ValidateStatus.Error : ValidateStatus.Default;
|
||||
}, [validObj, apiErrorMsg]);
|
||||
|
||||
const renderErrorMsg = useCallback(() => {
|
||||
if (apiErrorMsg) {
|
||||
return <div className={styles['input-error-msg']}>{apiErrorMsg}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{!validObj.valid && (
|
||||
<div className={styles['input-error-msg']}>{validObj.errorMsg}</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}, [apiErrorMsg, validObj]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
autoFocus={autofocus}
|
||||
value={inputValue}
|
||||
maxLength={maxStrLen}
|
||||
onChange={v => {
|
||||
setInputValue(v.substring(0, maxStrLen));
|
||||
!isBlurValid && validateValue(v);
|
||||
}}
|
||||
disabled={hasDisable}
|
||||
validateStatus={validateStatus}
|
||||
suffix={
|
||||
<span className={styles['input-suffix']}>
|
||||
{(inputValue || '').length}/{maxStrLen}
|
||||
</span>
|
||||
}
|
||||
onBlur={() => {
|
||||
onChange(inputValue?.substring(0, maxStrLen) || '');
|
||||
validateValue(inputValue);
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
{renderErrorMsg()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// TODO 待解
|
||||
// eslint-disable-next-line @coze-arch/max-line-per-function
|
||||
export const TableStructure: React.FC<TableStructureProps> = ({
|
||||
data = [],
|
||||
setData,
|
||||
verifyMap = {},
|
||||
initValid = false,
|
||||
isBlurValid = false,
|
||||
isPreview = false,
|
||||
baseKey,
|
||||
showTitle = false,
|
||||
children: childrenNode,
|
||||
tipsNode,
|
||||
isDragTable = false,
|
||||
...tableProps
|
||||
}) => {
|
||||
const opt = useOptFromQuery();
|
||||
const isResegment = opt === OptType.RESEGMENT;
|
||||
|
||||
const columns: ColumnProps<TableItem>[] = [
|
||||
{
|
||||
title: () => (
|
||||
<RequiredColRender
|
||||
dataTestId={KnowledgeE2e.TableLocalTableConfigurationIndex}
|
||||
tooltip={
|
||||
<Tooltip
|
||||
className="whitespace-pre-line"
|
||||
content={I18n.t('knowledge_multi_index')}
|
||||
>
|
||||
<UIButton
|
||||
size="small"
|
||||
theme="borderless"
|
||||
type="tertiary"
|
||||
style={{
|
||||
marginLeft: 4,
|
||||
}}
|
||||
icon={<IconCozInfoCircle className="coz-fg-secondary" />}
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={styles['table-header-tooltip']}
|
||||
data-testid={KnowledgeE2e.TableLocalTableConfigurationIndex}
|
||||
>
|
||||
<span>{I18n.t('knowledge_table_structure_semantic')}</span>
|
||||
</div>
|
||||
</RequiredColRender>
|
||||
),
|
||||
dataIndex: 'is_semantic',
|
||||
width: 90,
|
||||
align: 'left' as Align,
|
||||
render: (value, record, index: number) => {
|
||||
const onChange: CheckboxProps['onChange'] = e => {
|
||||
const newData = [...data];
|
||||
newData[index].is_semantic = Boolean(e.target.checked);
|
||||
setData(newData);
|
||||
};
|
||||
const { sequence } = record;
|
||||
function hasFormItemDisable() {
|
||||
const disabled = Object.keys(verifyMap).length
|
||||
? !verifyMap[sequence || index]?.valid
|
||||
: false;
|
||||
return disabled || isPreview;
|
||||
}
|
||||
const hasDisable = hasFormItemDisable();
|
||||
|
||||
const Wrapper = ({ children }: { children: JSX.Element }) => {
|
||||
if (hasDisable && verifyMap[sequence || index]?.msg) {
|
||||
return (
|
||||
<Tooltip
|
||||
trigger="hover"
|
||||
content={verifyMap[sequence || index]?.msg}
|
||||
>
|
||||
{children}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return children;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles['semantic-radio']}>
|
||||
{isDragTable ? (
|
||||
<IconDragOutlined className={'structure-table-drag-icon'} />
|
||||
) : null}
|
||||
<Wrapper>
|
||||
<Checkbox
|
||||
checked={value}
|
||||
disabled={hasDisable}
|
||||
onChange={onChange}
|
||||
data-testid={KnowledgeE2e.TableStructureIndexCheckbox}
|
||||
/>
|
||||
</Wrapper>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<RequiredColRender
|
||||
dataTestId={KnowledgeE2e.TableLocalTableConfigurationColumnName}
|
||||
>
|
||||
{I18n.t('knowledge_table_structure_column_name')}
|
||||
</RequiredColRender>
|
||||
),
|
||||
dataIndex: 'column_name',
|
||||
align: 'left' as Align,
|
||||
render: (value, record, index) => {
|
||||
const { autofocus = false } = record;
|
||||
const onChange = (v: string) => {
|
||||
const newData = [...data];
|
||||
newData[index].column_name = v;
|
||||
setData(newData);
|
||||
};
|
||||
|
||||
const validateFn = (v: string, emptyMsg: string) => {
|
||||
if (data?.filter(i => i.column_name === v).length >= 2) {
|
||||
return {
|
||||
valid: false,
|
||||
errorMsg: I18n.t('Manual_crawling_040'),
|
||||
};
|
||||
}
|
||||
return validateField(v, emptyMsg);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles['column-item']}>
|
||||
<InputRender
|
||||
initValid={initValid}
|
||||
isBlurValid={isBlurValid}
|
||||
key={`${baseKey}${record?.sequence}`}
|
||||
onChange={onChange}
|
||||
record={record}
|
||||
value={value}
|
||||
validate={validateFn}
|
||||
autofocus={autofocus}
|
||||
isPreview={isPreview}
|
||||
maxStrLen={NAME_MAX_STR_LEN}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<div data-testid={KnowledgeE2e.TableLocalTableConfigurationDesc}>
|
||||
{I18n.t('knowledge_table_structure_desc')}
|
||||
</div>
|
||||
),
|
||||
dataIndex: 'desc',
|
||||
align: 'left' as Align,
|
||||
render: (value, record, index) => {
|
||||
const onChange = (v: string) => {
|
||||
const newData = [...data];
|
||||
newData[index].desc = v;
|
||||
setData(newData);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles['column-item']}>
|
||||
<InputRender
|
||||
initValid={false}
|
||||
isBlurValid={false}
|
||||
key={`column-desc.${baseKey}${record?.sequence}`}
|
||||
placeholder={I18n.t('knowledge_variable_description_placeholder')}
|
||||
onChange={onChange}
|
||||
record={record}
|
||||
value={value}
|
||||
autofocus={false}
|
||||
isPreview={isPreview}
|
||||
maxStrLen={DESC_MAX_STR_LEN}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: () => (
|
||||
<RequiredColRender
|
||||
dataTestId={KnowledgeE2e.TableLocalTableConfigurationType}
|
||||
>
|
||||
{I18n.t('knowledge_table_structure_data_type')}
|
||||
</RequiredColRender>
|
||||
),
|
||||
dataIndex: 'column_type',
|
||||
align: 'left' as Align,
|
||||
render: (value, record, index) => {
|
||||
const valid = isBlurValid ? true : !!value;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`pr-[16px] ${styles['column-item']}`}
|
||||
key={record.sequence}
|
||||
>
|
||||
{isPreview ? (
|
||||
<Typography.Text className={styles['column-item-value']}>
|
||||
{getDataTypeText(value)}
|
||||
</Typography.Text>
|
||||
) : (
|
||||
<DataTypeSelect
|
||||
value={value || ''}
|
||||
selectProps={{
|
||||
disabled: !record.is_new_column && isResegment,
|
||||
optionList: getDataTypeOptions().map(option => {
|
||||
if (option.value === ColumnType.Image) {
|
||||
return {
|
||||
...option,
|
||||
disabled: record.is_semantic,
|
||||
};
|
||||
}
|
||||
return option;
|
||||
}),
|
||||
placeholder: I18n.t('db_table_save_exception_fieldtype'),
|
||||
}}
|
||||
errorMsg={
|
||||
valid
|
||||
? undefined
|
||||
: I18n.t(
|
||||
'datasets_segment_tableStructure_field_type_errEmpty',
|
||||
)
|
||||
}
|
||||
handleChange={v => {
|
||||
const newData = [...data];
|
||||
newData[index].column_type = v as ColumnType;
|
||||
setData(newData);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<div data-testid={KnowledgeE2e.TableLocalTableConfigurationAction}>
|
||||
{I18n.t('datasets_unit_upload_field_action')}
|
||||
</div>
|
||||
),
|
||||
dataIndex: 'operate',
|
||||
width: 82,
|
||||
align: 'left' as Align,
|
||||
render: (_text, record, index) => (
|
||||
<div
|
||||
className={styles['column-item-action']}
|
||||
onClick={() => {
|
||||
setData(data.filter((_, i) => index !== i));
|
||||
}}
|
||||
>
|
||||
<Tooltip
|
||||
content={I18n.t(
|
||||
record.is_semantic
|
||||
? 'datasets_segment_tableStructure_delTips'
|
||||
: 'datasets_table_title_actions_delete',
|
||||
)}
|
||||
>
|
||||
<IconCozTrashCan
|
||||
aria-disabled={Boolean(record.is_semantic)}
|
||||
className={styles['column-item-action-delete']}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
// 预览场景下,不需要操作列
|
||||
if (isPreview) {
|
||||
columns.pop();
|
||||
}
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, {
|
||||
activationConstraint: { distance: 1 },
|
||||
}),
|
||||
);
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
const { active, over } = event;
|
||||
if (active && over && active.id !== over.id) {
|
||||
const items = Array.from(data);
|
||||
const activeIndex = items.findIndex(item => item.key === active.id);
|
||||
const overIndex = items.findIndex(item => item.key === over.id);
|
||||
setData(arrayMove(items, activeIndex, overIndex));
|
||||
}
|
||||
};
|
||||
|
||||
const SortableRow: FC<{
|
||||
className: string;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
'data-row-key': string;
|
||||
style: CSSProperties;
|
||||
}> = props => {
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
isDragging,
|
||||
} = useSortable({
|
||||
id: props['data-row-key'],
|
||||
});
|
||||
const style: CSSProperties = {
|
||||
...props.style,
|
||||
transform: cssDndKit.Transform.toString(transform),
|
||||
transition,
|
||||
cursor: isDragging ? 'grabbing' : 'grab',
|
||||
...(isDragging
|
||||
? {
|
||||
zIndex: 999,
|
||||
position: 'relative',
|
||||
background: 'rgba(217, 220, 250, 1)',
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
return (
|
||||
<tr
|
||||
{...props}
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
></tr>
|
||||
);
|
||||
};
|
||||
|
||||
const renderTableContent = () => {
|
||||
if (isDragTable) {
|
||||
return (
|
||||
<DndContext
|
||||
// https://docs.dndkit.com/api-documentation/context-provider#autoscroll
|
||||
autoScroll={true}
|
||||
sensors={sensors}
|
||||
modifiers={[restrictToVerticalAxis]}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<SortableContext
|
||||
items={data.map(item => item.key || '')}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
<Table
|
||||
wrapperClassName={classNames(
|
||||
styles[`${baseClassName}-wrapper`],
|
||||
styles['drag-table'],
|
||||
)}
|
||||
key={baseKey}
|
||||
tableProps={{
|
||||
sticky: true,
|
||||
dataSource: data,
|
||||
columns,
|
||||
pagination: false,
|
||||
className: styles[baseClassName],
|
||||
components: {
|
||||
body: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
row: SortableRow as any,
|
||||
},
|
||||
},
|
||||
...tableProps,
|
||||
}}
|
||||
/>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Table
|
||||
wrapperClassName={styles[`${baseClassName}-wrapper`]}
|
||||
key={baseKey}
|
||||
tableProps={{
|
||||
sticky: true,
|
||||
dataSource: data,
|
||||
columns,
|
||||
pagination: false,
|
||||
className: styles[baseClassName],
|
||||
...tableProps,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles['structure-wrapper']}>
|
||||
{showTitle ? <TableStructureTitle /> : null}
|
||||
{tipsNode ? tipsNode : null}
|
||||
{renderTableContent()}
|
||||
{childrenNode}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const TableStructureTitle = () => (
|
||||
<div
|
||||
className={styles['table-structure-bar-title']}
|
||||
data-testid={KnowledgeE2e.TableLocalTableStructureTitle}
|
||||
>
|
||||
<span>{I18n.t('datasets_segment_tableStructure_title')}</span>
|
||||
<Tooltip content={I18n.t('knowledge_table_structure_column_tooltip')}>
|
||||
<IconCozInfoCircle
|
||||
className={classNames(styles.icon, 'coz-fg-secondary')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// 组件相关
|
||||
export {
|
||||
TableSettingBar,
|
||||
type TableSettingBarProps,
|
||||
} from './components/table-setting-bar';
|
||||
export {
|
||||
type TablePreviewProps,
|
||||
TablePreview,
|
||||
} from './components/table-preview';
|
||||
export {
|
||||
type TableStructureProps,
|
||||
TableStructure,
|
||||
TableStructureTitle,
|
||||
} from './components/table-structure';
|
||||
@@ -0,0 +1,58 @@
|
||||
@import '../../assets/common.less';
|
||||
|
||||
.embed-progress {
|
||||
flex: 1;
|
||||
|
||||
.progress-info {
|
||||
width: 100%;
|
||||
|
||||
.text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
height: 32px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
color: var(--coz-fg-primary);
|
||||
}
|
||||
|
||||
.progress-list {
|
||||
overflow-y: auto;
|
||||
max-height: 532px;
|
||||
}
|
||||
}
|
||||
|
||||
.banner {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.progress-success-icon {
|
||||
.common-svg-icon(16px, var(--semi-color-primary));
|
||||
}
|
||||
}
|
||||
|
||||
.data-processing {
|
||||
.finish-text {
|
||||
overflow: hidden;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 20px; /* 142.857% */
|
||||
color: var(--coz-fg-primary);
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
:global {
|
||||
.process-progress-item-actions {
|
||||
/* stylelint-disable-next-line declaration-no-important */
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { UnitProgress } from './unit-progress';
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { getFormatTypeFromUnitType } from '@coze-data/utils';
|
||||
import { useKnowledgeParams } from '@coze-data/knowledge-stores';
|
||||
import {
|
||||
type ProgressItem,
|
||||
CreateUnitStatus,
|
||||
UnitType,
|
||||
} from '@coze-data/knowledge-resource-processor-core';
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { DocumentStatus } from '@coze-arch/idl/knowledge';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { formatBytes } from '@coze-arch/bot-utils';
|
||||
|
||||
import { getTypeIcon } from '../upload-unit-table/utils';
|
||||
import { ProcessProgressItem } from '../process-progress-item';
|
||||
import { getFrequencyMap } from '../../utils';
|
||||
import { ProcessStatus } from '../../types';
|
||||
|
||||
const INIT_PERCENT = 10;
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
interface UnitProgressProps {
|
||||
progressList: ProgressItem[];
|
||||
createStatus: CreateUnitStatus;
|
||||
}
|
||||
|
||||
const OneHundred = 100;
|
||||
|
||||
function hoursToDays(hours: number | string) {
|
||||
const curHours = typeof hours === 'string' ? parseInt(hours) : hours;
|
||||
if (isNaN(curHours)) {
|
||||
return 0;
|
||||
}
|
||||
return curHours / 24;
|
||||
}
|
||||
|
||||
function formatRemainingTime(remainingSeconds: number) {
|
||||
const minutes = Math.floor(remainingSeconds / 60);
|
||||
const seconds = remainingSeconds % 60;
|
||||
return {
|
||||
minutes,
|
||||
seconds,
|
||||
};
|
||||
}
|
||||
|
||||
const renderSubText = (
|
||||
status: ProcessStatus,
|
||||
item: ProgressItem,
|
||||
hasURLImport?: boolean,
|
||||
) => {
|
||||
const statusDesc = item?.statusDesc || I18n.t('datasets_unit_upload_fail');
|
||||
if (status === ProcessStatus.Failed) {
|
||||
return (
|
||||
<div
|
||||
data-dtestid={`${KnowledgeE2e.CreateUnitListProgressName}.${'subText'}`}
|
||||
className={'text-12px'}
|
||||
>
|
||||
{statusDesc}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
let subDesc = '';
|
||||
if (hasURLImport) {
|
||||
// 更新频率
|
||||
const updateInterval = hoursToDays(item?.update_interval || 0);
|
||||
subDesc = getFrequencyMap(updateInterval);
|
||||
} else {
|
||||
subDesc = formatBytes((item?.size || 0) as number);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
data-dtestid={`${KnowledgeE2e.CreateUnitListProgressName}.${item?.name}`}
|
||||
className={'coz-fg-secondary text-12px'}
|
||||
>
|
||||
{subDesc}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const UnitProgress: React.FC<UnitProgressProps> = ({
|
||||
progressList,
|
||||
createStatus,
|
||||
}) => {
|
||||
const params = useKnowledgeParams();
|
||||
|
||||
const headerTitle = useMemo(() => {
|
||||
let msg: string = I18n.t('datasets_createFileModel_step4_processing');
|
||||
if (createStatus === CreateUnitStatus.TASK_FINISH) {
|
||||
const allFailed = progressList.every(
|
||||
status => status.status === DocumentStatus.Failed,
|
||||
);
|
||||
|
||||
if (allFailed) {
|
||||
msg = I18n.t('datasets_createFileModel_step4_failed');
|
||||
} else {
|
||||
msg = I18n.t('datasets_createFileModel_step4_Finish');
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}, [createStatus, progressList]);
|
||||
|
||||
const unitType = params.type as UnitType;
|
||||
const formatType = getFormatTypeFromUnitType(unitType);
|
||||
const hasURLImport = [
|
||||
UnitType.TABLE_API,
|
||||
UnitType.TABLE_FEISHU,
|
||||
UnitType.TABLE_GOOGLE_DRIVE,
|
||||
UnitType.TABLE_LARK,
|
||||
UnitType.TEXT_FEISHU,
|
||||
UnitType.TEXT_LARK,
|
||||
UnitType.TEXT_NOTION,
|
||||
UnitType.TEXT_URL,
|
||||
UnitType.TEXT_GOOGLE_DRIVE,
|
||||
].includes(unitType);
|
||||
|
||||
const percentFormat = (percent: number, remainingTime: number) => {
|
||||
const { minutes, seconds } = formatRemainingTime(remainingTime as number);
|
||||
const remainingTimeText = I18n.t('knowledge_upload_remaining_time_text', {
|
||||
minutes,
|
||||
seconds,
|
||||
});
|
||||
|
||||
return percent < OneHundred
|
||||
? `${percent}% ${
|
||||
Number(remainingTime) > 0 ? `(${remainingTimeText})` : ''
|
||||
}`
|
||||
: null;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles['embed-progress']}>
|
||||
<div className={styles['progress-info']}>
|
||||
<div
|
||||
className={styles.text}
|
||||
data-testid={KnowledgeE2e.CreateUnitProgressTitle}
|
||||
>
|
||||
{headerTitle}
|
||||
</div>
|
||||
<div className={styles['progress-list']}>
|
||||
{progressList.map(item => {
|
||||
const { status } = item;
|
||||
const getProcessStatus = () => {
|
||||
if (
|
||||
[DocumentStatus.Failed, DocumentStatus.AuditFailed].includes(
|
||||
status,
|
||||
)
|
||||
) {
|
||||
return ProcessStatus.Failed;
|
||||
}
|
||||
|
||||
if (
|
||||
[
|
||||
DocumentStatus.Processing,
|
||||
DocumentStatus.Resegment,
|
||||
DocumentStatus.Refreshing,
|
||||
].includes(status)
|
||||
) {
|
||||
return ProcessStatus.Processing;
|
||||
}
|
||||
|
||||
if (
|
||||
[DocumentStatus.Enable, DocumentStatus.Disable].includes(status)
|
||||
) {
|
||||
return ProcessStatus.Complete;
|
||||
}
|
||||
|
||||
return ProcessStatus.Processing;
|
||||
};
|
||||
|
||||
const curStatus = getProcessStatus();
|
||||
const percent = item?.progress || INIT_PERCENT;
|
||||
return (
|
||||
<ProcessProgressItem
|
||||
className={styles['data-processing']}
|
||||
key={item?.documentId}
|
||||
mainText={item?.name}
|
||||
subText={renderSubText(curStatus, item, hasURLImport)}
|
||||
tipText={renderSubText(curStatus, item, hasURLImport)}
|
||||
status={curStatus}
|
||||
avatar={getTypeIcon({
|
||||
type: item?.type,
|
||||
url: item?.url,
|
||||
formatType,
|
||||
})}
|
||||
percent={percent}
|
||||
percentFormat={percentFormat(
|
||||
percent,
|
||||
item?.remaining_time as number,
|
||||
)}
|
||||
actions={[
|
||||
ProcessStatus.Complete ? (
|
||||
<div
|
||||
className={styles['finish-text']}
|
||||
data-testid={`${
|
||||
KnowledgeE2e.CreateUnitListProgressSuccessIcon
|
||||
}.${item?.name || ''}`}
|
||||
>
|
||||
{I18n.t('datasets_unit_process_success')}
|
||||
</div>
|
||||
) : null,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
.upload-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
margin-top: auto;
|
||||
margin-bottom: 18px;
|
||||
padding-top: 24px;
|
||||
padding-bottom: 24px;
|
||||
|
||||
line-height: 32px;
|
||||
|
||||
:global {
|
||||
.semi-button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { UploadFooter } from './upload-footer';
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { isArray, isObject, get } from 'lodash-es';
|
||||
import {
|
||||
FooterBtnStatus,
|
||||
type FooterControlsProps,
|
||||
type FooterControlProp,
|
||||
type FooterBtnProps,
|
||||
type FooterPrefixType,
|
||||
} from '@coze-data/knowledge-resource-processor-core';
|
||||
import { Button, Tooltip } from '@coze-arch/coze-design';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
interface UploadFooterProps {
|
||||
controls: FooterControlsProps;
|
||||
}
|
||||
|
||||
/** 类型断言 入参是不是 按钮数组 */
|
||||
function isBtnArray(controls: unknown): controls is FooterBtnProps[] {
|
||||
return !!controls && isArray(controls);
|
||||
}
|
||||
|
||||
function isControlsObject(controls: unknown): controls is FooterControlProp {
|
||||
return (
|
||||
!!controls &&
|
||||
isObject(controls) &&
|
||||
!!get(controls, 'btns') &&
|
||||
!!get(controls, 'prefix')
|
||||
);
|
||||
}
|
||||
|
||||
export const UploadFooter = (props: UploadFooterProps) => {
|
||||
const { controls } = props;
|
||||
let btns: FooterBtnProps[] = [];
|
||||
let prefix: FooterPrefixType;
|
||||
if (isBtnArray(controls)) {
|
||||
btns = controls;
|
||||
}
|
||||
if (isControlsObject(controls)) {
|
||||
({ btns, prefix } = controls);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles['upload-footer']}>
|
||||
{prefix}
|
||||
{btns.map(btnItem => {
|
||||
const isShowHoverContent =
|
||||
btnItem.disableHoverContent &&
|
||||
btnItem.status === FooterBtnStatus.DISABLE;
|
||||
const buttonNode = (
|
||||
<Button
|
||||
data-testid={btnItem.e2e}
|
||||
key={btnItem.text}
|
||||
disabled={btnItem.status === FooterBtnStatus.DISABLE}
|
||||
loading={btnItem.status === FooterBtnStatus.LOADING}
|
||||
color={btnItem.type || 'hgltplus'}
|
||||
// theme={btnItem.theme || 'solid'}
|
||||
onClick={btnItem.onClick}
|
||||
>
|
||||
{btnItem.text}
|
||||
</Button>
|
||||
);
|
||||
if (!isShowHoverContent) {
|
||||
return buttonNode;
|
||||
}
|
||||
return (
|
||||
<Tooltip content={btnItem.disableHoverContent}>{buttonNode}</Tooltip>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { getKnowledgeIDEQuery } from '@coze-data/knowledge-common-services';
|
||||
import { useDataNavigate, useKnowledgeParams } from '@coze-data/knowledge-stores';
|
||||
import { IconCozArrowLeft } from '@coze-arch/coze-design/icons';
|
||||
import { IconButton, Typography } from '@coze-arch/coze-design';
|
||||
|
||||
interface UploadActionNavbarProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
// 上传页面导航栏
|
||||
export const UploadActionNavbar = ({ title }: UploadActionNavbarProps) => {
|
||||
const params = useKnowledgeParams();
|
||||
const resourceNavigate = useDataNavigate();
|
||||
|
||||
// TODO: hzf biz的分化在Scene层维护
|
||||
const fromProject = params.biz === 'project';
|
||||
const handleBack = () => {
|
||||
const query = getKnowledgeIDEQuery() as Record<string, string>;
|
||||
resourceNavigate.toResource?.('knowledge', params.datasetID, query);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'flex items-center justify-between shrink-0 h-[56px] coz-fg-primary',
|
||||
fromProject ? 'px-[12px]' : '',
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<IconButton
|
||||
color="secondary"
|
||||
icon={<IconCozArrowLeft className="text-[16px]" />}
|
||||
iconPosition="left"
|
||||
className="!p-[8px]"
|
||||
onClick={handleBack}
|
||||
></IconButton>
|
||||
<Typography.Text fontSize="16px" weight={500} className="ml-[8px]">
|
||||
{title}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DataNamespace, dataReporter } from '@coze-data/reporter';
|
||||
import { UploadStatus } from '@coze-data/knowledge-resource-processor-core';
|
||||
import { REPORT_EVENTS } from '@coze-arch/report-events';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { type UploadProps } from '@coze-arch/bot-semi/Upload';
|
||||
import { Toast } from '@coze-arch/coze-design';
|
||||
|
||||
import { getFileExtension, getUint8Array } from '../../utils';
|
||||
import { UNIT_MAX_MB, PDF_MAX_PAGES } from '../../constants';
|
||||
|
||||
export const getBeforeUpload: (params: {
|
||||
maxSizeMB: UploadProps['maxSize'];
|
||||
}) => UploadProps['beforeUpload'] =
|
||||
({ maxSizeMB }) =>
|
||||
async fileInfo => {
|
||||
// 不通过 maxSize 属性来限制的原因是
|
||||
// 只有 beforeUpload 钩子能改 validateMessage
|
||||
const res = {
|
||||
fileInstance: fileInfo.file.fileInstance,
|
||||
status: fileInfo.file.status,
|
||||
validateMessage: fileInfo.file.validateMessage,
|
||||
shouldUpload: true,
|
||||
autoRemove: false,
|
||||
};
|
||||
|
||||
const { fileInstance } = fileInfo.file;
|
||||
|
||||
if (!fileInstance) {
|
||||
return {
|
||||
...res,
|
||||
status: UploadStatus.UPLOAD_FAIL,
|
||||
shouldUpload: false,
|
||||
};
|
||||
}
|
||||
|
||||
const resultMaxSizeMB = maxSizeMB || UNIT_MAX_MB;
|
||||
|
||||
const maxSize = resultMaxSizeMB * 1024 * 1024;
|
||||
|
||||
if (fileInstance.size > maxSize) {
|
||||
Toast.warning({
|
||||
showClose: false,
|
||||
content: I18n.t('file_too_large', {
|
||||
max_size: `${resultMaxSizeMB}MB`,
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
...res,
|
||||
shouldUpload: false,
|
||||
status: UploadStatus.VALIDATE_FAIL,
|
||||
validateMessage: I18n.t('file_too_large', {
|
||||
max_size: `${resultMaxSizeMB}MB`,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (getFileExtension(fileInstance.name).toLowerCase() === 'pdf') {
|
||||
try {
|
||||
// TODO: 后续其他位置的 pdfjs 调用也都应该改成异步加载
|
||||
const pdfjs = await import('@coze-arch/pdfjs-shadow');
|
||||
const { getDocument, initPdfJsWorker } = pdfjs;
|
||||
|
||||
initPdfJsWorker();
|
||||
const uint8Array = await getUint8Array(fileInstance);
|
||||
const pdfDocument = await getDocument({ data: uint8Array }).promise;
|
||||
if (pdfDocument.numPages > PDF_MAX_PAGES) {
|
||||
Toast.warning({
|
||||
showClose: false,
|
||||
content: I18n.t('atasets_createpdf_over250'),
|
||||
});
|
||||
return {
|
||||
shouldUpload: false,
|
||||
status: UploadStatus.VALIDATE_FAIL,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
dataReporter.errorEvent(DataNamespace.KNOWLEDGE, {
|
||||
eventName: REPORT_EVENTS.KnowledgeParseFile,
|
||||
error,
|
||||
});
|
||||
if (error?.name === 'PasswordException') {
|
||||
Toast.error({
|
||||
showClose: false,
|
||||
content: I18n.t('pdf_encrypted'),
|
||||
});
|
||||
return {
|
||||
shouldUpload: false,
|
||||
status: UploadStatus.VALIDATE_FAIL,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DataNamespace, dataReporter } from '@coze-data/reporter';
|
||||
import { UploadStatus } from '@coze-data/knowledge-resource-processor-core';
|
||||
import { REPORT_EVENTS } from '@coze-arch/report-events';
|
||||
import { type UploadProps } from '@coze-arch/bot-semi/Upload';
|
||||
import { CustomError } from '@coze-arch/bot-error';
|
||||
import { FileBizType } from '@coze-arch/bot-api/developer_api';
|
||||
import { DeveloperApi } from '@coze-arch/bot-api';
|
||||
|
||||
import { getBase64, getFileExtension } from '../../utils';
|
||||
|
||||
export const customRequest: UploadProps['customRequest'] = async options => {
|
||||
const { onSuccess, onError, onProgress, file } = options;
|
||||
|
||||
try {
|
||||
// 业务
|
||||
const { name, fileInstance } = file;
|
||||
|
||||
if (fileInstance) {
|
||||
const extension = getFileExtension(name);
|
||||
|
||||
const base64 = await getBase64(fileInstance);
|
||||
const result = await DeveloperApi.UploadFile(
|
||||
{
|
||||
file_head: {
|
||||
file_type: extension,
|
||||
biz_type: FileBizType.BIZ_BOT_DATASET,
|
||||
},
|
||||
data: base64,
|
||||
},
|
||||
{
|
||||
onUploadProgress: e => {
|
||||
const status = file?.status;
|
||||
const response = file?.response;
|
||||
// 成功或失败、检验失败后,或者有返回接口数据,不再更新进度条
|
||||
if (
|
||||
status === UploadStatus.SUCCESS ||
|
||||
status === UploadStatus.UPLOAD_FAIL ||
|
||||
status === UploadStatus.VALIDATE_FAIL ||
|
||||
response?.upload_url
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { total, loaded } = e;
|
||||
if (total !== undefined && loaded < total) {
|
||||
onProgress({
|
||||
total: e.total ?? fileInstance.size,
|
||||
loaded: e.loaded,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
onSuccess(result.data);
|
||||
} else {
|
||||
onError({
|
||||
status: 0,
|
||||
});
|
||||
dataReporter.errorEvent(DataNamespace.KNOWLEDGE, {
|
||||
eventName: REPORT_EVENTS.KnowledgeUploadFile,
|
||||
error: new CustomError(
|
||||
REPORT_EVENTS.KnowledgeUploadFile,
|
||||
`${REPORT_EVENTS.KnowledgeUploadFile}: Failed to upload file`,
|
||||
),
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
onError({
|
||||
status: 0,
|
||||
});
|
||||
dataReporter.errorEvent(DataNamespace.KNOWLEDGE, {
|
||||
eventName: REPORT_EVENTS.KnowledgeUploadFile,
|
||||
error,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
.upload {
|
||||
width: 100%;
|
||||
|
||||
:global {
|
||||
.semi-upload-drag-area {
|
||||
height: 202px;
|
||||
background-color: var(--coz-mg-card);
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover,
|
||||
&.semi-upload-drag-area-legal {
|
||||
border-radius: var(--default, 8px);
|
||||
}
|
||||
}
|
||||
|
||||
.semi-upload-drag-area-sub-text {
|
||||
color: var(--coz-fg-dim);
|
||||
}
|
||||
|
||||
.semi-button-with-icon-only {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.semi-upload-file-list {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.create-enough-file {
|
||||
cursor: not-allowed;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.picture {
|
||||
width: 122px;
|
||||
height: 122px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: rgb(28 31 35 / 60%);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { UploadUnitFile } from './upload-unit-file';
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import { type RenderFileItemProps } from '@coze-arch/bot-semi/Upload';
|
||||
import {
|
||||
IconPDFFile,
|
||||
IconUnknowFile as IconUnknownFile,
|
||||
IconTextFile,
|
||||
IconDocxFile,
|
||||
} from '@coze-arch/bot-icons';
|
||||
|
||||
import { getFileExtension } from '../../utils/common';
|
||||
import { type FileType } from './types';
|
||||
|
||||
export const PreviewFile: FC<RenderFileItemProps> = props => {
|
||||
const type = (getFileExtension(props.name) || 'unknown') as FileType;
|
||||
|
||||
const components: Record<FileType, React.FC> = {
|
||||
unknown: IconUnknownFile,
|
||||
pdf: IconPDFFile,
|
||||
text: IconTextFile,
|
||||
docx: IconDocxFile,
|
||||
};
|
||||
|
||||
const ComponentToRender = components[type] || IconUnknownFile;
|
||||
|
||||
return <ComponentToRender />;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type FileType = 'unknown' | 'pdf' | 'docx' | 'text';
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback, useMemo, type FC } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { IllustrationSuccess } from '@douyinfe/semi-illustrations';
|
||||
import { abortable, useUnmountSignal } from '@coze-data/utils';
|
||||
import { type UnitItem } from '@coze-data/knowledge-resource-processor-core';
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
type FileItem,
|
||||
type UploadProps,
|
||||
type OnChangeProps,
|
||||
} from '@coze-arch/bot-semi/Upload';
|
||||
import { IconCozUpload } from '@coze-arch/coze-design/icons';
|
||||
import { Toast, Upload } from '@coze-arch/coze-design';
|
||||
|
||||
import { UNIT_MAX_MB } from '../../constants';
|
||||
import {
|
||||
filterFileListByUnitList,
|
||||
filterFileList,
|
||||
filterUnitList,
|
||||
} from './utils';
|
||||
import { PreviewFile } from './preview-file';
|
||||
import { customRequest } from './custom-request';
|
||||
import { getBeforeUpload } from './before-upload';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
interface UploadUnitFileProps extends UploadProps {
|
||||
unitList: UnitItem[];
|
||||
onFinish: (unitList: UnitItem[]) => void;
|
||||
limit: number;
|
||||
accept: string;
|
||||
setUnitList: (unitList: UnitItem[]) => void;
|
||||
showIllustration?: boolean;
|
||||
maxSizeMB?: number;
|
||||
}
|
||||
|
||||
export const UploadUnitFile: FC<UploadUnitFileProps> = props => {
|
||||
const {
|
||||
unitList,
|
||||
onFinish,
|
||||
setUnitList,
|
||||
showIllustration = true,
|
||||
multiple = true,
|
||||
maxSizeMB = UNIT_MAX_MB,
|
||||
...uploadProps
|
||||
} = props;
|
||||
const { limit } = uploadProps;
|
||||
|
||||
const [fileList, setFileList] = useState<FileItem[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (unitList.length < fileList.length) {
|
||||
setFileList(filterFileListByUnitList(fileList, unitList));
|
||||
}
|
||||
}, [unitList.length]);
|
||||
|
||||
const handleAcceptInvalid = useCallback(() => {
|
||||
Toast.warning({
|
||||
showClose: false,
|
||||
content: I18n.t('knowledge_upload_format_error'),
|
||||
});
|
||||
}, []);
|
||||
|
||||
const signal = useUnmountSignal();
|
||||
|
||||
const handleUploadProcess = abortable((data: OnChangeProps) => {
|
||||
setFileList(data.fileList);
|
||||
setUnitList(filterFileList(data.fileList));
|
||||
}, signal);
|
||||
|
||||
const handleUploadSuccess = abortable(() => {
|
||||
onFinish(filterUnitList(unitList, fileList));
|
||||
}, signal);
|
||||
|
||||
const uploadDisabled = useMemo(
|
||||
() => unitList.length >= limit,
|
||||
[unitList, limit],
|
||||
);
|
||||
const beforeUpload = getBeforeUpload({ maxSizeMB });
|
||||
return (
|
||||
<Upload
|
||||
draggable
|
||||
data-testid={KnowledgeE2e.UploadUnitFile}
|
||||
multiple={multiple}
|
||||
fileList={fileList}
|
||||
disabled={uploadDisabled}
|
||||
previewFile={PreviewFile}
|
||||
onAcceptInvalid={handleAcceptInvalid}
|
||||
beforeUpload={beforeUpload}
|
||||
customRequest={customRequest}
|
||||
onChange={handleUploadProcess}
|
||||
onSuccess={handleUploadSuccess}
|
||||
dragIcon={<IconCozUpload className={styles['upload-icon']} />}
|
||||
{...uploadProps}
|
||||
className={classNames(styles.upload, uploadProps.className)}
|
||||
>
|
||||
{unitList.length >= limit && showIllustration ? (
|
||||
<div
|
||||
className={styles['create-enough-file']}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<IllustrationSuccess className={styles.picture} />
|
||||
<div className={styles.text}>
|
||||
{I18n.t('knowledge_1218_001', {
|
||||
MaxDocs: limit,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</Upload>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
UploadStatus,
|
||||
type UnitItem,
|
||||
} from '@coze-data/knowledge-resource-processor-core';
|
||||
import { KNOWLEDGE_UNIT_NAME_MAX_LEN } from '@coze-data/knowledge-modal-base';
|
||||
import { type FileItem } from '@coze-arch/bot-semi/Upload';
|
||||
|
||||
import { getFileExtension } from '../../utils/common';
|
||||
|
||||
interface GetFileListMapRes {
|
||||
[key: string]: FileItem;
|
||||
}
|
||||
|
||||
const getFileName = (uri: string) => uri.substring(0, uri.lastIndexOf('.'));
|
||||
|
||||
const getNameFromFileList = (unitList: FileItem[], index: number) =>
|
||||
unitList?.[index]?.name;
|
||||
|
||||
const getFileListMap = (fileList: FileItem[]): GetFileListMapRes =>
|
||||
fileList.reduce((acc: GetFileListMapRes, item) => {
|
||||
acc[item.uid || ''] = item;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
/** 过滤 fileList,仅保留在 unitList 中的文件,即上传成功的文件 */
|
||||
export const filterFileListByUnitList = (
|
||||
fileList: FileItem[],
|
||||
unitList: UnitItem[],
|
||||
): FileItem[] =>
|
||||
fileList.filter(fileItem =>
|
||||
unitList?.find(unitItem => unitItem.uri === fileItem?.response?.upload_uri),
|
||||
);
|
||||
|
||||
const fileItem2UnitItem = (
|
||||
file: FileItem,
|
||||
config?: { filename?: string },
|
||||
): UnitItem => ({
|
||||
type: getFileExtension(file?.response?.upload_uri || file.name),
|
||||
uri: file?.response?.upload_uri,
|
||||
url: file?.response?.upload_url,
|
||||
name: (config?.filename ?? getFileName(file.name)).slice(
|
||||
0,
|
||||
KNOWLEDGE_UNIT_NAME_MAX_LEN,
|
||||
),
|
||||
size: file.size,
|
||||
status: file.status as UnitItem['status'],
|
||||
percent: file.percent || 0,
|
||||
fileInstance: file.fileInstance,
|
||||
uid: file.uid,
|
||||
validateMessage: (file.validateMessage as string) || '',
|
||||
});
|
||||
|
||||
export const filterFileList = (fileList: FileItem[]): UnitItem[] => {
|
||||
const filteredList: UnitItem[] = fileList
|
||||
.filter(
|
||||
item =>
|
||||
!(!item.shouldUpload && item.status === UploadStatus.VALIDATE_FAIL),
|
||||
)
|
||||
.map((file, index) => {
|
||||
const filename = getNameFromFileList(fileList, index);
|
||||
return fileItem2UnitItem(file, { filename });
|
||||
});
|
||||
return filteredList;
|
||||
};
|
||||
|
||||
export const filterUnitList = (
|
||||
unitList: UnitItem[],
|
||||
fileList: FileItem[],
|
||||
): UnitItem[] => {
|
||||
const fileListMap = getFileListMap(fileList);
|
||||
return unitList
|
||||
.filter(unit => {
|
||||
const file = fileListMap[unit.uid || ''];
|
||||
if (!file.shouldUpload && file.status === UploadStatus.VALIDATE_FAIL) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map((unit, index) => {
|
||||
const file = fileListMap[unit.uid || ''];
|
||||
const filename = getNameFromFileList(fileList, index);
|
||||
return {
|
||||
...unit,
|
||||
...fileItem2UnitItem(file, { filename }),
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type UnitItem } from '@coze-data/knowledge-resource-processor-core';
|
||||
import { useEditUnitNameModal } from '@coze-data/knowledge-modal-base';
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
IconCozEdit,
|
||||
IconCozRefresh,
|
||||
IconCozTrashCan,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
import { IconButton, Tooltip } from '@coze-arch/coze-design';
|
||||
|
||||
import { type RenderColumnsProps } from '../types';
|
||||
import { getFrequencyMap } from '../../../utils';
|
||||
|
||||
export function getFileSizeInfo(record: UnitItem) {
|
||||
return (
|
||||
<div
|
||||
data-dtestid={`${KnowledgeE2e.LocalUploadListFileSize}.${record.name}`}
|
||||
className={'coz-fg-secondary text-12px'}
|
||||
>
|
||||
{record?.size}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function getFrequencyInfo(record: UnitItem) {
|
||||
return (
|
||||
<div
|
||||
data-dtestid={`${KnowledgeE2e.LocalUploadListFrequency}.${record.name}`}
|
||||
className={'coz-fg-secondary text-12px'}
|
||||
>
|
||||
{getFrequencyMap(record.updateInterval || 0)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ActionRenderByDelete(props: RenderColumnsProps) {
|
||||
const { index, record, params } = props;
|
||||
const { onChange, unitList, onDelete } = params;
|
||||
|
||||
const handleDelete = () => {
|
||||
onChange(unitList.filter((u, i) => index !== i));
|
||||
if (typeof onDelete === 'function') {
|
||||
onDelete?.(record, index);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Tooltip spacing={12} content={I18n.t('Delete')} position="top">
|
||||
<IconButton
|
||||
color="secondary"
|
||||
icon={<IconCozTrashCan className="text-14px" />}
|
||||
iconPosition="left"
|
||||
size="small"
|
||||
onClick={handleDelete}
|
||||
></IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export function ActionRenderByEditName(props: RenderColumnsProps) {
|
||||
const { index, record, params } = props;
|
||||
const { onChange, unitList } = params;
|
||||
|
||||
const { node, open } = useEditUnitNameModal({
|
||||
name: record?.name ?? '',
|
||||
onOk: (name: string) => {
|
||||
const arr = [...unitList];
|
||||
arr[index].name = name;
|
||||
onChange(arr);
|
||||
},
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<Tooltip spacing={12} content={I18n.t('Edit')} position="top">
|
||||
<IconButton
|
||||
color="secondary"
|
||||
icon={<IconCozEdit className="text-14px" />}
|
||||
iconPosition="left"
|
||||
size="small"
|
||||
onClick={() => open()}
|
||||
/>
|
||||
</Tooltip>
|
||||
{node}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function ActionRenderByRetry(props: RenderColumnsProps) {
|
||||
const { index, record, params } = props;
|
||||
if (params.disableRetry) {
|
||||
return null;
|
||||
}
|
||||
const { onRetry } = params;
|
||||
|
||||
const handleRetry = () => {
|
||||
onRetry?.(record, index);
|
||||
};
|
||||
return (
|
||||
<Tooltip
|
||||
spacing={12}
|
||||
content={I18n.t('datasets_unit_update_retry')}
|
||||
position="top"
|
||||
>
|
||||
<IconButton
|
||||
color="secondary"
|
||||
icon={<IconCozRefresh className="text-14px" />}
|
||||
iconPosition="left"
|
||||
size="small"
|
||||
onClick={handleRetry}
|
||||
></IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import { UITableAction } from '@coze-arch/bot-semi';
|
||||
|
||||
import { type ActionProps } from '../../types';
|
||||
|
||||
export const Action: FC<ActionProps> = ({
|
||||
onDelete,
|
||||
showEdit,
|
||||
deleteProps,
|
||||
editProps,
|
||||
}: ActionProps) => (
|
||||
<UITableAction
|
||||
deleteProps={{
|
||||
disabled: false,
|
||||
handleClick: onDelete,
|
||||
...deleteProps,
|
||||
}}
|
||||
editProps={{
|
||||
hide: !showEdit,
|
||||
...editProps,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { Action } from './action';
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { UploadStatusComp } from './upload-status';
|
||||
export { UnitName } from './unit-name';
|
||||
export { Action } from './action-render';
|
||||
@@ -0,0 +1,44 @@
|
||||
.unit-name-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 3px;
|
||||
|
||||
:global {
|
||||
.semi-icon {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.view-name {
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.input-suffix {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
font-size: 12px;
|
||||
color: rgb(28 29 35 / 35%);
|
||||
}
|
||||
|
||||
.unit-name-input {
|
||||
width: 100%;
|
||||
|
||||
.error {
|
||||
position: absolute;
|
||||
color: rgb(249 57 32 / 100%);
|
||||
}
|
||||
}
|
||||
|
||||
.unit-name-error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.error {
|
||||
color: rgb(249 57 32 / 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import { KNOWLEDGE_UNIT_NAME_MAX_LEN } from '@coze-data/knowledge-modal-base';
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { UIInput } from '@coze-arch/bot-semi';
|
||||
|
||||
import { getTypeIcon } from '../../utils';
|
||||
import { type UnitNameProps } from '../../types';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const UnitName: React.FC<UnitNameProps> = ({
|
||||
edit,
|
||||
onChange,
|
||||
disabled,
|
||||
record,
|
||||
formatType,
|
||||
}) => {
|
||||
const { type, name, validateMessage } = record;
|
||||
const [value, setValue] = useState(name); // 需要用自身state,否则出现无法输入中文的bug
|
||||
const getValidateMessage = (val: string) =>
|
||||
!val ? I18n.t('datasets_unit_exception_name_empty') : validateMessage;
|
||||
useEffect(() => {
|
||||
setValue(name);
|
||||
}, [name]);
|
||||
return (
|
||||
<div
|
||||
className={styles['unit-name-wrap']}
|
||||
data-testid={`${KnowledgeE2e.FeishuUploadListName}.${name}`}
|
||||
>
|
||||
{getTypeIcon({ type, formatType })}
|
||||
{edit ? (
|
||||
<div className="unit-name-input">
|
||||
<UIInput
|
||||
disabled={disabled}
|
||||
value={value}
|
||||
onChange={val => {
|
||||
setValue(val);
|
||||
onChange(val);
|
||||
}}
|
||||
maxLength={KNOWLEDGE_UNIT_NAME_MAX_LEN}
|
||||
validateStatus={!name ? 'error' : 'default'}
|
||||
suffix={
|
||||
<span className="input-suffix">
|
||||
{(name || '').length}/{KNOWLEDGE_UNIT_NAME_MAX_LEN}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<div className="error">{getValidateMessage(name)}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="unit-name-error">
|
||||
<span className="view-name">{name}</span>
|
||||
{validateMessage ? (
|
||||
<div className="error">{validateMessage}</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useState, useEffect, type FC } from 'react';
|
||||
|
||||
import { KNOWLEDGE_UNIT_NAME_MAX_LEN } from '@coze-data/knowledge-modal-base';
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { UIInput } from '@coze-arch/bot-semi';
|
||||
import { FormatType } from '@coze-arch/bot-api/memory';
|
||||
|
||||
import { validateField } from '@/utils';
|
||||
|
||||
import { getTypeIcon } from '../../utils';
|
||||
import { type UnitNameProps } from '../../types';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const UnitName: FC<UnitNameProps> = ({
|
||||
edit,
|
||||
onChange,
|
||||
disabled,
|
||||
record,
|
||||
formatType,
|
||||
canValidator = true,
|
||||
inModal = false,
|
||||
}) => {
|
||||
const { type, name, validateMessage, dynamicErrorMessage } = record;
|
||||
const [value, setValue] = useState(name); // 需要用自身state,否则出现无法输入中文的bug
|
||||
const [validData, setValidData] = useState({ valid: true, errorMsg: '' });
|
||||
|
||||
const getValidateMessage = (val: string) =>
|
||||
!val ? I18n.t('datasets_unit_exception_name_empty') : validateMessage;
|
||||
|
||||
const validator = (val: string) => {
|
||||
const validObj = validateField(val, getValidateMessage(name));
|
||||
setValidData(
|
||||
formatType === FormatType.Table
|
||||
? validObj
|
||||
: {
|
||||
valid: !!name,
|
||||
errorMsg: '',
|
||||
},
|
||||
);
|
||||
};
|
||||
useEffect(() => {
|
||||
setValue(name);
|
||||
canValidator && !disabled && validator(name);
|
||||
}, [name, disabled]);
|
||||
|
||||
return (
|
||||
<div className={styles['unit-name-wrap']}>
|
||||
{getTypeIcon({ type, formatType, url: record.url, inModal })}
|
||||
{edit ? (
|
||||
<div className="unit-name-input">
|
||||
<UIInput
|
||||
data-dtestid={`${KnowledgeE2e.LocalUploadListName}.${record.name}`}
|
||||
disabled={disabled}
|
||||
autoFocus={true}
|
||||
value={value}
|
||||
onChange={val => {
|
||||
setValue(val);
|
||||
onChange(val);
|
||||
}}
|
||||
onBlur={() => {
|
||||
canValidator && validator(name);
|
||||
}}
|
||||
maxLength={KNOWLEDGE_UNIT_NAME_MAX_LEN}
|
||||
validateStatus={
|
||||
!validData.valid || dynamicErrorMessage ? 'error' : 'default'
|
||||
}
|
||||
suffix={
|
||||
<span className="input-suffix">
|
||||
{(name || '').length}/{KNOWLEDGE_UNIT_NAME_MAX_LEN}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<div className="error">
|
||||
{!disabled &&
|
||||
(validData.errorMsg ||
|
||||
getValidateMessage(name) ||
|
||||
dynamicErrorMessage)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<span
|
||||
className="view-name"
|
||||
data-dtestid={`${KnowledgeE2e.LocalUploadListNameView}.${record.name}`}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
.upload-status-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 22px;
|
||||
|
||||
:global {
|
||||
.semi-icon {
|
||||
float: left;
|
||||
margin: 0 5px -3px 0;
|
||||
}
|
||||
|
||||
.semi-progress-horizontal {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.retry {
|
||||
cursor: pointer;
|
||||
|
||||
:global {
|
||||
.semi-icon {
|
||||
float: left;
|
||||
margin: 3px 3px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.retry-text {
|
||||
margin-left: 3px;
|
||||
color: rgb(77 83 232 / 100%);
|
||||
}
|
||||
|
||||
|
||||
.disabled-retry {
|
||||
.retry-text {
|
||||
cursor: not-allowed;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.no-retry-text {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable complexity */
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
UploadStatus,
|
||||
EntityStatus,
|
||||
} from '@coze-data/knowledge-resource-processor-core';
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Tooltip, Progress, Spin } from '@coze-arch/bot-semi';
|
||||
import {
|
||||
IconUploadFileSuccess,
|
||||
IconUploadFileFail,
|
||||
} from '@coze-arch/bot-icons';
|
||||
import { WebStatus } from '@coze-arch/bot-api/knowledge';
|
||||
|
||||
import { type UploadStateProps } from '../../types';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const UploadStatusComp: React.FC<UploadStateProps> = ({
|
||||
record,
|
||||
onRetry,
|
||||
index,
|
||||
needLoading,
|
||||
overlayClassName,
|
||||
disableRetry,
|
||||
noRetry,
|
||||
}) => {
|
||||
const { status } = record;
|
||||
if (
|
||||
status === UploadStatus.UPLOADING ||
|
||||
status === UploadStatus.VALIDATING ||
|
||||
status === UploadStatus.WAIT ||
|
||||
status === WebStatus.Handling ||
|
||||
status === EntityStatus.EntityStatusProcess
|
||||
) {
|
||||
return (
|
||||
<span
|
||||
data-dtestid={`${KnowledgeE2e.LocalUploadListStatus}.${record.name}`}
|
||||
className={classNames(styles['upload-status-wrap'], overlayClassName)}
|
||||
>
|
||||
<span>{I18n.t('datasets_unit_upload_state')}</span>
|
||||
{needLoading ? (
|
||||
<Spin spinning={true} />
|
||||
) : (
|
||||
<Progress percent={record.percent} />
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (
|
||||
status === UploadStatus.SUCCESS ||
|
||||
status === WebStatus.Finish ||
|
||||
status === EntityStatus.EntityStatusSuccess
|
||||
) {
|
||||
return (
|
||||
<span
|
||||
className={styles['upload-status-wrap']}
|
||||
data-dtestid={`${KnowledgeE2e.LocalUploadListStatus}.${record.name}`}
|
||||
>
|
||||
<IconUploadFileSuccess />
|
||||
<span>{I18n.t('datasets_unit_upload_success')}</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (status === UploadStatus.VALIDATE_FAIL) {
|
||||
return (
|
||||
<span
|
||||
className={styles['upload-status-wrap']}
|
||||
data-dtestid={`${KnowledgeE2e.LocalUploadListStatus}.${record.name}`}
|
||||
>
|
||||
<IconUploadFileFail />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (
|
||||
status === UploadStatus.UPLOAD_FAIL ||
|
||||
status === WebStatus.Failed ||
|
||||
status === EntityStatus.EntityStatusFail
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
data-dtestid={`${KnowledgeE2e.LocalUploadListStatus}.${record.name}`}
|
||||
className={classNames(
|
||||
`${styles['upload-status-wrap']} ${styles.retry}`,
|
||||
overlayClassName,
|
||||
disableRetry ? styles['disabled-retry'] : '',
|
||||
noRetry ? styles['no-retry-text'] : '',
|
||||
)}
|
||||
onClick={() => {
|
||||
!disableRetry && !noRetry && onRetry && onRetry(record, index);
|
||||
}}
|
||||
>
|
||||
{!record.statusDescript ? (
|
||||
<IconUploadFileFail />
|
||||
) : (
|
||||
<Tooltip content={record.statusDescript} trigger="hover">
|
||||
<IconUploadFileFail />
|
||||
</Tooltip>
|
||||
)}
|
||||
{!noRetry && (
|
||||
<div className={classNames(styles['retry-text'])}>
|
||||
{I18n.t('datasets_unit_update_retry')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { UploadUnitTable } from './upload-unit-table';
|
||||
export { type RenderColumnsProps } from './types';
|
||||
export {
|
||||
ActionRenderByDelete,
|
||||
ActionRenderByEditName,
|
||||
ActionRenderByRetry,
|
||||
getFrequencyInfo,
|
||||
getFileSizeInfo,
|
||||
} from './actions';
|
||||
export { getProcessStatus } from './utils';
|
||||
export { type ColumnInfo, type UploadUnitTableProps } from './types';
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import {
|
||||
type UnitType,
|
||||
type UnitItem,
|
||||
} from '@coze-data/knowledge-resource-processor-core';
|
||||
import { type UIActionItemProps } from '@coze-arch/bot-semi';
|
||||
import { type FormatType } from '@coze-arch/bot-api/memory';
|
||||
|
||||
export interface UploadStateProps {
|
||||
record: UnitItem;
|
||||
onRetry?: (record: UnitItem, index: number) => void;
|
||||
index: number;
|
||||
needLoading?: boolean;
|
||||
overlayClassName?: string;
|
||||
disableRetry?: boolean;
|
||||
// 不显示重试文案
|
||||
noRetry?: boolean;
|
||||
}
|
||||
|
||||
export interface UnitNameProps {
|
||||
canValidator?: boolean;
|
||||
edit?: boolean;
|
||||
disabled?: boolean;
|
||||
inModal?: boolean;
|
||||
formatType: FormatType;
|
||||
onChange: (value: string) => void;
|
||||
record: UnitItem;
|
||||
}
|
||||
|
||||
export interface ActionProps {
|
||||
showEdit?: boolean;
|
||||
onDelete: () => void;
|
||||
deleteProps?: UIActionItemProps;
|
||||
editProps?: UIActionItemProps;
|
||||
record?: UnitItem;
|
||||
}
|
||||
|
||||
export enum ActionType {
|
||||
Edit = 'edit',
|
||||
Delete = 'delete',
|
||||
}
|
||||
|
||||
export type HandleChange = (
|
||||
unitList: UnitItem[],
|
||||
action?: {
|
||||
type: ActionType;
|
||||
index: number;
|
||||
},
|
||||
) => void;
|
||||
|
||||
export interface ColumnInfo {
|
||||
subText?: ReactNode;
|
||||
actions?: ReactNode[];
|
||||
formatType?: FormatType;
|
||||
}
|
||||
|
||||
export interface UploadUnitTableProps {
|
||||
type: UnitType;
|
||||
unitList: UnitItem[];
|
||||
onChange: HandleChange;
|
||||
edit: boolean;
|
||||
canValidator?: boolean;
|
||||
onRetry?: (record: UnitItem, index: number) => void;
|
||||
disableRetry?: boolean;
|
||||
onDelete?: (record: UnitItem, index: number) => void;
|
||||
showEdit?: boolean;
|
||||
inModal?: boolean;
|
||||
getColumns?: (record: UnitItem, index: number) => ColumnInfo;
|
||||
}
|
||||
export interface GetColumnsParams extends UploadUnitTableProps {
|
||||
formatType: FormatType;
|
||||
}
|
||||
|
||||
export interface RenderColumnsProps {
|
||||
params: UploadUnitTableProps;
|
||||
record: UnitItem;
|
||||
index: number;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { type FC, type ReactNode } from 'react';
|
||||
|
||||
import { KnowledgeE2e } from '@coze-data/e2e';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
import { ProcessProgressItem } from '../process-progress-item/process-progress-item';
|
||||
import { ProcessStatus } from '../../types';
|
||||
import { getProcessStatus, getTypeIcon } from './utils';
|
||||
import { type ColumnInfo, type UploadUnitTableProps } from './types';
|
||||
|
||||
const INIT_PERCENT = 10;
|
||||
|
||||
const renderSubText = (
|
||||
status: ProcessStatus,
|
||||
statusDesc: string,
|
||||
subText: ReactNode,
|
||||
) => {
|
||||
if (status === ProcessStatus.Failed) {
|
||||
return (
|
||||
<div
|
||||
data-dtestid={`${KnowledgeE2e.CreateUnitListProgressName}.${'subText'}`}
|
||||
className={'text-12px'}
|
||||
>
|
||||
{statusDesc || I18n.t('datasets_unit_upload_fail')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return subText;
|
||||
};
|
||||
|
||||
export const UploadUnitTable: FC<UploadUnitTableProps> = props => {
|
||||
const { unitList = [], getColumns } = props;
|
||||
if (unitList.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="upload-container">
|
||||
{unitList.map((item, index) => {
|
||||
const curStatus = getProcessStatus(item?.status);
|
||||
const statusDescript = item?.statusDescript || '';
|
||||
|
||||
// 使用getColumns获取每个项目的信息
|
||||
const columnInfo: ColumnInfo = getColumns
|
||||
? getColumns(item, index)
|
||||
: {};
|
||||
const { subText, actions, formatType } = columnInfo;
|
||||
|
||||
return (
|
||||
<ProcessProgressItem
|
||||
key={item.uid}
|
||||
mainText={item.name || '--'}
|
||||
subText={renderSubText(curStatus, statusDescript, subText)}
|
||||
tipText={
|
||||
<span
|
||||
data-dtestid={`${KnowledgeE2e.LocalUploadListStatus}.${item.name}`}
|
||||
>
|
||||
{curStatus === ProcessStatus.Failed
|
||||
? statusDescript || I18n.t('datasets_unit_upload_fail')
|
||||
: I18n.t('datasets_unit_upload_success')}
|
||||
</span>
|
||||
}
|
||||
status={curStatus}
|
||||
avatar={getTypeIcon({ ...item, formatType })}
|
||||
actions={actions}
|
||||
percent={item.percent || INIT_PERCENT}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
EntityStatus,
|
||||
UploadStatus,
|
||||
} from '@coze-data/knowledge-resource-processor-core';
|
||||
import { WebStatus } from '@coze-arch/idl/knowledge';
|
||||
import { Image } from '@coze-arch/bot-semi';
|
||||
import {
|
||||
IconUploadPDF,
|
||||
IconUploadCSV,
|
||||
IconUploadDoc,
|
||||
IconUploadTxt,
|
||||
IconUploadTableUrl,
|
||||
IconUploadXLS,
|
||||
IconUploadTextUrl,
|
||||
IconUploadMD,
|
||||
} from '@coze-arch/bot-icons';
|
||||
import { FormatType } from '@coze-arch/bot-api/knowledge';
|
||||
|
||||
import { ProcessStatus } from '../../types';
|
||||
|
||||
enum UploadType {
|
||||
PDF = 'pdf',
|
||||
DOCX = 'docx',
|
||||
TXT = 'txt',
|
||||
XLSX = 'xlsx',
|
||||
XLTX = 'xltx',
|
||||
CSV = 'csv',
|
||||
PNG = 'png',
|
||||
JPG = 'jpg',
|
||||
JPEG = 'jpeg',
|
||||
WEBP = 'webp',
|
||||
XLS = 'xls',
|
||||
MD = 'md',
|
||||
}
|
||||
export const getTypeIcon = (params: {
|
||||
type: string | undefined;
|
||||
formatType?: FormatType;
|
||||
url?: string;
|
||||
inModal?: boolean;
|
||||
}) => {
|
||||
const { type, formatType, url } = params;
|
||||
// const iconClassName = inModal ? styles['icon-size-24'] : styles.icon;
|
||||
if (
|
||||
formatType === FormatType.Image &&
|
||||
[UploadType.JPG, UploadType.JPEG, UploadType.PNG, UploadType.WEBP].includes(
|
||||
type as UploadType,
|
||||
)
|
||||
) {
|
||||
return (
|
||||
<Image
|
||||
src={url}
|
||||
width={24}
|
||||
height={24}
|
||||
style={{
|
||||
borderRadius: '4px',
|
||||
marginRight: '12px',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (type === UploadType.MD) {
|
||||
return <IconUploadMD />;
|
||||
}
|
||||
if (type === UploadType.PDF) {
|
||||
return <IconUploadPDF />;
|
||||
}
|
||||
if (type === UploadType.DOCX) {
|
||||
return <IconUploadDoc />;
|
||||
}
|
||||
if (type === UploadType.TXT) {
|
||||
return <IconUploadTxt />;
|
||||
}
|
||||
if (
|
||||
type === UploadType.XLSX ||
|
||||
type === UploadType.XLTX ||
|
||||
type === UploadType.XLS
|
||||
) {
|
||||
return <IconUploadXLS />;
|
||||
}
|
||||
if (type === UploadType.CSV) {
|
||||
return <IconUploadCSV />;
|
||||
}
|
||||
|
||||
return formatType === FormatType.Table ? (
|
||||
<IconUploadTableUrl />
|
||||
) : (
|
||||
<IconUploadTextUrl />
|
||||
);
|
||||
};
|
||||
|
||||
export const getProcessStatus = (
|
||||
status: UploadStatus | WebStatus | EntityStatus,
|
||||
) => {
|
||||
if (
|
||||
status === UploadStatus.UPLOADING ||
|
||||
status === UploadStatus.VALIDATING ||
|
||||
status === UploadStatus.WAIT ||
|
||||
status === WebStatus.Handling ||
|
||||
status === EntityStatus.EntityStatusProcess
|
||||
) {
|
||||
return ProcessStatus.Processing;
|
||||
}
|
||||
|
||||
if (
|
||||
status === UploadStatus.SUCCESS ||
|
||||
status === WebStatus.Finish ||
|
||||
status === EntityStatus.EntityStatusSuccess
|
||||
) {
|
||||
return ProcessStatus.Complete;
|
||||
}
|
||||
|
||||
if (
|
||||
status === UploadStatus.VALIDATE_FAIL ||
|
||||
status === UploadStatus.UPLOAD_FAIL ||
|
||||
status === WebStatus.Failed ||
|
||||
status === EntityStatus.EntityStatusFail
|
||||
) {
|
||||
return ProcessStatus.Failed;
|
||||
}
|
||||
return ProcessStatus.Processing;
|
||||
};
|
||||
Reference in New Issue
Block a user