|
@@ -0,0 +1,938 @@
|
|
|
|
|
+import * as React from 'react';
|
|
|
|
|
+import { useLocation } from 'react-router-dom';
|
|
|
|
|
+import { observer } from 'mobx-react';
|
|
|
|
|
+import './style.less';
|
|
|
|
|
+import {
|
|
|
|
|
+ Button, Input, Form, Divider, Splitter, Select, InputNumber, InputNumberProps,
|
|
|
|
|
+ Radio, Switch, Row, Col, Slider, Space, RadioChangeEvent,
|
|
|
|
|
+ Spin, message, Typography, Tooltip,
|
|
|
|
|
+ Cascader
|
|
|
|
|
+} from 'antd';
|
|
|
|
|
+import { PlusCircleOutlined, MinusCircleOutlined, ArrowLeftOutlined, InfoCircleOutlined } from '@ant-design/icons';
|
|
|
|
|
+import { apis } from '@/apis';
|
|
|
|
|
+import router from '@/router';
|
|
|
|
|
+import LocalStorage from '@/LocalStorage';
|
|
|
|
|
+import Chat from '@/components/chat';
|
|
|
|
|
+
|
|
|
|
|
+const { TextArea } = Input;
|
|
|
|
|
+const FormItem = Form.Item;
|
|
|
|
|
+const { Option } = Select;
|
|
|
|
|
+const MAX_COUNT = 5;
|
|
|
|
|
+const Index = 1;
|
|
|
|
|
+interface QuestionAnswerInfoProps {
|
|
|
|
|
+ AuditAppId?: string | number,
|
|
|
|
|
+ isComponent?: boolean
|
|
|
|
|
+}
|
|
|
|
|
+const QuestionAnswerInfo: React.FC<QuestionAnswerInfoProps> = ({AuditAppId,isComponent=false}) => {
|
|
|
|
|
+
|
|
|
|
|
+ const [form] = Form.useForm();
|
|
|
|
|
+
|
|
|
|
|
+ // top_p
|
|
|
|
|
+ const [topPValue, setTopPValue] = React.useState(0.1);
|
|
|
|
|
+
|
|
|
|
|
+ const TopPDecimalStep: React.FC = () => {
|
|
|
|
|
+
|
|
|
|
|
+ const onChange: InputNumberProps['onChange'] = (value) => {
|
|
|
|
|
+ if (Number.isNaN(value)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ setTopPValue(value as number);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <Row>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Slider
|
|
|
|
|
+ min={0}
|
|
|
|
|
+ max={1}
|
|
|
|
|
+ onChange={onChange}
|
|
|
|
|
+ // value={typeof topPValue === 'number' ? topPValue : 0}
|
|
|
|
|
+ value={topPValue}
|
|
|
|
|
+ step={0.1}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={4}>
|
|
|
|
|
+ <InputNumber
|
|
|
|
|
+ min={0}
|
|
|
|
|
+ max={1}
|
|
|
|
|
+ className='form-input-number-small'
|
|
|
|
|
+ step={0.01}
|
|
|
|
|
+ value={topPValue}
|
|
|
|
|
+ onChange={onChange}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ );
|
|
|
|
|
+ };
|
|
|
|
|
+ const [tempValue, setTempValue] = React.useState(0.01);
|
|
|
|
|
+ // temperature
|
|
|
|
|
+ const TempDecimalStep: React.FC = () => {
|
|
|
|
|
+ const onChange: InputNumberProps['onChange'] = (value) => {
|
|
|
|
|
+ if (Number.isNaN(value)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ setTempValue(value as number);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <Row>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Slider
|
|
|
|
|
+ min={0}
|
|
|
|
|
+ max={1}
|
|
|
|
|
+ onChange={onChange}
|
|
|
|
|
+ // value={typeof tempValue === 'number' ? tempValue : 0}
|
|
|
|
|
+ value={tempValue}
|
|
|
|
|
+ step={0.01}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={4}>
|
|
|
|
|
+ <InputNumber
|
|
|
|
|
+ min={0}
|
|
|
|
|
+ max={1}
|
|
|
|
|
+ className='form-input-number-small'
|
|
|
|
|
+ step={0.01}
|
|
|
|
|
+ value={tempValue}
|
|
|
|
|
+ onChange={onChange}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ );
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ type ModelList = {
|
|
|
|
|
+ label: string,
|
|
|
|
|
+ value: string,
|
|
|
|
|
+ }[];
|
|
|
|
|
+
|
|
|
|
|
+ type KnowledgeList = {
|
|
|
|
|
+ label: string,
|
|
|
|
|
+ value: string,
|
|
|
|
|
+ }[];
|
|
|
|
|
+
|
|
|
|
|
+ type AppTypeList = {
|
|
|
|
|
+ label: string,
|
|
|
|
|
+ value: string,
|
|
|
|
|
+ children: {
|
|
|
|
|
+ label: string,
|
|
|
|
|
+ value: string,
|
|
|
|
|
+ }[],
|
|
|
|
|
+ }[];
|
|
|
|
|
+
|
|
|
|
|
+ const [step, setStep] = React.useState(1);
|
|
|
|
|
+ const [pageLoading, setPageLoading] = React.useState(false);
|
|
|
|
|
+ const [modelList, setModelList] = React.useState<ModelList>([]);
|
|
|
|
|
+ const [knowledgeList, setKnowledgeList] = React.useState<KnowledgeList>([]);
|
|
|
|
|
+ const [isVisible, setIsVisible] = React.useState(false);
|
|
|
|
|
+ const [isVisibleCus, setIsVisibleCus] = React.useState(false);
|
|
|
|
|
+ const [isVisibleSlice, setIsVisibleSlice] = React.useState(false);
|
|
|
|
|
+ const [isVisibleRerank, setIsVisibleRerank] = React.useState(false);
|
|
|
|
|
+ const [isDeepThinkVisible, setIsDeepThinkVisible] = React.useState(false);
|
|
|
|
|
+ const [name, setName] = React.useState('');
|
|
|
|
|
+ const [appTypeList, setAppTypeList] = React.useState<AppTypeList>([]);
|
|
|
|
|
+ const [appVisibleList, setAppVisibleList] = React.useState<AppTypeList>([]); // 是否公开
|
|
|
|
|
+ const [updateFlag, setUpdateFlag] = React.useState<boolean>();
|
|
|
|
|
+ const [createFlag, setCreateFlag] = React.useState<boolean>();
|
|
|
|
|
+ const [appProjectList, setAppProjectList] = React.useState<AppTypeList>([]);
|
|
|
|
|
+ const [isAppPro, setIsAppPro] = React.useState<boolean>(false);
|
|
|
|
|
+ const [appId, setAppId] = React.useState<string>('');
|
|
|
|
|
+
|
|
|
|
|
+ const style: React.CSSProperties = {
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ flexDirection: 'column',
|
|
|
|
|
+ gap: 8,
|
|
|
|
|
+ width: 230,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const location = useLocation();
|
|
|
|
|
+
|
|
|
|
|
+ const init = async (id: string) => {
|
|
|
|
|
+ await Promise.all([
|
|
|
|
|
+ api.fetchKnowlegde(),
|
|
|
|
|
+ api.fetchAppType(),
|
|
|
|
|
+ // api.fetchModelList(),
|
|
|
|
|
+ api.fetchAppProType(),
|
|
|
|
|
+ api.fetchAppVisible(id)
|
|
|
|
|
+ ])
|
|
|
|
|
+ if (id) {
|
|
|
|
|
+ await api.fetchDetail(id);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ React.useEffect(() => {
|
|
|
|
|
+ console.log('isComponent',isComponent)
|
|
|
|
|
+ const id = location?.state?.id||AuditAppId;
|
|
|
|
|
+ init(id);
|
|
|
|
|
+ const uFlag = LocalStorage.getStatusFlag('deepseek:application:update');
|
|
|
|
|
+ setUpdateFlag(uFlag);
|
|
|
|
|
+ const cFlag = LocalStorage.getStatusFlag('deepseek:application:create');
|
|
|
|
|
+ setCreateFlag(cFlag);
|
|
|
|
|
+ }, []);
|
|
|
|
|
+
|
|
|
|
|
+ // 定义一个状态来存储输入框数组
|
|
|
|
|
+ const [inputs, setInputs] = React.useState([{ id: 1, value: '' }]);
|
|
|
|
|
+
|
|
|
|
|
+ // 添加新输入框的函数
|
|
|
|
|
+ const addInput = () => {
|
|
|
|
|
+ const newId = inputs.length + 1; // 生成新的唯一ID
|
|
|
|
|
+ setInputs([...inputs, { id: newId, value: '' }]);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 删除输入框(按id删除+最少数量限制)
|
|
|
|
|
+ const delInput = (id: number) => {
|
|
|
|
|
+ if (inputs.length <= 1) {
|
|
|
|
|
+ message.warning("至少保留1个预设问题");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ setInputs(inputs.filter(input => input.id !== id));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 处理输入变更的函数
|
|
|
|
|
+ const handleChange = (id: number, value: string) => {
|
|
|
|
|
+ setInputs(inputs.map(input => (input.id === id ? { ...input, value } : input)));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleAppChange = (typeId: number) => {
|
|
|
|
|
+ if (typeId === 41) { // 根据实际值进行判断
|
|
|
|
|
+ setIsAppPro(true);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setIsAppPro(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // const onChange: InputNumberProps['onChange'] = (value) => {
|
|
|
|
|
+ // console.log('changed', value);
|
|
|
|
|
+ // };
|
|
|
|
|
+
|
|
|
|
|
+ const onChangeShow = (checked: boolean) => {
|
|
|
|
|
+ console.log(`switch to ${checked}`);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const onChangeModel = (checked: boolean) => {
|
|
|
|
|
+ setIsVisibleRerank(!isVisibleRerank);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const onChangeCount = (value: string) => {
|
|
|
|
|
+ if (value === 'fixed') {
|
|
|
|
|
+ setIsVisibleSlice(!isVisibleSlice);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setIsVisibleSlice(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 召回方式
|
|
|
|
|
+ const onChangeRecallMethod = (e: RadioChangeEvent) => {
|
|
|
|
|
+
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 获取应用详情
|
|
|
|
|
+ const api = {
|
|
|
|
|
+ fetchDetail: async (app_id: string) => {
|
|
|
|
|
+ setPageLoading(true);
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await apis.fetchTakaiApplicationDetail(app_id);
|
|
|
|
|
+ const sd = res.data.questionlist.map((item: any, index: number) => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ "id": index + 1,
|
|
|
|
|
+ "value": item.question,
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const info = res.data.detail;
|
|
|
|
|
+
|
|
|
|
|
+ setAppId(info.appId);
|
|
|
|
|
+
|
|
|
|
|
+ setTopPValue(info.topP as number);
|
|
|
|
|
+ setTempValue(info.temperature as number);
|
|
|
|
|
+ setName(info.name);
|
|
|
|
|
+
|
|
|
|
|
+ interface Item2 {
|
|
|
|
|
+ index_type_id: number,
|
|
|
|
|
+ knowledge_id: string
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ interface Item {
|
|
|
|
|
+ show_recall_result: boolean,
|
|
|
|
|
+ recall_method: string,
|
|
|
|
|
+ rerank_status: boolean,
|
|
|
|
|
+ slice_config_type: string,
|
|
|
|
|
+ slice_count: number,
|
|
|
|
|
+ recall_slice_splicing_method: string,
|
|
|
|
|
+ param_desc: string,
|
|
|
|
|
+ rerank_model_name: string,
|
|
|
|
|
+ rerank_index_type_list: [Item2],
|
|
|
|
|
+ recall_index_type_list: [Item2]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const data_info: Item = JSON.parse(info.knowledgeInfo);
|
|
|
|
|
+
|
|
|
|
|
+ if (data_info.param_desc === 'custom') {
|
|
|
|
|
+
|
|
|
|
|
+ setIsVisibleCus(!isVisibleCus); //自定义回答风格
|
|
|
|
|
+ }
|
|
|
|
|
+ if (data_info.rerank_status === true) {
|
|
|
|
|
+ setIsVisibleRerank(!isVisibleRerank) //模型
|
|
|
|
|
+ }
|
|
|
|
|
+ //召回切片数量
|
|
|
|
|
+ if (data_info.slice_config_type === 'fixed') {
|
|
|
|
|
+ setIsVisibleSlice(!isVisibleSlice);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setIsVisibleSlice(false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (info.typeId === 41) {
|
|
|
|
|
+ setIsAppPro(true);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setIsAppPro(false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (info.model === 'Qwen3-30B') {
|
|
|
|
|
+ setIsDeepThinkVisible(true);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setIsDeepThinkVisible(false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.log(info.knowledgeIds, 'info.knowledgeIds')
|
|
|
|
|
+ form.setFieldsValue({
|
|
|
|
|
+ id: info.id,
|
|
|
|
|
+ name: info.name, //应用名称
|
|
|
|
|
+ desc: info.desc, //应用描述
|
|
|
|
|
+ prompt: info.prompt, //应用提示语
|
|
|
|
|
+ top_p: info.topP as number, //topP
|
|
|
|
|
+ temperature: info.temperature as number, //温度
|
|
|
|
|
+ knowledge_ids: info.knowledgeIds,
|
|
|
|
|
+ model: info.model,
|
|
|
|
|
+ isDeepThink: info.isDeepThink,
|
|
|
|
|
+ icon_color: info.icon_color,
|
|
|
|
|
+ icon_type: info.icon_type,
|
|
|
|
|
+ questionList: sd, //问题列表
|
|
|
|
|
+ max_token: info.maxToken, //应用最大token
|
|
|
|
|
+ updateDate: info.updateDate, // 更新时间
|
|
|
|
|
+ appProId: info.appProId,// 项目
|
|
|
|
|
+ typeId: info.typeId, //应用类型
|
|
|
|
|
+ visible: info.visible||'0', //是否公开
|
|
|
|
|
+ param_desc: data_info.param_desc, //回答风格
|
|
|
|
|
+ show_recall_result: data_info.show_recall_result, //是否展示召回结果
|
|
|
|
|
+ recall_method: data_info.recall_method, //召回方式
|
|
|
|
|
+ rerank_status: data_info.rerank_status, //开启rerank
|
|
|
|
|
+ rerank_model_name: data_info.rerank_model_name, //模型名称
|
|
|
|
|
+ slice_config_type: data_info.slice_config_type, // 召回切片数量
|
|
|
|
|
+ slice_count: data_info.slice_count, // 切片数量
|
|
|
|
|
+ recall_slice_splicing_method: data_info.recall_slice_splicing_method, // 切片内容
|
|
|
|
|
+ // rerank_status = 1 rerank_index_type_list
|
|
|
|
|
+ // recall_method = 'embedding' || 'mixed' recall_index_type_list
|
|
|
|
|
+ //recall_index_type_list: info.recall_index_type_list, //知识库id
|
|
|
|
|
+ //rerank_index_type_list: info.rerank_index_type_list, //知识库id
|
|
|
|
|
+ })
|
|
|
|
|
+ if (sd.length > 0) {
|
|
|
|
|
+ setInputs(sd);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ setPageLoading(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //获取知识库列表
|
|
|
|
|
+ fetchKnowlegde: async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await apis.fetchTakaiKnowledgeList();
|
|
|
|
|
+ const list = res.data.map((item: any) => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ label: item.name,
|
|
|
|
|
+ value: item.knowledgeId,
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ console.log(list, 'list')
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ setKnowledgeList(list);
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 获取应用类型
|
|
|
|
|
+ fetchAppType: async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await apis.fetchTakaiAppTypeList('app_type');
|
|
|
|
|
+ const list = res.data.map((item: any) => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ label: item.dictLabel,
|
|
|
|
|
+ value: item.dictCode,
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ setAppTypeList(list);
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ // 获取是否公开类型
|
|
|
|
|
+ fetchAppVisible: async (id:string) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await apis.fetchTakaiAppTypeList('app_visible');
|
|
|
|
|
+ console.log('res.data',res.data)
|
|
|
|
|
+ const list = res.data.map((item: any) => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ label: item.dictLabel,
|
|
|
|
|
+ value: item.dictValue,
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ setAppVisibleList(list);
|
|
|
|
|
+ if(!id){
|
|
|
|
|
+ form.setFieldsValue({
|
|
|
|
|
+ visible: list[0].value
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 获取模型列表
|
|
|
|
|
+ fetchModelList: async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await apis.fetchModelList();
|
|
|
|
|
+ const list = res.data.data.map((item: any) => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ label: item.modelName,
|
|
|
|
|
+ value: item.modelCode,
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ setModelList(list);
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 项目级应用下的类型
|
|
|
|
|
+ fetchAppProType: async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await apis.fetchTakaiAppTypeList('projectTree');
|
|
|
|
|
+ const list: AppTypeList = res.data;
|
|
|
|
|
+ setAppProjectList(list);
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const handleRedioClick = (value: string) => {
|
|
|
|
|
+ setIsVisibleCus(false);
|
|
|
|
|
+ if (value === 'strict') {
|
|
|
|
|
+ setTopPValue(0.5);
|
|
|
|
|
+ setTempValue(0.01);
|
|
|
|
|
+ } else if (value === 'moderate') {
|
|
|
|
|
+ setTopPValue(0.7);
|
|
|
|
|
+ setTempValue(0.50);
|
|
|
|
|
+ } else if (value === 'flexib') {
|
|
|
|
|
+ setTopPValue(0.9);
|
|
|
|
|
+ setTempValue(0.90);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className='questionAnswerInfo'>
|
|
|
|
|
+ <Spin spinning={pageLoading}>
|
|
|
|
|
+ <Form
|
|
|
|
|
+ variant={'underlined'}
|
|
|
|
|
+ form={form}
|
|
|
|
|
+ layout='vertical'
|
|
|
|
|
+ initialValues={{
|
|
|
|
|
+ isDeepThink: false,
|
|
|
|
|
+ max_token: 1024
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <div style={{ display: step === 1 ? 'block' : 'none' }} className='questionAnswerInfo-content'>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='问答应用名称'
|
|
|
|
|
+ name='name'
|
|
|
|
|
+ rules={[{ required: true, message: '问答应用名称不能为空' }]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Input placeholder="请输入问答应用名称" disabled={true} className='form-input-large' />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='应用类型'
|
|
|
|
|
+ name='typeId'
|
|
|
|
|
+ >
|
|
|
|
|
+ <Select
|
|
|
|
|
+ className='questionAnswerInfo-content-title'
|
|
|
|
|
+ placeholder='请选择问答应用类型'
|
|
|
|
|
+ onChange={handleAppChange}
|
|
|
|
|
+ allowClear={true}
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ >
|
|
|
|
|
+ {
|
|
|
|
|
+ appTypeList.map((item, index) => {
|
|
|
|
|
+ return <Option value={item.value} key={index}>
|
|
|
|
|
+ {item.label}
|
|
|
|
|
+ </Option>
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='是否公开'
|
|
|
|
|
+ name='visible'
|
|
|
|
|
+ >
|
|
|
|
|
+ <Select
|
|
|
|
|
+ className='questionAnswerInfo-content-title'
|
|
|
|
|
+ placeholder='请选择是否公开'
|
|
|
|
|
+ allowClear={true}
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ >
|
|
|
|
|
+ {
|
|
|
|
|
+ appVisibleList.map((item, index) => {
|
|
|
|
|
+ return <Option value={item.value} key={index}>
|
|
|
|
|
+ {item.label}
|
|
|
|
|
+ </Option>
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ {
|
|
|
|
|
+ isAppPro &&
|
|
|
|
|
+ <>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='项目'
|
|
|
|
|
+ name='appProId'
|
|
|
|
|
+ rules={[{ required: true, message: '项目不能为空' }]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Cascader
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ options={appProjectList}
|
|
|
|
|
+ placeholder="请选择项目"
|
|
|
|
|
+ showSearch
|
|
|
|
|
+ className="questionAnswerInfo-content-title"
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </>
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='问答应用描述'
|
|
|
|
|
+ name='desc'
|
|
|
|
|
+ rules={[{ required: true, message: '问答应用描述不能为空' }]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <TextArea
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ showCount
|
|
|
|
|
+ maxLength={500}
|
|
|
|
|
+ placeholder="请输入当前应用的描述"
|
|
|
|
|
+ className='form-textarea-large'
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+
|
|
|
|
|
+ <div className='preset-questions'>
|
|
|
|
|
+ <h4>添加预设问题</h4>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ {
|
|
|
|
|
+ inputs.map(input => (
|
|
|
|
|
+ <div key={input.id} className='question-item'>
|
|
|
|
|
+ <label>问题 {input.id}</label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ className='question-input'
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ value={input.value}
|
|
|
|
|
+ onChange={e => handleChange(input.id, e.target.value)}
|
|
|
|
|
+ />
|
|
|
|
|
+ {/* <div className='question-actions'>
|
|
|
|
|
+ <PlusCircleOutlined className='question-icon' onClick={addInput} />
|
|
|
|
|
+ <MinusCircleOutlined className='question-icon' onClick={() => delInput(input.id)} />
|
|
|
|
|
+ </div> */}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div style={{ display: 'flex', gap: '12px', marginTop: '24px', paddingTop: '24px', borderTop: '1px solid #f0f0f0' }}>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ type='primary'
|
|
|
|
|
+ disabled={false}
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ form.validateFields(['name', 'desc', 'appProId']).then(async (values) => {
|
|
|
|
|
+ setStep(2);
|
|
|
|
|
+ setInputs(inputs);
|
|
|
|
|
+ }).catch((error) => {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ });
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ 下一步
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div style={{ display: step === 2 ? 'block' : 'none' }} className='questionAnswerInfo-content'>
|
|
|
|
|
+ <div className='flex-between padding-bottom-16'>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ className='btn-back'
|
|
|
|
|
+ disabled={false}
|
|
|
|
|
+ icon={<ArrowLeftOutlined />}
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ setStep(1);
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ 上一步
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className='section-title'>
|
|
|
|
|
+ Prompt编写与参数配置
|
|
|
|
|
+ <Tooltip
|
|
|
|
|
+ title="Prompt用于对大模型的回复做出一些列指令和约束。这段Prompt不会被用户看到。"
|
|
|
|
|
+ placement="right"
|
|
|
|
|
+ overlayStyle={{ fontSize: '12px' }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <InfoCircleOutlined style={{ marginLeft: '8px', color: '#999', fontSize: '14px' }} />
|
|
|
|
|
+ </Tooltip>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <Splitter style={{ border: '1px solid #f0f0f0', borderRadius: '6px', height: 550 }}>
|
|
|
|
|
+ <Splitter.Panel defaultSize="45%">
|
|
|
|
|
+ <div className='prompt'>
|
|
|
|
|
+ <div className='prompt-info'>
|
|
|
|
|
+ <div className='prompt-info-text'>
|
|
|
|
|
+ <Typography.Paragraph style={{ fontSize: '12px', lineHeight: '1.6', color: '#999', margin: 0 }}>
|
|
|
|
|
+ 编写Prompt过程中可以引入2项变量:
|
|
|
|
|
+ <span className='variable-highlight'>{'{{知识}}'}</span>
|
|
|
|
|
+ 代表知识库中检索到的知识内容,
|
|
|
|
|
+ <span className='variable-highlight'>{'{{用户}}'}</span>
|
|
|
|
|
+ 代表用户输入的内容。您可以在编写Prompt过程中将变量拼接在合适的位置。
|
|
|
|
|
+ <br />
|
|
|
|
|
+ 插入:
|
|
|
|
|
+ <span className='variable-highlight'>{'{{知识}}'}</span>,
|
|
|
|
|
+ 插入:
|
|
|
|
|
+ <span className='variable-highlight'>{'{{用户}}'}</span>。
|
|
|
|
|
+ </Typography.Paragraph>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <Divider style={{ margin: '0' }} />
|
|
|
|
|
+ <div className='prompt-editor-area'>
|
|
|
|
|
+ <FormItem name='prompt'
|
|
|
|
|
+ initialValue={
|
|
|
|
|
+ `你是一位知识检索助手,你必须并且只能从我发送的众多知识片段中寻找能够解决用户输入问题的最优答案,并且在执行任务的过程中严格执行规定的要求。 \n
|
|
|
|
|
+知识片段如下:
|
|
|
|
|
+{{知识}}
|
|
|
|
|
+
|
|
|
|
|
+规定要求:
|
|
|
|
|
+- 找到答案就仅使用知识片段中的原文回答用户的提问;- 找不到答案就用自身知识并且告诉用户该信息不是来自文档;
|
|
|
|
|
+- 所引用的文本片段中所包含的示意图占位符必须进行返回,占位符格式参考:【示意图序号_编号】
|
|
|
|
|
+- 严禁输出任何知识片段中不存在的示意图占位符;
|
|
|
|
|
+- 输出的内容必须删除其中包含的任何图注、序号等信息。例如:“进入登录页面(图1.1)”需要从文字中删除图序,回复效果为:“进入登录页面”;“如图所示1.1”,回复效果为:“如图所示”;
|
|
|
|
|
+
|
|
|
|
|
+格式规范:
|
|
|
|
|
+- 文档中会出现包含表格的情况,表格是以图片标识符的形式呈现,表格中缺失数据时候返回空单元格;
|
|
|
|
|
+- 如果需要用到表格中的数据,以代码块语法输出表格中的数据;
|
|
|
|
|
+- 避免使用代码块语法回复信息;
|
|
|
|
|
+- 回复的开头语不要输出诸如:“我想”,“我认为”,“think”等相关语义的文本。
|
|
|
|
|
+
|
|
|
|
|
+严格执行规定要求,不要复述问题,直接开始回答。
|
|
|
|
|
+
|
|
|
|
|
+用户输入问题:
|
|
|
|
|
+{{用户}}`
|
|
|
|
|
+ }
|
|
|
|
|
+ rules={[{ required: true, message: '提示词不能为空' }]}>
|
|
|
|
|
+ <TextArea
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ placeholder="提示词"
|
|
|
|
|
+ rows={50}
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Splitter.Panel>
|
|
|
|
|
+ <Splitter.Panel defaultSize="25%">
|
|
|
|
|
+ <div className='flex-center-container'>
|
|
|
|
|
+ <div className='half-width'>
|
|
|
|
|
+ <div className='flex-center-top'>
|
|
|
|
|
+ 欢迎使用 {name}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className='flex-center padding-top-20'>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='引用知识库'
|
|
|
|
|
+ name='knowledge_ids'
|
|
|
|
|
+ rules={[{ required: true, message: '知识库不能为空' }]}>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ mode='multiple'
|
|
|
|
|
+ maxCount={MAX_COUNT}
|
|
|
|
|
+ showSearch={true}
|
|
|
|
|
+ // suffixIcon={suffix}
|
|
|
|
|
+ // className='questionAnswerInfo-content-title'
|
|
|
|
|
+ placeholder='请选择知识库'
|
|
|
|
|
+ >
|
|
|
|
|
+ {
|
|
|
|
|
+ knowledgeList.map((item, index) => {
|
|
|
|
|
+ return <Option value={item.value} key={index}>
|
|
|
|
|
+ {item.label}
|
|
|
|
|
+ </Option>
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className='flex-center'>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='调用模型'
|
|
|
|
|
+ name="model"
|
|
|
|
|
+ rules={[{ required: true, message: '模型不能为空' }]}>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ placeholder='请选择模型'
|
|
|
|
|
+ allowClear={true}
|
|
|
|
|
+ className='questionAnswerInfo-content-title'
|
|
|
|
|
+ onChange={(value) => {
|
|
|
|
|
+ if (value === 'Qwen3-30B') {
|
|
|
|
|
+ setIsDeepThinkVisible(true);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setIsDeepThinkVisible(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Option value='Qwen3-30B'>Qwen3-30B</Option>
|
|
|
|
|
+ <Option value='Qwen2-72B'>Qwen2-72B</Option>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className='flex-center' style={{
|
|
|
|
|
+ display: isDeepThinkVisible ? 'flex' : 'none'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='深度思考'
|
|
|
|
|
+ name='isDeepThink'
|
|
|
|
|
+ rules={[{ required: true, message: '请选择是否深度思考' }]}>
|
|
|
|
|
+ <Radio.Group buttonStyle="solid" className='form-control-width' disabled={true}>
|
|
|
|
|
+ <Radio.Button value='Y'>是</Radio.Button>
|
|
|
|
|
+ <Radio.Button value='N'>否</Radio.Button>
|
|
|
|
|
+ </Radio.Group>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className='flex-center'>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='max token'
|
|
|
|
|
+ name='max_token'
|
|
|
|
|
+ rules={[{ required: true, message: 'max token不能为空' }]}>
|
|
|
|
|
+ <InputNumber disabled={true}
|
|
|
|
|
+ className='questionAnswerInfo-content-title'
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ {
|
|
|
|
|
+ !isVisible &&
|
|
|
|
|
+ <div className='flex-center'>
|
|
|
|
|
+ <a onClick={() => {
|
|
|
|
|
+ setIsVisible(!isVisible);
|
|
|
|
|
+ }} className='link-more-settings'>
|
|
|
|
|
+ 更多设置
|
|
|
|
|
+ </a>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ {/* {isVisible && */}
|
|
|
|
|
+ <div style={{ display: isVisible ? 'block' : 'none', paddingTop: '20px' }}>
|
|
|
|
|
+ {isVisibleCus &&
|
|
|
|
|
+ <Space style={{ width: '100%' }} direction="vertical">
|
|
|
|
|
+ <div className='flex-center'>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='Top-p'
|
|
|
|
|
+ name='topP'
|
|
|
|
|
+ className='form-control-width'
|
|
|
|
|
+ >
|
|
|
|
|
+ <TopPDecimalStep />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className='flex-center'>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='Temperature'
|
|
|
|
|
+ name='temperature'
|
|
|
|
|
+ className='form-control-width'
|
|
|
|
|
+ >
|
|
|
|
|
+ <TempDecimalStep />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Space>
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
|
+ alignItems: 'center'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='回答风格'
|
|
|
|
|
+ name='param_desc'
|
|
|
|
|
+ rules={[{ required: true, message: '回答风格不能为空' }]}>
|
|
|
|
|
+ <Radio.Group buttonStyle="solid" disabled={true}
|
|
|
|
|
+ className='form-control-width'>
|
|
|
|
|
+ <Radio.Button onClick={() => {
|
|
|
|
|
+ handleRedioClick('strict')
|
|
|
|
|
+ }} value='strict'>严谨</Radio.Button>
|
|
|
|
|
+ <Radio.Button onClick={() => {
|
|
|
|
|
+ handleRedioClick('moderate')
|
|
|
|
|
+ }} value='moderate'>适中</Radio.Button>
|
|
|
|
|
+ <Radio.Button onClick={() => {
|
|
|
|
|
+ handleRedioClick('flexib')
|
|
|
|
|
+ }} value='flexib'>发散</Radio.Button>
|
|
|
|
|
+ <Radio.Button value='custom'
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ setIsVisibleCus(!isVisibleCus);
|
|
|
|
|
+ setTopPValue(0.1);
|
|
|
|
|
+ setTempValue(0.01);
|
|
|
|
|
+ }}
|
|
|
|
|
+ >自定义
|
|
|
|
|
+ </Radio.Button>
|
|
|
|
|
+ </Radio.Group>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
|
+ alignItems: 'center'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='展示引用知识'
|
|
|
|
|
+ name='show_recall_result'
|
|
|
|
|
+ className='form-control-width'>
|
|
|
|
|
+ <Switch onChange={onChangeShow} disabled={true} />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
|
+ alignItems: 'center'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='召回方式'
|
|
|
|
|
+ name='recall_method'
|
|
|
|
|
+ rules={[{ required: true, message: '召回方式不能为空' }]}>
|
|
|
|
|
+
|
|
|
|
|
+ <Radio.Group
|
|
|
|
|
+ style={style}
|
|
|
|
|
+ onChange={onChangeRecallMethod}
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ options={[
|
|
|
|
|
+ { value: 'embedding', label: '向量化检索' },
|
|
|
|
|
+ { value: 'keyword', label: '关键词检索' },
|
|
|
|
|
+ { value: 'mixed', label: '混合检索' },
|
|
|
|
|
+ ]}
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
|
+ alignItems: 'center'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <div className='section-title'>重排方式</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
|
+ alignItems: 'center'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='Rerank模型'
|
|
|
|
|
+ name='rerank_status'
|
|
|
|
|
+ valuePropName='checked'
|
|
|
|
|
+ className='form-control-width'
|
|
|
|
|
+ >
|
|
|
|
|
+ <Switch onChange={onChangeModel} disabled={true} />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {isVisibleRerank &&
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
|
+ alignItems: 'center'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='模型选择'
|
|
|
|
|
+ name='rerank_model_name'
|
|
|
|
|
+ >
|
|
|
|
|
+ <Select
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ className='questionAnswerInfo-content-title'
|
|
|
|
|
+ placeholder='请选择模型'
|
|
|
|
|
+ // defaultValue={'rerank'}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Option value='rerank'>默认rerank模型</Option>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
|
+ alignItems: 'center'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='召回切片数量'
|
|
|
|
|
+ name='slice_config_type'
|
|
|
|
|
+ rules={[{ required: true, message: '召回方式不能为空' }]}>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ className='questionAnswerInfo-content-title'
|
|
|
|
|
+ placeholder='请选择'
|
|
|
|
|
+ onChange={onChangeCount}>
|
|
|
|
|
+ <Option value="fixed">手动设置</Option>
|
|
|
|
|
+ <Option value="customized">自动设置</Option>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ {isVisibleSlice &&
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
|
+ alignItems: 'center'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='召回切片数'
|
|
|
|
|
+ name='slice_count'
|
|
|
|
|
+ rules={[{ required: true, message: '切片数不能为空' }]}>
|
|
|
|
|
+ <InputNumber max={1024} changeOnWheel disabled={true}
|
|
|
|
|
+ className='questionAnswerInfo-content-title' />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
|
+ alignItems: 'center'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='召回切片拼接方式'
|
|
|
|
|
+ name='recall_slice_splicing_method'
|
|
|
|
|
+ >
|
|
|
|
|
+ <TextArea
|
|
|
|
|
+ disabled={true}
|
|
|
|
|
+ rows={4}
|
|
|
|
|
+ className='form-textarea-large'
|
|
|
|
|
+ placeholder="请输入内容"
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {/* } */}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Splitter.Panel>
|
|
|
|
|
+ <Splitter.Panel defaultSize="30%">
|
|
|
|
|
+ <Chat appId={appId} />
|
|
|
|
|
+ </Splitter.Panel>
|
|
|
|
|
+ </Splitter>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Form>
|
|
|
|
|
+ </Spin>
|
|
|
|
|
+ </div >
|
|
|
|
|
+ );
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+export default observer(QuestionAnswerInfo);
|