index.tsx 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  1. import * as React from 'react';
  2. import { observer } from 'mobx-react';
  3. import {
  4. List,
  5. Button,
  6. Divider,
  7. Flex,
  8. Layout,
  9. Empty,
  10. Image,
  11. Modal,
  12. Tag,
  13. message,
  14. Tooltip,
  15. Select,
  16. Form,
  17. Space,
  18. Row,
  19. Col,
  20. Input, Cascader, Card
  21. } from 'antd';
  22. import {
  23. PlusOutlined,
  24. FileOutlined,
  25. SettingOutlined,
  26. DeleteOutlined,
  27. StepForwardOutlined,
  28. SearchOutlined,
  29. ReloadOutlined,
  30. BookOutlined,
  31. TeamOutlined,
  32. AppstoreOutlined,
  33. EditOutlined, CloseOutlined, BulbOutlined
  34. } from '@ant-design/icons';
  35. import { apis } from '@/apis';
  36. import './style.less';
  37. import { PaginationConfig } from 'antd/es/pagination';
  38. import router from '@/router';
  39. import LocalStorage from '@/LocalStorage';
  40. import { create } from 'domain';
  41. import audit from '../../audit';
  42. import { set } from 'mobx';
  43. import IconSvg from "@/assets/public/icon.svg";
  44. import dayjs from 'dayjs';
  45. const { Header, Footer, Sider, Content } = Layout;
  46. const { Option } = Select;
  47. const FormItem = Form.Item;
  48. const headerStyle: React.CSSProperties = {
  49. textAlign: 'center',
  50. height: 24,
  51. paddingInline: 48,
  52. lineHeight: '30px',
  53. backgroundColor: '#fff',
  54. };
  55. const contentStyle: React.CSSProperties = {
  56. textAlign: 'center',
  57. lineHeight: '40px',
  58. backgroundColor: '#fff',
  59. };
  60. const siderStyle: React.CSSProperties = {
  61. paddingLeft: 30,
  62. paddingTop: 30,
  63. height: 80,
  64. backgroundColor: '#fff',
  65. };
  66. const footerStyle: React.CSSProperties = {
  67. textAlign: 'center',
  68. color: '#fff',
  69. height: 24,
  70. backgroundColor: '#4096ff',
  71. };
  72. const layoutStyle = {
  73. borderRadius: 8,
  74. overflow: 'hidden',
  75. width: 'calc(10% - 8px)',
  76. maxWidth: 'calc(20% - 8px)',
  77. };
  78. const QuestionAnswerList: React.FC = () => {
  79. const [form] = Form.useForm();
  80. interface Item {
  81. name: string,
  82. desc: string,
  83. appId: number,
  84. createBy: string,
  85. typeId: string;
  86. status: string;
  87. comment: string;
  88. auditStatus: string;
  89. projectName: string;
  90. updateTime: string;
  91. };
  92. interface PageInfo {
  93. pageNumber: number,
  94. pageSize: number,
  95. total: number,
  96. };
  97. type AppTypeList = {
  98. label: string,
  99. value: string,
  100. }[];
  101. type ProjectTypeList = {
  102. label: string,
  103. value: string,
  104. }[];
  105. const [listLoading, setListLoading] = React.useState(false);
  106. const [list, setList] = React.useState<Item[]>([]);
  107. const [page, setPage] = React.useState<PageInfo>({
  108. pageNumber: 1,
  109. pageSize: 10,
  110. total: 0,
  111. });
  112. const [appCount, setAppCount] = React.useState<string>();
  113. const [knowCount, setKnowCount] = React.useState<string>();
  114. const { Header, Footer, Sider, Content } = Layout;
  115. const [appTypeList, setAppTypeList] = React.useState<AppTypeList>([]);
  116. const [createFlag, setCreateFlag] = React.useState(false);
  117. const [deleteFlag, setDeleteFlag] = React.useState(false);
  118. const [updateFlag, setUpdateFlag] = React.useState(false);
  119. const [userInfoAll, setUserInfoAll] = React.useState<any>({});
  120. const [projectList, setProjectList] = React.useState<ProjectTypeList>([]);
  121. const [appProjectList, setAppProjectList] = React.useState<AppTypeList>([]);
  122. const [showSubPanel, setShowSubPanel] = React.useState(false);
  123. const [selectedType, setSelectedType] = React.useState<string | null>('全部');
  124. const wrapperRef = React.useRef<HTMLDivElement>(null);
  125. const selectRef = React.useRef<any>(null);
  126. const [levelTypeList, setLevelTypeList] = React.useState<AppTypeList>([]);
  127. // 新手引导整体可见性(持久化到 localStorage)
  128. const [showGuide, setShowGuide] = React.useState<boolean>(() => localStorage.getItem('appGuideHidden') !== 'true');
  129. const hideGuide = () => { localStorage.setItem('appGuideHidden', 'true'); setShowGuide(false); };
  130. const appApi = {
  131. fetchList: async (typeId: any, projectId: any) => {
  132. setListLoading(true);
  133. try {
  134. const userInfo = LocalStorage.getUserInfo();
  135. const userId = (userInfo?.id ?? '').toString();
  136. const keyword = form.getFieldValue('keyword');
  137. const res = await apis.fetchTakaiAppList({
  138. pageSize: page.pageSize,
  139. pageNumber: page.pageNumber,
  140. userId: userId,
  141. typeId: typeId,
  142. projectId: projectId?.toString(),
  143. keyword: keyword,
  144. })
  145. const list = res.rows.map((item: any) => {
  146. return {
  147. name: item.name,
  148. desc: item.desc,
  149. appId: item.appId,
  150. createBy: item.createBy,
  151. typeId: item.typeId,
  152. status: item.status,
  153. comment: item.comment,
  154. auditStatus: item.auditStatus,
  155. projectName: item.projectName,
  156. updateTime: item.updateTime
  157. }
  158. });
  159. const c = LocalStorage.getStatusFlag('deepseek:application:create');
  160. const u = LocalStorage.getStatusFlag('deepseek:application:delete');
  161. const filteredList = list.filter((item: any) => {
  162. // 如果有 createFlag 或 updateFlag 权限,显示所有数据
  163. if (c || u) {
  164. return true;
  165. }
  166. // 没有权限时排除 status='5' 的数据
  167. return item.status !== '5';
  168. });
  169. setList(filteredList);
  170. setPage({
  171. pageNumber: page.pageNumber,
  172. pageSize: page.pageSize,
  173. total: res.total,
  174. });
  175. } catch (error) {
  176. console.error(error);
  177. } finally {
  178. setListLoading(false);
  179. }
  180. },
  181. auditApplication: async (appId: string, userId: string) => {
  182. const res = await apis.auditTakaiApplicationLibApi(appId, userId);
  183. if (res.data === 9) {
  184. message.error('您没有添加审核人');
  185. }
  186. await appApi.fetchList(null, null);
  187. }
  188. };
  189. // 立即使用应用
  190. const useNowAppLication = (appId: number) => {
  191. const userInfo = LocalStorage.getUserInfo();
  192. const getToken = LocalStorage.getToken();
  193. // const baseUrl = import.meta.env.VITE_JUMP_URL;
  194. const baseUrl = 'https://llm.jkec.info:11432/#/knowledgeChat?showMenu=false&chatMode=LOCAL'
  195. window.open(`${baseUrl}&appId=${appId}&userId=${userInfo?.id}&nickName=${userInfo?.name}&token=${getToken}`, '_blank');
  196. }
  197. // 删除应用
  198. const delApplication = async (appId: string) => {
  199. try {
  200. await apis.deleteTakaiApplicationApi(appId);
  201. await appApi.fetchList(null, null);
  202. } catch (error) {
  203. console.error(error);
  204. }
  205. }
  206. const indexApi = {
  207. fetchIndex: async (typeId: any, projectId: any) => {
  208. try {
  209. const userInfo = LocalStorage.getUserInfo();
  210. const userId = (userInfo?.id ?? '').toString();
  211. const keyword = form.getFieldValue('keyword');
  212. const res = await apis.fetchTakaiIndexCount({
  213. pageSize: page.pageSize,
  214. pageNumber: page.pageNumber,
  215. userId: userId,
  216. typeId: typeId,
  217. projectId: projectId?.toString(),
  218. keyword: keyword,
  219. })
  220. setAppCount(res.data.applicationCount);
  221. setKnowCount(res.data.knowledgeCount);
  222. } catch (error) {
  223. console.error(error);
  224. } finally {
  225. setListLoading(false);
  226. }
  227. }
  228. };
  229. // 获取应用类型
  230. const appTypeApi = {
  231. fetchAppType: async () => {
  232. try {
  233. const res = await apis.fetchTakaiAppTypeList('app_type');
  234. const list = res.data.map((item: any) => {
  235. return {
  236. label: item.dictLabel,
  237. value: item.dictCode,
  238. }
  239. });
  240. setAppTypeList(list);
  241. } catch (error: any) {
  242. console.error(error);
  243. }
  244. },
  245. };
  246. // 项目级应用下的类型
  247. const appProTypeApi = {
  248. fetchAppProType: async () => {
  249. try {
  250. const res = await apis.fetchTakaiAppTypeList('projectTree');
  251. // const list: AppTypeList = res.data;
  252. const list: AppTypeList = res.data?.reduce((acc: any, item: any) => {
  253. if (item.children.length > 0) {
  254. item.children.forEach((val: any) => {
  255. acc.push({
  256. label: val.label,
  257. value: `${val.value}`,
  258. })
  259. })
  260. }
  261. return acc;
  262. }, []);
  263. setAppProjectList(list);
  264. // const res = await apis.fetchTakaiAppTypeList('projectTree');
  265. // const list = res.data.map((item: any) => {
  266. // return {
  267. // label: item.dictLabel,
  268. // value: item.dictCode,
  269. // }
  270. // });
  271. // setAppProjectList(list);
  272. } catch (error: any) {
  273. console.error(error);
  274. }
  275. },
  276. };
  277. const projectApi = {
  278. fetchProject: async () => {
  279. try {
  280. const res = await apis.fetchTakaiProjectLibApi();
  281. const list = res.data.map((item: any) => {
  282. return {
  283. label: item.projectName,
  284. value: item.projectId,
  285. }
  286. });
  287. setProjectList(list);
  288. } catch (error: any) {
  289. console.error(error);
  290. }
  291. },
  292. };
  293. // 获取应用类型
  294. const levelTypeApi = {
  295. fetchLevelAppType: async () => {
  296. try {
  297. const res = await apis.fetchTakaiAppTypeList('project_type');
  298. const list = res.data.map((item: any) => {
  299. return {
  300. label: item.dictLabel,
  301. value: item.dictCode,
  302. }
  303. });
  304. setLevelTypeList(list);
  305. } catch (error: any) {
  306. console.error(error);
  307. }
  308. },
  309. };
  310. const init = async () => {
  311. await appApi.fetchList(null, null);
  312. await indexApi.fetchIndex(null, null);
  313. await appTypeApi.fetchAppType();
  314. await projectApi.fetchProject();
  315. await appProTypeApi.fetchAppProType();
  316. await levelTypeApi.fetchLevelAppType();
  317. // 设置默认选择"全部"
  318. form.setFieldsValue({ typeId: '全部' });
  319. }
  320. React.useEffect(() => {
  321. setCreateFlag(LocalStorage.getStatusFlag('deepseek:application:create'));
  322. setDeleteFlag(LocalStorage.getStatusFlag('deepseek:application:delete'));
  323. setUpdateFlag(LocalStorage.getStatusFlag('deepseek:application:update'));
  324. setUserInfoAll(LocalStorage.getUserInfo());
  325. init();
  326. }, [page.pageSize, page.pageNumber])
  327. const paginationConfig: PaginationConfig = {
  328. // 显示数据总量
  329. showTotal: (total: number) => {
  330. return `共 ${total} 条`;
  331. },
  332. // 展示分页条数切换
  333. showSizeChanger: true,
  334. // 指定每页显示条数
  335. // pageSizeOptions: ['2', '20', '50', '100'],
  336. // 快速跳转至某页
  337. showQuickJumper: true,
  338. current: page.pageNumber,
  339. pageSize: page.pageSize,
  340. total: page.total,
  341. onChange: (pageNumber, pageSize) => {
  342. setPage({
  343. pageNumber: pageNumber,
  344. pageSize: pageSize,
  345. total: page.total,
  346. });
  347. },
  348. };
  349. // 点击查询
  350. const handleClickSearch = async () => {
  351. form.validateFields().then(async (values) => {
  352. if (values.proTypeId) {
  353. values.typeId = values.proTypeId;
  354. }
  355. if (values.typeId === '全部') {
  356. values.typeId = null;
  357. }
  358. if (values.projectId instanceof Array && values.projectId.length == 2) {
  359. values.projectId = values.projectId[1];
  360. }
  361. await indexApi.fetchIndex(values.typeId, values.projectId);
  362. await appApi.fetchList(values.typeId, values.projectId);
  363. }).catch((error) => {
  364. console.error(error);
  365. });
  366. };
  367. // 点击重置
  368. const handleClickReset = async () => {
  369. form.resetFields();
  370. setShowSubPanel(false);
  371. setSelectedType('全部'); // 重置为"全部"
  372. page.pageNumber = 1;
  373. page.pageSize = 10;
  374. await appApi.fetchList(null, null);
  375. await indexApi.fetchIndex(null, null);
  376. };
  377. /** 点击外部关闭面板 */
  378. React.useEffect(() => {
  379. const handleClickOutside = (event: MouseEvent) => {
  380. if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) {
  381. setShowSubPanel(false);
  382. }
  383. };
  384. document.addEventListener('mousedown', handleClickOutside, true);
  385. return () => {
  386. document.removeEventListener('mousedown', handleClickOutside, true);
  387. };
  388. }, []);
  389. const handleAppTypeChange = (value: string) => {
  390. if (value === '41') {
  391. // 如果是项目级应用,切换面板状态
  392. // setShowSubPanel(prev => !prev);
  393. setShowSubPanel(true);
  394. } else {
  395. // 其他选项,隐藏面板
  396. setShowSubPanel(false);
  397. }
  398. setSelectedType(value);
  399. form.setFieldsValue({ typeId: value });
  400. // 自动提交逻辑
  401. if (value === '全部') {
  402. // 全部选项,传递null给后端
  403. appApi.fetchList(null, null);
  404. indexApi.fetchIndex(null, null);
  405. } else {
  406. // 其他选项,传递对应的typeId
  407. appApi.fetchList(value, null);
  408. indexApi.fetchIndex(value, null);
  409. }
  410. };
  411. const handleAppProTypeChange = (value: string) => {
  412. console.log(value, 'valuevalue');
  413. setSelectedType(value);
  414. form.setFieldsValue({ typeId: value });
  415. };
  416. return (
  417. <div>
  418. <div style={{ padding: '16px 20px', display: 'flex' }}>
  419. <Form
  420. form={form}
  421. layout='inline'
  422. colon={false}
  423. style={{ flex: 1 }}
  424. >
  425. <div>
  426. {/* 主选择器 - 修改为按钮组形式 */}
  427. <FormItem
  428. name="typeId"
  429. style={{ marginBottom: 0 }}
  430. >
  431. <div className="filter-button-group">
  432. {/* 全部按钮 */}
  433. <Button
  434. key="全部"
  435. type={selectedType === '全部' ? 'primary' : 'default'}
  436. size="small"
  437. onClick={() => handleAppTypeChange('全部')}
  438. >
  439. 全部
  440. </Button>
  441. {/* 动态应用类型按钮 */}
  442. {appTypeList.map(item => {
  443. // 根据label匹配对应的图标
  444. let icon = null;
  445. const isSelected = selectedType === item.value;
  446. if (item.label === '专业知识') {
  447. icon = <BookOutlined style={{ fontSize: '14px', marginRight: '0' }} />;
  448. } else if (item.label === '职能管理') {
  449. icon = <TeamOutlined style={{ fontSize: '14px', marginRight: '0' }} />;
  450. } else if (item.label === '项目级应用') {
  451. icon = <AppstoreOutlined style={{ fontSize: '14px', marginRight: '0' }} />;
  452. }
  453. return (
  454. <Button
  455. key={item.value}
  456. type={isSelected ? 'primary' : 'default'}
  457. size="small"
  458. onClick={() => handleAppTypeChange(item.value)}
  459. >
  460. {icon}
  461. {item.label}
  462. </Button>
  463. );
  464. })}
  465. </div>
  466. </FormItem>
  467. {/* 子选项面板 */}
  468. {showSubPanel && selectedType === '41' && (
  469. <FormItem
  470. label='类型'
  471. name='proTypeId'
  472. rules={[{ required: true, message: '类型不能为空' }]}
  473. >
  474. <Select
  475. placeholder='请选择'
  476. allowClear
  477. // style={ { width: 200 } }
  478. onChange={(value) => {
  479. // 项目类型选择器自动提交逻辑
  480. const currentProjectId = form.getFieldValue('projectId');
  481. appApi.fetchList(value, currentProjectId);
  482. indexApi.fetchIndex(value, currentProjectId);
  483. }}
  484. >
  485. {
  486. appProjectList.map((item, index) => {
  487. return <Option value={item.value} key={index}>
  488. {item.label}
  489. </Option>
  490. })
  491. }
  492. </Select>
  493. </FormItem>
  494. )}
  495. </div>
  496. {/* {
  497. appProjectList.map((subItem, index) => (
  498. <div key={index}>
  499. {subItem.label}
  500. </div>
  501. ))
  502. } */ }
  503. <FormItem name='projectId'>
  504. <Cascader
  505. options={appProjectList}
  506. placeholder="请选择项目"
  507. showSearch
  508. />
  509. {/*<Select*/}
  510. {/* placeholder='请选择项目'*/}
  511. {/* allowClear*/}
  512. {/* onChange={(value) => {*/}
  513. {/* // 项目选择器自动提交逻辑*/}
  514. {/* const currentTypeId = form.getFieldValue('typeId');*/}
  515. {/* const typeId = currentTypeId === '全部' ? null : currentTypeId;*/}
  516. {/* appApi.fetchList(typeId, value);*/}
  517. {/* indexApi.fetchIndex(typeId, value);*/}
  518. {/* }}*/}
  519. {/*>*/}
  520. {/* {*/}
  521. {/* projectList.map((item, index) => {*/}
  522. {/* return <Option value={item.value} key={index}>*/}
  523. {/* {item.label}*/}
  524. {/* </Option>*/}
  525. {/* })*/}
  526. {/* }*/}
  527. {/*</Select>*/}
  528. </FormItem>
  529. {/*<FormItem name='keyword'>*/}
  530. {/* <Input placeholder='请输入关键字' allowClear />*/}
  531. {/*</FormItem>*/}
  532. <FormItem>
  533. <Space size={12}>
  534. <Tooltip title="重置">
  535. <Button
  536. shape="circle"
  537. icon={<ReloadOutlined />}
  538. onClick={handleClickReset}
  539. />
  540. </Tooltip>
  541. <Tooltip title="查询">
  542. <Button
  543. type='primary'
  544. shape="circle"
  545. onClick={handleClickSearch}
  546. icon={<SearchOutlined />}
  547. />
  548. </Tooltip>
  549. </Space>
  550. </FormItem>
  551. {/* {
  552. createFlag && (
  553. <div style={ { marginLeft: 'auto' } }>
  554. <Button type='primary'
  555. icon={ <PlusOutlined /> }
  556. onClick={ () => {
  557. router.navigate( { pathname: '/deepseek/questionAnswer/create' } );
  558. } }>创建问答应用</Button>
  559. </div>
  560. )
  561. }
  562. */}
  563. {/* 创建按钮已移至面包屑组件 */}
  564. </Form>
  565. </div>
  566. {/* 新手使用提示区块 */}
  567. {showGuide && (
  568. <div style={{ padding: '12px 20px 12px 20px' }}>
  569. <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
  570. <div style={{ fontSize: 13, color: '#333', display: 'flex', alignItems: 'center', gap: 6 }}>
  571. <BulbOutlined style={{ color: '#faad14' }} />
  572. 提示:如何创建并发布自己的RAG(增强检索生成)应用?
  573. </div>
  574. <CloseOutlined onClick={hideGuide} style={{ color: '#999', cursor: 'pointer' }} />
  575. </div>
  576. <Row gutter={12}>
  577. <Col xs={24} sm={24} md={12} lg={8}>
  578. <Card
  579. size="small"
  580. bordered={false}
  581. style={{
  582. background: 'linear-gradient(90deg, #e6f4ff 0%, #f0f7ff 100%)',
  583. boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
  584. borderRadius: 8,
  585. height: '100%'
  586. }}
  587. bodyStyle={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', minHeight: 80 }}
  588. title={<span style={{ fontWeight: 600 }}>创建或选用公共知识库</span>}
  589. >
  590. <div style={{ color: '#666', fontSize: 12 }}>选择公共知识库,或点击知识库菜单栏,创建自己的知识库。</div>
  591. <div style={{ textAlign: 'right', color: '#1677ff', fontWeight: 600 }}>step 1</div>
  592. </Card>
  593. </Col>
  594. <Col xs={24} sm={24} md={12} lg={8}>
  595. <Card
  596. size="small"
  597. bordered={false}
  598. style={{
  599. background: 'linear-gradient(90deg, #e6fffb 0%, #f0fffe 100%)',
  600. boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
  601. borderRadius: 8,
  602. height: '100%'
  603. }}
  604. bodyStyle={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', minHeight: 80 }}
  605. title={<span style={{ fontWeight: 600 }}>参数配置</span>}
  606. >
  607. <div style={{ color: '#666', fontSize: 12 }}>点击右上角,创建应用,选择模型与知识范围,配置Prompt、检索策略并调试。</div>
  608. <div style={{ textAlign: 'right', color: '#1677ff', fontWeight: 600 }}>step 2</div>
  609. </Card>
  610. </Col>
  611. <Col xs={24} sm={24} md={12} lg={8}>
  612. <Card
  613. size="small"
  614. bordered={false}
  615. style={{
  616. background: 'linear-gradient(90deg, #fff7e6 0%, #fffaf0 100%)',
  617. boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
  618. borderRadius: 8,
  619. height: '100%'
  620. }}
  621. bodyStyle={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', minHeight: 80 }}
  622. title={<span style={{ fontWeight: 600 }}>发布应用</span>}
  623. >
  624. <div style={{ color: '#666', fontSize: 12 }}>发布后可在相应的项目内使用。</div>
  625. <div style={{ textAlign: 'right', color: '#1677ff', fontWeight: 600 }}>step 3</div>
  626. </Card>
  627. </Col>
  628. </Row>
  629. </div>
  630. )}
  631. {
  632. list.length
  633. ?
  634. <div className='questionAnswerList'>
  635. {/*<div style={ { overflow: 'auto' } }>*/}
  636. {/* <Flex gap="middle" wrap>*/}
  637. {/* <Layout style={ layoutStyle }>*/}
  638. {/* <Sider width="25%" style={ siderStyle }>*/}
  639. {/* <FileOutlined />*/}
  640. {/* </Sider>*/}
  641. {/* <Layout>*/}
  642. {/* <Header style={ headerStyle }>问答应用总数</Header>*/}
  643. {/* <Content style={ contentStyle }>{ appCount }个</Content>*/}
  644. {/* </Layout>*/}
  645. {/* </Layout>*/}
  646. {/* <Layout style={ layoutStyle }>*/}
  647. {/* <Sider width="25%" style={ siderStyle }>*/}
  648. {/* <FileOutlined />*/}
  649. {/* </Sider>*/}
  650. {/* <Layout>*/}
  651. {/* <Header style={ headerStyle }>知识库总数</Header>*/}
  652. {/* <Content style={ contentStyle }>{ knowCount } 个</Content>*/}
  653. {/* </Layout>*/}
  654. {/* </Layout>*/}
  655. {/* </Flex>*/}
  656. {/*</div>*/}
  657. {/*<div style={ { display: 'flex', justifyContent: 'space-between', padding: '16px 20px' } }>*/}
  658. {/* <div>所有问答应用</div>*/}
  659. {/* /!*{*!/*/}
  660. {/* /!* createFlag &&*!/*/}
  661. {/* /!* <Button type='primary'*!/*/}
  662. {/* /!* icon={ <PlusOutlined /> }*!/*/}
  663. {/* /!* onClick={ () => {*!/*/}
  664. {/* /!* router.navigate( { pathname: '/deepseek/questionAnswer/create' } );*!/*/}
  665. {/* /!* } }>创建问答应用</Button>*!/*/}
  666. {/* /!*}*!/*/}
  667. {/*</div>*/}
  668. <div className='applicationList'>
  669. <List grid={{
  670. gutter: 16,
  671. xs: 1,
  672. sm: 1,
  673. md: 2,
  674. lg: 2,
  675. xl: 3,
  676. xxl: 4, // 展示的列数
  677. }}
  678. dataSource={list}
  679. renderItem={(item) => (
  680. <List.Item>
  681. <div className='card'>
  682. <div style={{
  683. display: 'flex',
  684. justifyContent: 'space-between',
  685. alignItems: 'center',
  686. overflow: 'hidden',
  687. }}>
  688. <div style={{ display: 'flex', alignItems: 'center', overflow: 'auto' }}>
  689. <div style={{ marginRight: 10, overflow: 'auto' }}>
  690. <Image
  691. width={32}
  692. height={32}
  693. src={IconSvg}
  694. preview={false}
  695. />
  696. </div>
  697. {/*<div style={ { overflow: 'auto' } }>*/}
  698. {/* { item.name }*/}
  699. {/*</div>*/}
  700. <div style={{
  701. display: 'flex',
  702. flexDirection: 'column',
  703. justifyContent: 'center',
  704. overflow: 'hidden',
  705. maxWidth: '85%',
  706. height: '100%'
  707. }}>
  708. <Tooltip title={item.name} placement="top">
  709. <div style={{
  710. lineHeight: '18px',
  711. fontSize: 14,
  712. fontWeight: 500,
  713. overflow: 'hidden',
  714. textOverflow: 'ellipsis',
  715. whiteSpace: 'nowrap',
  716. // maxWidth: '200px', // 可以根据需要调整宽度
  717. cursor: 'pointer'
  718. }}>
  719. {item.name.length > 20 ? `${item.name.substring(0, 30)}...` : item.name}
  720. </div>
  721. </Tooltip>
  722. <Space size={4} style={{ lineHeight: '18px' }}>
  723. <span style={{
  724. color: '#999',
  725. fontSize: 12,
  726. margin: 0
  727. }}>ID:{item.appId}</span>
  728. <Divider type="vertical" style={{ color: '999', margin: 0, height: 12 }} />
  729. <span
  730. style={ {
  731. color: '#999',
  732. fontSize: 12,
  733. display: 'inline-block',
  734. maxWidth: '100%', // 限制最大宽度
  735. flex: 1, // 使span占据剩余空间
  736. whiteSpace: 'nowrap', // 禁止换行
  737. overflow: 'hidden', // 隐藏溢出内容
  738. textOverflow: 'ellipsis', // 显示省略号
  739. verticalAlign: 'middle' // 垂直居中
  740. } }
  741. title={ item.projectName } // 鼠标悬停时显示完整名称
  742. >
  743. {item.projectName}
  744. </span>
  745. {/*<span style={{ color: '#999', fontSize: 12 }}>*/}
  746. {/* {*/}
  747. {/* item.projectName && item.projectName.length > 8 ?*/}
  748. {/* `${item.projectName.substring(0, 8)}...` :*/}
  749. {/* item.projectName*/}
  750. {/* }*/}
  751. {/*</span>*/}
  752. </Space>
  753. </div>
  754. </div>
  755. <div>
  756. <>
  757. {/*{ item.projectName } 移动599 line*/}
  758. {
  759. (item.status !== null && item.status !== '3') &&
  760. < Tag style={{
  761. // marginLeft: 16,
  762. width: 65,
  763. color: '#fff',
  764. height: 24,
  765. backgroundColor: item.status === '1' ? '#D26900' : item.status === '2' ? '#408080' : item.auditStatus === '4' ? '#CE0000' : item.status === '5' ? '#5151A2' : ''
  766. }}>
  767. {item.status === '1' ? '待审核' : item.status === '2' ? '审核中' : item.auditStatus === '4' ? '审核拒绝' : item.status === '5' ? '待提交' : '未知'}
  768. </Tag>
  769. }
  770. {
  771. (item.auditStatus === '4') &&
  772. <Tooltip title={item.comment}>
  773. {
  774. item.comment !== '' && item.comment !== null && item.comment.length > 10 ?
  775. item.comment.substring(0, 10) + '......' :
  776. item.comment
  777. }
  778. </Tooltip>
  779. }
  780. </>
  781. </div>
  782. </div>
  783. <Divider plain style={{ margin: '16px 0' }}></Divider>
  784. <div className='desc'>
  785. {
  786. item.desc !== '' && item.desc !== null && item.desc.length > 40 ? item.desc.substring(0, 40) + '......' : item.desc
  787. }
  788. </div>
  789. <div style={{
  790. display: 'flex',
  791. justifyContent: 'space-between',
  792. alignItems: 'center'
  793. }}>
  794. <span style={{
  795. color: '#999',
  796. fontSize: 12,
  797. margin: 0
  798. }}>更新时间: {dayjs(item.updateTime).format("YYYY-MM-DD HH:mm:ss")}</span>
  799. </div>
  800. <div style={{
  801. display: 'flex',
  802. justifyContent: 'space-between',
  803. alignItems: 'flex-end',
  804. paddingTop: 3,
  805. // overflow: 'auto',
  806. // paddingTop: 16,
  807. // height: '100%'
  808. }}>
  809. <div style={{
  810. overflow: 'auto'
  811. }}>
  812. {
  813. (item.status === '5' || item.status === '4' || item.status === '3' || item.status === '' || item.status === null) &&
  814. <>
  815. {
  816. (updateFlag || userInfoAll.id === item.createBy) &&
  817. <a
  818. className="action-button"
  819. style={{ marginRight: 16 }}
  820. onClick={() => {
  821. router.navigate({ pathname: '/deepseek/questionAnswer/modify' }, { state: { id: item.appId } });
  822. }}>
  823. <EditOutlined /> 编辑
  824. </a>
  825. }
  826. {
  827. (deleteFlag || userInfoAll.id === item.createBy) &&
  828. <a className='delete-button' onClick={() => {
  829. Modal.confirm({
  830. title: '删除',
  831. content: `确定删除应用: ` + item.name + ` 吗?`,
  832. okType: 'danger',
  833. onOk: async () => {
  834. await delApplication(item.appId.toString());
  835. }
  836. });
  837. }}>
  838. <DeleteOutlined /> 删除
  839. </a>
  840. }
  841. </>
  842. }
  843. {
  844. createFlag && item.status === '5' &&
  845. <a className="action-button" style={{ marginLeft: 16 }} onClick={() => {
  846. Modal.confirm({
  847. title: '提交审核',
  848. content: `确认提交审核应用: ` + item.name + `吗?`,
  849. okType: 'danger',
  850. onOk: async () => {
  851. const userInfo = LocalStorage.getUserInfo();
  852. const userId = (userInfo?.id ?? '').toString();
  853. appApi.auditApplication(item.appId.toString(), userId);
  854. }
  855. });
  856. }}>
  857. <StepForwardOutlined /> 提交审核
  858. </a>
  859. }
  860. <Button size='small' style={{
  861. background: 'transparent',
  862. border: '1px solid #1677ff',
  863. color: '#1677ff',
  864. marginTop: '2px',
  865. marginLeft: createFlag && item.status === '5' || (item.status === '5' || item.status === '4' || item.status === '3' || item.status === '' || item.status === null) && updateFlag||userInfoAll.id === item.createBy ? '10px' : 0,
  866. fontSize: 12,
  867. }} type="primary" variant="outlined" onClick={() => { useNowAppLication(item.appId) }}>立即使用</Button>
  868. </div>
  869. <div>
  870. <Tag
  871. style={{
  872. // padding: '4px 8px',
  873. marginRight: 0,
  874. fontSize: 12,
  875. fontWeight: 600,
  876. background: '#f8f8f8',
  877. // border: '1px solid #d9d9d9'
  878. }}
  879. >
  880. {
  881. appTypeList
  882. .find(item1 => item1.value.toString() === item.typeId)?.label || levelTypeList.find(item2 => item2.value.toString() === item.typeId)?.label || '未分类'
  883. }
  884. </Tag>
  885. </div>
  886. </div>
  887. </div>
  888. </List.Item>
  889. )}
  890. pagination={paginationConfig} // 分页
  891. />
  892. </div>
  893. </div>
  894. :
  895. <div>
  896. {/* {
  897. createFlag &&
  898. <Button type='primary'
  899. icon={ <PlusOutlined /> }
  900. onClick={ () => {
  901. router.navigate( { pathname: '/deepseek/questionAnswer/create' } );
  902. } }>创建问答应用</Button>
  903. } */}
  904. <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
  905. </div>
  906. }
  907. </div>
  908. )
  909. };
  910. export default observer(QuestionAnswerList);