项目目录

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();
}}>
<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>
{renderMedal()}
</div>
<div className="card-agent-body">
<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>
)

View File

@ -70,7 +70,7 @@ export default function CardProj(props: { item: IProj }) {
} else {
nav(`/proj-edit/config-mod-list/${data.projId}`)
}
}}><SettingOutlined/> (0)</Button>
}}><SettingOutlined/> ({data.projModCount})</Button>
<Button size="small" type="text" onClick={() => {
if(data.generate.generateStatus == GenerateStatus.SUCCESS) {
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}`)
}
}}><SettingOutlined/> (0)</Button>
}}><SettingOutlined/> ({data.projModCount})</Button>
</div>
{
data.generate.generateStatus == GenerateStatus.SUCCESS ? (
@ -150,7 +150,19 @@ export default function CardProj(props: { item: IProj }) {
</div>
</div>
<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">
<span>
<FolderOutlined/>

View File

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

View File

@ -74,5 +74,12 @@
}
.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 CardProj from "../card/CardProj.tsx";
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 {IndexListContext} from "../../context/IndexListContext.ts";
import {IListPage} from "../../interfaces/listpage/IListPage.ts";
import {IProj} from "../../interfaces/proj/IProj.ts";
import NoData from "../../assets/no-data.png";
const {Search} = Input;
@ -50,6 +51,14 @@ export default function ListProj() {
}
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}`}/>);
}
@ -57,11 +66,23 @@ export default function ListProj() {
reqData(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 (
<>
{contextHolder}
<div className="list-proj" ref={listProjRef}>
<div className="top">
{renderStatus()}
<Search placeholder="按项目名搜索" onSearch={(value) => {
setKeywords(value)
}} style={{width: 200}}/>

View File

@ -1,12 +1,13 @@
import './list-proj-agent.css';
import CardProjAgent from "../card/CardProjAgent.tsx";
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 useMessage from "antd/es/message/useMessage";
import {IListPage} from "../../interfaces/listpage/IListPage.ts";
import {IndexListContext} from "../../context/IndexListContext.ts";
import {IAgent} from "../../interfaces/agent/IAgent.ts";
import NoData from "../../assets/no-data.png";
const {Search} = Input;
@ -51,42 +52,62 @@ export default function ListProjAgent() {
})
}, [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 (
<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 agents.map(item => <CardProjAgent key={item.orderNumber}
projId={item.projId}
projName={item.projName}
projOrderNo={item.projOrderNo}
agentBasicsId={item.agentBasicsId}
basicsName={item.basicsName}
isAgreement={item.isAgreement}
isOver={item.isOver}
isResult={item.isResult}
isTake={item.isTake}
materialAmendApplyCount={item.materialAmendApplyCount}
orderId={item.orderId}
orderNote={item.orderNote}
orderNumber={item.orderNumber}
orderShoppingAmount={item.orderShoppingAmount}
orderType={item.orderType}
orderTypeAmount={item.orderTypeAmount}
orderTypeName={item.orderTypeName}
overTime={item.overTime}
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 className="body">
<Spin tip="加载中..." spinning={isLoading}>
<div className="list" ref={listRef} style={{height: `${domHeight}px`}}>
{
agents.map(item => <CardProjAgent key={item.orderNumber}
projId={item.projId}
projName={item.projName}
projOrderNo={item.projOrderNo}
agentBasicsId={item.agentBasicsId}
basicsName={item.basicsName}
isAgreement={item.isAgreement}
isOver={item.isOver}
isResult={item.isResult}
isTake={item.isTake}
materialAmendApplyCount={item.materialAmendApplyCount}
orderId={item.orderId}
orderNote={item.orderNote}
orderNumber={item.orderNumber}
orderShoppingAmount={item.orderShoppingAmount}
orderType={item.orderType}
orderTypeAmount={item.orderTypeAmount}
orderTypeName={item.orderTypeName}
overTime={item.overTime}
gmtCreate={item.gmtCreate}
/>)
}
</div>
<div className="list" ref={listRef} style={{height: `${domHeight}px`}}>{renderList()}</div>
</Spin>
<div className="page">
<Pagination defaultCurrent={page} total={total}/>

View File

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

View File

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

View File

@ -1,23 +1,27 @@
import './menu-tree-with-top-button.css';
import MenuTree from "./MenuTree.tsx";
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 {
children: Array<IMenuTreeItem> | null;
id: string;
pId: string;
level: number;
isEdit: boolean;
isOpen: boolean;
isParent: boolean;
level: number;
name: string;
oldName: string;
constructor(id: string, name: string, level: number) {
constructor(id: string, pId: string, name: string, level: number) {
this.id = id;
this.pId = pId;
this.level = level;
this.name = name;
this.oldName = name;
this.level = level;
this.isEdit = false;
this.isOpen = false;
this.isParent = false;
@ -26,86 +30,161 @@ class MenuTreeItem implements IMenuTreeItem {
}
type ProjCategoryDTO = {
projCategoryId: string;
projCategoryParentId: string;
projCategoryParentName: string;
isParent: boolean;
projCategoryName: string;
projCategoryCode: string;
subProjCategory: ProjCategoryDTO[];
}
export default function MenuTreeWithTopButton() {
const [messageApi, messageContext] = useMessage();
const menuTrees: Array<IMenuTreeItem> = [];
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 (
<div className="menu-tree-with-top-button">
<button type="button" className="btn btn-orange" onClick={() => {
const menuTreeItem = new MenuTreeItem(`${new Date().getTime()}`, '一级目录', 1);
menuTreeItem.isParent = true;
setMenuTreeArray([
...menuTreeArray,
menuTreeItem,
]);
}}>
</button>
<MenuTree
menus={menuTreeArray}
url={''}
setMenuTreeArray={() => {
setMenuTreeArray([
...menuTreeArray
])
}}
handleExpand={() => {
}}
handleAddClick={(item) => {
item.isParent = true;
item.isOpen = true;
if (!item.children) {
item.children = new Array<IMenuTreeItem>();
}
const menuTreeItem = new MenuTreeItem(`${new Date().getTime()}`, item.level + '级目录', item.level + 1);
menuTreeItem.isOpen = true;
item.children.push(menuTreeItem);
setMenuTreeArray([
...menuTreeArray
])
}
}
handleEditClick={(item) => {
item.isEdit = true;
setMenuTreeArray([
...menuTreeArray
])
}}
handleRemoveClick={(_item, index, parent) => {
if (parent) {
parent?.children?.splice(index, 1);
} else {
menuTreeArray.splice(index, 1);
}
setMenuTreeArray([
...menuTreeArray
])
}}
handleEditSaveClick={(item) => {
// 这里发请求,成功之后修改,失败还原
if (item.name === '') {
return;
}
item.oldName = item.name;
item.isEdit = false;
setMenuTreeArray([
...menuTreeArray
])
}}
handleEditCancelClick={(item) => {
item.name = item.oldName;
item.isEdit = false;
setMenuTreeArray([
...menuTreeArray
])
}}
handleNameChange={() => {
setMenuTreeArray([
...menuTreeArray
])
}}
/>
</div>
<>
<div className="menu-tree-with-top-button">
<button type="button" className="btn btn-orange" onClick={() => {
// 新增
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([
...menuTreeArray,
menuTreeItem,
]);
}
})
}}>
</button>
<MenuTree
menus={menuTreeArray}
url={''}
setMenuTreeArray={() => {
setMenuTreeArray([
...menuTreeArray
])
}}
handleExpand={() => {
}}
handleAddClick={(item) => {
post<any>({
messageApi,
url: '/api/proj/category/save',
body: {
projCategoryParentId: item.id,
projCategoryName: newCategoryName
},
onSuccess({data}) {
item.isParent = true;
item.isOpen = true;
if (!item.children) {
item.children = new Array<IMenuTreeItem>();
}
const menuTreeItem = new MenuTreeItem(`${data.data}`, item.id, newCategoryName, item.level + 1);
menuTreeItem.isParent = false;
item.children.push(menuTreeItem);
setMenuTreeArray([
...menuTreeArray
])
}
})
}}
handleEditClick={(item) => {
item.isEdit = true;
setMenuTreeArray([
...menuTreeArray
])
}}
handleRemoveClick={(item, index, parent) => {
del<any>({
messageApi,
url: `/api/proj/category/delete/${item.id}`,
onSuccess() {
if (parent) {
parent?.children?.splice(index, 1);
} else {
menuTreeArray.splice(index, 1);
}
setMenuTreeArray([
...menuTreeArray
])
}
})
}}
handleEditSaveClick={(item) => {
// 这里发请求,成功之后修改,失败还原
if (item.name === '') {
return;
}
put<any>({
messageApi,
url: `/api/proj/category/update/${item.id}`,
body: {
projCategoryParentId: item.pId,
projCategoryName: item.name
},
onSuccess() {
item.oldName = item.name;
item.isEdit = false;
setMenuTreeArray([
...menuTreeArray
])
}
})
}}
handleEditCancelClick={(item) => {
item.name = item.oldName;
item.isEdit = false;
setMenuTreeArray([
...menuTreeArray
])
}}
handleNameChange={() => {
setMenuTreeArray([
...menuTreeArray
])
}}
/>
</div>
{messageContext}
</>
)
}

View File

@ -19,7 +19,8 @@
}
.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 {

View File

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

View File

@ -69,6 +69,7 @@ export interface IProj {
projStyleType: ProjStyleType;
previewUrl: string;
gmtCreate: string;
projModCount: number;
generate: IProjGenerate;
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}
height={170}
src={downloadUrl(id, false)}
preview={false}
fallback={errorImage}
/>
))
@ -167,7 +168,7 @@ export default function AgentSelect() {
if (!selectedAgent) {
return (
<div className="no-data">
<Image src={NoData}/>
<Image src={NoData} preview={false}/>
<span></span>
</div>
)
@ -237,7 +238,7 @@ export default function AgentSelect() {
setIsConfirmLoading(true);
},
onSuccess() {
messageApi.success('提交成功', 1000, () => {
messageApi.success('提交成功', 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 {MouseEvent, Reducer, useReducer} from "react";
import {Link, useNavigate} from "react-router-dom";
import {MouseEvent, Reducer, useEffect, useReducer} from "react";
import {Link, useNavigate, useSearchParams} from "react-router-dom";
import {IMenuListItem, IMenuWithTopButton} from "../../interfaces/menu/IMenuWithTopButton.ts";
import MenuWithTopButton from "../../components/menu/MenuWithTopButton.tsx";
import MenuTreeWithTopButton from "../../components/menu/MenuTreeWithTopButton.tsx";
@ -17,6 +17,7 @@ import {
export default function Index() {
const nav = useNavigate();
const [searchParams] = useSearchParams();
const listReducer = (state: ListData, action: ListAction) => {
if(action.type == IndexListDataType.PROJ) {
@ -59,7 +60,10 @@ export default function Index() {
button: {
name: '代理服务',
handle() {
dispatch({
type: IndexListDataType.PROJ,
value: 'COMPLETE'
})
}
},
list: [
@ -75,6 +79,16 @@ export default function Index() {
}
}
useEffect(() => {
if(searchParams.get('type') == 'agent') {
dispatch({
type: IndexListDataType.AGENT,
value: 'ALL'
})
}
}, []);
return (
<>
<Breadcrumb

View File

@ -25,6 +25,9 @@ import ProjConfigModShow from "./proj/edit/ProjConfigModShow.tsx";
import ProjConfigMenuList from "./proj/edit/ProjConfigMenuList.tsx";
import ProjConfigMenuListShow from "./proj/edit/ProjConfigMenuListShow.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([
{
@ -130,5 +133,17 @@ export const router = createBrowserRouter([
{
path: '/agent-select/:projId',
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';
if (!isJpgOrPng) {
message.error('只能上传 JPG/PNG 格式文件!');
return;
}
return isJpgOrPng;
};
useEffect(() => {
get({
get<any>({
messageApi,
url: 'api/user-info/get-self',
onSuccess({data}) {