export default function()

in spring-ai-alibaba-graph/spring-ai-alibaba-graph-studio/graph-ui/src/pages/Chatbot/Edit/index.tsx [54:985]


export default function () {
  // 提示词
  const [prompt, setPrompt] = useState('');

  // 添加变量可选内容
  const variableContent = (
    <Space direction="vertical" style={{ width: '100px' }}>
      <Button
        onClick={() => addVariable('text')}
        type="text"
        style={{ width: '100%' }}
      >
        文本
      </Button>
      <Button
        onClick={() => addVariable('paragraph')}
        type="text"
        style={{ width: '100%' }}
      >
        段落
      </Button>
      <Button
        onClick={() => addVariable('select')}
        type="text"
        style={{ width: '100%' }}
      >
        下拉选项
      </Button>
      <Button
        onClick={() => addVariable('number')}
        type="text"
        style={{ width: '100%' }}
      >
        数字
      </Button>
    </Space>
  );

  // 变量数据
  const [variableData, setVariableData] = useState<VariableDataType[]>([
    {
      id: 1,
      key: 'input',
      name: '变量1',
      optional: true,
      type: 'text',
      value: '变量1',
      maxLength: 48,
    },
    {
      id: 2,
      key: 'paragraph',
      name: '变量2',
      optional: true,
      type: 'paragraph',
      value: '变量2',
      maxLength: 148,
    },
    {
      id: 3,
      key: 'select',
      name: '变量3',
      optional: true,
      type: 'select',
      value: '变量3',
      options: ['选项1', '选项2'],
    },
    {
      id: 4,
      key: 'number',
      name: '变量4',
      optional: true,
      type: 'number',
      value: '变量4',
    },
  ]);

  // 改变变量key
  const changeVariableKey = (value: string, record: VariableDataType) => {
    console.log(value, record);
    // 改变变量key
    setVariableData(
      variableData.map((item) =>
        item.id === record.id ? { ...item, key: value } : item,
      ),
    );
  };

  // 改变变量名称
  const changeVariableName = (value: string, record: VariableDataType) => {
    // 改变所有相同key的变量名称
    setVariableData(
      variableData.map((item) =>
        item.key === record.key ? { ...item, name: value } : item,
      ),
    );
  };

  // 改变量可选
  const changeVariableOptional = (value: boolean, record: VariableDataType) => {
    // 改变所有相同key的变量可选状态
    setVariableData(
      variableData.map((item) =>
        item.key === record.key ? { ...item, optional: value } : item,
      ),
    );
  };

  // 删除变量
  const deleteVariable = (record: VariableDataType) => {
    console.log(record);
    // 删除变量
    setVariableData(variableData.filter((item) => item.key !== record.key));
  };

  // 根据变量类型添加变量
  const addVariable = (type: string) => {
    console.log('添加变量');
    // 添加变量
    setVariableData([
      ...variableData,
      {
        id: variableData.length + 1,
        key: type,
        name: '变量' + (variableData.length + 1),
        optional: true,
        type: type,
        value: '变量' + (variableData.length + 1),
        options: type === 'select' ? [] : undefined,
      },
    ]);
  };

  useEffect(() => {
    console.log(variableData);
  }, [variableData]);

  const variableDataColumns: TableProps<VariableDataType>['columns'] = [
    {
      title: '变量 KEY',
      dataIndex: 'key',
      key: 'key',
      render: (text: string, record: VariableDataType) => (
        <Input
          value={text}
          onChange={(e) => changeVariableKey(e.target.value, record)}
          placeholder="Filled"
          variant="filled"
        />
      ),
    },
    {
      title: '字段名称',
      dataIndex: 'name',
      key: 'name',
      render: (text: string, record: VariableDataType) => (
        <Input
          value={text}
          onChange={(e) => changeVariableName(e.target.value, record)}
          placeholder="Filled"
          variant="filled"
        />
      ),
    },
    {
      title: '可选',
      dataIndex: 'optional',
      key: 'optional',
      render: (text: boolean, record: VariableDataType) => (
        <Switch
          checked={text}
          onChange={(value) => changeVariableOptional(value, record)}
        />
      ),
    },
    {
      title: '操作',
      dataIndex: 'operation',
      key: 'operation',
      render: (text: string, record: VariableDataType) => (
        <Space>
          <Button
            onClick={() => handleEditClick(record)}
            type="text"
            icon={
              <Icon
                style={{ fontSize: '16px' }}
                icon="material-symbols:settings-outline-rounded"
              />
            }
          />
          <Button
            type="text"
            onClick={() => deleteVariable(record)}
            icon={
              <Icon
                style={{ fontSize: '16px' }}
                icon="material-symbols:delete-outline-rounded"
              />
            }
          />
        </Space>
      ),
    },
  ];

  // 编辑变量Modal
  const [isEditModalOpen, setIsEditModalOpen] = useState(false);
  const [currentVariable, setCurrentVariable] =
    useState<VariableDataType | null>(null);

  // 编辑form
  const [editForm] = Form.useForm();
  // 点击编辑图标时,设置当前编辑的变量
  const handleEditClick = (record: VariableDataType) => {
    setCurrentVariable(record);
    editForm.setFieldsValue({ ...record });
    console.log('curent variable', record);
    setIsEditModalOpen(true);
  };

  // 在 Modal 中绑定表单字段到状态
  const handleFieldChange = (field: string, value: any) => {
    console.log(value);
    if (currentVariable) {
      switch (field) {
        case 'type':
          if (value === 'select') {
            setCurrentVariable({
              ...currentVariable,
              maxLength: 0,
              value: '',
              options: [],
              type: value,
            });
          } else if (value === 'paragraph') {
            setCurrentVariable({
              ...currentVariable,
              maxLength: 48,
              options: [],
              type: value,
            });
          } else if (value === 'text') {
            setCurrentVariable({
              ...currentVariable,
              maxLength: 48,
              options: [],
              type: value,
            });
          } else if (value === 'number') {
            setCurrentVariable({
              ...currentVariable,
              maxLength: 0,
              options: [],
              type: value,
            });
          } else {
            setCurrentVariable({
              ...currentVariable,
              maxLength: undefined,
              options: undefined,
              type: value,
            });
          }
          break;
        case 'maxLength':
          setCurrentVariable({ ...currentVariable, maxLength: value });
          break;
        case 'options':
          setCurrentVariable({ ...currentVariable, options: value });
          break;
        default:
          setCurrentVariable({ ...currentVariable });
          break;
      }
    }
  };

  // 在保存时更新 variableData
  const handleEditOk = () => {
    if (currentVariable) {
      setVariableData(
        variableData.map((item) =>
          item.id === currentVariable.id ? currentVariable : item,
        ),
      );
    }
    setIsEditModalOpen(false);
  };

  const handleEditCancel = () => {
    setIsEditModalOpen(false);
  };

  // 字段类型
  const fieldTypeOptions = [
    { label: '文本', value: 'text' },
    { label: '段落', value: 'paragraph' },
    { label: '下拉选项', value: 'select' },
    { label: '数字', value: 'number' },
  ];

  // 删除选项
  const deleteOption = (index: number) => {
    console.log(index);
    // 删除选项
    setCurrentVariable({
      ...currentVariable!,
      options: currentVariable!.options?.filter((_, i) => i !== index),
    });
  };

  // 添加选项
  const addOption = () => {
    console.log('添加选项');
    // 添加选项
    setCurrentVariable({
      ...currentVariable!,
      options: [...(currentVariable!.options || []), ''],
    });
  };

  // 调试和预览显示
  const [isDebugAndPreviewOpen, setIsDebugAndPreviewOpen] = useState(false);

  // 是否开启视觉
  const [isVisualOpen, setIsVisualOpen] = useState(false);

  // 格式化变量数据
  const formatVariableData = () => {
    return variableData.map((item) => {
      const baseConfig = {
        default: item.value || '',
        label: item.name,
        required: item.optional || false,
        variable: item.key,
      };

      const config: any = {
        text: {
          ...baseConfig,
          max_length: item.maxLength || 48,
        },
        paragraph: baseConfig,
        select: {
          ...baseConfig,
          options: item.options || [],
        },
        number: baseConfig,
      };

      return {
        [item.type === 'text' ? 'text-input' : item.type]:
          config[item.type as keyof typeof config],
      };
    });
  };

  // 导出
  const exportData = () => {
    let exportData = {
      app: {
        description: '',
        icon: '🤖',
        icon_background: '#FFEAD5',
        mode: 'chat',
        name: 'f',
        use_icon_as_answer_icon: false,
      },
      kind: 'app',
      model_config: {
        agent_mode: {
          enabled: false,
          max_iteration: 5,
          strategy: 'function_call',
          tools: [],
        },
        annotation_reply: {
          enabled: false,
        },
        chat_prompt_config: {},
        completion_prompt_config: {},
        dataset_configs: {
          datasets: {
            datasets: [],
          },
          reranking_enable: true,
          retrieval_model: 'multiple',
          top_k: 4,
        },
        dataset_query_variable: '',
        external_data_tools: [],
        file_upload: {
          allowed_file_extensions: [
            '.JPG',
            '.JPEG',
            '.PNG',
            '.GIF',
            '.WEBP',
            '.SVG',
            '.MP4',
            '.MOV',
            '.MPEG',
            '.MPGA',
          ],
          allowed_file_types: [],
          allowed_file_upload_methods: ['remote_url', 'local_file'],
          enabled: false,
          image: {
            detail: 'high',
            enabled: false,
            number_limits: 3,
            transfer_methods: ['remote_url', 'local_file'],
          },
          number_limits: 3,
        },
        model: {
          completion_params: {
            stop: [],
          },
          mode: 'chat',
          name: 'gpt-4o-mini',
          provider: 'openai',
        },
        more_like_this: {
          enabled: false,
        },
        opening_statement: '',
        pre_prompt: prompt,
        prompt_type: 'simple',
        retriever_resource: {
          enabled: true,
        },
        sensitive_word_avoidance: {
          configs: [],
          enabled: false,
          type: '',
        },
        speech_to_text: {
          enabled: false,
        },
        suggested_questions: [],
        suggested_questions_after_answer: {
          enabled: false,
        },
        text_to_speech: {
          enabled: false,
          language: '',
          voice: '',
        },
        user_input_form: formatVariableData(),
      },
      version: '0.1.4',
    };
  };

  // 添加上下文modal
  const [isAddContextModalOpen, setAddContextModalOpen] = useState(false);
  // 可用的资料库
  type AvailableDataset = {
    id: string;
    name: string;
    tag: string;
  };
  const [availableDatasets, setAvailableDatasets] = useState<
    AvailableDataset[]
  >([
    {
      id: '1',
      name: 'index.htmldadadadadasdada',
      tag: '高质量·向量检索',
    },
    {
      id: '2',
      name: 'index.html',
      tag: '高质量·向量检索',
    },
  ]);

  // 选择的资料库
  const [selectedDataset, setSelectedDataset] = useState<AvailableDataset[]>(
    [],
  );

  // 使用的资料库
  const [usedDatasets, setUsedDatasets] = useState<AvailableDataset[]>([]);
  // 点击资料库添加选择
  const handleDatasetSelect = (dataset: AvailableDataset) => {
    const isDatasetSelected = selectedDataset.some(
      (item) => item.id === dataset.id,
    );
    if (isDatasetSelected) {
      setSelectedDataset(
        selectedDataset.filter((item) => item.id !== dataset.id),
      );
    } else {
      setSelectedDataset([...selectedDataset, dataset]);
    }
  };

  // 添加上下文
  const handleAddContextOk = () => {
    setAddContextModalOpen(false);
    setUsedDatasets(usedDatasets.concat(selectedDataset));
  };

  return (
    <>
      {/* 编辑变量Modal */}
      <Modal
        centered
        title="编辑变量"
        open={isEditModalOpen}
        okText="保存"
        onOk={handleEditOk}
        onCancel={handleEditCancel}
      >
        <Form
          form={editForm}
          layout="vertical"
          initialValues={currentVariable || undefined}
        >
          <Form.Item label="字段类型" name="type">
            <Radio.Group
              block
              options={fieldTypeOptions}
              value={currentVariable?.type}
              onChange={(e) => handleFieldChange('type', e.target.value)}
              optionType="button"
              buttonStyle="solid"
            />
          </Form.Item>
          <Form.Item label="变量名称" name="name">
            <Input
              value={currentVariable?.key}
              onChange={(e) => handleFieldChange('key', e.target.value)}
              placeholder="请输入"
              variant="filled"
            />
          </Form.Item>
          <Form.Item label="显示名称" name="optional">
            <Input
              value={currentVariable?.name}
              onChange={(e) => handleFieldChange('name', e.target.value)}
              placeholder="请输入"
              variant="filled"
            />
          </Form.Item>
          {currentVariable?.maxLength && (
            <Form.Item label="最大长度" name="maxLength">
              <Input
                min={1}
                type="number"
                value={currentVariable?.maxLength}
                onChange={(e) =>
                  handleFieldChange('maxLength', parseInt(e.target.value))
                }
                placeholder="请输入"
                variant="filled"
              />
            </Form.Item>
          )}
          {currentVariable?.options && (
            <Form.Item label="选项" name="options">
              <Space direction="vertical" style={{ width: '100%' }}>
                {currentVariable.options.map((option, index) => (
                  <Flex key={index} gap={10} style={{ width: '100%' }}>
                    <Input
                      style={{ width: '100%' }}
                      value={option}
                      onChange={(e) =>
                        handleFieldChange('options', e.target.value)
                      }
                      placeholder="请输入"
                      variant="filled"
                    />
                    <Button
                      type="text"
                      icon={
                        <Icon
                          style={{ fontSize: '16px' }}
                          icon="material-symbols:delete-outline-rounded"
                        />
                      }
                      onClick={() => deleteOption(index)}
                    />
                  </Flex>
                ))}
                <Button
                  style={{ width: '100%' }}
                  type="text"
                  icon={
                    <Icon
                      style={{ fontSize: '16px' }}
                      icon="material-symbols:add"
                    />
                  }
                  onClick={addOption}
                >
                  添加选项
                </Button>
              </Space>
            </Form.Item>
          )}
          <Form.Item label="" name="required">
            <Checkbox
              checked={!currentVariable?.optional}
              onChange={(e) => handleFieldChange('optional', !e.target.checked)}
            >
              必填
            </Checkbox>
          </Form.Item>
        </Form>
      </Modal>
      {/* 添加上下文modal */}
      <Modal
        onCancel={() => setAddContextModalOpen(false)}
        centered
        title="选择引用知识库"
        open={isAddContextModalOpen}
        okText="添加"
        onOk={handleAddContextOk}
      >
        {availableDatasets.length > 0 ? (
          availableDatasets.map((dataset, index) => (
            <div
              onClick={() => handleDatasetSelect(dataset)}
              key={index}
              className={`${styles['dataset-item']} ${
                selectedDataset.some((item) => item.id === dataset.id)
                  ? styles['dataset-item-selected']
                  : ''
              }`}
            >
              <div className={styles['dataset-item-name']}>{dataset.name}</div>
              <Tag>{dataset.tag}</Tag>
            </div>
          ))
        ) : (
          <Empty
            imageStyle={{ display: 'none' }}
            description={
              <Typography.Text>
                未找到知识库 <a href="#API">去创建</a>
              </Typography.Text>
            }
          />
        )}
      </Modal>
      <Card
        title="编辑"
        extra={
          <Flex justify="end">
            <Button type="link" onClick={exportData}>
              导出
            </Button>
            <Button type="primary">发布</Button>
          </Flex>
        }
        style={{ width: '100%' }}
      >
        <Flex justify="space-between" gap={10} style={{ width: '100%' }}>
          {/* 左侧 */}
          <Space
            direction="vertical"
            style={{
              width: '50%',
              maxHeight: 'calc(100vh - 170px)',
              overflow: 'auto',
            }}
          >
            {/* 提示词 */}
            <Card
              size="small"
              title="提示词"
              extra={
                <Button size="small" type="primary">
                  生成
                </Button>
              }
            >
              <TextArea
                onChange={(e) => setPrompt(e.target.value)}
                value={prompt}
                rows={10}
                placeholder="在这里写你的提示词,输入'插入变量'插入变量、输入'插入提示内容块'插入提示内容块"
              />
            </Card>

            {/* 变量 */}
            <Card
              size="small"
              title="变量"
              extra={
                <Popover
                  placement="bottomRight"
                  trigger="click"
                  content={variableContent}
                >
                  <Button size="small" type="primary">
                    添加
                  </Button>
                </Popover>
              }
              style={{ width: '100%' }}
            >
              {variableData.length > 0 ? (
                <Table<VariableDataType>
                  columns={variableDataColumns}
                  dataSource={variableData}
                  pagination={false}
                  rowKey="id"
                />
              ) : (
                <Text
                  disabled
                >{`变量能使用户输入表单引入提示词或开场白,你可以试试在提示词中输入 {{ input }}`}</Text>
              )}
            </Card>
            {/* 上下文 */}
            <Card
              size="small"
              title="上下文"
              extra={
                <Space>
                  <Button
                    size="small"
                    type="text"
                    icon={
                      <Icon
                        style={{ fontSize: '16px' }}
                        icon="material-symbols:settings-outline-rounded"
                      />
                    }
                  >
                    召回设置
                  </Button>
                  <Button
                    size="small"
                    type="primary"
                    onClick={() => setAddContextModalOpen(true)}
                  >
                    添加
                  </Button>
                </Space>
              }
            >
              {usedDatasets.length > 0 ? (
                usedDatasets.map((dataset, index) => (
                  <div
                    onClick={() => handleDatasetSelect(dataset)}
                    key={index}
                    className={`${styles.dataset} ${
                      selectedDataset.some((item) => item.id === dataset.id)
                        ? styles.datasetSelected
                        : ''
                    }`}
                  >
                    <div className={styles.datasetName}>{dataset.name}</div>
                    <Tag>{dataset.tag}</Tag>
                  </div>
                ))
              ) : (
                <span>您可以导入知识库作为上下文</span>
              )}
            </Card>
            {/* 视觉 */}
            <Card size="small">
              <Flex justify="space-between" gap={10} style={{ width: '100%' }}>
                <Space>视觉</Space>
                <Space>
                  <Button
                    type="text"
                    icon={
                      <Icon
                        style={{ fontSize: '16px' }}
                        icon="material-symbols:settings-outline-rounded"
                      />
                    }
                  >
                    设置
                  </Button>
                  <Switch
                    checked={isVisualOpen}
                    onChange={(value) => setIsVisualOpen(value)}
                  />
                </Space>
              </Flex>
            </Card>
          </Space>
          {/* 右侧 */}
          <Card
            size="small"
            title="调试和预览"
            style={{ width: '50%', height: 'calc(100vh - 170px)' }}
            extra={
              <Space>
                <Button
                  onClick={() =>
                    setIsDebugAndPreviewOpen(!isDebugAndPreviewOpen)
                  }
                  type="text"
                  icon={
                    <Icon
                      style={{ fontSize: '16px' }}
                      icon="tdesign:adjustment"
                    />
                  }
                />
              </Space>
            }
          >
            <Space
              direction="vertical"
              style={{ width: '100%', height: '100%' }}
            >
              {/* 用户输入字段调试与预览 */}
              {isDebugAndPreviewOpen && (
                <Card>
                  <Form layout="vertical" initialValues={{}}>
                    {variableData.map((item) => {
                      switch (item.type) {
                        case 'text':
                          return (
                            <Form.Item
                              label={
                                item.name + (item?.optional ? '(选填)' : '')
                              }
                              name={item.key}
                            >
                              <Input
                                maxLength={item.maxLength}
                                placeholder="请输入"
                                variant="filled"
                              />
                            </Form.Item>
                          );
                        case 'paragraph':
                          return (
                            <Form.Item
                              label={
                                item.name + (item?.optional ? '(选填)' : '')
                              }
                              name={item.key}
                            >
                              <TextArea
                                maxLength={item.maxLength}
                                placeholder="请输入"
                                variant="filled"
                              />
                            </Form.Item>
                          );
                        case 'select':
                          return (
                            <Form.Item
                              label={
                                item.name + (item?.optional ? '(选填)' : '')
                              }
                              name={item.key}
                            >
                              <Select
                                variant="filled"
                                placeholder="请选择"
                                value={item.value}
                                onChange={(value) => {
                                  setVariableData(
                                    variableData.map((variable) => {
                                      if (variable.key === item.key) {
                                        return {
                                          ...variable,
                                          value: value,
                                        };
                                      }
                                      return variable;
                                    }),
                                  );
                                }}
                                options={item.options?.map((option) => ({
                                  label: option,
                                  value: option,
                                }))}
                              />
                            </Form.Item>
                          );
                        case 'number':
                          return (
                            <Form.Item
                              label={
                                item.name + (item?.optional ? '(选填)' : '')
                              }
                              name={item.key}
                            >
                              <Input
                                min={1}
                                type="number"
                                placeholder="请输入"
                                variant="filled"
                              />
                            </Form.Item>
                          );
                      }
                    })}
                  </Form>
                </Card>
              )}
              {/* 气泡 */}
              <Flex gap="middle" vertical>
                <Bubble
                  placement="start"
                  content="What a beautiful day!"
                  avatar={{}}
                />

                <Bubble placement="end" content="Thank you!" avatar={{}} />
              </Flex>
            </Space>
            <Sender
              style={{
                width: '90%',
                position: 'absolute',
                left: '50%',
                bottom: 20,
                transform: 'translateX(-50%)',
              }}
              value="Force as loading"
            />
          </Card>
        </Flex>
      </Card>
    </>
  );
}