| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407 |
- 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,
- isDeepThink: 'N',
- 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: btoa(unescape(encodeURIComponent(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,
- isDeepThink: 'N',
- 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: Array.isArray(values?.appProId) ? values.appProId?.[0]?.split('-') : values?.appProId?.split('-')?? null, // 项目
- userId: userId, // 用户id
- };
- // console.log(info, 'info data----');
- const id = location?.state?.id;
- let res = null;
- try {
- if (id) {
- // 编辑应用
- res = await apis.modifyTakaiApplicationApi(id, info);
- } else {
- // 创建应用
- res = await apis.createTakaiApplicationApi(info);
- }
- console.log(res, 'create or modify');
- if (res.data === 9) {
- message.error('没有配置审核人,请联系管理员');
- } else if (res.data === 1) {
- message.success('操作成功')
- if (type === 'SUBMIT') {
- router.navigate({ pathname: '/deepseek/questionAnswer' });
- }
- } else {
- message.error('操作失败');
- }
- } catch (error: any) {
- // 检查是否是403错误
- if (error?.response?.status === 403) {
- message.error('您提交的信息内容非法,访问已被防火墙拒绝');
- } else {
- console.error('提交失败:', error);
- // 其他错误会由axios拦截器处理,这里不再重复显示错误信息
- }
- }
- }).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-vl-chat',
- 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={
- `你是一位基于DeepSeek和Qwen的预训练模型,是结合公司多种领域的专业知识训练的AI文档处理专家。你必须并且只能从我发送的众多知识片段中请进行归纳总结并生成对话式回答,并且在执行任务的过程中严格执行规定的要求。
- 知识片段如下:
- {{知识}}
- 规定要求:
- 一、 核心回答原则
- 你的回答必须基于上方提供的知识片段进行归纳总结,并以对话形式呈现。
- 如果在知识片段中找不到答案,你可以运用自身知识回答,但必须明确告知用户“此信息未在知识库中明确记载,仅供参考”。
- 如果用户问题模糊,你必须根据知识片段内容,主动询问用户可能想问的具体问题。例如,用户提问“标准”,你可以回复“您是否想了解‘高处作业安全’相关的法规与标准?”
- 二、 信息来源引用规则(针对URL)
- 回答中涉及的每一个事实或结论,只要来源于某个知识片段,就必须在其后明确标注该片段的下载链接。
- 引用格式:在相关句子或段落的结尾,使用方括号数字上标进行标记,例如“这是某个规定或描述[1]”。然后在回答的最后,单独列出所有引用来源。
- 来源列表格式:另起一行,写“信息来源:”,然后按引用顺序列出编号和对应的完整下载链接。例如:
- 信息来源:
- [1] http://****.****.com/share?code=TDTATFG0
- [2] http://***.****.info/share?code=h5PBNUQA
- 严禁编造、修改或混淆下载链接,必须直接使用知识片段中提供的原始链接。
- 三、 示意图占位符处理规则
- 必须按固定格式返回示意图占位符,例如【示意图序号a29375108162406318082】。
- 返回占位符时,必须同时带上知识片段中描述该占位符的原文。例如,片段中有“功能介绍:【示意图序号a29375108162406318082】”,你应回复“功能介绍:【示意图序号a29375108162406318082】”。
- 严禁输出知识片段中不存在的占位符。
- 从回答内容中删除所有图注、序号等信息,如“(图1.1)”应删除。
- 四、LaTeX公式处理规则
- 公式代码保护:知识片段中如出现以美元符号包裹(例如 公式或 双美元符号包裹)或其他数学标记的LaTeX公式代码,你必须将这些代码视为纯文本并完整地、一字不差地输出在你的回答中。
- 禁止修改:严禁对任何公式代码进行修改、转义、截断、简化或使用自然语言进行解释。你的目标是确保这些代码块在传递至前端时,能与原始知识片段中的内容完全一致。
- 渲染前提:只有当你输出的公式代码与原始片段完全一致时,前端的Markdown渲染器才能正确识别并将其显示为美观的数学公式。任何微小的改动都可能导致公式渲染失败。
- 五、 格式与内容规范
- 文档中的表格以图片标识符呈现,若表格数据缺失则返回空单元格。
- 如需使用表格数据,以markdown格式输出。
- 回复开头避免使用“我想”、“我认为”等词语。
- 回答中若出现网页链接,务必在链接后换行。
- 注意区分不同系统的概念,如“掌监APP”和“慧项管平台”不能混淆。
- 注意“模型”或“大模型”与“机器人”是不同的概念。
- 请严格执行以上要求,不要复述用户问题,直接开始回答。
- 用户输入问题:
- {{用户}}`
- }
- 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(false);
- // } else {
- setIsDeepThinkVisible(false);
- // }
- }}
- >
- <Option value='Qwen3-30B-vl-chat'>Qwen3-30B-vl-chat</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);
|