| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370 |
- 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,
- Tag, Modal, Table,TablePaginationConfig,Drawer
- } from 'antd';
- import type { TableProps } from 'antd';
- import { PlusCircleOutlined, MinusCircleOutlined, ArrowLeftOutlined, InfoCircleOutlined,CloseCircleOutlined,LinkOutlined } from '@ant-design/icons';
- import { apis } from '@/apis';
- import router from '@/router';
- import LocalStorage from '@/LocalStorage';
- import Chat from '@/components/chat';
- import store from './store';
- import DrawerIndex from '@/pages/deepseek/knowledgeLib/detail/drawerIndex'
- const { TextArea } = Input;
- const FormItem = Form.Item;
- const { Option } = Select;
- const MAX_COUNT = 5;
- const Index = 1;
- const QuestionAnswerInfo: React.FC = () => {
- const { state,onChangePagination,onFetchUserListApi } = store;
- const { page,sourceData } = state;
- 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,
- createBy: string,
- }[];
- type AppTypeList = {
- label: string,
- value: string,
- children: {
- label: string,
- value: string,
- }[],
- }[];
- const tagRender = (props:any) => {
- const { label, value, closable, onClose } = props;
- return (
- <Tag
- color="blue"
- closable={closable}
- onClose={onClose} // 保留原有的删除事件
- style={{ marginRight: 4 }}
- >
- {label}
- {/* 在删除按钮后添加自定义图标 */}
- <LinkOutlined style={{
- marginLeft: 4,
- fontSize: 12,
- cursor: 'pointer',
- }}
- onMouseDown={(e) => {
- e.stopPropagation();
- e.preventDefault();
- }}
- onMouseUp={(e) => {
- e.stopPropagation();
- e.preventDefault();
- }}
- // 自定义图标点击事件
- onClick={(e) => {
- // 阻止事件冒泡到Tag,避免触发删除
- e.stopPropagation();
- e.preventDefault();
- knowledgeList.forEach((item) => {
- if (item.value === value) {
- // router.navigate({ pathname: `/deepseek/knowledgeLib/${value}/${item.createBy}`,},);
- setDrawerItem(item);
- setOpenDrawer(true)
- e.stopPropagation();
- }
- });
- // console.log('点击了额外图标,当前选项值:', value,props);
- // 这里可以添加你的业务逻辑,如:打开详情、编辑等
- }} />
- </Tag>
- );
- };
- 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(true);
- const [isVisibleCus, setIsVisibleCus] = React.useState(false);
- const [isVisibleSlice, setIsVisibleSlice] = React.useState(false);
- const [isVisibleRerank, setIsVisibleRerank] = React.useState(true);
- const [isDeepThinkVisible, setIsDeepThinkVisible] = React.useState(true);
- const [name, setName] = React.useState('');
- const [appTypeList, setAppTypeList] = React.useState<AppTypeList>([]);
- const [appVisibleList, setAppVisibleList] = React.useState<AppTypeList>([]); // 是否公开
- const [visibleFlag, setVisibleFlag] = React.useState<string|number>(0); // 是否公开用来判断是否展示VIP用户
- 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 [fetchUserTypeList, setFetchUserTypeList] = React.useState<AppTypeList>([]); // 用户类型
- const [userName, setUserName] = React.useState<string>(''); // 用户名
- const [userNickName, setUserNickName] = React.useState<string>(''); // 用户昵称
- const [userType, setUserType] = React.useState<string>(''); // 用户类型
- const [vipList, setVipList] = React.useState<any>([]); // 用户列表
- const style: React.CSSProperties = {
- display: 'flex',
- flexDirection: 'column',
- gap: 8,
- width: 300,
- };
- 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);
- }
- onFetchUserListApi('','','');
- await api.fetchUserType();
- }
- React.useEffect(() => {
- const id = location?.state?.id;
- 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 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(data_info.rerank_status) //模型
- }
- //召回切片数量
- 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);
- }
- 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?.join('-'),// 项目
- typeId: info.typeId, //应用类型
- visible: info.visible || '0', //是否公开
- sort: info.sort || null, //显示顺序
- 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: 'rerank', //模型名称
- 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
- })
- setVisibleFlag(info.visible || '0')
- if(info.vipList&&info.vipList.length>0){
- setVipList(info.vipList);
- }
- 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,
- createBy: item.createBy,
- }
- });
- 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');
- const list = res.data.map((item: any) => {
- return {
- label: item.dictLabel,
- value: item.dictValue,
- }
- });
- setAppVisibleList(list);
- if (!id) {
- form.setFieldsValue({
- visible: list[0]?.value
- });
- setVisibleFlag(list[0]?.value);
- }
- } catch (error: any) {
- console.error(error);
- }
- },
- // 获取用户类型
- fetchUserType: async () => {
- try {
- const res = await apis.fetchTakaiAppTypeList('sys_user_type');
- const list = res.data.map((item: any) => {
- return {
- label: item.dictLabel,
- value: item.dictValue,
- }
- });
- setFetchUserTypeList(list);
- } 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');
- console.log('res.data',res.data)
- const list: AppTypeList = res.data?.reduce((acc:any,item: any) => {
- if(item.children.length>0){
- item.children.forEach((val:any)=>{
- acc.push({
- label: val.label,
- value: `${item.value}-${val.value}`,
- })
- })
- }
- return acc;
- },[]);
- setAppProjectList(list);
- } catch (error: any) {
- console.error(error);
- }
- },
- // 获取用户列表信息
- fetchUserListApi: async () => {
- try {
- const res = await apis.fetchUserListApi({
- pageNum:page.pageNumber,
- pageSize: page.pageSize,
- userName:userName,
- nickName:userNickName,
- userType:userType
- });
- // setSourceData(res.rows)
- } catch (error) {
- 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);
- }
- }
- const saveConfig = (type: 'SAVE' | 'SUBMIT') => {
- form.validateFields().then(async (values) => {
- const data = values;
- console.log(values, 'values');
- // 问题列表
- const question: string[] = [];
- if (inputs) {
- inputs.map((item, index) => {
- question.push(item.value);
- });
- }
- console.log(question, 'question');
- interface Item {
- index_type_id: number,
- knowledge_id: string
- }
- const indexTypeList: Item[] = [];
- if (values.knowledge_ids && values.knowledge_ids.length > 0) {
- console.log("knowledge_ids", values.knowledge_ids);
- const index_type: Item = {
- index_type_id: 0,
- knowledge_id: values.knowledge_ids
- };
- indexTypeList.push(index_type);
- }
- const userInfo = LocalStorage.getUserInfo();
- const userId = (userInfo?.id ?? '').toString();
- const data_info = {
- param_desc: values.param_desc, //回答风格
- show_recall_result: values.show_recall_result, //是否展示召回结果
- recall_method: values.recall_method, //召回方式
- rerank_status: values.rerank_status, //开启rerank
- rerank_model_name: values.rerank_model_name, //模型名称
- slice_config_type: values.slice_config_type, // 召回切片数量
- slice_count: values.slice_count, // 切片数量
- recall_slice_splicing_method: values.recall_slice_splicing_method, // 切片内容
- rerank_index_type_list: values.rerank_status === true ? indexTypeList : [], //知识库id
- recall_index_type_list: values.recall_method === 'embedding' || 'mixed' ? indexTypeList : []
- // rerank_status = 1 rerank_index_type_list
- // recall_method = 'embedding' || 'embedding' recall_index_type_list
- };
- // const knowledgeIds: string[] = [];
- // knowledgeIds.push(values.knowledge_ids);
- const info = {
- id: values.id,
- name: values.name, //应用名称
- desc: values.desc, //应用描述
- prompt: values.prompt, //应用提示语
- top_p: topPValue.toString(), //topP
- temperature: tempValue.toString(), //温度
- knowledge_ids: values.knowledge_ids,
- slice_count: values.slice_count,
- model: values.model,
- isDeepThink: values.isDeepThink,
- icon_color: values.icon_color,
- icon_type: values.icon_type,
- questionList: question,
- knowledge_info: JSON.stringify(data_info),
- max_token: values.max_token, //应用最大token
- typeId: values.typeId, // 应用类型
- visible: values.visible, // 是否公开
- sort: values.sort||null, // 显示顺序
- vipList: vipList, // vip用户列表
- appProId: values?.appProId?.[0]?.split('-') ?? null,// 项目
- userId: userId, // 用户id
- };
- console.log(info, 'info data');
- const id = location?.state?.id;
- let res = null;
- if (id) {
- // 编辑应用
- res = await apis.modifyTakaiApplicationApi(id, info);
- } else {
- // 创建应用
- res = await await apis.createTakaiApplicationApi(info);
- }
- console.log(res, 'create or modify');
- if (res.data === 9) {
- message.error('没有配置审核人,请联系管理员');
- } else if (res.data === 1) {
- message.success('操作成功')
- } else {
- message.error('操作失败');
- }
- if (type === 'SUBMIT') {
- router.navigate({ pathname: '/deepseek/questionAnswer' });
- }
- }).catch((error) => {
- console.error(error);
- error.errorFields && error.errorFields.map((item: any) => {
- console.log(item, 'item');
- message.error(`字段 ${item.name} ${item.errors[0]}`);
- });
- });
- }
- /*
- 选择VIP用户弹窗start
- */
- const [isModalOpen, setIsModalOpen] = React.useState(false);
- let falgVipList:any = [];
- const handleOk = () => {
- setIsModalOpen(false);
- let vipListFalg:any = [...vipList];
- const vipIds = new Set(vipListFalg.map((vip:any) => vip.userId));
- const merged = [...vipListFalg];
- falgVipList.forEach((item:any) => {
- if (!vipIds.has(item.userId)) {
- merged.push(item);
- }
- });
- setVipList([...merged]);
- };
- const handleCancel = async () => {
- setIsModalOpen(false);
- };
- interface DataType {
- name: string;
- key: string;
- nickName: string;
- userName: string;
- deptName: string;
- userTypeName: string;
- }
- // const [sourceData, setSourceData] = React.useState<DataType[]>([]);
- const paginationConfig: TablePaginationConfig = {
- // 显示数据总量
- showTotal: (total: number) => {
- return `共 ${total} 条`;
- },
- // 展示分页条数切换
- showSizeChanger: false,
- // 指定每页显示条数
- // 快速跳转至某页
- showQuickJumper: false,
- current: page.pageNumber,
- pageSize: page.pageSize,
- total: page.total,
- onChange: async (page, pageSize) => {
- await onChangePagination(page, pageSize,userName,userNickName,userType);
- },
- };
- const vipModal = () => {
- const columns: TableProps<DataType>['columns'] = [
- {
- title: '昵称',
- dataIndex: 'nickName',
- render: (text) => <p>{text}</p>,
- },
- {
- title: '用户名称',
- dataIndex: 'userName',
- render: (text) => <p>{text}</p>,
- },
- {
- title: '部门',
- dataIndex: 'deptName',
- },
- {
- title: '用户类型',
- dataIndex: 'userTypeName',
- },
- ];
- const rowSelection: TableProps<DataType>['rowSelection'] = {
- type: 'checkbox',
- onChange: (selectedRowKeys: React.Key[], selectedRows: DataType[]) => {
- console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
- falgVipList = selectedRows
- },
- getCheckboxProps: (record: DataType) => ({
- disabled: record.name === 'Disabled User', // Column configuration not to be checked
- name: record.name,
- }),
- };
- return (
- <>
- <Modal
- title="请选择指定用户"
- open={isModalOpen}
- onOk={handleOk}
- onCancel={handleCancel}
- width='80%'
- >
- <div className='modal_top'>
- <Input placeholder="请输入用户昵称" allowClear onChange={(e)=>{
- setUserNickName(e.target.value)
- }} />
- <Input placeholder="请输入用户名称" allowClear onChange={(e)=>{
- setUserName(e.target.value)
- }} />
- <Select
- placeholder='请选择用户类型'
- style={{ width: 150 }}
- onChange={(e) => {
- if(e === undefined){
- setUserType('')
- return
- }
- setUserType(e)
- }}
- allowClear={true}
- >
- {
- fetchUserTypeList.map((item, index) => {
- return <Option value={item.value} key={index}>
- {item.label}
- </Option>
- })
- }
- </Select>
- <Button value="large" style={{
- background: 'transparent',
- border: '1px solid #1677ff',
- color: '#1677ff'
- }}
- onClick={() => { onFetchUserListApi(userName,userNickName,userType) }}
- > 搜索 </Button>
- {/* <Button value="large"
- onClick={() => { api.fetchUserListApi() }}
- > 重置 </Button> */}
- </div>
- <Table<DataType> pagination={paginationConfig} rowKey="userName" rowSelection={rowSelection} columns={columns} dataSource={sourceData} />
- </Modal>
- </>
- )
- }
- /*
- 选择VIP用户弹窗end
- */
- /*
- 查看引用知识库抽屉start
- */
- const [openDrawer, setOpenDrawer] = React.useState(false);
- const [drawerItem,setDrawerItem] = React.useState<any>({});
- const onCloseDrawer = () => {
- setOpenDrawer(false);
- }
- const DrawerDetail = ()=>{
- return (
- <Drawer
- title={drawerItem?.label}
- width={'80%'}
- closable={{ 'aria-label': 'Close Button' }}
- onClose={onCloseDrawer}
- open={openDrawer}
- style={{zIndex:11111}}
- >
- <DrawerIndex drawerItem={drawerItem}></DrawerIndex>
- </Drawer>
- )
- }
- /*
- 查看引用知识库抽屉end
- */
- return (
- <>
- <div className='questionAnswerInfo'>
- <Spin spinning={pageLoading}>
- <Form
- form={form}
- layout='vertical'
- initialValues={{
- isDeepThink: 'N',
- max_token: 4096,
- model:'Qwen3-30B',
- show_recall_result: true,
- rerank_model_name:'rerank',
- slice_config_type:'customized',
- rerank_status: true,
- param_desc:'strict',
- recall_method:'mixed'
- }}
- >
- <div style={{ display: step === 1 ? 'block' : 'none' }} className='questionAnswerInfo-content'>
- <FormItem
- label='问答应用名称'
- name='name'
- rules={[{ required: true, message: '问答应用名称不能为空' }]}
- >
- <Input placeholder="请输入问答应用名称" className='form-element-standard' style={{ height: '48px'}}/>
- </FormItem>
- <FormItem
- label='应用类型'
- name='typeId'
- >
- <Select
- className='form-element-select'
- style={{ height: '48px'}}
- placeholder='请选择问答应用类型'
- onChange={handleAppChange}
- allowClear={true}
- >
- {
- appTypeList.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
- options={appProjectList}
- placeholder="请选择项目"
- showSearch
- className="form-element-select"
- style={{ height: '48px'}}
- />
- </FormItem>
- </>
- }
- <FormItem
- label='是否公开'
- name='visible'
- >
- <Select
- className='form-element-select'
- style={{ height: '48px'}}
- placeholder='请选择是否公开'
- allowClear={true}
- onChange={(e) => {
- setVisibleFlag(e)
- }}
- >
- {
- appVisibleList.map((item, index) => {
- return <Option value={item.value} key={index}>
- {item.label}
- </Option>
- })
- }
- </Select>
- </FormItem>
- <FormItem
- label='显示顺序'
- name='sort'
- >
- <InputNumber placeholder="请输入显示顺序" value={''} className='form-element-standard' style={{ height: '48px',lineHeight:'48px'}}/>
- </FormItem>
- {/* VIP用户 */}
- {visibleFlag==1&&<FormItem
- label='指定用户'
- >
- <div className='tags-info'>
- <p className='tags-list'>
- {vipList.map((item: any) =>
- (<Tag key={item.userId} color="blue" closeIcon onClose={(e)=>{
- const newVipList = vipList.filter((vip:any) => vip.userId !== item.userId);
- setVipList(newVipList);
- e.preventDefault();
- }}>
- {item.userName}
- </Tag>)
- )}
- </p>
- <p>
- {vipList.length>0&&<CloseCircleOutlined className='cup' onClick={()=>{
- setVipList([]);
- }} />}
- <Button style={{
- background: 'transparent',
- border: '1px solid #1677ff',
- color: '#1677ff'
- }} type="primary" variant="outlined" onClick={() => { setIsModalOpen(true) }}>选择</Button>
- </p>
- </div>
- </FormItem>}
- <FormItem
- label='问答应用描述'
- name='desc'
- rules={[{ required: true, message: '问答应用描述不能为空' }]}
- >
- <TextArea
- 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
- 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
- className='btn-cancel'
- onClick={() => {
- router.navigate({ pathname: '/deepseek/questionAnswer' });
- }}
- >
- 返回
- </Button>
- <Button
- type='primary'
- 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'
- icon={<ArrowLeftOutlined />}
- onClick={() => {
- setStep(1);
- }}
- >
- 上一步
- </Button>
- </div>
- <div style={{ display: 'flex', gap: '12px' }}>
- <Button
- className='btn-cancel'
- onClick={() => {
- router.navigate({ pathname: '/deepseek/questionAnswer' });
- }}
- >
- 取消
- </Button>
- {
- appId && (<Button
- type='primary'
- className='btn-cancel'
- onClick={() => {
- saveConfig('SAVE');
- }}
- >
- 保存
- </Button>)
- }
- {createFlag && (
- <Button
- type='primary'
- onClick={() => {
- saveConfig('SUBMIT');
- }}
- >
- 提交应用
- </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="35%">
- <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,使用 CSS 边框替代 */}
- <div className='prompt-editor-area'>
- <FormItem name='prompt'
- initialValue={
- `你是一位知识检索助手,你必须并且只能从我发送的众多知识片段中寻找能够解决用户输入问题的最优答案,并且在执行任务的过程中严格执行规定的要求。 \n
- 知识片段如下:
- {{知识}}
- 规定要求:
- - 找到答案就仅使用知识片段中的原文回答用户的提问;- 找不到答案就用自身知识并且告诉用户该信息不是来自文档;
- - 所引用的文本片段中所包含的示意图占位符必须进行返回,占位符格式参考:【示意图序号_编号】
- - 严禁输出任何知识片段中不存在的示意图占位符;
- - 输出的内容必须删除其中包含的任何图注、序号等信息。例如:“进入登录页面(图1.1)”需要从文字中删除图序,回复效果为:“进入登录页面”;“如图所示1.1”,回复效果为:“如图所示”;
- 格式规范:
- - 文档中会出现包含表格的情况,表格是以图片标识符的形式呈现,表格中缺失数据时候返回空单元格;
- - 如果需要用到表格中的数据,以代码块语法输出表格中的数据;
- - 避免使用代码块语法回复信息;
- - 回复的开头语不要输出诸如:“我想”,“我认为”,“think”等相关语义的文本。
- 严格执行规定要求,不要复述问题,直接开始回答。
- 用户输入问题:
- {{用户}}`
- }
- rules={[{ required: true, message: '提示词不能为空' }]}>
- <TextArea
- placeholder="提示词"
- rows={50}
- />
- </FormItem>
- </div>
- </div>
- </Splitter.Panel>
- <Splitter.Panel defaultSize="30%">
- <div className='flex-center-container'>
- <div className='half-width'>
- <div className='flex-center-top'>
- 欢迎使用 {name || '问答应用'}
- </div>
- <div className='flex-start pl-20 padding-top-20'>
- <FormItem
- label='引用知识库'
- name='knowledge_ids'
- rules={[{ required: true, message: '知识库不能为空' }]}>
- <Select
- mode='multiple'
- maxCount={MAX_COUNT}
- showSearch={true}
- className='form-element-select'
- placeholder='请选择需要引用的知识库'
- tagRender={tagRender}
- >
- {
- knowledgeList.map((item, index) => {
- return <Option value={item.value} key={index}>
- {item.label}
- </Option>
- })
- }
- </Select>
- </FormItem>
- </div>
- <div className='flex-start pl-20'>
- <FormItem
- label='调用模型'
- name="model"
- rules={[{ required: true, message: '模型不能为空' }]}>
- <Select
- placeholder='请选择模型'
- allowClear={true}
- className='form-element-select'
- 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-start pl-20' style={{
- display: isDeepThinkVisible ? 'flex' : 'none'
- }}>
- <FormItem
- label='深度思考'
- name='isDeepThink'
- rules={[{ required: true, message: '请选择是否深度思考' }]}>
- <Radio.Group buttonStyle="solid" className='form-element-button-group'>
- <Radio.Button value='Y'>是</Radio.Button>
- <Radio.Button value='N'>否</Radio.Button>
- </Radio.Group>
- </FormItem>
- </div>
- <div className='flex-start pl-20'>
- <FormItem
- label='max token'
- name='max_token'
- rules={[{ required: true, message: 'max token不能为空' }]}>
- <InputNumber
- className='form-element-input-number'
- />
- </FormItem>
- </div>
- {
- !isVisible &&
- <div className='flex-start pl-20'>
- <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-start pl-20'>
- <FormItem
- label='Top-p'
- name='topP'
- className='form-element-standard'
- >
- <TopPDecimalStep />
- </FormItem>
- </div>
- <div className='flex-start pl-20'>
- <FormItem
- label='Temperature'
- name='temperature'
- className='form-element-standard'
- >
- <TempDecimalStep />
- </FormItem>
- </div>
- </Space>
- }
- <div style={{
- display: 'flex',
- justifyContent: 'flex-start',
- alignItems: 'center'
- }} className='pl-20'>
- <FormItem
- label='回答风格'
- name='param_desc'
- rules={[{ required: true, message: '回答风格不能为空' }]}>
- <Radio.Group buttonStyle="solid"
- className='form-element-button-group'>
- <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: 'flex-start',
- alignItems: 'center'
- }} className='pl-20'>
- <FormItem
- label='展示引用知识'
- name='show_recall_result'
- className='form-element-standard'>
- <Switch onChange={onChangeShow} />
- </FormItem>
- </div>
- <div style={{
- display: 'flex',
- justifyContent: 'flex-start',
- alignItems: 'center'
- }} className='pl-20'>
- <FormItem
- label='召回方式'
- name='recall_method'
- rules={[{ required: true, message: '召回方式不能为空' }]}>
- <Radio.Group
- style={style}
- onChange={onChangeRecallMethod}
- options={[
- { value: 'embedding', label: '向量化检索' },
- { value: 'keyword', label: '关键词检索' },
- { value: 'mixed', label: '混合检索' },
- ]}
- />
- </FormItem>
- </div>
- <div style={{
- display: 'flex',
- justifyContent: 'flex-start',
- alignItems: 'center'
- }} className='pl-20'>
- <div className='section-title'>重排方式</div>
- </div>
- <div style={{
- display: 'flex',
- justifyContent: 'flex-start',
- alignItems: 'center'
- }} className='pl-20'>
- <FormItem
- label='Rerank模型'
- name='rerank_status'
- valuePropName='checked'
- className='form-control-width'
- >
- <Switch onChange={onChangeModel} />
- </FormItem>
- </div>
- {isVisibleRerank &&
- <div style={{
- display: 'flex',
- justifyContent: 'flex-start',
- alignItems: 'center'
- }} className='pl-20'>
- <FormItem
- label='模型选择'
- name='rerank_model_name'
- >
- <Select
- className='questionAnswerInfo-content-title'
- placeholder='请选择模型'
- // defaultValue={'rerank'}
- >
- <Option value='rerank'>默认Rerank模型</Option>
- </Select>
- </FormItem>
- </div>
- }
- <div style={{
- display: 'flex',
- justifyContent: 'flex-start',
- alignItems: 'center'
- }} className='pl-20'>
- <FormItem
- label='召回切片数量'
- name='slice_config_type'
- rules={[{ required: true, message: '召回方式不能为空' }]}>
- <Select
- // className='questionAnswerInfo-content-title'
- className='form-element-select'
- placeholder='请选择'
- onChange={onChangeCount}>
- <Option value="fixed">手动设置</Option>
- <Option value="customized">自动设置</Option>
- </Select>
- </FormItem>
- </div>
- {isVisibleSlice &&
- <div style={{
- display: 'flex',
- justifyContent: 'flex-start',
- alignItems: 'center'
- }} className='pl-20'>
- <FormItem
- label='召回切片数'
- name='slice_count'
- rules={[{ required: true, message: '切片数不能为空' }]}>
- <InputNumber max={1024} changeOnWheel
- // className='questionAnswerInfo-content-title'
- className='form-element-standard'
- />
- </FormItem>
- </div>
- }
- <div style={{
- display: 'flex',
- justifyContent: 'flex-start',
- alignItems: 'center'
- }} className='pl-20'>
- <FormItem
- label='召回切片拼接方式'
- name='recall_slice_splicing_method'
- >
- <TextArea
- rows={4}
- className='form-textarea-large'
- placeholder="请输入内容"
- />
- </FormItem>
- </div>
- </div>
- {/* } */}
- </div>
- </div>
- </Splitter.Panel>
- {appId && (<Splitter.Panel defaultSize="35%">
- <Chat appId={appId} />
- </Splitter.Panel>)}
- </Splitter>
- </div>
- </Form>
- </Spin>
- </div >
- {isModalOpen && vipModal()}
- {DrawerDetail()}
- </>
- );
- };
- export default observer(QuestionAnswerInfo);
|