Files
bishengWeb/src/pages/FileLibPage/files.tsx
zhangkai 94be64311c 1
2024-09-03 11:12:51 +08:00

278 lines
13 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Link, useParams } from "react-router-dom";
import { Button } from "../../components/ui/button";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow
} from "../../components/ui/table";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "../../components/ui/tabs";
import { ArrowLeft, Filter, RotateCw, Search, X } from "lucide-react";
import { useContext, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { bsconfirm } from "../../alerts/confirm";
import PaginationComponent from "../../components/PaginationComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import { Input } from "../../components/ui/input";
import { Select, SelectContent, SelectGroup, SelectIconTrigger, SelectItem } from "../../components/ui/select1";
import { locationContext } from "../../contexts/locationContext";
import { deleteFile, readFileByLibDatabase, retryKnowledgeFileApi } from "../../controllers/API";
import { captureAndAlertRequestErrorHoc } from "../../controllers/request";
import UploadModal from "../../modals/UploadModal";
import { useTable } from "../../util/hook";
import del from "../../assets/npc/del.png"
export default function FilesPage() {
const { t } = useTranslation()
const { id } = useParams()
// 上传 上传成功添加到列表
const [open, setOpen] = useState(false)
const [title, setTitle] = useState('')
const { page, pageSize, data: datalist, total, setPage, search, reload, filterData, refreshData,loadData } = useTable({}, (param) =>
readFileByLibDatabase({ ...param, id, name: param.keyword }).then(res => {
setHasPermission(res.writeable)
return res
})
)
// loadData();
setTimeout(() => reload(), 5000);
const [hasPermission, setHasPermission] = useState(true)
const { appConfig } = useContext(locationContext)
// filter
const [filter, setFilter] = useState(999)
useEffect(() => {
filterData({ status: filter })
}, [filter])
useEffect(() => {
// @ts-ignore
const libname = window.libname // 临时记忆
if (libname) {
localStorage.setItem('libname', window.libname)
}
setTitle(window.libname || localStorage.getItem('libname'))
}, [])
const handleOpen = (e) => {
setOpen(e)
reload()
}
// 删除
const { delShow, idRef, close, delConfim } = useDelete()
const handleDelete = () => {
captureAndAlertRequestErrorHoc(deleteFile(idRef.current).then(res => {
reload()
close()
}))
}
const [repeatFiles, setRepeatFiles] = useState([])
// 上传结果展示
const handleUploadResult = (fileCount, failFiles, res) => {
const _repeatFiles = res.filter(e => e.status === 3)
if (_repeatFiles.length) {
setRepeatFiles(_repeatFiles)
} else {
failFiles.length && bsconfirm({
desc: <div>
<p>{t('lib.fileUploadResult', { total: fileCount, failed: failFiles.length })}</p>
<div className="max-h-[160px] overflow-y-auto no-scrollbar">
{failFiles.map(str => <p className=" text-red-400" key={str}>{str}</p>)}
</div>
</div>,
onOk(next) {
next()
}
})
}
}
// 重试解析
const [retryLoad, setRetryLoad] = useState(false)
const handleRetry = (objs) => {
setRetryLoad(true)
captureAndAlertRequestErrorHoc(retryKnowledgeFileApi(objs).then(res => {
// 乐观更新
// refreshData(
// (item) => ids.includes(item.id),
// { status: 1 }
// )
reload()
setRepeatFiles([])
setRetryLoad(false)
}))
}
const selectChange = (id) => {
setFilter(Number(id))
}
return <div className="w-full h-screen p-6 relative overflow-y-auto">
{/* {loading && <div className="absolute w-full h-full top-0 left-0 flex justify-center items-center z-10 bg-[rgba(255,255,255,0.6)] dark:bg-blur-shared">
<span className="loading loading-infinity loading-lg"></span>
</div>} */}
<ShadTooltip content="back" side="top">
<Link to='/filelib'>
<button className="extra-side-bar-buttons w-[36px] absolute top-[26px]" onClick={() => { }} >
<ArrowLeft className="side-bar-button-size" />
</button>
</Link>
</ShadTooltip>
<Tabs defaultValue="account" className="w-full">
{/* <TabsList className="ml-12">
<TabsTrigger value="account" className="roundedrounded-xl">{t('lib.fileList')}</TabsTrigger>
<TabsTrigger disabled value="password">{t('lib.systemIntegration')}</TabsTrigger>
</TabsList> */}
<div className="flex justify-between">
<p className="text-[16px] ml-[40px]" style={{color:"#FFF"}}>{t('lib.fileData')}</p>
<div className="flex justify-center items-center w-[74px] h-[27px] cursor-pointer" onClick={() => setOpen(true)} style={{background: "#FFD025",borderRadius: "7px"}}>
{/* <img src={jia1} className="w-[14px] mr-[5px]" alt=""/> */}
<span className="text-[12px]" style={{color:"#333333"}}> </span>
</div>
</div>
<TabsContent value="account">
{/* <div className="flex justify-between items-center">
<span className=" text-gray-800">{title}</span>
<div className="flex gap-4 items-center">
<div className="w-[180px] relative">
<Input placeholder={t('lib.fileName')} onChange={(e) => search(e.target.value)}></Input>
<Search className="absolute right-4 top-2 text-gray-300 pointer-events-none"></Search>
</div>
{hasPermission && <Button className="h-8 rounded-full" onClick={() => setOpen(true)}>{t('lib.upload')}</Button>}
</div>
</div> */}
<Table>
<TableCaption>
<div className="join grid grid-cols-2 w-[200px]">
<PaginationComponent
page={page}
pageSize={pageSize}
total={total}
onChange={(newPage) => setPage(newPage)}
/>
</div>
</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[600px] dialogueLog-header">{t('lib.fileName')}</TableHead>
{/* 状态 */}
<TableHead className="flex items-center gap-4 dialogueLog-header">{t('lib.status')}
{/* Select component */}
<Select onValueChange={selectChange}>
<SelectIconTrigger className="">
<Filter size={16} className={`cursor-pointer ${filter === 999 ? '' : 'text-gray-950'}`} />
</SelectIconTrigger>
<SelectContent className="">
<SelectGroup>
<SelectItem value={'999'}>{t('all')}</SelectItem>
<SelectItem value={'1'}>{t('lib.parsing')}</SelectItem>
<SelectItem value={'2'}>{t('lib.completed')}</SelectItem>
<SelectItem value={'3'}>{t('lib.parseFailed')}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</TableHead>
<TableHead className=" dialogueLog-header">{t('lib.uploadTime')}</TableHead>
<TableHead className=" dialogueLog-header">{t('operations')}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{datalist.map(el => (
<TableRow key={el.id}>
<TableCell className="dialogueLog-body">{el.file_name}</TableCell>
<TableCell className="dialogueLog-body">
{el.status === 3 ? <div className="flex items-center">
<div className="tooltip" data-tip={el.remark}>
<span className='text-red-500'>{t('lib.parseFailed')}</span>
</div>
<Button variant="link"><RotateCw size={16} onClick={() => handleRetry([el])} /></Button>
</div> :
<span className={el.status === 3 && 'text-red-500'}>{[t('lib.parseFailed'), t('lib.parsing'), t('lib.completed'), t('lib.parseFailed')][el.status]}</span>
}
</TableCell>
<TableCell className="dialogueLog-body">{el.update_time.replace('T', ' ')}</TableCell>
<TableCell className="text-right dialogueLog-body">
{hasPermission ?
// <a href="javascript:;" onClick={() => delConfim(el.id)} className="underline ml-4">{t('delete')}</a>
<img src={del} onClick={() => delConfim(el.id)} className="w-[20px] cursor-pointer" alt=""/>
:
// <a href="javascript:;" className="underline ml-4 text-gray-400">{t('delete')}</a>
<img src={del} className="w-[20px] cursor-pointer" alt=""/>
}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{/* Pagination */}
</TabsContent>
<TabsContent value="password"></TabsContent>
</Tabs>
{/* upload modal */}
<UploadModal id={id} accept={appConfig.libAccepts} open={open} setOpen={handleOpen} onResult={handleUploadResult}></UploadModal>
{/* 重复文件提醒 */}
<dialog className={`modal ${repeatFiles.length && 'modal-open'}`}>
<div className="modal-box w-[560px] bg-[#262626] shadow-lg">
<h3 className="font-bold text-lg relative text-[#fff]">
<X className="absolute right-0 top-0 text-[#fff] cursor-pointer" size={20} onClick={() => setRepeatFiles([])}></X>
</h3>
<p className="py-4 text-[#fff]"></p>
<ul className="overflow-y-auto max-h-[400px]">
{repeatFiles.map(el => (
<li key={el.id} className="py-2 text-red-500">{el.remark}</li>
))}
</ul>
<div className="modal-action">
<Button className="h-8 rounded-full" variant="outline" onClick={() => setRepeatFiles([])}></Button>
<Button className="h-8 rounded-full bg-[#FFD025] hover:bg-[#FFD025]" disabled={retryLoad} onClick={() => handleRetry(repeatFiles)}>
{retryLoad && <span className="loading loading-spinner loading-xs"></span>}
</Button>
</div>
</div>
</dialog>
{/* Delete confirmation */}
<dialog className={`modal ${delShow && 'modal-open'}`}>
<form method="dialog" className="modal-box w-[400px] bg-[#262626] shadow-lg">
<h3 className="text-[16px] font-bold text-center" style={{color:"#FFFFFF"}}>{t('prompt')}</h3>
<p className="text-[12px] text-center mt-[18px]" style={{color:"#FFFFFF"}}>{t('lib.confirmDeleteFile')}</p>
<div className="flex justify-center mt-[27px]">
<Button className="baogao-btn" variant="outline" onClick={close}>{t('cancel')}</Button>
<Button className="baogao-btn ml-[27px]" variant="destructive" onClick={handleDelete}>{t('delete')}</Button>
</div>
</form>
</dialog>
</div>
};
const useDelete = () => {
const [delShow, setDelShow] = useState(false)
const idRef = useRef<any>(null)
return {
delShow,
idRef,
close: () => {
setDelShow(false)
},
delConfim: (id) => {
idRef.current = id
setDelShow(true)
}
}
}