项目目录

This commit is contained in:
WenC 2024-03-28 19:35:54 +08:00
parent c412ff3ec6
commit 953737294a
22 changed files with 993 additions and 144 deletions

View File

@ -24,13 +24,13 @@ export default function CardAgent(props: IAgent) {
props.handleSelect(); props.handleSelect();
}}> }}>
<div className="card-agent-head"> <div className="card-agent-head">
{props.logo ? <Image width={30} src={downloadUrl(props.logo.split(',')[0], false)} fallback={errorImage}/> : <></>} {props.logo ? <Image width={30} src={downloadUrl(props.logo.split(',')[0], false)} preview={false} fallback={errorImage}/> : <></>}
<span className="card-agent-title">{props.name}</span> <span className="card-agent-title">{props.name}</span>
{renderMedal()} {renderMedal()}
</div> </div>
<div className="card-agent-body"> <div className="card-agent-body">
<div className="card-agent-body-content" dangerouslySetInnerHTML={{__html: props.desc}}></div> <div className="card-agent-body-content" dangerouslySetInnerHTML={{__html: props.desc}}></div>
<Image width={140} height={100} src={props.certificateImg} fallback={errorImage}/> <Image width={140} height={100} src={props.certificateImg} preview={false} fallback={errorImage}/>
</div> </div>
</div> </div>
) )

View File

@ -70,7 +70,7 @@ export default function CardProj(props: { item: IProj }) {
} else { } else {
nav(`/proj-edit/config-mod-list/${data.projId}`) nav(`/proj-edit/config-mod-list/${data.projId}`)
} }
}}><SettingOutlined/> (0)</Button> }}><SettingOutlined/> ({data.projModCount})</Button>
<Button size="small" type="text" onClick={() => { <Button size="small" type="text" onClick={() => {
if(data.generate.generateStatus == GenerateStatus.SUCCESS) { if(data.generate.generateStatus == GenerateStatus.SUCCESS) {
nav(`/proj-edit/config-menu-list-show/${data.projId}`) nav(`/proj-edit/config-menu-list-show/${data.projId}`)
@ -78,7 +78,7 @@ export default function CardProj(props: { item: IProj }) {
nav(`/proj-edit/config-menu-list/${data.projId}`) nav(`/proj-edit/config-menu-list/${data.projId}`)
} }
}}><SettingOutlined/> (0)</Button> }}><SettingOutlined/> ({data.projModCount})</Button>
</div> </div>
{ {
data.generate.generateStatus == GenerateStatus.SUCCESS ? ( data.generate.generateStatus == GenerateStatus.SUCCESS ? (
@ -150,7 +150,19 @@ export default function CardProj(props: { item: IProj }) {
</div> </div>
</div> </div>
<div className="line"> <div className="line">
<div className="left"></div> <div className="left">
{
data.generate.generateStatus == GenerateStatus.SUCCESS ? (
<span>
<SearchOutlined/>
<a href="/#" onClick={(e) => {
e.preventDefault();
nav(`/agent-select/${data.projId}`);
}}></a>
</span>
) : <></>
}
</div>
<div className="right"> <div className="right">
<span> <span>
<FolderOutlined/> <FolderOutlined/>

View File

@ -1,10 +1,13 @@
import './card-proj-agent.css' import './card-proj-agent.css'
import {OrderedListOutlined, NumberOutlined, SearchOutlined} from "@ant-design/icons"; import {OrderedListOutlined, BarsOutlined, SearchOutlined} from "@ant-design/icons";
import {Tag} from 'antd'; import {Tag} from 'antd';
import {IAgent} from "../../interfaces/agent/IAgent.ts"; import {IAgent} from "../../interfaces/agent/IAgent.ts";
import {useNavigate} from "react-router-dom";
export default function CardProjAgent(props: IAgent) { export default function CardProjAgent(props: IAgent) {
const nav = useNavigate();
/** /**
* *
*/ */
@ -75,27 +78,45 @@ export default function CardProjAgent(props: IAgent) {
<div className="left"> <div className="left">
<span> <span>
<OrderedListOutlined/> <OrderedListOutlined/>
<a href="/#"></a> <a href="/#" onClick={(e) => {
e.preventDefault();
nav(`/agent-agreement/${props.orderId}`);
}}></a>
</span> </span>
<span> <span style={{marginLeft: '15px'}}>
<NumberOutlined /> <BarsOutlined />
<a href="/#">({props.materialAmendApplyCount})</a> <a href="/#" onClick={(e) => {
e.preventDefault();
nav(`/agent-correction/${props.orderId}`);
}}>({props.materialAmendApplyCount})</a>
</span> </span>
</div> </div>
{
props.isResult ? (
<div className="right"> <div className="right">
<span> <span>
<SearchOutlined/> <SearchOutlined/>
<a href="/#"></a> <a href="/#" onClick={(e) => {
e.preventDefault();
nav(`/agent-result/${props.orderId}`);
}}></a>
</span> </span>
</div> </div>
) : <></>
}
</div> </div>
</div> </div>
<hr/> <hr/>
<div className="tail"> <div className="tail">
<div className="tail-left">
<span className="status order-status">{renderTakeStatus()}</span> <span className="status order-status">{renderTakeStatus()}</span>
<span className="status agreement-status">{renderAgreementStatus()}</span> <span className="status agreement-status">{renderAgreementStatus()}</span>
</div>
<div className="tail-left">
<span className="status process-status">{renderOverStatus()}</span> <span className="status process-status">{renderOverStatus()}</span>
</div> </div>
</div> </div>
</div>
) )
} }

View File

@ -74,5 +74,12 @@
} }
.card-proj-agent .tail { .card-proj-agent .tail {
padding: 5px 0;
display: flex;
justify-content: space-between;
}
.card-proj-agent .tail .tail-left {
margin-right: 0;
} }

View File

@ -1,11 +1,12 @@
import './list-proj.css' import './list-proj.css'
import CardProj from "../card/CardProj.tsx"; import CardProj from "../card/CardProj.tsx";
import {useRef, MutableRefObject, useState, useEffect, useContext} from "react"; import {useRef, MutableRefObject, useState, useEffect, useContext} from "react";
import {Input, Pagination, message, Spin} from 'antd'; import {Input, Pagination, message, Spin, Tag, Image} from 'antd';
import {get} from "../../util/AjaxUtils.ts"; import {get} from "../../util/AjaxUtils.ts";
import {IndexListContext} from "../../context/IndexListContext.ts"; import {IndexListContext} from "../../context/IndexListContext.ts";
import {IListPage} from "../../interfaces/listpage/IListPage.ts"; import {IListPage} from "../../interfaces/listpage/IListPage.ts";
import {IProj} from "../../interfaces/proj/IProj.ts"; import {IProj} from "../../interfaces/proj/IProj.ts";
import NoData from "../../assets/no-data.png";
const {Search} = Input; const {Search} = Input;
@ -50,6 +51,14 @@ export default function ListProj() {
} }
const renderList = () => { const renderList = () => {
if(projs.length == 0) {
return (
<div className="no-data" style={{width: '100%', height: '100%', backgroundColor: 'var(--color-light)', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center'}}>
<Image src={NoData} preview={false}/>
<span></span>
</div>
)
}
return projs.map((item, index) => <CardProj item={item} key={`proj${index}`}/>); return projs.map((item, index) => <CardProj item={item} key={`proj${index}`}/>);
} }
@ -57,11 +66,23 @@ export default function ListProj() {
reqData(page); reqData(page);
}, [indexListContext.status, keywords, page]) }, [indexListContext.status, keywords, page])
const renderStatus = () => {
if(indexListContext.status == 'ALL') {
return <Tag color="blue"></Tag>
} else if(indexListContext.status == 'PROCESSING') {
return <Tag color="blue"></Tag>
} else if(indexListContext.status == 'COMPLETE') {
return <Tag color="blue"></Tag>
}
return <></>
}
return ( return (
<> <>
{contextHolder} {contextHolder}
<div className="list-proj" ref={listProjRef}> <div className="list-proj" ref={listProjRef}>
<div className="top"> <div className="top">
{renderStatus()}
<Search placeholder="按项目名搜索" onSearch={(value) => { <Search placeholder="按项目名搜索" onSearch={(value) => {
setKeywords(value) setKeywords(value)
}} style={{width: 200}}/> }} style={{width: 200}}/>

View File

@ -1,12 +1,13 @@
import './list-proj-agent.css'; import './list-proj-agent.css';
import CardProjAgent from "../card/CardProjAgent.tsx"; import CardProjAgent from "../card/CardProjAgent.tsx";
import {useRef, MutableRefObject, useEffect, useState, useContext} from "react"; import {useRef, MutableRefObject, useEffect, useState, useContext} from "react";
import {Input, Pagination, Spin} from 'antd'; import {Image, Input, Pagination, Spin, Tag} from 'antd';
import {get} from "../../util/AjaxUtils.ts"; import {get} from "../../util/AjaxUtils.ts";
import useMessage from "antd/es/message/useMessage"; import useMessage from "antd/es/message/useMessage";
import {IListPage} from "../../interfaces/listpage/IListPage.ts"; import {IListPage} from "../../interfaces/listpage/IListPage.ts";
import {IndexListContext} from "../../context/IndexListContext.ts"; import {IndexListContext} from "../../context/IndexListContext.ts";
import {IAgent} from "../../interfaces/agent/IAgent.ts"; import {IAgent} from "../../interfaces/agent/IAgent.ts";
import NoData from "../../assets/no-data.png";
const {Search} = Input; const {Search} = Input;
@ -51,20 +52,27 @@ export default function ListProjAgent() {
}) })
}, [indexListContext.status, keywords, page]) }, [indexListContext.status, keywords, page])
const renderStatus = () => {
if (indexListContext.status == 'ALL') {
return <Tag color="blue"></Tag>
} else if (indexListContext.status == 'PROCESSING') {
return <Tag color="blue"></Tag>
} else if (indexListContext.status == 'COMPLETE') {
return <Tag color="blue"></Tag>
}
return <></>
}
const renderList = () => {
if(agents.length == 0) {
return ( return (
<> <div className="no-data" style={{width: '100%', height: '100%', backgroundColor: 'var(--color-light)', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center'}}>
<div className="list-proj-agent" ref={listProjRef}> <Image src={NoData} preview={false}/>
<div className="top"> <span></span>
<Search placeholder="按项目名搜索" onSearch={(value) => {
setKeywords(value)
}} style={{width: 200}}/>
</div> </div>
<div className="body"> )
<Spin tip="加载中..." spinning={isLoading}> }
<div className="list" ref={listRef} style={{height: `${domHeight}px`}}> return agents.map(item => <CardProjAgent key={item.orderNumber}
{
agents.map(item => <CardProjAgent key={item.orderNumber}
projId={item.projId} projId={item.projId}
projName={item.projName} projName={item.projName}
projOrderNo={item.projOrderNo} projOrderNo={item.projOrderNo}
@ -84,9 +92,22 @@ export default function ListProjAgent() {
orderTypeName={item.orderTypeName} orderTypeName={item.orderTypeName}
overTime={item.overTime} overTime={item.overTime}
gmtCreate={item.gmtCreate} gmtCreate={item.gmtCreate}
/>) />
)
} }
return (
<>
<div className="list-proj-agent" ref={listProjRef}>
<div className="top">
{renderStatus()}
<Search placeholder="按项目名搜索" onSearch={(value) => {
setKeywords(value)
}} style={{width: 200}}/>
</div> </div>
<div className="body">
<Spin tip="加载中..." spinning={isLoading}>
<div className="list" ref={listRef} style={{height: `${domHeight}px`}}>{renderList()}</div>
</Spin> </Spin>
<div className="page"> <div className="page">
<Pagination defaultCurrent={page} total={total}/> <Pagination defaultCurrent={page} total={total}/>

View File

@ -5,7 +5,8 @@
.list-proj-agent .top { .list-proj-agent .top {
padding: 10px 15px; padding: 10px 15px;
display: flex; display: flex;
justify-content: right; justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border);
background-color: var(--color-light); background-color: var(--color-light);
} }

View File

@ -5,7 +5,8 @@
.list-proj .top { .list-proj .top {
padding: 10px 15px; padding: 10px 15px;
display: flex; display: flex;
justify-content: right; justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border);
background-color: var(--color-light); background-color: var(--color-light);
} }

View File

@ -1,23 +1,27 @@
import './menu-tree-with-top-button.css'; import './menu-tree-with-top-button.css';
import MenuTree from "./MenuTree.tsx"; import MenuTree from "./MenuTree.tsx";
import {IMenuTreeItem} from "../../interfaces/menu/IMenuTree.ts"; import {IMenuTreeItem} from "../../interfaces/menu/IMenuTree.ts";
import {useState} from "react"; import {useEffect, useState} from "react";
import {del, get, post, put} from "../../util/AjaxUtils.ts";
import useMessage from "antd/es/message/useMessage";
class MenuTreeItem implements IMenuTreeItem { class MenuTreeItem implements IMenuTreeItem {
children: Array<IMenuTreeItem> | null; children: Array<IMenuTreeItem> | null;
id: string; id: string;
pId: string;
level: number;
isEdit: boolean; isEdit: boolean;
isOpen: boolean; isOpen: boolean;
isParent: boolean; isParent: boolean;
level: number;
name: string; name: string;
oldName: string; oldName: string;
constructor(id: string, name: string, level: number) { constructor(id: string, pId: string, name: string, level: number) {
this.id = id; this.id = id;
this.pId = pId;
this.level = level;
this.name = name; this.name = name;
this.oldName = name; this.oldName = name;
this.level = level;
this.isEdit = false; this.isEdit = false;
this.isOpen = false; this.isOpen = false;
this.isParent = false; this.isParent = false;
@ -26,20 +30,68 @@ class MenuTreeItem implements IMenuTreeItem {
} }
type ProjCategoryDTO = {
projCategoryId: string;
projCategoryParentId: string;
projCategoryParentName: string;
isParent: boolean;
projCategoryName: string;
projCategoryCode: string;
subProjCategory: ProjCategoryDTO[];
}
export default function MenuTreeWithTopButton() { export default function MenuTreeWithTopButton() {
const [messageApi, messageContext] = useMessage();
const menuTrees: Array<IMenuTreeItem> = []; const menuTrees: Array<IMenuTreeItem> = [];
const [menuTreeArray, setMenuTreeArray] = useState(menuTrees); const [menuTreeArray, setMenuTreeArray] = useState(menuTrees);
const newCategoryName: string = '新目录';
const menuArray = (datas: ProjCategoryDTO[], level: number) => {
const menus: MenuTreeItem[] = [];
datas.forEach((item) => {
const menu = new MenuTreeItem(item.projCategoryId, item.projCategoryParentId, item.projCategoryName, level);
if (item.subProjCategory && item.subProjCategory.length > 0) {
menu.isParent = true;
menu.children = menuArray(item.subProjCategory, level + 1);
}
menus.push(menu);
})
return menus;
}
useEffect(() => {
get<ProjCategoryDTO[]>({
messageApi,
url: '/api/proj/category/listallbyparentid/0',
onSuccess({data}) {
setMenuTreeArray(menuArray(data, 1));
}
})
}, []);
return ( return (
<>
<div className="menu-tree-with-top-button"> <div className="menu-tree-with-top-button">
<button type="button" className="btn btn-orange" onClick={() => { <button type="button" className="btn btn-orange" onClick={() => {
const menuTreeItem = new MenuTreeItem(`${new Date().getTime()}`, '一级目录', 1); // 新增
menuTreeItem.isParent = true; post<any>({
messageApi,
url: '/api/proj/category/save',
body: {
projCategoryParentId: '0',
projCategoryName: newCategoryName
},
onSuccess({data}) {
const menuTreeItem = new MenuTreeItem(`${data.data}`, '0', newCategoryName, 1);
menuTreeItem.isParent = false;
setMenuTreeArray([ setMenuTreeArray([
...menuTreeArray, ...menuTreeArray,
menuTreeItem, menuTreeItem,
]); ]);
}
})
}}> }}>
</button> </button>
<MenuTree <MenuTree
@ -53,26 +105,39 @@ export default function MenuTreeWithTopButton() {
handleExpand={() => { handleExpand={() => {
}} }}
handleAddClick={(item) => { handleAddClick={(item) => {
post<any>({
messageApi,
url: '/api/proj/category/save',
body: {
projCategoryParentId: item.id,
projCategoryName: newCategoryName
},
onSuccess({data}) {
item.isParent = true; item.isParent = true;
item.isOpen = true; item.isOpen = true;
if (!item.children) { if (!item.children) {
item.children = new Array<IMenuTreeItem>(); item.children = new Array<IMenuTreeItem>();
} }
const menuTreeItem = new MenuTreeItem(`${new Date().getTime()}`, item.level + '级目录', item.level + 1); const menuTreeItem = new MenuTreeItem(`${data.data}`, item.id, newCategoryName, item.level + 1);
menuTreeItem.isOpen = true; menuTreeItem.isParent = false;
item.children.push(menuTreeItem); item.children.push(menuTreeItem);
setMenuTreeArray([ setMenuTreeArray([
...menuTreeArray ...menuTreeArray
]) ])
} }
} })
}}
handleEditClick={(item) => { handleEditClick={(item) => {
item.isEdit = true; item.isEdit = true;
setMenuTreeArray([ setMenuTreeArray([
...menuTreeArray ...menuTreeArray
]) ])
}} }}
handleRemoveClick={(_item, index, parent) => { handleRemoveClick={(item, index, parent) => {
del<any>({
messageApi,
url: `/api/proj/category/delete/${item.id}`,
onSuccess() {
if (parent) { if (parent) {
parent?.children?.splice(index, 1); parent?.children?.splice(index, 1);
} else { } else {
@ -81,17 +146,29 @@ export default function MenuTreeWithTopButton() {
setMenuTreeArray([ setMenuTreeArray([
...menuTreeArray ...menuTreeArray
]) ])
}
})
}} }}
handleEditSaveClick={(item) => { handleEditSaveClick={(item) => {
// 这里发请求,成功之后修改,失败还原 // 这里发请求,成功之后修改,失败还原
if (item.name === '') { if (item.name === '') {
return; return;
} }
put<any>({
messageApi,
url: `/api/proj/category/update/${item.id}`,
body: {
projCategoryParentId: item.pId,
projCategoryName: item.name
},
onSuccess() {
item.oldName = item.name; item.oldName = item.name;
item.isEdit = false; item.isEdit = false;
setMenuTreeArray([ setMenuTreeArray([
...menuTreeArray ...menuTreeArray
]) ])
}
})
}} }}
handleEditCancelClick={(item) => { handleEditCancelClick={(item) => {
item.name = item.oldName; item.name = item.oldName;
@ -107,5 +184,7 @@ export default function MenuTreeWithTopButton() {
}} }}
/> />
</div> </div>
{messageContext}
</>
) )
} }

View File

@ -19,7 +19,8 @@
} }
.menu-with-top-button ul li:hover { .menu-with-top-button ul li:hover {
background-color: var(--color-hover); text-decoration-line: underline;
text-underline-offset: 5px;
} }
.menu-with-top-button ul li:last-child { .menu-with-top-button ul li:last-child {

View File

@ -1,8 +1,9 @@
export interface IMenuTreeItem { export interface IMenuTreeItem {
id: string; id: string;
pId: string;
level: number;
name: string; name: string;
oldName: string; oldName: string;
level: number;
isEdit: boolean; isEdit: boolean;
isOpen: boolean; isOpen: boolean;
isParent: boolean; isParent: boolean;

View File

@ -69,6 +69,7 @@ export interface IProj {
projStyleType: ProjStyleType; projStyleType: ProjStyleType;
previewUrl: string; previewUrl: string;
gmtCreate: string; gmtCreate: string;
projModCount: number;
generate: IProjGenerate; generate: IProjGenerate;
pay: IProjPay; pay: IProjPay;

View File

@ -0,0 +1,254 @@
import './agent-agreement.css'
import {Link, useParams} from "react-router-dom";
import {
Breadcrumb,
Button,
GetProp,
message,
Spin,
Table,
TableProps,
Tag,
Upload,
UploadProps
} from "antd";
import {useEffect, useState} from "react";
import {DevUserId, get, put, uploadFileUrl} from "../../util/AjaxUtils.ts";
import {IListPage} from "../../interfaces/listpage/IListPage.ts";
import useMessage from "antd/es/message/useMessage";
import useModal from "antd/es/modal/useModal";
import {DownloadOutlined, UploadOutlined} from "@ant-design/icons";
type TableDataType = {
orderId: string;
signDate: string;
signFileId: string;
creator: string;
gmtCreate: string;
orderAgreementFile: string;
orderAgreementId: string;
orderAgreementName: string;
orderAgreementNote: string;
orderAgreementStatus: string;
orderAgreementStatusText: string;
}
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
export default function AgentAgreement() {
const pathParams = useParams();
const [messageApi, contextHolder] = useMessage();
const [modal, modalHolder] = useModal();
const [isLoading, setIsLoading] = useState(false);
const [isConfirmLoading, setIsConfirmLoading] = useState(false);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [dataArray, setDataArray] = useState<TableDataType[]>([]);
const [uploadSignFileId, setUploadSignFileId] = useState('');
const domHeight = window.innerHeight - 180;
const beforeUpload = (file: FileType) => {
const isJpgOrPng = file.type === 'application/pdf';
if (!isJpgOrPng) {
message.error('只能上传 PDF 格式文件!');
return;
}
return isJpgOrPng;
};
const columns: TableProps<TableDataType>['columns'] = [
{
title: '协议名称',
dataIndex: 'orderAgreementName',
key: 'orderAgreementName',
align: 'center',
width: 200
},
{
title: '协议备注',
dataIndex: 'orderAgreementNote',
key: 'orderAgreementNote',
align: 'center',
},
{
title: '协议状态',
dataIndex: 'orderAgreementStatusText',
key: 'orderAgreementStatusText',
align: 'center',
width: 150,
render: value => {
return <Tag color="geekblue">{value}</Tag>
}
},
{
title: '创建时间',
dataIndex: 'gmtCreate',
key: 'gmtCreate',
align: 'center',
width: 180
},
{
title: '协议签订时间',
dataIndex: 'signDate',
key: 'signDate',
align: 'center',
width: 180,
render: (value) => {
if(value) {
return value;
}
return '-'
}
},
{
title: '甲方协议',
dataIndex: 'orderAgreementId',
key: 'orderAgreementId',
align: 'center',
width: 100,
render: (value) => {
return <a href={value} download><DownloadOutlined /> </a>
}
},
{
title: '乙方协议',
dataIndex: 'signFileId',
key: 'signFileId',
align: 'center',
width: 150,
render: (value) => {
if(value) {
return <a href={value} download><DownloadOutlined /> </a>
}
return (
<Upload name="file"
action={uploadFileUrl()}
headers={{'X-USER-ID': DevUserId}}
beforeUpload={beforeUpload}
maxCount={1}
onChange={
(info) => {
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
message.success('上传成功');
info.file.name = '协议文件';
setUploadSignFileId(info.file.response.data.fileId);
} else if (info.file.status === 'error') {
message.error('上传失败');
}
}
}
onRemove={() => {
setUploadSignFileId('');
}}
>
<a href="/#" onClick={(e)=>{
e.preventDefault();
}}><UploadOutlined /> {uploadSignFileId ? '重新上传' : '上传'}</a>
</Upload>
)
}
},
{
title: '操作',
dataIndex: 'option',
key: 'option',
align: 'center',
width: 120,
render: (_value, record) => {
if(!uploadSignFileId) {
return <>-</>
}
return (
<Button size="small" type="primary" style={{backgroundColor: 'var(--color-primary)'}} onClick={() => {
modal.confirm({
title: '提示',
content: '确定提交吗?',
okText: '确定',
cancelText: '取消',
okButtonProps: {
style: {
backgroundColor: 'var(--color-primary)'
}
},
onOk: () => {
put<any>({
messageApi,
url: `/api/agent/order/agreement/update`,
body: {
orderAgreementId: record.orderAgreementId,
fileId: uploadSignFileId
},
onBefore() {
setIsConfirmLoading(true);
},
onSuccess() {
messageApi.success('提交成功,等待审核');
getData();
},
onFinally() {
setIsConfirmLoading(false);
}
})
}
})
}}></Button>
)
}
},
];
const getData = () => {
get<IListPage<TableDataType>>({
messageApi,
url: `/api/agent/order/agreement/listpage/order-id/${pathParams.orderId}`,
onBefore() {
setIsLoading(true)
},
onSuccess({data}) {
setDataArray([
...data.rows
])
setPage(data.page);
setTotal(data.total);
},
onFinally() {
setIsLoading(false)
}
})
}
useEffect(() => {
getData();
}, [page])
return (
<>
<Breadcrumb
items={[
{title: <Link to="/?type=agent"></Link>},
{title: '协议清单'},
]}
/>
<div className="agreement-container" style={{height: `${domHeight}px`}}>
<Spin tip="加载中..." spinning={isLoading}>
<Table columns={columns} dataSource={dataArray} rowKey="orderId" scroll={{y: domHeight - 105}} bordered pagination={{
current: page,
pageSize: 20,
total: total,
onChange: (page) => {
setPage(page);
}
}}/>
</Spin>
</div>
<Spin tip="正在提交..." spinning={isConfirmLoading} fullscreen/>
{contextHolder}
{modalHolder}
</>
)
}

View File

@ -0,0 +1,211 @@
import './agent-correction.css'
import {Link, useParams} from "react-router-dom";
import {
Breadcrumb,
message,
Spin,
Table,
TableProps,
Upload,
} from "antd";
import {useEffect, useState} from "react";
import {DownloadOutlined, UploadOutlined} from '@ant-design/icons';
import {DevUserId, get, put, uploadFileUrl} from "../../util/AjaxUtils.ts";
import {IListPage} from "../../interfaces/listpage/IListPage.ts";
import useMessage from "antd/es/message/useMessage";
type TableDataType = {
creator: string;
gmtCreate: string;
isMaterial: number;
isUpload: number;
materialAmendAttr: string;
materialAmendExplain: string;
materialAmendFile: string;
materialAmendType: string;
orderId: string;
orderMaterialAmendId: string;
orderNumber: string;
}
export default function AgentCorrection() {
const pathParams = useParams();
const [messageApi, contextHolder] = useMessage();
const [isLoading, setIsLoading] = useState(false);
const [isConfirmLoading, setIsConfirmLoading] = useState(false);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [dataArray, setDataArray] = useState<TableDataType[]>([]);
const [uploadSignFileId, setUploadSignFileId] = useState('');
const domHeight = window.innerHeight - 180;
const columns: TableProps<TableDataType>['columns'] = [
{
title: '资料类型',
dataIndex: 'materialAmendType',
key: 'materialAmendType',
align: 'center',
width: 200,
render: (value) => {
if(value === 'OM_FILE') {
return '操作手册';
}
if(value === 'CODE_FILE') {
return '源代码';
}
if(value === 'Af_File') {
return '申请表';
}
if(value === 'OTHER') {
return '其他';
}
return '错误'
}
},
{
title: '说明',
dataIndex: 'materialAmendExplain',
key: 'materialAmendExplain',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'gmtCreate',
key: 'gmtCreate',
align: 'center',
width: 180
},
{
title: '附件',
dataIndex: 'materialAmendAttr',
key: 'materialAmendAttr',
align: 'center',
width: 100,
render: (value) => {
return <a href={value} download><DownloadOutlined /> </a>
}
},
{
title: '补正资料',
dataIndex: 'materialAmendFile',
key: 'materialAmendFile',
align: 'center',
width: 100,
render: (value) => {
if(!value) {
return '-';
}
return <a href={value} download><DownloadOutlined /> </a>
}
},
{
title: '操作',
dataIndex: 'option',
key: 'option',
align: 'center',
width: 120,
render: (_value, record) => {
if(record.materialAmendFile) {
return '-';
}
return (
<Upload name="file"
action={uploadFileUrl()}
headers={{'X-USER-ID': DevUserId}}
maxCount={1}
showUploadList={false}
onChange={
(info) => {
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
message.success('上传成功');
info.fileList.splice(0);
put<any>({
messageApi,
url: 'api/agent/order/correction/update',
body: {
orderMaterialAmendId: record.orderMaterialAmendId,
fileId : info.file.response.data.fileId
},
onBefore() {
setIsConfirmLoading(true);
},
onSuccess() {
messageApi.success('提交成功')
getData();
},
onFinally() {
setIsConfirmLoading(false);
}
})
} else if (info.file.status === 'error') {
message.error('上传失败');
}
}
}
onRemove={() => {
setUploadSignFileId('');
}}
>
<a href="/#" onClick={(e)=>{
e.preventDefault();
}}><UploadOutlined /> {uploadSignFileId ? '重新上传' : '上传'}</a>
</Upload>
)
}
},
];
const getData = () => {
get<IListPage<TableDataType>>({
messageApi,
url: `/api/agent/order/correction/listpage/order-id/${pathParams.orderId}`,
onBefore() {
setIsLoading(true)
},
onSuccess({data}) {
setDataArray([
...data.rows
])
setPage(data.page);
setTotal(data.total);
},
onFinally() {
setIsLoading(false)
}
})
}
useEffect(() => {
getData();
}, [page])
return (
<>
<Breadcrumb
items={[
{title: <Link to="/?type=agent"></Link>},
{title: '补正列表'},
]}
/>
<div className="correction-container" style={{height: `${domHeight}px`}}>
<Spin tip="加载中..." spinning={isLoading}>
<Table columns={columns} dataSource={dataArray} rowKey="orderMaterialAmendId" scroll={{y: domHeight - 105}} bordered pagination={{
current: page,
pageSize: 20,
total: total,
onChange: (page) => {
setPage(page);
}
}}/>
</Spin>
</div>
<Spin tip="正在提交..." spinning={isConfirmLoading} fullscreen/>
{contextHolder}
</>
)
}

View File

@ -0,0 +1,175 @@
import './agent-result.css'
import {Link, useParams} from "react-router-dom";
import {
Breadcrumb, Button,
Spin,
Table,
TableProps,
} from "antd";
import {useEffect, useState} from "react";
import {get, put} from "../../util/AjaxUtils.ts";
import {IListPage} from "../../interfaces/listpage/IListPage.ts";
import useMessage from "antd/es/message/useMessage";
import useModal from "antd/es/modal/useModal";
type TableDataType = {
creator: string;
gmtCreate: string;
isSure: number;
isUpload: number;
orderId: string;
orderNumber: string;
orderResultExplain: string;
orderResultFile: string;
orderResultId: string;
sureDate: string;
sureExplain: string;
}
export default function AgentResult() {
const pathParams = useParams();
const [messageApi, contextHolder] = useMessage();
const [modal, modalContext] = useModal();
const [isLoading, setIsLoading] = useState(false);
const [isConfirmLoading, setIsConfirmLoading] = useState(false);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [dataArray, setDataArray] = useState<TableDataType[]>([]);
const domHeight = window.innerHeight - 180;
const columns: TableProps<TableDataType>['columns'] = [
{
title: '说明',
dataIndex: 'orderResultExplain',
key: 'orderResultExplain',
align: 'center',
},
{
title: '上传时间',
dataIndex: 'gmtCreate',
key: 'gmtCreate',
align: 'center',
width: 180
},
{
title: '资料',
dataIndex: 'orderResultFile',
key: 'orderResultFile',
align: 'center',
width: 200,
render: (value) => {
return <a href={value} download></a>
}
},
{
title: '确认时间',
dataIndex: 'sureDate',
key: 'sureDate',
align: 'center',
width: 180,
render: (value) => {
if (!value) {
return '-';
}
return value;
}
},
{
title: '操作',
dataIndex: 'option',
key: 'option',
align: 'center',
width: 120,
render: (_value, record) => {
if (record.isSure == 1) {
return <>-</>;
}
return <Button size="small" type="primary" style={{backgroundColor: 'var(--color-primary)'}}
onClick={() => {
modal.confirm({
title: '提示',
content: '确定确认吗?确认后订单将会结束',
okText: '确认',
cancelText: '取消',
okButtonProps: {
style: {
backgroundColor: 'var(--color-primary)'
}
},
onOk: () => {
put<any>({
messageApi,
url: `/api/agent/order/result/update`,
body: {
orderResultId: record.orderResultId,
},
onBefore() {
setIsConfirmLoading(true);
},
onSuccess() {
messageApi.success('确认成功');
getData();
},
onFinally() {
setIsConfirmLoading(false);
}
})
}
})
}}></Button>
}
},
];
const getData = () => {
get<IListPage<TableDataType>>({
messageApi,
url: `/api/agent/order/result/listpage/order-id/${pathParams.orderId}`,
onBefore() {
setIsLoading(true)
},
onSuccess({data}) {
setDataArray([
...data.rows
])
setPage(data.page);
setTotal(data.total);
},
onFinally() {
setIsLoading(false)
}
})
}
useEffect(() => {
getData();
}, [page])
return (
<>
<Breadcrumb
items={[
{title: <Link to="/?type=agent"></Link>},
{title: '结果列表'},
]}
/>
<div className="result-container" style={{height: `${domHeight}px`}}>
<Spin tip="加载中..." spinning={isLoading}>
<Table columns={columns} dataSource={dataArray} rowKey="orderResultId" scroll={{y: domHeight - 105}}
bordered pagination={{
current: page,
pageSize: 20,
total: total,
onChange: (page) => {
setPage(page);
}
}}/>
</Spin>
</div>
<Spin tip="正在提交..." spinning={isConfirmLoading} fullscreen/>
{contextHolder}
{modalContext}
</>
)
}

View File

@ -150,6 +150,7 @@ export default function AgentSelect() {
<Image width={270} <Image width={270}
height={170} height={170}
src={downloadUrl(id, false)} src={downloadUrl(id, false)}
preview={false}
fallback={errorImage} fallback={errorImage}
/> />
)) ))
@ -167,7 +168,7 @@ export default function AgentSelect() {
if (!selectedAgent) { if (!selectedAgent) {
return ( return (
<div className="no-data"> <div className="no-data">
<Image src={NoData}/> <Image src={NoData} preview={false}/>
<span></span> <span></span>
</div> </div>
) )
@ -237,7 +238,7 @@ export default function AgentSelect() {
setIsConfirmLoading(true); setIsConfirmLoading(true);
}, },
onSuccess() { onSuccess() {
messageApi.success('提交成功', 1000, () => { messageApi.success('提交成功', 1, () => {
nav(-1); nav(-1);
}); });
}, },

View File

@ -0,0 +1,4 @@
.agreement-container {
background-color: var(--color-light);
padding: 15px;
}

View File

@ -0,0 +1,4 @@
.correction-container {
background-color: var(--color-light);
padding: 15px;
}

View File

@ -0,0 +1,4 @@
.result-container {
background-color: var(--color-light);
padding: 15px;
}

View File

@ -1,6 +1,6 @@
import './index.css'; import './index.css';
import {MouseEvent, Reducer, useReducer} from "react"; import {MouseEvent, Reducer, useEffect, useReducer} from "react";
import {Link, useNavigate} from "react-router-dom"; import {Link, useNavigate, useSearchParams} from "react-router-dom";
import {IMenuListItem, IMenuWithTopButton} from "../../interfaces/menu/IMenuWithTopButton.ts"; import {IMenuListItem, IMenuWithTopButton} from "../../interfaces/menu/IMenuWithTopButton.ts";
import MenuWithTopButton from "../../components/menu/MenuWithTopButton.tsx"; import MenuWithTopButton from "../../components/menu/MenuWithTopButton.tsx";
import MenuTreeWithTopButton from "../../components/menu/MenuTreeWithTopButton.tsx"; import MenuTreeWithTopButton from "../../components/menu/MenuTreeWithTopButton.tsx";
@ -17,6 +17,7 @@ import {
export default function Index() { export default function Index() {
const nav = useNavigate(); const nav = useNavigate();
const [searchParams] = useSearchParams();
const listReducer = (state: ListData, action: ListAction) => { const listReducer = (state: ListData, action: ListAction) => {
if(action.type == IndexListDataType.PROJ) { if(action.type == IndexListDataType.PROJ) {
@ -59,7 +60,10 @@ export default function Index() {
button: { button: {
name: '代理服务', name: '代理服务',
handle() { handle() {
dispatch({
type: IndexListDataType.PROJ,
value: 'COMPLETE'
})
} }
}, },
list: [ list: [
@ -75,6 +79,16 @@ export default function Index() {
} }
} }
useEffect(() => {
if(searchParams.get('type') == 'agent') {
dispatch({
type: IndexListDataType.AGENT,
value: 'ALL'
})
}
}, []);
return ( return (
<> <>
<Breadcrumb <Breadcrumb

View File

@ -25,6 +25,9 @@ import ProjConfigModShow from "./proj/edit/ProjConfigModShow.tsx";
import ProjConfigMenuList from "./proj/edit/ProjConfigMenuList.tsx"; import ProjConfigMenuList from "./proj/edit/ProjConfigMenuList.tsx";
import ProjConfigMenuListShow from "./proj/edit/ProjConfigMenuListShow.tsx"; import ProjConfigMenuListShow from "./proj/edit/ProjConfigMenuListShow.tsx";
import AgentSelect from "./agent/AgentSelect.tsx"; import AgentSelect from "./agent/AgentSelect.tsx";
import AgentAgreement from "./agent/AgentAgreement.tsx";
import AgentCorrection from "./agent/AgentCorrection.tsx";
import AgentResult from "./agent/AgentResult.tsx";
export const router = createBrowserRouter([ export const router = createBrowserRouter([
{ {
@ -130,5 +133,17 @@ export const router = createBrowserRouter([
{ {
path: '/agent-select/:projId', path: '/agent-select/:projId',
element: <AgentSelect/> element: <AgentSelect/>
},
{
path: '/agent-agreement/:orderId',
element: <AgentAgreement/>
},
{
path: '/agent-correction/:orderId',
element: <AgentCorrection />
},
{
path: '/agent-result/:orderId',
element: <AgentResult />
} }
]) ])

View File

@ -57,12 +57,13 @@ export default function UserEdit(props: IUserEditProps) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'; const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) { if (!isJpgOrPng) {
message.error('只能上传 JPG/PNG 格式文件!'); message.error('只能上传 JPG/PNG 格式文件!');
return;
} }
return isJpgOrPng; return isJpgOrPng;
}; };
useEffect(() => { useEffect(() => {
get({ get<any>({
messageApi, messageApi,
url: 'api/user-info/get-self', url: 'api/user-info/get-self',
onSuccess({data}) { onSuccess({data}) {