feat(dataset): Added data pipeline configuration functionality #9869 (#10132)

### What problem does this PR solve?

feat(dataset): Added data pipeline configuration functionality #9869

- Added a data pipeline selection component to link data pipelines with
knowledge bases
- Added file filtering functionality, supporting custom file filtering
rules
- Optimized the configuration interface layout, adjusting style and
spacing
- Introduced new icons and buttons to enhance the user experience

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-09-18 09:31:57 +08:00
committed by GitHub
parent a7abc57f68
commit e82617f6de
25 changed files with 397 additions and 134 deletions

View File

@ -1,5 +1,6 @@
(window._iconfont_svg_string_4909832 = (window._iconfont_svg_string_4909832 =
'<svg>' + '<svg>' +
'<symbol id="icon-Pipeline" viewBox="0 0 1024 1024"><path d="M610.9184 729.6a59.392 59.392 0 0 1 59.3408 59.392v79.104a59.3408 59.3408 0 0 1-59.392 59.3408H413.1328a59.392 59.392 0 0 1-59.3408-59.392V788.992a59.392 59.392 0 0 1 59.392-59.3408h197.7856z m0-316.4672a59.392 59.392 0 0 1 59.3408 59.3408v79.104a59.3408 59.3408 0 0 1-59.392 59.392H413.1328a59.3408 59.3408 0 0 1-59.3408-59.392V472.4736a59.392 59.392 0 0 1 59.392-59.392h197.7856z m0-316.5184a59.392 59.392 0 0 1 59.3408 59.3408V235.008a59.3408 59.3408 0 0 1-59.392 59.392H413.1328A59.3408 59.3408 0 0 1 353.792 235.008V155.9552a59.392 59.392 0 0 1 59.392-59.392h197.7856z" fill="#00BEB4" ></path><path d="M749.3632 472.4224a197.8368 197.8368 0 0 1 0 395.6224l-4.608-0.256a39.5776 39.5776 0 0 1 4.608-78.848l6.9632-0.2048a118.6816 118.6816 0 0 0-6.9632-237.2096l-4.608-0.256a39.5776 39.5776 0 0 1 4.608-78.848zM274.6368 155.904l4.608 0.256a39.5776 39.5776 0 0 1-4.608 78.848 118.6816 118.6816 0 1 0 0 237.4144 39.5776 39.5776 0 1 1 0 79.104 197.7856 197.7856 0 1 1 0-395.6224z" fill="#1177D7" ></path></symbol>' +
'<symbol id="icon-dataflow-01" viewBox="0 0 1024 1024"><path d="M636.202667 214.954667c-18.688 1.493333-28.288 4.266667-34.944 7.68a85.333333 85.333333 0 0 0-37.290667 37.290666c-3.413333 6.656-6.186667 16.213333-7.68 34.944C554.666667 314.069333 554.666667 338.901333 554.666667 375.466667V469.333333h135.253333a128.042667 128.042667 0 1 1 0 85.333334H554.666667v93.866666c0 36.565333 0 61.397333 1.621333 80.597334 1.493333 18.688 4.266667 28.288 7.68 34.944a85.333333 85.333333 0 0 0 37.290667 37.290666c6.656 3.413333 16.213333 6.186667 34.944 7.68 14.08 1.152 31.232 1.493333 53.76 1.578667A128.042667 128.042667 0 0 1 938.666667 853.333333a128 128 0 0 1-248.746667 42.666667 814.037333 814.037333 0 0 1-60.672-1.877333c-23.978667-1.962667-46.037333-6.186667-66.730667-16.725334a170.666667 170.666667 0 0 1-74.581333-74.581333c-10.538667-20.693333-14.762667-42.752-16.725333-66.730667C469.333333 712.96 469.333333 684.629333 469.333333 650.325333V554.666667H334.08a128.042667 128.042667 0 1 1 0-85.333334H469.333333V373.717333c0-34.346667 0-62.72 1.877334-85.76 1.962667-24.021333 6.186667-46.08 16.725333-66.773333a170.666667 170.666667 0 0 1 74.581333-74.581333c20.693333-10.538667 42.752-14.762667 66.730667-16.725334a813.653333 813.653333 0 0 1 60.714667-1.834666 128.042667 128.042667 0 1 1 0 85.333333c-22.528 0.085333-39.68 0.426667-53.76 1.578667z" ></path></symbol>' + '<symbol id="icon-dataflow-01" viewBox="0 0 1024 1024"><path d="M636.202667 214.954667c-18.688 1.493333-28.288 4.266667-34.944 7.68a85.333333 85.333333 0 0 0-37.290667 37.290666c-3.413333 6.656-6.186667 16.213333-7.68 34.944C554.666667 314.069333 554.666667 338.901333 554.666667 375.466667V469.333333h135.253333a128.042667 128.042667 0 1 1 0 85.333334H554.666667v93.866666c0 36.565333 0 61.397333 1.621333 80.597334 1.493333 18.688 4.266667 28.288 7.68 34.944a85.333333 85.333333 0 0 0 37.290667 37.290666c6.656 3.413333 16.213333 6.186667 34.944 7.68 14.08 1.152 31.232 1.493333 53.76 1.578667A128.042667 128.042667 0 0 1 938.666667 853.333333a128 128 0 0 1-248.746667 42.666667 814.037333 814.037333 0 0 1-60.672-1.877333c-23.978667-1.962667-46.037333-6.186667-66.730667-16.725334a170.666667 170.666667 0 0 1-74.581333-74.581333c-10.538667-20.693333-14.762667-42.752-16.725333-66.730667C469.333333 712.96 469.333333 684.629333 469.333333 650.325333V554.666667H334.08a128.042667 128.042667 0 1 1 0-85.333334H469.333333V373.717333c0-34.346667 0-62.72 1.877334-85.76 1.962667-24.021333 6.186667-46.08 16.725333-66.773333a170.666667 170.666667 0 0 1 74.581333-74.581333c20.693333-10.538667 42.752-14.762667 66.730667-16.725334a813.653333 813.653333 0 0 1 60.714667-1.834666 128.042667 128.042667 0 1 1 0 85.333333c-22.528 0.085333-39.68 0.426667-53.76 1.578667z" ></path></symbol>' +
'<symbol id="icon-knowledgegraph" viewBox="0 0 1024 1024"><path d="M190.464 489.472h327.68v40.96h-327.68z" ></path><path d="M482.34496 516.5056l111.26784-308.20352 38.54336 13.9264L520.86784 530.432z" ></path><path d="M620.544 196.608m-122.88 0a122.88 122.88 0 1 0 245.76 0 122.88 122.88 0 1 0-245.76 0Z" ></path><path d="M182.272 509.952m-122.88 0a122.88 122.88 0 1 0 245.76 0 122.88 122.88 0 1 0-245.76 0Z" ></path><path d="M558.65344 520.9088l283.77088 163.84-20.48 35.47136-283.77088-163.84z" ></path><path d="M841.728 686.08m-122.88 0a122.88 122.88 0 1 0 245.76 0 122.88 122.88 0 1 0-245.76 0Z" ></path><path d="M448.67584 803.77856l49.60256-323.91168 40.48896 6.20544-49.60256 323.91168z" ></path><path d="M512 530.432m-143.36 0a143.36 143.36 0 1 0 286.72 0 143.36 143.36 0 1 0-286.72 0Z" ></path><path d="M462.848 843.776m-102.4 0a102.4 102.4 0 1 0 204.8 0 102.4 102.4 0 1 0-204.8 0Z"></path></symbol>' + '<symbol id="icon-knowledgegraph" viewBox="0 0 1024 1024"><path d="M190.464 489.472h327.68v40.96h-327.68z" ></path><path d="M482.34496 516.5056l111.26784-308.20352 38.54336 13.9264L520.86784 530.432z" ></path><path d="M620.544 196.608m-122.88 0a122.88 122.88 0 1 0 245.76 0 122.88 122.88 0 1 0-245.76 0Z" ></path><path d="M182.272 509.952m-122.88 0a122.88 122.88 0 1 0 245.76 0 122.88 122.88 0 1 0-245.76 0Z" ></path><path d="M558.65344 520.9088l283.77088 163.84-20.48 35.47136-283.77088-163.84z" ></path><path d="M841.728 686.08m-122.88 0a122.88 122.88 0 1 0 245.76 0 122.88 122.88 0 1 0-245.76 0Z" ></path><path d="M448.67584 803.77856l49.60256-323.91168 40.48896 6.20544-49.60256 323.91168z" ></path><path d="M512 530.432m-143.36 0a143.36 143.36 0 1 0 286.72 0 143.36 143.36 0 1 0-286.72 0Z" ></path><path d="M462.848 843.776m-102.4 0a102.4 102.4 0 1 0 204.8 0 102.4 102.4 0 1 0-204.8 0Z"></path></symbol>' +
'<symbol id="icon-mcp" viewBox="0 0 1024 1024"><path d="M171.84 477.184l296.192-296.128q39.808-39.808 96.064-39.808 56.32 0 96.128 39.808t39.808 96.064q0 7.488-0.704 14.72 6.848-0.64 13.888-0.64 57.152 0 97.536 40.32 40.32 40.448 40.32 97.536t-40.32 97.472l-269.696 269.696q-4.928 4.928 0 9.856l57.152 57.152a30.464 30.464 0 0 1-43.072 43.072l-57.152-57.152q-19.84-19.84-19.84-48t19.84-48l269.696-269.696q22.528-22.528 22.528-54.4t-22.528-54.4q-22.592-22.592-54.4-22.592-31.936 0-54.464 22.592l-17.28 17.216L435.2 598.336a30.464 30.464 0 0 1-43.008-0.064l-0.192-0.128a30.272 30.272 0 0 1 0.128-42.88L599.68 347.52l17.408-17.408q21.952-22.016 21.952-53.056t-21.952-52.992q-22.016-22.016-53.056-22.016t-52.992 22.016L214.912 520.256a30.464 30.464 0 0 1-43.072-43.072z m567.104-29.44L513.92 672.96q-39.808 39.808-96.128 39.808t-96.128-39.808q-39.808-39.808-39.808-96.128t39.808-96.128l225.088-225.024a30.464 30.464 0 0 1 43.008 43.008L364.8 523.712q-21.952 22.016-21.952 53.056t21.952 53.056q22.016 21.952 53.056 21.952t52.992-21.952l225.088-225.088a30.464 30.464 0 0 1 43.072 43.072z" ></path></symbol>' + '<symbol id="icon-mcp" viewBox="0 0 1024 1024"><path d="M171.84 477.184l296.192-296.128q39.808-39.808 96.064-39.808 56.32 0 96.128 39.808t39.808 96.064q0 7.488-0.704 14.72 6.848-0.64 13.888-0.64 57.152 0 97.536 40.32 40.32 40.448 40.32 97.536t-40.32 97.472l-269.696 269.696q-4.928 4.928 0 9.856l57.152 57.152a30.464 30.464 0 0 1-43.072 43.072l-57.152-57.152q-19.84-19.84-19.84-48t19.84-48l269.696-269.696q22.528-22.528 22.528-54.4t-22.528-54.4q-22.592-22.592-54.4-22.592-31.936 0-54.464 22.592l-17.28 17.216L435.2 598.336a30.464 30.464 0 0 1-43.008-0.064l-0.192-0.128a30.272 30.272 0 0 1 0.128-42.88L599.68 347.52l17.408-17.408q21.952-22.016 21.952-53.056t-21.952-52.992q-22.016-22.016-53.056-22.016t-52.992 22.016L214.912 520.256a30.464 30.464 0 0 1-43.072-43.072z m567.104-29.44L513.92 672.96q-39.808 39.808-96.128 39.808t-96.128-39.808q-39.808-39.808-39.808-96.128t39.808-96.128l225.088-225.024a30.464 30.464 0 0 1 43.008 43.008L364.8 523.712q-21.952 22.016-21.952 53.056t21.952 53.056q22.016 21.952 53.056 21.952t52.992-21.952l225.088-225.088a30.464 30.464 0 0 1 43.072 43.072z" ></path></symbol>' +

View File

@ -1,7 +1,10 @@
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { useFetchAgentList } from '@/hooks/use-agent-request';
import { buildSelectOptions } from '@/utils/component-util'; import { buildSelectOptions } from '@/utils/component-util';
import { ArrowUpRight } from 'lucide-react'; import { ArrowUpRight } from 'lucide-react';
import { useMemo } from 'react';
import { useFormContext } from 'react-hook-form'; import { useFormContext } from 'react-hook-form';
import { SelectWithSearch } from '../originui/select-with-search';
import { import {
FormControl, FormControl,
FormField, FormField,
@ -9,11 +12,12 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from '../ui/form'; } from '../ui/form';
import { RAGFlowSelect } from '../ui/select'; import { MultiSelect } from '../ui/multi-select';
interface IProps { interface IProps {
toDataPipeline?: () => void; toDataPipeline?: () => void;
formFieldName: string; formFieldName: string;
isMult?: boolean;
} }
const data = [ const data = [
@ -22,16 +26,25 @@ const data = [
{ id: '3', name: 'data-pipeline-3' }, { id: '3', name: 'data-pipeline-3' },
{ id: '4', name: 'data-pipeline-4' }, { id: '4', name: 'data-pipeline-4' },
]; ];
export function DataFlowItem(props: IProps) { export function DataFlowSelect(props: IProps) {
const { toDataPipeline, formFieldName } = props; const { toDataPipeline, formFieldName, isMult = true } = props;
const { t } = useTranslate('knowledgeConfiguration'); const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext(); const form = useFormContext();
console.log('data-pipline form', form);
const toDataPipLine = () => { const toDataPipLine = () => {
// window.open('/data-pipeline');
toDataPipeline?.(); toDataPipeline?.();
}; };
const options = buildSelectOptions(data, 'id', 'name'); const { data: dataPipelineOptions, loading } = useFetchAgentList({
canvas_category: 'dataflow_canvas',
});
const options = useMemo(() => {
const option = buildSelectOptions(
dataPipelineOptions?.canvas,
'id',
'title',
);
return option || [];
}, [dataPipelineOptions]);
return ( return (
<FormField <FormField
control={form.control} control={form.control}
@ -57,16 +70,27 @@ export function DataFlowItem(props: IProps) {
<div className="text-muted-foreground"> <div className="text-muted-foreground">
<FormControl> <FormControl>
<RAGFlowSelect <>
{!isMult && (
<SelectWithSearch
{...field} {...field}
placeholder={t('dataFlowPlaceholder')} placeholder={t('dataFlowPlaceholder')}
options={options} options={options}
/> />
)}
{isMult && (
<MultiSelect
{...field}
onValueChange={field.onChange}
placeholder={t('dataFlowPlaceholder')}
options={options}
/>
)}
</>
</FormControl> </FormControl>
</div> </div>
</div> </div>
<div className="flex pt-1"> <div className="flex pt-1">
<div className="w-1/4"></div>
<FormMessage /> <FormMessage />
</div> </div>
</FormItem> </FormItem>

View File

@ -26,7 +26,7 @@ export function EntityTypesFormField({
return ( return (
<FormItem className=" items-center space-y-0 "> <FormItem className=" items-center space-y-0 ">
<div className="flex items-center"> <div className="flex items-center">
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4"> <FormLabel className="text-sm whitespace-nowrap w-1/4">
<span className="text-red-600">*</span> {t('entityTypes')} <span className="text-red-600">*</span> {t('entityTypes')}
</FormLabel> </FormLabel>
<div className="w-3/4"> <div className="w-3/4">

View File

@ -39,8 +39,8 @@ export function HomeCard({
/> />
</div> </div>
<div className="flex flex-col justify-between gap-1 flex-1 h-full w-[calc(100%-50px)]"> <div className="flex flex-col justify-between gap-1 flex-1 h-full w-[calc(100%-50px)]">
<section className="flex justify-between"> <section className="flex justify-between w-full">
<section className="flex gap-1 items-center"> <section className="flex gap-1 items-center w-full">
<div className="text-[20px] font-bold w-80% leading-5 text-ellipsis overflow-hidden"> <div className="text-[20px] font-bold w-80% leading-5 text-ellipsis overflow-hidden">
{data.name} {data.name}
</div> </div>

View File

@ -45,6 +45,8 @@ export type SelectWithSearchFlagProps = {
onChange?(value: string): void; onChange?(value: string): void;
triggerClassName?: string; triggerClassName?: string;
allowClear?: boolean; allowClear?: boolean;
disabled?: boolean;
placeholder?: string;
}; };
export const SelectWithSearch = forwardRef< export const SelectWithSearch = forwardRef<
@ -58,6 +60,8 @@ export const SelectWithSearch = forwardRef<
options = [], options = [],
triggerClassName, triggerClassName,
allowClear = false, allowClear = false,
disabled = false,
placeholder = t('common.selectPlaceholder'),
}, },
ref, ref,
) => { ) => {
@ -105,6 +109,7 @@ export const SelectWithSearch = forwardRef<
role="combobox" role="combobox"
aria-expanded={open} aria-expanded={open}
ref={ref} ref={ref}
disabled={disabled}
className={cn( className={cn(
'bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px] [&_svg]:pointer-events-auto', 'bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px] [&_svg]:pointer-events-auto',
triggerClassName, triggerClassName,
@ -115,9 +120,7 @@ export const SelectWithSearch = forwardRef<
<span className="leading-none truncate">{selectLabel}</span> <span className="leading-none truncate">{selectLabel}</span>
</span> </span>
) : ( ) : (
<span className="text-muted-foreground"> <span className="text-muted-foreground">{placeholder}</span>
{t('common.selectPlaceholder')}
</span>
)} )}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
{value && allowClear && ( {value && allowClear && (

View File

@ -62,7 +62,7 @@ export function UseGraphRagFormField() {
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FormLabel <FormLabel
tooltip={t('useGraphRagTip')} tooltip={t('useGraphRagTip')}
className="text-sm text-muted-foreground whitespace-break-spaces w-1/4" className="text-sm whitespace-break-spaces w-1/4"
> >
{t('useGraphRag')} {t('useGraphRag')}
</FormLabel> </FormLabel>
@ -125,7 +125,7 @@ const GraphRagItems = ({
<FormItem className=" items-center space-y-0 "> <FormItem className=" items-center space-y-0 ">
<div className="flex items-center"> <div className="flex items-center">
<FormLabel <FormLabel
className="text-sm text-muted-foreground whitespace-nowrap w-1/4" className="text-sm whitespace-nowrap w-1/4"
tooltip={renderWideTooltip( tooltip={renderWideTooltip(
<div <div
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
@ -161,7 +161,7 @@ const GraphRagItems = ({
<div className="flex items-center"> <div className="flex items-center">
<FormLabel <FormLabel
tooltip={renderWideTooltip('resolutionTip')} tooltip={renderWideTooltip('resolutionTip')}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4" className="text-sm whitespace-nowrap w-1/4"
> >
{t('resolution')} {t('resolution')}
</FormLabel> </FormLabel>
@ -190,7 +190,7 @@ const GraphRagItems = ({
<div className="flex items-center"> <div className="flex items-center">
<FormLabel <FormLabel
tooltip={renderWideTooltip('communityTip')} tooltip={renderWideTooltip('communityTip')}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4" className="text-sm whitespace-nowrap w-1/4"
> >
{t('community')} {t('community')}
</FormLabel> </FormLabel>

View File

@ -2,11 +2,10 @@ import { FormLayout } from '@/constants/form';
import { DocumentParserType } from '@/constants/knowledge'; import { DocumentParserType } from '@/constants/knowledge';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import random from 'lodash/random'; import random from 'lodash/random';
import { Plus } from 'lucide-react'; import { Shuffle } from 'lucide-react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useFormContext, useWatch } from 'react-hook-form'; import { useFormContext, useWatch } from 'react-hook-form';
import { SliderInputFormField } from '../slider-input-form-field'; import { SliderInputFormField } from '../slider-input-form-field';
import { Button } from '../ui/button';
import { import {
FormControl, FormControl,
FormField, FormField,
@ -14,7 +13,7 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from '../ui/form'; } from '../ui/form';
import { Input } from '../ui/input'; import { ExpandedInput } from '../ui/input';
import { Switch } from '../ui/switch'; import { Switch } from '../ui/switch';
import { Textarea } from '../ui/textarea'; import { Textarea } from '../ui/textarea';
@ -93,7 +92,7 @@ const RaptorFormFields = () => {
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FormLabel <FormLabel
tooltip={t('useRaptorTip')} tooltip={t('useRaptorTip')}
className="text-sm text-muted-foreground w-1/4 whitespace-break-spaces" className="text-sm w-1/4 whitespace-break-spaces"
> >
<div className="w-auto xl:w-20 2xl:w-24 3xl:w-28 4xl:w-auto "> <div className="w-auto xl:w-20 2xl:w-24 3xl:w-28 4xl:w-auto ">
{t('useRaptor')} {t('useRaptor')}
@ -130,7 +129,7 @@ const RaptorFormFields = () => {
<div className="flex items-start"> <div className="flex items-start">
<FormLabel <FormLabel
tooltip={t('promptTip')} tooltip={t('promptTip')}
className="text-sm text-muted-foreground whitespace-nowrap w-1/4" className="text-sm whitespace-nowrap w-1/4"
> >
{t('prompt')} {t('prompt')}
</FormLabel> </FormLabel>
@ -185,21 +184,23 @@ const RaptorFormFields = () => {
render={({ field }) => ( render={({ field }) => (
<FormItem className=" items-center space-y-0 "> <FormItem className=" items-center space-y-0 ">
<div className="flex items-center"> <div className="flex items-center">
<FormLabel className="text-sm text-muted-foreground whitespace-wrap w-1/4"> <FormLabel className="text-sm whitespace-wrap w-1/4">
{t('randomSeed')} {t('randomSeed')}
</FormLabel> </FormLabel>
<div className="w-3/4"> <div className="w-3/4">
<FormControl defaultValue={0}> <FormControl defaultValue={0}>
<div className="flex gap-4 items-center"> <ExpandedInput
<Input {...field} defaultValue={0} type="number" /> {...field}
<Button className="w-full"
size={'sm'} defaultValue={0}
type="number"
suffix={
<Shuffle
className="size-3.5 cursor-pointer"
onClick={handleGenerate} onClick={handleGenerate}
type={'button'} />
> }
<Plus /> />
</Button>
</div>
</FormControl> </FormControl>
</div> </div>
</div> </div>

View File

@ -54,8 +54,7 @@ export function SliderInputFormField({
<FormLabel <FormLabel
tooltip={tooltip} tooltip={tooltip}
className={cn({ className={cn({
'text-sm text-muted-foreground whitespace-break-spaces w-1/4': 'text-sm whitespace-break-spaces w-1/4': isHorizontal,
isHorizontal,
})} })}
> >
{label} {label}

View File

@ -18,7 +18,7 @@ export const useNavigatePage = () => {
const navigateToDataset = useCallback( const navigateToDataset = useCallback(
(id: string) => () => { (id: string) => () => {
navigate(`${Routes.Dataset}/${id}`); navigate(`${Routes.DatasetBase}${Routes.DataSetOverview}/${id}`);
}, },
[navigate], [navigate],
); );

View File

@ -7,6 +7,7 @@ import {
IAgentLogsResponse, IAgentLogsResponse,
IFlow, IFlow,
IFlowTemplate, IFlowTemplate,
IPipeLineListRequest,
ITraceData, ITraceData,
} from '@/interfaces/database/agent'; } from '@/interfaces/database/agent';
import { IDebugSingleRequestBody } from '@/interfaces/request/agent'; import { IDebugSingleRequestBody } from '@/interfaces/request/agent';
@ -16,6 +17,7 @@ import { IInputs } from '@/pages/agent/interface';
import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks'; import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks';
import agentService, { import agentService, {
fetchAgentLogsByCanvasId, fetchAgentLogsByCanvasId,
fetchPipeLineList,
fetchTrace, fetchTrace,
} from '@/services/agent-service'; } from '@/services/agent-service';
import api from '@/utils/api'; import api from '@/utils/api';
@ -648,3 +650,26 @@ export const useFetchPrompt = () => {
return { data, loading, refetch }; return { data, loading, refetch };
}; };
export const useFetchAgentList = ({
canvas_category = 'agent_canvas',
}: IPipeLineListRequest): {
data: {
canvas: IFlow[];
total: number;
};
loading: boolean;
} => {
const { data, isFetching: loading } = useQuery({
queryKey: ['fetchPipeLineList'],
initialData: [],
gcTime: 0,
queryFn: async () => {
const { data } = await fetchPipeLineList({ canvas_category });
return data?.data ?? [];
},
});
return { data, loading };
};

View File

@ -236,7 +236,11 @@ export const useUpdateKnowledge = (shouldFetchList = false) => {
return { data, loading, saveKnowledgeConfiguration: mutateAsync }; return { data, loading, saveKnowledgeConfiguration: mutateAsync };
}; };
export const useFetchKnowledgeBaseConfiguration = (refreshCount?: number) => { export const useFetchKnowledgeBaseConfiguration = (props?: {
isEdit?: boolean;
refreshCount?: number;
}) => {
const { isEdit = true, refreshCount } = props || { isEdit: true };
const { id } = useParams(); const { id } = useParams();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const knowledgeBaseId = searchParams.get('id') || id; const knowledgeBaseId = searchParams.get('id') || id;
@ -253,10 +257,14 @@ export const useFetchKnowledgeBaseConfiguration = (refreshCount?: number) => {
initialData: {} as IKnowledge, initialData: {} as IKnowledge,
gcTime: 0, gcTime: 0,
queryFn: async () => { queryFn: async () => {
if (isEdit) {
const { data } = await kbService.get_kb_detail({ const { data } = await kbService.get_kb_detail({
kb_id: knowledgeBaseId, kb_id: knowledgeBaseId,
}); });
return data?.data ?? {}; return data?.data ?? {};
} else {
return {};
}
}, },
}); });

View File

@ -266,3 +266,12 @@ export interface IAgentLogMessage {
role: 'user' | 'assistant'; role: 'user' | 'assistant';
id: string; id: string;
} }
export interface IPipeLineListRequest {
page?: number;
page_size?: number;
keywords?: string;
orderby?: string;
desc?: boolean;
canvas_category?: 'agent_canvas' | 'dataflow_canvas';
}

View File

@ -1,17 +1,21 @@
import { IconFont } from '@/components/icon-font';
import { RAGFlowAvatar } from '@/components/ragflow-avatar'; import { RAGFlowAvatar } from '@/components/ragflow-avatar';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Link, Route, Settings2, Unlink } from 'lucide-react'; import { Link, Settings2, Unlink } from 'lucide-react';
import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import LinkDataPipelineModal from './link-data-pipline-modal';
interface DataPipelineItemProps { interface DataPipelineItemProps {
name: string; name: string;
avatar?: string; avatar?: string;
isDefault?: boolean; isDefault?: boolean;
linked?: boolean; linked?: boolean;
openLinkModalFunc?: (open: boolean) => void;
} }
const DataPipelineItem = (props: DataPipelineItemProps) => { const DataPipelineItem = (props: DataPipelineItemProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { name, avatar, isDefault, linked } = props; const { name, avatar, isDefault, linked, openLinkModalFunc } = props;
return ( return (
<div className="flex items-center justify-between gap-1 px-2 rounded-lg border"> <div className="flex items-center justify-between gap-1 px-2 rounded-lg border">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
@ -27,15 +31,24 @@ const DataPipelineItem = (props: DataPipelineItemProps) => {
<Button variant={'transparent'} className="border-none"> <Button variant={'transparent'} className="border-none">
<Settings2 /> <Settings2 />
</Button> </Button>
<Button variant={'transparent'} className="border-none"> {!isDefault && (
<Button
variant={'transparent'}
className="border-none"
onClick={() => {
openLinkModalFunc?.(true);
}}
>
{linked ? <Link /> : <Unlink />} {linked ? <Link /> : <Unlink />}
</Button> </Button>
)}
</div> </div>
</div> </div>
); );
}; };
const LinkDataPipeline = () => { const LinkDataPipeline = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [openLinkModal, setOpenLinkModal] = useState(false);
const testNode = [ const testNode = [
{ {
name: 'Data Pipeline 1', name: 'Data Pipeline 1',
@ -49,11 +62,14 @@ const LinkDataPipeline = () => {
linked: false, linked: false,
}, },
]; ];
const openLinkModalFunc = (open: boolean) => {
setOpenLinkModal(open);
};
return ( return (
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<section className="flex flex-col"> <section className="flex flex-col">
<div className="flex items-center gap-1 text-text-primary text-sm"> <div className="flex items-center gap-1 text-text-primary text-sm">
<Route className="size-4" /> <IconFont name="Pipeline" />
{t('knowledgeConfiguration.dataPipeline')} {t('knowledgeConfiguration.dataPipeline')}
</div> </div>
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
@ -70,9 +86,19 @@ const LinkDataPipeline = () => {
</section> </section>
<section className="flex flex-col gap-2"> <section className="flex flex-col gap-2">
{testNode.map((item) => ( {testNode.map((item) => (
<DataPipelineItem key={item.name} {...item} /> <DataPipelineItem
key={item.name}
openLinkModalFunc={openLinkModalFunc}
{...item}
/>
))} ))}
</section> </section>
<LinkDataPipelineModal
open={openLinkModal}
setOpen={(open: boolean) => {
openLinkModalFunc(open);
}}
/>
</div> </div>
); );
}; };

View File

@ -0,0 +1,95 @@
import { DataFlowSelect } from '@/components/data-pipeline-select';
import Input from '@/components/originui/input';
import { Button } from '@/components/ui/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Modal } from '@/components/ui/modal/modal';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { zodResolver } from '@hookform/resolvers/zod';
import { t } from 'i18next';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { linkPiplineFormSchema } from '../form-schema';
const LinkDataPipelineModal = ({
open,
setOpen,
}: {
open: boolean;
setOpen: (open: boolean) => void;
}) => {
const form = useForm<z.infer<typeof linkPiplineFormSchema>>({
resolver: zodResolver(linkPiplineFormSchema),
defaultValues: { data_flow: ['888'], file_filter: '' },
});
// const [open, setOpen] = useState(false);
const { navigateToAgents } = useNavigatePage();
const handleFormSubmit = (values: any) => {
console.log(values);
};
return (
<Modal
title={t('knowledgeConfiguration.linkDataPipeline')}
open={open}
onOpenChange={setOpen}
showfooter={false}
>
<Form {...form}>
<form onSubmit={form.handleSubmit(handleFormSubmit)}>
<div className="flex flex-col gap-4 ">
<DataFlowSelect
toDataPipeline={navigateToAgents}
formFieldName="data_flow"
/>
<FormField
control={form.control}
name={'file_filter'}
render={({ field }) => (
<FormItem className=" items-center space-y-0 ">
<div className="flex flex-col gap-1">
<div className="flex gap-2 justify-between ">
<FormLabel
tooltip={t('knowledgeConfiguration.fileFilterTip')}
className="text-sm text-text-primary whitespace-wrap "
>
{t('knowledgeConfiguration.fileFilter')}
</FormLabel>
</div>
<div className="text-muted-foreground">
<FormControl>
<Input
placeholder={t('dataFlowPlaceholder')}
{...field}
/>
</FormControl>
</div>
</div>
<div className="flex pt-1">
<div className="w-full"></div>
<FormMessage />
</div>
</FormItem>
)}
/>
<div className="flex justify-end gap-1">
<Button type="reset" variant={'outline'} className="btn-primary">
{t('modal.cancelText')}
</Button>
<Button type="submit" variant={'default'} className="btn-primary">
{t('modal.okText')}
</Button>
</div>
</div>
</form>
</Form>
</Modal>
);
};
export default LinkDataPipelineModal;

View File

@ -9,6 +9,9 @@ export function ConfigurationFormContainer({
return <section className={cn('space-y-4', className)}>{children}</section>; return <section className={cn('space-y-4', className)}>{children}</section>;
} }
export function MainContainer({ children }: PropsWithChildren) { export function MainContainer({
return <section className="space-y-5">{children}</section>; children,
className,
}: PropsWithChildren & { className?: string }) {
return <section className={cn('space-y-5', className)}>{children}</section>;
} }

View File

@ -1,3 +1,4 @@
import { SelectWithSearch } from '@/components/originui/select-with-search';
import { import {
FormControl, FormControl,
FormField, FormField,
@ -16,8 +17,12 @@ import {
useSelectChunkMethodList, useSelectChunkMethodList,
useSelectEmbeddingModelOptions, useSelectEmbeddingModelOptions,
} from '../hooks'; } from '../hooks';
interface IProps {
export function ChunkMethodItem() { line?: 1 | 2;
isEdit?: boolean;
}
export function ChunkMethodItem(props: IProps) {
const { line } = props;
const { t } = useTranslate('knowledgeConfiguration'); const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext(); const form = useFormContext();
// const handleChunkMethodSelectChange = useHandleChunkMethodSelectChange(form); // const handleChunkMethodSelectChange = useHandleChunkMethodSelectChange(form);
@ -28,28 +33,29 @@ export function ChunkMethodItem() {
control={form.control} control={form.control}
name={'parser_id'} name={'parser_id'}
render={({ field }) => ( render={({ field }) => (
<FormItem className=" items-center space-y-0 "> <FormItem className=" items-center space-y-1">
<div className="flex items-center"> <div className={line === 1 ? 'flex items-center' : ''}>
<FormLabel <FormLabel
required required
tooltip={t('chunkMethodTip')} tooltip={t('chunkMethodTip')}
className="text-sm text-muted-foreground whitespace-wrap w-1/4" className={cn('text-sm', {
'w-1/4 whitespace-pre-wrap': line === 1,
})}
> >
{t('chunkMethod')} {t('chunkMethod')}
</FormLabel> </FormLabel>
<div className="w-3/4 "> <div className={line === 1 ? 'w-3/4 ' : 'w-full'}>
<FormControl> <FormControl>
<RAGFlowSelect <RAGFlowSelect
{...field} {...field}
options={parserList} options={parserList}
placeholder={t('chunkMethodPlaceholder')} placeholder={t('chunkMethodPlaceholder')}
// onChange={handleChunkMethodSelectChange}
/> />
</FormControl> </FormControl>
</div> </div>
</div> </div>
<div className="flex pt-1"> <div className="flex pt-1">
<div className="w-1/4"></div> <div className={line === 1 ? 'w-1/4' : ''}></div>
<FormMessage /> <FormMessage />
</div> </div>
</FormItem> </FormItem>
@ -57,14 +63,13 @@ export function ChunkMethodItem() {
/> />
); );
} }
export function EmbeddingModelItem({ line = 1, isEdit = true }: IProps) {
export function EmbeddingModelItem({ line = 1 }: { line?: 1 | 2 }) {
const { t } = useTranslate('knowledgeConfiguration'); const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext(); const form = useFormContext();
const embeddingModelOptions = useSelectEmbeddingModelOptions(); const embeddingModelOptions = useSelectEmbeddingModelOptions();
const disabled = useHasParsedDocument(); const disabled = useHasParsedDocument(isEdit);
return ( return (
<>
<FormField <FormField
control={form.control} control={form.control}
name={'embd_id'} name={'embd_id'}
@ -89,22 +94,24 @@ export function EmbeddingModelItem({ line = 1 }: { line?: 1 | 2 }) {
className={cn('text-muted-foreground', { 'w-3/4': line === 1 })} className={cn('text-muted-foreground', { 'w-3/4': line === 1 })}
> >
<FormControl> <FormControl>
<RAGFlowSelect <SelectWithSearch
{...field} onChange={field.onChange}
value={field.value}
options={embeddingModelOptions} options={embeddingModelOptions}
disabled={disabled} disabled={isEdit ? disabled : false}
placeholder={t('embeddingModelPlaceholder')} placeholder={t('embeddingModelPlaceholder')}
/> />
</FormControl> </FormControl>
</div> </div>
</div> </div>
<div className="flex pt-1"> <div className="flex pt-1">
<div className="w-1/4"></div> <div className={line === 1 ? 'w-1/4' : ''}></div>
<FormMessage /> <FormMessage />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
</>
); );
} }

View File

@ -71,3 +71,8 @@ export const formSchema = z.object({
pagerank: z.number(), pagerank: z.number(),
// icon: z.array(z.instanceof(File)), // icon: z.array(z.instanceof(File)),
}); });
export const linkPiplineFormSchema = z.object({
data_flow: z.array(z.string()),
file_filter: z.string().optional(),
});

View File

@ -24,7 +24,7 @@ export function GeneralForm() {
render={({ field }) => ( render={({ field }) => (
<FormItem className="items-center space-y-0"> <FormItem className="items-center space-y-0">
<div className="flex"> <div className="flex">
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4"> <FormLabel className="text-sm whitespace-nowrap w-1/4">
<span className="text-red-600">*</span> <span className="text-red-600">*</span>
{t('common.name')} {t('common.name')}
</FormLabel> </FormLabel>
@ -45,7 +45,7 @@ export function GeneralForm() {
render={({ field }) => ( render={({ field }) => (
<FormItem className="items-center space-y-0"> <FormItem className="items-center space-y-0">
<div className="flex"> <div className="flex">
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4"> <FormLabel className="text-sm whitespace-nowrap w-1/4">
{t('setting.avatar')} {t('setting.avatar')}
</FormLabel> </FormLabel>
<FormControl className="w-3/4"> <FormControl className="w-3/4">
@ -70,7 +70,7 @@ export function GeneralForm() {
return ( return (
<FormItem className="items-center space-y-0"> <FormItem className="items-center space-y-0">
<div className="flex"> <div className="flex">
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4"> <FormLabel className="text-sm whitespace-nowrap w-1/4">
{t('flow.description')} {t('flow.description')}
</FormLabel> </FormLabel>
<FormControl className="w-3/4"> <FormControl className="w-3/4">

View File

@ -25,8 +25,10 @@ export function useSelectEmbeddingModelOptions() {
return allOptions[LlmModelType.Embedding]; return allOptions[LlmModelType.Embedding];
} }
export function useHasParsedDocument() { export function useHasParsedDocument(isEdit?: boolean) {
const { data: knowledgeDetails } = useFetchKnowledgeBaseConfiguration(); const { data: knowledgeDetails } = useFetchKnowledgeBaseConfiguration({
isEdit,
});
return knowledgeDetails.chunk_num > 0; return knowledgeDetails.chunk_num > 0;
} }
@ -52,7 +54,7 @@ export const useFetchKnowledgeConfigurationOnMount = (
'pagerank', 'pagerank',
'avatar', 'avatar',
]), ]),
}; } as z.infer<typeof formSchema>;
form.reset(formValues); form.reset(formValues);
}, [form, knowledgeDetails]); }, [form, knowledgeDetails]);

View File

@ -82,7 +82,7 @@ export default function DatasetSettings() {
className="space-y-6 flex-1" className="space-y-6 flex-1"
> >
<div className="w-[768px] h-[calc(100vh-240px)] pr-1 overflow-y-auto scrollbar-auto"> <div className="w-[768px] h-[calc(100vh-240px)] pr-1 overflow-y-auto scrollbar-auto">
<MainContainer> <MainContainer className="text-text-secondary">
<GeneralForm></GeneralForm> <GeneralForm></GeneralForm>
<Divider /> <Divider />

View File

@ -28,7 +28,7 @@ export function SideBar({ refreshCount }: PropType) {
const pathName = useSecondPathName(); const pathName = useSecondPathName();
const { handleMenuClick } = useHandleMenuClick(); const { handleMenuClick } = useHandleMenuClick();
// refreshCount: be for avatar img sync update on top left // refreshCount: be for avatar img sync update on top left
const { data } = useFetchKnowledgeBaseConfiguration(refreshCount); const { data } = useFetchKnowledgeBaseConfiguration({ refreshCount });
const { data: routerData } = useFetchKnowledgeGraph(); const { data: routerData } = useFetchKnowledgeGraph();
const { t } = useTranslation(); const { t } = useTranslation();
@ -52,7 +52,7 @@ export function SideBar({ refreshCount }: PropType) {
{ {
icon: Banknote, icon: Banknote,
label: t(`knowledgeDetails.configuration`), label: t(`knowledgeDetails.configuration`),
key: Routes.DatasetSetting, key: Routes.DataSetSetting,
}, },
]; ];
if (!isEmpty(routerData?.graph)) { if (!isEmpty(routerData?.graph)) {

View File

@ -1,4 +1,4 @@
import { DataFlowItem } from '@/components/data-pipeline-select'; import { DataFlowSelect } from '@/components/data-pipeline-select';
import { ButtonLoading } from '@/components/ui/button'; import { ButtonLoading } from '@/components/ui/button';
import { import {
Dialog, Dialog,
@ -23,6 +23,7 @@ import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { z } from 'zod'; import { z } from 'zod';
import { import {
ChunkMethodItem,
EmbeddingModelItem, EmbeddingModelItem,
ParseTypeItem, ParseTypeItem,
} from '../dataset/dataset-setting/configuration/common-item'; } from '../dataset/dataset-setting/configuration/common-item';
@ -32,7 +33,8 @@ const FormId = 'dataset-creating-form';
export function InputForm({ onOk }: IModalProps<any>) { export function InputForm({ onOk }: IModalProps<any>) {
const { t } = useTranslation(); const { t } = useTranslation();
const FormSchema = z.object({ const FormSchema = z
.object({
name: z name: z
.string() .string()
.min(1, { .min(1, {
@ -40,6 +42,37 @@ export function InputForm({ onOk }: IModalProps<any>) {
}) })
.trim(), .trim(),
parseType: z.number().optional(), parseType: z.number().optional(),
embd_id: z
.string()
.min(1, {
message: t('knowledgeConfiguration.embeddingModelPlaceholder'),
})
.trim(),
parser_id: z.string().optional(),
pipline_id: z.string().optional(),
})
.superRefine((data, ctx) => {
// When parseType === 1, parser_id is required
if (
data.parseType === 1 &&
(!data.parser_id || data.parser_id.trim() === '')
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: t('knowledgeList.parserRequired'),
path: ['parser_id'],
});
}
console.log('form-data', data);
// When parseType === 1, pipline_id required
if (data.parseType === 2 && !data.pipline_id) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: t('knowledgeList.dataFlowRequired'),
path: ['pipline_id'],
});
}
}); });
const form = useForm<z.infer<typeof FormSchema>>({ const form = useForm<z.infer<typeof FormSchema>>({
@ -47,17 +80,21 @@ export function InputForm({ onOk }: IModalProps<any>) {
defaultValues: { defaultValues: {
name: '', name: '',
parseType: 1, parseType: 1,
parser_id: '',
embd_id: '',
}, },
}); });
function onSubmit(data: z.infer<typeof FormSchema>) { function onSubmit(data: z.infer<typeof FormSchema>) {
onOk?.(data.name); console.log('submit', data);
onOk?.(data);
} }
const parseType = useWatch({ const parseType = useWatch({
control: form.control, control: form.control,
name: 'parseType', name: 'parseType',
}); });
const { navigateToAgents } = useNavigatePage(); const { navigateToAgents } = useNavigatePage();
return ( return (
<Form {...form}> <Form {...form}>
<form <form
@ -84,13 +121,19 @@ export function InputForm({ onOk }: IModalProps<any>) {
</FormItem> </FormItem>
)} )}
/> />
<EmbeddingModelItem line={2} /> <EmbeddingModelItem line={2} isEdit={false} />
<ParseTypeItem /> <ParseTypeItem />
{parseType === 1 && (
<>
<ChunkMethodItem></ChunkMethodItem>
</>
)}
{parseType === 2 && ( {parseType === 2 && (
<> <>
<DataFlowItem <DataFlowSelect
isMult={false}
toDataPipeline={navigateToAgents} toDataPipeline={navigateToAgents}
formFieldName="data_flow" formFieldName="pipline_id"
/> />
</> </>
)} )}
@ -108,7 +151,7 @@ export function DatasetCreatingDialog({
return ( return (
<Dialog open onOpenChange={hideModal}> <Dialog open onOpenChange={hideModal}>
<DialogContent className="sm:max-w-[425px]"> <DialogContent className="sm:max-w-[425px] focus-visible:!outline-none">
<DialogHeader> <DialogHeader>
<DialogTitle>{t('knowledgeList.createKnowledgeBase')}</DialogTitle> <DialogTitle>{t('knowledgeList.createKnowledgeBase')}</DialogTitle>
</DialogHeader> </DialogHeader>

View File

@ -2,7 +2,6 @@ import { useSetModalState } from '@/hooks/common-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useCreateKnowledge } from '@/hooks/use-knowledge-request'; import { useCreateKnowledge } from '@/hooks/use-knowledge-request';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
export const useSearchKnowledge = () => { export const useSearchKnowledge = () => {
const [searchString, setSearchString] = useState<string>(''); const [searchString, setSearchString] = useState<string>('');
@ -15,16 +14,19 @@ export const useSearchKnowledge = () => {
}; };
}; };
export interface Iknowledge {
name: string;
embd_id: string;
parser_id: string;
}
export const useSaveKnowledge = () => { export const useSaveKnowledge = () => {
const { visible: visible, hideModal, showModal } = useSetModalState(); const { visible: visible, hideModal, showModal } = useSetModalState();
const { loading, createKnowledge } = useCreateKnowledge(); const { loading, createKnowledge } = useCreateKnowledge();
const { navigateToDataset } = useNavigatePage(); const { navigateToDataset } = useNavigatePage();
const onCreateOk = useCallback( const onCreateOk = useCallback(
async (name: string) => { async (data: Iknowledge) => {
const ret = await createKnowledge({ const ret = await createKnowledge(data);
name,
});
if (ret?.code === 0) { if (ret?.code === 0) {
hideModal(); hideModal();

View File

@ -1,4 +1,7 @@
import { IAgentLogsRequest } from '@/interfaces/database/agent'; import {
IAgentLogsRequest,
IPipeLineListRequest,
} from '@/interfaces/database/agent';
import api from '@/utils/api'; import api from '@/utils/api';
import { registerNextServer } from '@/utils/register-server'; import { registerNextServer } from '@/utils/register-server';
import request from '@/utils/request'; import request from '@/utils/request';
@ -131,4 +134,8 @@ export const fetchAgentLogsByCanvasId = (
return request.get(methods.fetchAgentLogs.url(canvasId), { params: params }); return request.get(methods.fetchAgentLogs.url(canvasId), { params: params });
}; };
export const fetchPipeLineList = (params: IPipeLineListRequest) => {
return request.get(api.listCanvas, { params: params });
};
export default agentService; export default agentService;

View File

@ -3,6 +3,9 @@ export function buildSelectOptions(
keyName?: string, keyName?: string,
valueName?: string, valueName?: string,
) { ) {
if (!Array.isArray(list) || !list.length) {
return [];
}
if (keyName && valueName) { if (keyName && valueName) {
return list.map((x) => ({ label: x[valueName], value: x[keyName] })); return list.map((x) => ({ label: x[valueName], value: x[keyName] }));
} }