import * as React from 'react'; import { observer } from 'mobx-react'; import { List, Button, Divider, Flex, Layout, Empty, Image, Modal, Tag, message, Tooltip, Select, Form, Space, Row, Col, Input, Cascader, Card, Spin } from 'antd'; import { PlusOutlined, FileOutlined, SettingOutlined, DeleteOutlined, StepForwardOutlined, SearchOutlined, ReloadOutlined, BookOutlined, TeamOutlined, AppstoreOutlined, EditOutlined, CloseOutlined, BulbOutlined, UpOutlined, DownOutlined } from '@ant-design/icons'; import { apis } from '@/apis'; import './style.less'; import { PaginationConfig } from 'antd/es/pagination'; import router from '@/router'; import LocalStorage from '@/LocalStorage'; import { create } from 'domain'; import audit from '../../audit'; import { set } from 'mobx'; import IconSvg from "@/assets/public/icon.svg"; import dayjs from 'dayjs'; import UpdateNotification from '@/help/components/UpdateNotification'; import { CURRENT_VERSION } from '@/help/components/UpdateNotification/version'; import { useLocation } from 'react-router-dom'; const { Header, Footer, Sider, Content } = Layout; const { Option } = Select; const FormItem = Form.Item; const headerStyle: React.CSSProperties = { textAlign: 'center', height: 24, paddingInline: 48, lineHeight: '30px', backgroundColor: '#fff', }; const contentStyle: React.CSSProperties = { textAlign: 'center', lineHeight: '40px', backgroundColor: '#fff', }; const siderStyle: React.CSSProperties = { paddingLeft: 30, paddingTop: 30, height: 80, backgroundColor: '#fff', }; const footerStyle: React.CSSProperties = { textAlign: 'center', color: '#fff', height: 24, backgroundColor: '#4096ff', }; const layoutStyle = { borderRadius: 8, overflow: 'hidden', width: 'calc(10% - 8px)', maxWidth: 'calc(20% - 8px)', }; const QuestionAnswerList: React.FC = () => { const [form] = Form.useForm(); const location = useLocation(); interface Item { name: string, desc: string, appId: number, createBy: string, typeId: string; status: string; comment: string; auditStatus: string; projectName: string; updateTime: string; }; interface PageInfo { pageNumber: number, pageSize: number, total: number, }; type AppTypeList = { label: string, value: string, }[]; type ProjectTypeList = { label: string, value: string, }[]; const [listLoading, setListLoading] = React.useState(false); const [list, setList] = React.useState([]); const [page, setPage] = React.useState({ pageNumber: 1, pageSize: 10, total: 0, }); const [appCount, setAppCount] = React.useState(); const [knowCount, setKnowCount] = React.useState(); const { Header, Footer, Sider, Content } = Layout; const [appTypeList, setAppTypeList] = React.useState([]); const [createFlag, setCreateFlag] = React.useState(false); const [deleteFlag, setDeleteFlag] = React.useState(false); const [updateFlag, setUpdateFlag] = React.useState(false); const [userInfoAll, setUserInfoAll] = React.useState({}); const [projectList, setProjectList] = React.useState([]); const [appProjectList, setAppProjectList] = React.useState([]); const [showSubPanel, setShowSubPanel] = React.useState(false); const [selectedType, setSelectedType] = React.useState('全部'); const wrapperRef = React.useRef(null); const selectRef = React.useRef(null); const [levelTypeList, setLevelTypeList] = React.useState([]); // 新手引导整体可见性(持久化到 localStorage) const [showGuide, setShowGuide] = React.useState(() => localStorage.getItem('appGuideHidden') !== 'true'); // 新手引导展开/折叠状态(持久化到 localStorage),默认展开 const [isGuideExpanded, setIsGuideExpanded] = React.useState(() => { const saved = localStorage.getItem('appGuideExpanded'); // 如果 localStorage 中没有值,默认展开(返回 true) return saved === null ? true : saved === 'true'; }); const toggleGuide = () => { setIsGuideExpanded(prev => { const newValue = !prev; // 保存到 localStorage localStorage.setItem('appGuideExpanded', String(newValue)); return newValue; }); }; // 标记是否正在重置,避免触发 useEffect 循环 const isResettingRef = React.useRef(false); // 标记是否正在分页切换,避免触发 useEffect 循环 const isPaginatingRef = React.useRef(false); // 搜索输入框展开状态 const [isSearchExpanded, setIsSearchExpanded] = React.useState(false); const searchWrapperRef = React.useRef(null); const searchInputRef = React.useRef(null); const appApi = { fetchList: async (typeId: any, projectId: any, forceRefresh: boolean = false, pageNumber?: number, name?: string) => { const currentPageNumber = pageNumber !== undefined ? pageNumber : page.pageNumber; const searchName = name !== undefined ? name : (form.getFieldValue('keyword') || ''); setListLoading(true); try { const userInfo = LocalStorage.getUserInfo(); const userId = (userInfo?.id ?? '').toString(); const res = await apis.fetchTakaiAppList({ pageSize: page.pageSize, pageNumber: currentPageNumber, userId: userId, typeId: typeId, projectId: projectId?.toString(), keyword: '', name: searchName, // 传递 name 参数给后端 }) const list = res.rows.map((item: any) => { return { name: item.name, desc: item.desc, appId: item.appId, createBy: item.createBy, typeId: item.typeId, status: item.status, comment: item.comment, auditStatus: item.auditStatus, projectName: item.projectName, updateTime: item.updateTime } }); const c = LocalStorage.getStatusFlag('deepseek:application:create'); const u = LocalStorage.getStatusFlag('deepseek:application:delete'); const filteredList = list.filter((item: any) => { // 如果有 createFlag 或 updateFlag 权限,显示所有数据 if (c || u) { return true; } // 没有权限时排除 status='5' 的数据 return item.status !== '5'; }); setList(filteredList); setPage({ pageNumber: currentPageNumber, pageSize: page.pageSize, total: res.total, }); } catch (error) { console.error(error); } finally { setListLoading(false); } }, auditApplication: async (appId: string, userId: string) => { const res = await apis.auditTakaiApplicationLibApi(appId, userId); if (res.data === 9) { message.error('您没有添加审核人'); } await appApi.fetchList(null, null); } }; // 立即使用应用 const useNowAppLication = (appId: number) => { const userInfo = LocalStorage.getUserInfo(); const getToken = LocalStorage.getToken(); // const baseUrl = import.meta.env.VITE_JUMP_URL; const baseUrl = 'https://llm.jkec.info:11432/#/knowledgeChat?showMenu=false&chatMode=LOCAL' window.open(`${baseUrl}&appId=${appId}&userId=${userInfo?.id}&nickName=${userInfo?.name}&token=${getToken}`, '_blank'); } // 删除应用 const delApplication = async (appId: string) => { try { await apis.deleteTakaiApplicationApi(appId); await appApi.fetchList(null, null); } catch (error) { console.error(error); } } const indexApi = { fetchIndex: async (typeId: any, projectId: any, pageNumber?: number) => { try { const userInfo = LocalStorage.getUserInfo(); const userId = (userInfo?.id ?? '').toString(); const keyword = form.getFieldValue('keyword'); const currentPageNumber = pageNumber !== undefined ? pageNumber : page.pageNumber; const res = await apis.fetchTakaiIndexCount({ pageSize: page.pageSize, pageNumber: currentPageNumber, userId: userId, typeId: typeId, projectId: projectId?.toString(), keyword: keyword, }) setAppCount(res.data.applicationCount); setKnowCount(res.data.knowledgeCount); } catch (error) { console.error(error); } finally { setListLoading(false); } } }; // 获取应用类型 const appTypeApi = { fetchAppType: async () => { try { // 1. 先请求应用列表,获取项目名称数据 const userInfo = LocalStorage.getUserInfo(); const userId = (userInfo?.id ?? '1').toString(); const appListRes = await apis.fetchTakaiAppList({ keyword: '', name: '', pageNumber: 1, pageSize: 1000, typeId: '41', userId: userId, projectId: null }); // 提取所有的 projectName 并去重 const projectNames = appListRes.rows.map((item: any) => item.projectName).filter(Boolean); const uniqueProjectNames = new Set(projectNames); // 2. 请求应用类型列表 const res = await apis.fetchTakaiAppTypeList('app_type'); // 3. 对比数据,保留所有类型,但标记是否有对应的项目数据 const list = res.data.map((item: any) => { return { label: item.dictLabel, value: item.dictCode, } }); // 不过滤项目级应用,始终显示所有类型 setAppTypeList(list); } catch (error: any) { console.error(error); } }, }; // 项目级应用下的类型 const appProTypeApi = { fetchAppProType: async () => { try { const res = await apis.fetchTakaiAppTypeList('projectTree'); // const list: AppTypeList = 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: `${val.value}`, }) }) } return acc; }, []); setAppProjectList(list); // const res = await apis.fetchTakaiAppTypeList('projectTree'); // const list = res.data.map((item: any) => { // return { // label: item.dictLabel, // value: item.dictCode, // } // }); // setAppProjectList(list); } catch (error: any) { console.error(error); } }, }; const projectApi = { fetchProject: async () => { try { const res = await apis.fetchTakaiProjectLibApi(); const list = res.data.map((item: any) => { return { label: item.projectName, value: item.projectId, } }); setProjectList(list); } catch (error: any) { console.error(error); } }, }; // 获取应用类型 const levelTypeApi = { fetchLevelAppType: async () => { try { const res = await apis.fetchTakaiAppTypeList('project_type'); const list = res.data.map((item: any) => { return { label: item.dictLabel, value: item.dictCode, } }); setLevelTypeList(list); } catch (error: any) { console.error(error); } }, }; const init = async () => { await appTypeApi.fetchAppType(); await projectApi.fetchProject(); await appProTypeApi.fetchAppProType(); await levelTypeApi.fetchLevelAppType(); // 检查是否从导航跳转过来,需要选中"项目级应用" const shouldSelectProjectApp = location.state?.selectProjectApp; if (!shouldSelectProjectApp) { // 默认加载全部数据 await appApi.fetchList(null, null, true); // 强制刷新 await indexApi.fetchIndex(null, null); // 设置默认选择"全部" form.setFieldsValue({ typeId: '全部' }); } // 如果需要选中项目级应用,会在 useEffect 中处理 } React.useEffect(() => { // 如果正在重置,跳过执行,避免循环 if (isResettingRef.current) { isResettingRef.current = false; return; } // 如果正在分页切换,跳过执行,避免重复请求 if (isPaginatingRef.current) { isPaginatingRef.current = false; return; } setCreateFlag(LocalStorage.getStatusFlag('deepseek:application:create')); setDeleteFlag(LocalStorage.getStatusFlag('deepseek:application:delete')); setUpdateFlag(LocalStorage.getStatusFlag('deepseek:application:update')); setUserInfoAll(LocalStorage.getUserInfo()); init(); }, [page.pageSize, page.pageNumber]) // 监听 appTypeList 变化,处理从导航跳转过来选中"项目级应用"的逻辑 React.useEffect(() => { const shouldSelectProjectApp = location.state?.selectProjectApp; if (shouldSelectProjectApp && appTypeList.length > 0) { // 找到"项目级应用"的配置 const projectAppType = appTypeList.find(item => item.label === '项目级应用'); if (projectAppType) { const projectAppValue = projectAppType.value; setSelectedType(projectAppValue); form.setFieldsValue({ typeId: projectAppValue }); // 如果是项目级应用(value为'41'),显示子面板 if (projectAppValue === '41') { setShowSubPanel(true); } // 加载对应的数据 appApi.fetchList(projectAppValue, null, true); indexApi.fetchIndex(projectAppValue, null); // 清除 location.state,避免重复触发 window.history.replaceState({}, document.title); } } }, [appTypeList, location.state]) // 监听 selectedType 变化,通知 Header 组件更新选中状态 React.useEffect(() => { if (appTypeList.length > 0) { const projectAppType = appTypeList.find(item => item.label === '项目级应用'); const isProjectApp = projectAppType && selectedType === projectAppType.value; // 触发自定义事件通知 Header 组件 const event = new CustomEvent('projectAppActiveChange', { detail: { isActive: isProjectApp } }); window.dispatchEvent(event); } }, [selectedType, appTypeList]) const paginationConfig: PaginationConfig = { // 显示数据总量 showTotal: (total: number) => { return `共 ${total} 条`; }, // 展示分页条数切换 showSizeChanger: true, // 指定每页显示条数 // pageSizeOptions: ['2', '20', '50', '100'], // 快速跳转至某页 showQuickJumper: true, current: page.pageNumber, pageSize: page.pageSize, total: page.total, onChange: (pageNumber, pageSize) => { const keyword = form.getFieldValue('keyword'); const typeId = form.getFieldValue('proTypeId') || form.getFieldValue('typeId'); let projectId = form.getFieldValue('projectId'); // 处理类型ID const finalTypeId = typeId === '全部' ? null : typeId; // 处理项目ID if (projectId instanceof Array && projectId.length === 2) { projectId = projectId[1]; } // 标记正在分页切换,避免触发 useEffect isPaginatingRef.current = true; // 更新页码和页大小 setPage({ pageNumber: pageNumber, pageSize: pageSize, total: page.total, }); // 调用接口获取新页数据 appApi.fetchList(finalTypeId, projectId, true, pageNumber, keyword || ''); }, }; // 点击查询 const handleClickSearch = async () => { form.validateFields().then(async (values) => { const keyword = values.keyword; // 处理类型ID if (values.proTypeId) { values.typeId = values.proTypeId; } if (values.typeId === '全部') { values.typeId = null; } if (values.projectId instanceof Array && values.projectId.length == 2) { values.projectId = values.projectId[1]; } // 重置到第一页 setPage(prev => ({ ...prev, pageNumber: 1 })); await indexApi.fetchIndex(values.typeId, values.projectId); // 使用接口搜索,传递 name 参数 await appApi.fetchList(values.typeId, values.projectId, true, 1, keyword || ''); }).catch((error) => { console.error(error); }); }; // 点击重置 const handleClickReset = async () => { form.resetFields(); setShowSubPanel(false); setSelectedType('全部'); // 重置为"全部" // 标记正在重置,避免触发 useEffect 循环 isResettingRef.current = true; // 先更新 page 到第一页 setPage({ pageNumber: 1, pageSize: 10, total: 0, }); // 调用接口获取数据,传递 pageNumber: 1 确保返回第一页 await appApi.fetchList(null, null, true, 1); // 强制刷新,第一页 await indexApi.fetchIndex(null, null, 1); // 第一页 }; /** 点击外部关闭面板 */ React.useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) { setShowSubPanel(false); } }; document.addEventListener('mousedown', handleClickOutside, true); return () => { document.removeEventListener('mousedown', handleClickOutside, true); }; }, []); /** 点击外部关闭搜索框 */ React.useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (searchWrapperRef.current && !searchWrapperRef.current.contains(event.target as Node)) { setIsSearchExpanded(false); } }; if (isSearchExpanded) { document.addEventListener('mousedown', handleClickOutside, true); return () => { document.removeEventListener('mousedown', handleClickOutside, true); }; } }, [isSearchExpanded]); // 展开搜索框并聚焦输入框 const handleExpandSearch = () => { setIsSearchExpanded(true); // 延迟聚焦,确保输入框已渲染 setTimeout(() => { const input = searchInputRef.current?.input || searchInputRef.current; if (input) { input.focus(); input.select(); // 选中输入框中的文本 } }, 150); }; const handleAppTypeChange = (value: string) => { if (value === '41') { // 如果是项目级应用,切换面板状态 // setShowSubPanel(prev => !prev); setShowSubPanel(true); } else { // 其他选项,隐藏面板 setShowSubPanel(false); } setSelectedType(value); form.setFieldsValue({ typeId: value }); // 获取当前搜索关键字 const keyword = form.getFieldValue('keyword') || ''; // 自动提交逻辑 if (value === '全部') { // 全部选项,传递null给后端 appApi.fetchList(null, null, true, 1, keyword); // 强制刷新 indexApi.fetchIndex(null, null); } else { // 其他选项,传递对应的typeId appApi.fetchList(value, null, true, 1, keyword); // 强制刷新 indexApi.fetchIndex(value, null); } }; const handleAppProTypeChange = (value: string) => { console.log(value, 'valuevalue'); setSelectedType(value); form.setFieldsValue({ typeId: value }); }; return (
{/* 更新通知弹窗 - 只在此页面显示 */}
{/* 主选择器 - 修改为按钮组形式 */}
{/* 全部按钮 */} {/* 动态应用类型按钮 */} {appTypeList.map(item => { // 根据label匹配对应的图标 let icon = null; const isSelected = selectedType === item.value; if (item.label === '专业知识') { icon = ; } else if (item.label === '职能管理') { icon = ; } else if (item.label === '项目级应用') { icon = ; } return ( ); })}
{/* 子选项面板 */} {showSubPanel && selectedType === '41' && ( )}
{/* { appProjectList.map((subItem, index) => (
{subItem.label}
)) } */ } {appTypeList.find(item => item.value === selectedType)?.label === '项目级应用' && ( (
{menus}
)} onChange={(value) => { // 项目选择器自动提交逻辑 const currentProTypeId = form.getFieldValue('proTypeId'); const keyword = form.getFieldValue('keyword') || ''; let projectId: any = value; // 如果是数组且长度为2,取第二个元素(项目ID) if (projectId instanceof Array && projectId.length === 2) { projectId = projectId[1]; } appApi.fetchList(currentProTypeId, projectId, true, 1, keyword); // 强制刷新 indexApi.fetchIndex(currentProTypeId, projectId); }} />
)} {/* {*/} {/* // 项目选择器自动提交逻辑*/} {/* const currentTypeId = form.getFieldValue('typeId');*/} {/* const typeId = currentTypeId === '全部' ? null : currentTypeId;*/} {/* appApi.fetchList(typeId, value);*/} {/* indexApi.fetchIndex(typeId, value);*/} {/* }}*/} {/*>*/} {/* {*/} {/* projectList.map((item, index) => {*/} {/* return */} {/* })*/} {/* }*/} {/**/} {/* */}
setIsSearchExpanded(true)} />
) } */} {/* 创建按钮已移至面包屑组件 */}
{/* 新手使用提示区块 */} {showGuide && (
提示:如何创建并发布自己的RAG(增强检索生成)应用?
{isGuideExpanded ? : }
{isGuideExpanded && ( 创建或选用公共知识库} >
选择公共知识库,或点击知识库菜单栏,创建自己的知识库。
step 1
参数配置} >
点击右上角,创建应用,选择模型与知识范围,配置Prompt、检索策略并调试。
step 2
发布应用} >
发布后可在相应的项目内使用。
step 3
)}
)} { listLoading ? (
) : list.length ? (
{/*
*/} {/* */} {/* */} {/* */} {/* */} {/* */} {/* */} {/*
问答应用总数
*/} {/* { appCount }个*/} {/*
*/} {/*
*/} {/* */} {/* */} {/* */} {/* */} {/* */} {/*
知识库总数
*/} {/* { knowCount } 个*/} {/*
*/} {/*
*/} {/*
*/} {/*
*/} {/*
*/} {/*
所有问答应用
*/} {/* /!*{*!/*/} {/* /!* createFlag &&*!/*/} {/* /!* *!/*/} {/* /!*}*!/*/} {/*
*/}
(
{/*
*/} {/* { item.name }*/} {/*
*/}
{item.name.length > 20 ? `${item.name.substring(0, 30)}...` : item.name}
ID:{item.appId} {item.projectName} {/**/} {/* {*/} {/* item.projectName && item.projectName.length > 8 ?*/} {/* `${item.projectName.substring(0, 8)}...` :*/} {/* item.projectName*/} {/* }*/} {/**/}
<> {/*{ item.projectName } 移动599 line*/} { (item.status !== null && item.status !== '3') && < Tag style={{ // marginLeft: 16, width: 65, color: '#fff', height: 24, backgroundColor: item.status === '1' ? '#D26900' : item.status === '2' ? '#408080' : item.auditStatus === '4' ? '#CE0000' : item.status === '5' ? '#5151A2' : '' }}> {item.status === '1' ? '待审核' : item.status === '2' ? '审核中' : item.auditStatus === '4' ? '审核拒绝' : item.status === '5' ? '待提交' : '未知'} } { (item.auditStatus === '4') && { item.comment !== '' && item.comment !== null && item.comment.length > 10 ? item.comment.substring(0, 10) + '......' : item.comment } }
{ item.desc !== '' && item.desc !== null && item.desc.length > 40 ? item.desc.substring(0, 40) + '......' : item.desc }
更新时间: {dayjs(item.updateTime).format("YYYY-MM-DD HH:mm:ss")}
{ (item.status === '5' || item.status === '4' || item.status === '3' || item.status === '' || item.status === null) && <> { (updateFlag || userInfoAll.id === item.createBy) && { router.navigate({ pathname: '/deepseek/questionAnswer/modify' }, { state: { id: item.appId } }); }}> 编辑 } { (deleteFlag || userInfoAll.id === item.createBy) && { Modal.confirm({ title: '删除', content: `确定删除应用: ` + item.name + ` 吗?`, okType: 'danger', onOk: async () => { await delApplication(item.appId.toString()); } }); }}> 删除 } } { createFlag && item.status === '5' && { Modal.confirm({ title: '提交审核', content: `确认提交审核应用: ` + item.name + `吗?`, okType: 'danger', onOk: async () => { const userInfo = LocalStorage.getUserInfo(); const userId = (userInfo?.id ?? '').toString(); appApi.auditApplication(item.appId.toString(), userId); } }); }}> 提交审核 }
{ appTypeList .find(item1 => item1.value.toString() === item.typeId)?.label || levelTypeList.find(item2 => item2.value.toString() === item.typeId)?.label || '未分类' }
)} pagination={paginationConfig} // 分页 />
) : (
{/* { createFlag && } */}
) } ) }; export default observer(QuestionAnswerList);