This commit is contained in:
WenC 2024-04-01 20:39:22 +08:00
parent 953737294a
commit 44eca6fbdf
36 changed files with 799 additions and 158 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -4,23 +4,29 @@ import {
ClockCircleOutlined,
CloseCircleOutlined,
CreditCardOutlined,
DownloadOutlined,
DownloadOutlined, DownOutlined,
EditOutlined,
EyeOutlined,
FolderOutlined,
LoadingOutlined,
SearchOutlined,
SettingOutlined,
WarningOutlined
} from '@ant-design/icons';
import {Button, ConfigProvider, Tag} from 'antd';
import {Button, ConfigProvider, Dropdown, Tag} from 'antd';
import {GenerateStatus, IProj, PayStatus} from "../../interfaces/proj/IProj.ts";
import {useNavigate} from "react-router-dom";
import {Axios} from "../../util/AjaxUtils.ts";
import {Axios, put} from "../../util/AjaxUtils.ts";
import {useContext, useState} from "react";
import {IndexListContext} from "../../context/IndexListContext.ts";
import useMessage from "antd/es/message/useMessage";
export default function CardProj(props: { item: IProj }) {
const nav = useNavigate();
const data = props.item;
const [messageApi, messageContext] = useMessage();
const [projCategoryName, setProjCategoryName] = useState(data.projCategoryName);
const indexListContext = useContext(IndexListContext);
/**
*
@ -45,11 +51,11 @@ export default function CardProj(props: { item: IProj }) {
}
const renderOption = () => {
if(data.pay.payStatus == PayStatus.UNPAID) {
if (data.pay.payStatus == PayStatus.UNPAID) {
return (
<>
<div className="option">
<Button size="small" type="text"><CreditCardOutlined /> </Button>
<Button size="small" type="text"><CreditCardOutlined/> </Button>
</div>
</>
)
@ -58,21 +64,21 @@ export default function CardProj(props: { item: IProj }) {
<>
<div className="option">
<Button size="small" type="text" onClick={() => {
if(data.generate.generateStatus == GenerateStatus.SUCCESS) {
if (data.generate.generateStatus == GenerateStatus.SUCCESS) {
nav(`/proj-edit/config-loginpage-show/${data.projId}`)
} else {
nav(`/proj-edit/config-loginpage/${data.projId}`)
}
}}><SettingOutlined/> </Button>
<Button size="small" type="text" onClick={() => {
if(data.generate.generateStatus == GenerateStatus.SUCCESS) {
if (data.generate.generateStatus == GenerateStatus.SUCCESS) {
nav(`/proj-edit/config-mod-list-show/${data.projId}`)
} else {
nav(`/proj-edit/config-mod-list/${data.projId}`)
}
}}><SettingOutlined/> ({data.projModCount})</Button>
<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}`)
} else {
nav(`/proj-edit/config-menu-list/${data.projId}`)
@ -103,86 +109,114 @@ export default function CardProj(props: { item: IProj }) {
}
return (
<div className="card-proj">
<div className="title">
<div className="left">
<a href="/#">{data.projName}</a>
</div>
<div className="right">
<span className="context">{data.projContext}</span>
<span className="date">{data.gmtCreate}</span>
<span className="status">{renderGenerateStatus()}</span>
</div>
</div>
<hr/>
<div className="body">
<div className="line">
<>
{messageContext}
<div className="card-proj">
<div className="title">
<div className="left">
<span>{data.pay.payment / 100}</span>
<a href="/#">{data.projName}</a>
</div>
<div className="right">
{
data.generate.generateStatus == GenerateStatus.SUCCESS ? (
<span>
<SearchOutlined/>
<a href="/#" onClick={(e) => {
e.preventDefault();
nav(`/proj-edit/${data.projId}`)
}}></a>
</span>
) : (
<span>
<EditOutlined/>
<a href="/#" onClick={(e) => {
e.preventDefault();
nav(`/proj-edit/${data.projId}`)
}}></a>
</span>
)
}
<span>
<EyeOutlined/>
<a href="/#" onClick={(e) => {
e.stopPropagation();
window.open(`${Axios.defaults?.baseURL}/${data.previewUrl}`, '_blank')
}}></a>
</span>
<span className="context">{data.projContext}</span>
<span className="date">{data.gmtCreate}</span>
<span className="status">{renderGenerateStatus()}</span>
</div>
</div>
<div className="line">
<div className="left">
{
data.generate.generateStatus == GenerateStatus.SUCCESS ? (
<span>
<SearchOutlined/>
<a href="/#" onClick={(e) => {
e.preventDefault();
nav(`/agent-select/${data.projId}`);
}}></a>
</span>
) : <></>
}
<hr/>
<div className="body">
<div className="line">
<div className="left">
<span>{data.pay.payment / 100}</span>
</div>
<div className="right">
{
data.generate.generateStatus == GenerateStatus.SUCCESS ? (
<span>
<SearchOutlined/>
<a href="/#" onClick={(e) => {
e.preventDefault();
nav(`/proj-edit/${data.projId}`)
}}></a>
</span>
) : (
<span>
<EditOutlined/>
<a href="/#" onClick={(e) => {
e.preventDefault();
nav(`/proj-edit/${data.projId}`)
}}></a>
</span>
)
}
<span>
<EyeOutlined/>
<a href="/#" onClick={(e) => {
e.stopPropagation();
window.open(`${Axios.defaults?.baseURL}/${data.previewUrl}`, '_blank')
}}></a>
</span>
</div>
</div>
<div className="right">
<span>
<FolderOutlined/>
<a href="/#"></a>
</span>
<div className="line">
<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">
<Dropdown menu={{
items: indexListContext.categorys,
onClick: (e) => {
const span = e.domEvent.target as HTMLSpanElement;
put<any>({
messageApi,
url: `/api/proj/update-category/${data.projId}/${e.key}`,
onSuccess() {
messageApi.success('修改成功');
setProjCategoryName(span.innerText);
}
});
}
}}>
<span onClick={() => {
put<any>({
messageApi,
url: `/api/proj/cancel-category/${data.projId}`,
onSuccess() {
messageApi.success('取消成功');
setProjCategoryName('');
}
});
}}>
<a href="/#">{projCategoryName ? projCategoryName : '无目录'}</a>
<DownOutlined/>
</span>
</Dropdown>
</div>
</div>
</div>
</div>
<hr/>
<div className="foot">
<ConfigProvider theme={{
components: {
Button: {
contentFontSizeSM: 12,
<hr/>
<div className="foot">
<ConfigProvider theme={{
components: {
Button: {
contentFontSizeSM: 12,
}
}
}
}}>
{renderOption()}
</ConfigProvider>
}}>
{renderOption()}
</ConfigProvider>
</div>
</div>
</div>
</>
)
}

View File

@ -33,6 +33,7 @@ export default function ListProj() {
page: currentPage,
rows: 20,
keywords: keywords,
projCategoryId: indexListContext.category,
status: indexListContext.status ? indexListContext.status : ''
}
},
@ -51,27 +52,41 @@ export default function ListProj() {
}
const renderList = () => {
if(projs.length == 0) {
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'}}>
<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) => {
return <CardProj item={item} key={`proj${index}`}/>;
});
}
const renderCategory = () => {
}
useEffect(() => {
reqData(page);
}, [indexListContext.status, keywords, page])
renderCategory();
}, [indexListContext.status, indexListContext.categorys, indexListContext.category, keywords, page])
const renderStatus = () => {
if(indexListContext.status == 'ALL') {
if (indexListContext.status == 'ALL') {
return <Tag color="blue"></Tag>
} else if(indexListContext.status == 'PROCESSING') {
} else if (indexListContext.status == 'PROCESSING') {
return <Tag color="blue"></Tag>
} else if(indexListContext.status == 'COMPLETE') {
} else if (indexListContext.status == 'COMPLETE') {
return <Tag color="blue"></Tag>
}
return <></>

View File

@ -85,7 +85,9 @@ export default function MenuTree(props: IMenuTree) {
const lis = children.map((item, index) => {
const renderChildrenMenu = renderMenu(item.children, item);
return (
<li key={item.id}>
<li className={item.active ? 'active' : ''} key={item.id} onClick={() => {
props.handleClick(item);
}}>
<div className="menu-title">
<div className="label">{renderLabel(item)}</div>
<div className="icon-group">{renderBtnGroup(item, index, parent)}</div>

View File

@ -1,9 +1,23 @@
import './menu-tree-with-top-button.css';
import MenuTree from "./MenuTree.tsx";
import {IMenuTreeItem} from "../../interfaces/menu/IMenuTree.ts";
import {useEffect, useState} from "react";
import {useContext, useEffect, useState} from "react";
import {del, get, post, put} from "../../util/AjaxUtils.ts";
import useMessage from "antd/es/message/useMessage";
import {IndexListDataType, IndexListDispatchContext,} from "../../context/IndexListContext.ts";
import {MenuProps} from "antd";
type ProjCategoryDTO = {
projCategoryId: string;
projCategoryParentId: string;
projCategoryParentName: string;
isParent: boolean;
projCategoryName: string;
projCategoryCode: string;
subProjCategory: ProjCategoryDTO[];
}
class MenuTreeItem implements IMenuTreeItem {
children: Array<IMenuTreeItem> | null;
@ -30,18 +44,8 @@ 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 indexListDispatchContext = useContext(IndexListDispatchContext);
const [messageApi, messageContext] = useMessage();
const menuTrees: Array<IMenuTreeItem> = [];
@ -61,12 +65,34 @@ export default function MenuTreeWithTopButton() {
return menus;
}
const menus2Dropdowns = (datas: MenuTreeItem[] | null) => {
if (!datas) {
return;
}
const dropdowns: MenuProps['items'] = [];
datas.forEach((item) => {
const children = menus2Dropdowns(item.children);
dropdowns.push({
key: item.id,
label: item.name,
title: item.name,
children: children
})
})
return dropdowns;
}
useEffect(() => {
get<ProjCategoryDTO[]>({
messageApi,
url: '/api/proj/category/listallbyparentid/0',
onSuccess({data}) {
setMenuTreeArray(menuArray(data, 1));
const menus = menuArray(data, 1);
setMenuTreeArray(menus);
indexListDispatchContext({
type: IndexListDataType.CATEGORY,
value: menus2Dropdowns(menus)
})
}
})
}, []);
@ -86,10 +112,12 @@ export default function MenuTreeWithTopButton() {
onSuccess({data}) {
const menuTreeItem = new MenuTreeItem(`${data.data}`, '0', newCategoryName, 1);
menuTreeItem.isParent = false;
setMenuTreeArray([
...menuTreeArray,
menuTreeItem,
]);
menuTreeArray.push(menuTreeItem);
setMenuTreeArray([...menuTreeArray]);
indexListDispatchContext({
type: IndexListDataType.CATEGORY,
value: menus2Dropdowns(menuTreeArray)
})
}
})
}}>
@ -102,6 +130,21 @@ export default function MenuTreeWithTopButton() {
...menuTreeArray
])
}}
handleClick={(item) => {
const isActive = item.active;
menuTreeArray.forEach(item => item.active = false);
if(!isActive) {
item.active = true;
}
setMenuTreeArray([
...menuTreeArray
])
indexListDispatchContext({
type: IndexListDataType.CATEGORY_CHANGE,
value: item.active ? item.id : ''
})
console.log(item)
}}
handleExpand={() => {
}}
handleAddClick={(item) => {
@ -124,6 +167,10 @@ export default function MenuTreeWithTopButton() {
setMenuTreeArray([
...menuTreeArray
])
indexListDispatchContext({
type: IndexListDataType.CATEGORY,
value: menus2Dropdowns(menuTreeArray)
})
}
})
}}
@ -146,6 +193,10 @@ export default function MenuTreeWithTopButton() {
setMenuTreeArray([
...menuTreeArray
])
indexListDispatchContext({
type: IndexListDataType.CATEGORY,
value: menus2Dropdowns(menuTreeArray)
})
}
})
}}
@ -167,6 +218,10 @@ export default function MenuTreeWithTopButton() {
setMenuTreeArray([
...menuTreeArray
])
indexListDispatchContext({
type: IndexListDataType.CATEGORY,
value: menus2Dropdowns(menuTreeArray)
})
}
})
}}
@ -177,10 +232,11 @@ export default function MenuTreeWithTopButton() {
...menuTreeArray
])
}}
handleNameChange={() => {
setMenuTreeArray([
...menuTreeArray
])
handleNameChange={(item) => {
indexListDispatchContext({
type: IndexListDataType.CATEGORY_CHANGE,
value: item.id
})
}}
/>
</div>

View File

@ -3,7 +3,7 @@ import {IMenuWithTopButton} from "../../interfaces/menu/IMenuWithTopButton.ts";
export default function MenuWithTopButton(props: IMenuWithTopButton) {
const list = props.list.map((item, index) => (
<li key={item.id} onClick={(e) => {
<li className={item.active ? 'active' : ''} key={item.id} onClick={(e) => {
props.handleListItem(e, index, item);
}}>
{item.icon ? (<img src={item.icon} className="menu-icon" alt="加载失败"/>) : <></>}

View File

@ -7,6 +7,11 @@
.menu-tree ul li {
}
.menu-tree ul li.active {
text-decoration-line: underline;
text-underline-offset: 5px;
}
.menu-tree ul li .menu-title {
display: flex;
justify-content: space-between;

View File

@ -18,6 +18,11 @@
cursor: pointer;
}
.menu-with-top-button ul li.active {
text-decoration-line: underline;
text-underline-offset: 5px;
}
.menu-with-top-button ul li:hover {
text-decoration-line: underline;
text-underline-offset: 5px;

View File

@ -1,22 +1,29 @@
import {createContext, Dispatch} from "react";
import {MenuProps} from "antd";
export enum IndexListDataType {
PROJ,
AGENT,
CATEGORY,
CATEGORY_CHANGE
}
export interface ListData {
type: IndexListDataType;
status?: string;
category?: string;
categorys?: MenuProps['items'];
}
export interface ListAction {
type: IndexListDataType;
value: string;
value: string | MenuProps['items'];
}
export const IndexListContext = createContext<ListData>({
type: IndexListDataType.PROJ
})
export const IndexListDispatchContext = createContext<Dispatch<ListAction>>(() => {})
export const IndexListDispatchContext = createContext<Dispatch<ListAction>>(() => {
})

View File

@ -7,6 +7,7 @@ export interface IMenuTreeItem {
isEdit: boolean;
isOpen: boolean;
isParent: boolean;
active?: boolean;
children: Array<IMenuTreeItem> | null;
}
@ -16,6 +17,8 @@ export interface IMenuTree {
setMenuTreeArray(item: IMenuTreeItem): void;
handleClick(item: IMenuTreeItem): void;
handleExpand(item: IMenuTreeItem): void;
handleEditClick(item: IMenuTreeItem): void;

View File

@ -4,6 +4,7 @@ export interface IMenuListItem {
id: string;
icon?: string;
name: string;
active?: boolean;
}
export interface IMenuButton {

View File

@ -70,6 +70,8 @@ export interface IProj {
previewUrl: string;
gmtCreate: string;
projModCount: number;
projCategoryId: string;
projCategoryName: string;
generate: IProjGenerate;
pay: IProjPay;

View File

@ -8,6 +8,9 @@ import {get, put} from "../../util/AjaxUtils.ts";
import {GlobalContext, GlobalDataActionType, GlobalDispatchContext} from "../../context/GlobalContext.ts";
import UserEdit from "../../route/user/UserEdit.tsx";
import PasswordChange from "../../route/password/PasswordChange.tsx";
import headRightBg from '../../assets/head-right-bg.png';
import Payment from "../../route/payment/Payment.tsx";
export default function Head() {
const globalContext = useContext(GlobalContext);
@ -84,7 +87,7 @@ export default function Head() {
<Divider type="vertical"/>
<span className="sys-title-sub"></span>
</div>
<div className="right">
<div className="right" style={{backgroundImage: `url(${headRightBg})`}}>
<BalanceHead/>
<RechargeHead/>
{/*<MessageHead/>*/}
@ -179,6 +182,12 @@ export default function Head() {
});
}}/>
</Modal>
<Modal open={true}
title="充值"
footer={false}
>
<Payment/>
</Modal>
<Spin tip="正在提交..." spinning={loading} fullscreen/>
{contextHolder}
{modalHolder}

View File

@ -45,7 +45,6 @@
padding-left: 60px;
display: flex;
justify-content: right;
background-image: url('./head-right-bg.png');
}
.head .center .head-item {

View File

@ -1,18 +1,18 @@
import './index.css';
import {MouseEvent, Reducer, useEffect, useReducer} from "react";
import {MouseEvent, Reducer, useEffect, useReducer, useState} 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";
import ListProj from "../../components/list/ListProj.tsx";
import ListProjAgent from "../../components/list/ListProjAgent.tsx";
import {Breadcrumb} from 'antd';
import {Breadcrumb, MenuProps} from 'antd';
import {
IndexListContext,
IndexListDataType,
IndexListDispatchContext,
ListAction,
ListData
ListData,
} from "../../context/IndexListContext.ts";
export default function Index() {
@ -20,12 +20,16 @@ export default function Index() {
const [searchParams] = useSearchParams();
const listReducer = (state: ListData, action: ListAction) => {
if(action.type == IndexListDataType.PROJ) {
if (action.type == IndexListDataType.PROJ) {
state.type = IndexListDataType.PROJ;
state.status = action.value;
} else if(action.type == IndexListDataType.AGENT) {
state.status = action.value as string;
} else if (action.type == IndexListDataType.AGENT) {
state.type = IndexListDataType.AGENT;
state.status = action.value;
state.status = action.value as string;
} else if (action.type == IndexListDataType.CATEGORY) {
state.categorys = action.value as MenuProps['items'];
} else if (action.type == IndexListDataType.CATEGORY_CHANGE) {
state.category = action.value as string
}
return {
...state
@ -36,7 +40,7 @@ export default function Index() {
type: IndexListDataType.PROJ
});
const projMenu: IMenuWithTopButton = {
const [projMenu, setProjMenu] = useState<IMenuWithTopButton>({
button: {
name: '创建项目',
handle() {
@ -44,19 +48,25 @@ export default function Index() {
}
},
list: [
{id: 'ALL', name: '全部项目'},
{id: 'ALL', name: '全部项目', active: true},
{id: 'PROCESSING', name: '进行中的'},
{id: 'COMPLETE', name: '已完成的'}
],
handleListItem(_e, _index,item: IMenuListItem) {
handleListItem(_e, _index, item: IMenuListItem) {
projMenu.list.forEach(item => item.active = false);
agentMenu.list.forEach(item => item.active = false);
item.active = true;
setProjMenu({
...projMenu
})
dispatch({
type: IndexListDataType.PROJ,
value: item.id
})
}
}
});
const agentMenu: IMenuWithTopButton = {
const [agentMenu, setAgentMenu] = useState<IMenuWithTopButton>({
button: {
name: '代理服务',
handle() {
@ -72,16 +82,21 @@ export default function Index() {
{id: 'COMPLETE', name: '已完成的'},
],
handleListItem(_e: MouseEvent<HTMLLIElement>, _index: number, item: IMenuListItem) {
projMenu.list.forEach(item => item.active = false);
agentMenu.list.forEach(item => item.active = false);
item.active = true;
setAgentMenu({
...agentMenu
})
dispatch({
type: IndexListDataType.AGENT,
value: item.id
})
}
}
})
useEffect(() => {
if(searchParams.get('type') == 'agent') {
if (searchParams.get('type') == 'agent') {
dispatch({
type: IndexListDataType.AGENT,
value: 'ALL'

View File

@ -0,0 +1,438 @@
import './payment.css';
import {
Button,
ConfigProvider,
DatePicker,
Divider, Flex,
Form, GetProp, Image,
Input,
InputNumber,
message,
Radio,
Spin,
Upload, UploadFile, UploadProps
} from "antd";
import {
ReloadOutlined
} from '@ant-design/icons'
import {useEffect, useState} from "react";
import locale from 'antd/locale/zh_CN';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import {DevUserId, downloadUrl, get, post, uploadImageUrl} from "../../util/AjaxUtils.ts";
import {UploadOutlined} from "@ant-design/icons";
import useMessage from "antd/es/message/useMessage";
import {errorImage} from "../../util/CommonUtil.ts";
dayjs.locale('zh-cn');
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
type FormFieldType = {
thirdParty: string;
rechargeMoney: number;
orgName: string;
orgBank: string;
orgNumber: string;
rechargeFinalTime: string;
rechargeVoucher: string;
}
type PayType = {
accountRechargeId: string;
thirdPartyPayUrl: string;
}
type PaySystemBank = {
bankAccountName: string;
bankName: string;
bankNumber: string;
bankRemark: string;
bankUnionpayNumber: string;
}
enum ThirdPartyEnum {
WX = '微信',
ZFB = '支付宝',
DGZZ = '对公转账'
}
interface IPaymentProps {
handleConfirm(): void;
handleCancel(): void;
}
export default function Payment(props: IPaymentProps) {
const [form] = Form.useForm<FormFieldType>();
const [messageApi, messageApiContext] = useMessage();
const [isRechargeMoneyEdit, setIsRechargeMoneyEdit] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [rechargeVoucherArray, setRechargeVoucherArray] = useState<string[]>([]);
const [thirdParty, setThirdParty] = useState('');
const [accountRechargeId, setAccountRechargeId] = useState('');
const [thirdPartyPayUrl, setThirdPartyPayUrl] = useState('');
const [paySystemBank, setPaySystemBank] = useState<PaySystemBank>({
bankAccountName: '',
bankName: '',
bankNumber: '',
bankRemark: '',
bankUnionpayNumber: ''
});
const [countdownTime, setCountdownTime] = useState('');
const [isCountdownTimeout, setIsCountdownTimeout] = useState(false);
const moneyRange: number[] = [0.01, 2000];
let countdownInterval: number = -1;
/**
*
*/
const countdown = () => {
if (countdownInterval > -1) {
clearInterval(countdownInterval);
}
const Time = new Date().getTime();
// 设定计时器的时间为60秒
const countDownDate = new Date(Time + 270 * 1000).getTime();
// 更新计时器的秒数
const updateCountdown = () => {
const now = new Date().getTime();
const distance = countDownDate - now;
// 将毫秒数转换为分钟和秒数
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
// 渲染计时器
setCountdownTime(minutes + ":" + (seconds < 10 ? "0" : "") + seconds);
if (distance <= 0) {
setCountdownTime('已失效');
setIsCountdownTimeout(true);
clearInterval(countdownInterval);
}
}
// 每秒更新一次计时器
countdownInterval = setInterval(() => {
updateCountdown();
}, 1000);
}
const getPay = () => {
post<PayType>({
messageApi,
url: '/api/pay/get-pay',
body: {
rechargeMoney: form.getFieldValue('rechargeMoney'),
thirdParty: form.getFieldValue('thirdParty')
},
onBefore() {
setIsLoading(true);
},
onSuccess({data}) {
setAccountRechargeId(data.accountRechargeId);
setThirdPartyPayUrl(data.thirdPartyPayUrl);
clearInterval(countdownInterval);
if (thirdParty != ThirdPartyEnum.DGZZ) {
countdown();
}
},
onFinally() {
setIsLoading(false);
}
})
}
const getPaySystemBank = () => {
get<PaySystemBank>({
messageApi,
url: '/api/pay/get-pay-system-bank',
onSuccess({data}) {
setPaySystemBank(data);
}
})
}
useEffect(() => {
form.setFieldsValue({
thirdParty: ThirdPartyEnum.DGZZ,
rechargeMoney: 300
})
setThirdParty(ThirdPartyEnum.DGZZ);
getPaySystemBank();
getPay();
}, []);
const renderMoney = () => {
if (isRechargeMoneyEdit) {
return (
<div>
<InputNumber min={0.01} max={2000} defaultValue={form.getFieldValue('rechargeMoney')}/>
<Button type="link" onClick={() => {
const money = form.getFieldValue('rechargeMoney');
if (money < moneyRange[0]) {
form.setFieldValue('rechargeMoney', moneyRange[0]);
messageApi.error(`金额最小为${moneyRange[0]}`)
} else if (money > moneyRange[1]) {
form.setFieldValue('rechargeMoney', moneyRange[1]);
messageApi.error(`金额最大为${moneyRange[1]}`)
}
setIsRechargeMoneyEdit(false);
getPay();
}}></Button>
</div>
)
}
return (
<div>
<span></span>
<span style={{fontWeight: 'bold', fontSize: '16px'}}>{form.getFieldValue('rechargeMoney')}</span>
<Button type="link" onClick={() => {
setIsRechargeMoneyEdit(true);
}}></Button>
</div>
)
}
const renderPayBody = () => {
if (thirdParty == '对公转账') {
return (
<>
<div>
<Divider orientation="left" plain></Divider>
<table className="pay-table">
<colgroup>
<col width="100"/>
<col/>
</colgroup>
<tbody>
<tr>
<td className="table-label"></td>
<td>
<span id="bankAccountName">{paySystemBank.bankAccountName}</span>
</td>
</tr>
<tr>
<td className="table-label"></td>
<td>
<span id="bankName">{paySystemBank.bankName}</span>
</td>
</tr>
<tr>
<td className="table-label"></td>
<td>
<span id="bankNumber">{paySystemBank.bankNumber}</span>
</td>
</tr>
<tr>
<td className="table-label"></td>
<td>
<span id="bankUnionpayNumber">{paySystemBank.bankUnionpayNumber}</span>
</td>
</tr>
<tr>
<td className="table-label"></td>
<td>
<div id="bankRemark">{paySystemBank.bankRemark}</div>
</td>
</tr>
<tr>
<td className="table-label"></td>
<td>
<div className="mark"></div>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<Divider orientation="left" plain></Divider>
<table className="pay-table">
<colgroup>
<col width="100"/>
<col/>
</colgroup>
<tbody>
<tr>
<td className="table-label"> *</td>
<td>
<Form.Item
name="orgName"
style={{marginBottom: '0'}}
rules={[{required: true, message: '请输入公司名称'}]}
>
<Input placeholder="请输入公司名称"/>
</Form.Item>
</td>
</tr>
<tr>
<td className="table-label"> *</td>
<td>
<Form.Item
name="orgBank"
style={{marginBottom: '0'}}
rules={[{required: true, message: '请输入开户行'}]}
>
<Input placeholder="请输入开户行"/>
</Form.Item>
</td>
</tr>
<tr>
<td className="table-label"> *</td>
<td>
<Form.Item
name="rechargeFinalTime"
style={{marginBottom: '0'}}
rules={[{required: true, message: '请选择打款时间'}]}
>
<ConfigProvider locale={locale}>
<DatePicker showTime placeholder="请选择打款时间" onChange={(_date, dateString) => {
form.setFieldValue('rechargeFinalTime', dateString);
}}/>
</ConfigProvider>
</Form.Item>
</td>
</tr>
<tr>
<td className="table-label"> *</td>
<td>
<Form.Item
name="rechargeVoucher"
style={{marginBottom: '0'}}
rules={[{required: true, message: '请上传打款凭证'}]}
>
<Upload name="image"
maxCount={9}
action={uploadImageUrl()}
headers={{'X-USER-ID': DevUserId}}
beforeUpload={(file: FileType) => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('只能上传 JPG/PNG 格式文件!');
}
return isJpgOrPng;
}}
onChange={(info) => {
if (info.file.status === 'uploading') {
setIsLoading(true);
return;
}
if (info.file.status === 'done') {
setIsLoading(false);
info.file.uid = info.file.response.data.fileId;
rechargeVoucherArray.push(info.file.uid);
setRechargeVoucherArray([...rechargeVoucherArray]);
form.setFieldValue('rechargeVoucher', rechargeVoucherArray.join(','));
return;
}
}}
onRemove={(info) => {
console.log((info.uid))
const idArray = rechargeVoucherArray.filter(item => item != info.uid);
setRechargeVoucherArray([...idArray]);
form.setFieldValue('rechargeVoucher', rechargeVoucherArray.join(','));
}}
>
<Button icon={<UploadOutlined/>}></Button>
</Upload>
</Form.Item>
</td>
</tr>
</tbody>
</table>
</div>
</>
)
}
return <>
<Flex gap="middle" align="center" justify="center" vertical>
<div className="qr-code">
<Image src={thirdPartyPayUrl} fallback={errorImage} preview={false}/>
{
isCountdownTimeout ? (
<div className="qr-timeout" onClick={() => {
getPay()
}}>
<ReloadOutlined/>
<span className="label"></span>
</div>
) : <></>
}
</div>
<div>{countdownTime}</div>
</Flex>
</>
}
return (
<>
<Spin tip="正在提交..." spinning={isLoading}>
<Form
name="basic"
initialValues={{remember: true}}
form={form}
onFinish={() => {
post<any>({
messageApi,
url: `/api/pay/pay-account-recharge/${accountRechargeId}`,
body: {
thirdParty: form.getFieldValue('thirdParty'),
rechargeMoney: form.getFieldValue('rechargeMoney'),
orgName: thirdParty == ThirdPartyEnum.DGZZ ? form.getFieldValue('orgName') : '',
orgBank: thirdParty == ThirdPartyEnum.DGZZ ? form.getFieldValue('orgBank') : '',
orgNumber: thirdParty == ThirdPartyEnum.DGZZ ? form.getFieldValue('orgNumber') : '',
rechargeFinalTime: thirdParty == ThirdPartyEnum.DGZZ ? form.getFieldValue('rechargeFinalTime') : '',
rechargeVoucher: thirdParty == ThirdPartyEnum.DGZZ ? form.getFieldValue('rechargeVoucher') : '',
},
onBefore() {
setIsLoading(true);
},
onSuccess() {
props.handleConfirm();
},
onFinally() {
setIsLoading(false);
}
});
}}
autoComplete="off"
>
<Form.Item
label="支付方式"
name="thirdParty"
rules={[{required: true}]}
>
<Radio.Group onChange={(e) => {
form.setFieldValue('thirdParty', e.target.value);
setThirdParty(e.target.value);
getPay();
}} defaultValue="a">
<Radio value="微信"></Radio>
<Radio value="支付宝"></Radio>
{/*<Radio value="银联">银联</Radio>*/}
<Radio value="对公转账"></Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="充值金额"
name="rechargeMoney"
rules={[{required: true, message: '请输入金额'}]}
>
{renderMoney()}
</Form.Item>
{renderPayBody()}
<div style={{marginTop: '15px', textAlign: 'center'}}>
<Button type="primary" htmlType="submit" style={{backgroundColor: 'var(--color-primary)'}}>
</Button>
<Button type="default" style={{marginLeft: '15px'}} onClick={() => {
props.handleCancel();
}}></Button>
</div>
</Form>
</Spin>
{messageApiContext}
</>
)
}

View File

@ -0,0 +1,46 @@
.pay-table {
width: 100%;
border: 1px solid var(--color-border);
border-collapse: collapse;
}
.pay-table tr td {
padding: 10px 10px;
border: 1px solid var(--color-border);
}
.pay-table tr .table-label {
text-align: center;
background-color: #EEEEEE;
}
.pay-table tr td .mark {
color: red;
}
.qr-code {
position: relative;
}
.qr-timeout {
width: 330px;
height: 330px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: rgba(0,0,0,0.8);
position: absolute;
top: 0;
left: 0;
color: #FFF;
cursor: pointer;
}
.qr-timeout .anticon {
font-size: 90px;
}
.qr-timeout .label {
padding: 15px;
}

View File

@ -25,7 +25,7 @@ export default function ProjCreate() {
const height = window.innerHeight - 150;
useEffect(() => {
get({
get<any>({
messageApi: messageApi,
url: '/api/proj/charge/get',
onSuccess({data}) {

View File

@ -141,7 +141,7 @@ export default function ProjNew() {
open={isCreateModalOpen}
onOk={() => {
setIsCreateModalOpen(false);
post({
post<any>({
messageApi,
url: '/api/proj/create',
body: {

View File

@ -53,7 +53,7 @@ export default function ProjConfigLoginpage() {
};
const preview = () => {
post({
post<any>({
messageApi,
url: '/api/proj/preview',
body: {
@ -72,7 +72,7 @@ export default function ProjConfigLoginpage() {
}
useEffect(() => {
get({
get<any>({
messageApi,
url: `/api/proj/get/config-loginpage/${pathParams.projId}`,
onSuccess({data}) {

View File

@ -48,7 +48,7 @@ export default function ProjConfigLoginpageShow() {
};
const preview = () => {
post({
post<any>({
messageApi,
url: '/api/proj/preview',
body: {
@ -67,7 +67,7 @@ export default function ProjConfigLoginpageShow() {
}
useEffect(() => {
get({
get<any>({
messageApi,
url: `/api/proj/get/config-loginpage/${pathParams.projId}`,
onSuccess({data}) {

View File

@ -29,7 +29,7 @@ export default function ProjConfigMenuList() {
const height = window.innerHeight - 165;
const renderData = () => {
get<DataType>({
get<DataType[]>({
messageApi,
url: '/api/proj-menu/list',
config: {

View File

@ -25,7 +25,7 @@ export default function ProjConfigMenuListShow() {
const height = window.innerHeight - 165;
const renderData = () => {
get<DataType>({
get<DataType[]>({
messageApi,
url: '/api/proj-menu/list',
config: {

View File

@ -32,7 +32,7 @@ export default function ProjConfigModEdit() {
const height = window.innerHeight - 180;
useEffect(() => {
get({
get<any>({
messageApi,
url: `api/proj-mod/get/${pathParams.projModId}`,
onSuccess({data}) {

View File

@ -32,7 +32,7 @@ export default function ProjConfigModList() {
const height = window.innerHeight - 165;
const renderData = () => {
get<DataType>({
get<DataType[]>({
messageApi,
url: '/api/proj-mod/list',
config: {

View File

@ -31,7 +31,7 @@ export default function ProjConfigModListShow() {
const height = window.innerHeight - 165;
const renderData = () => {
get<DataType>({
get<DataType[]>({
messageApi,
url: '/api/proj-mod/list',
config: {

View File

@ -28,7 +28,7 @@ export default function ProjConfigModShow() {
const height = window.innerHeight - 180;
useEffect(() => {
get({
get<any>({
messageApi,
url: `api/proj-mod/get/${pathParams.projModId}`,
onSuccess({data}) {

View File

@ -46,7 +46,7 @@ export default function ProjEditStep3() {
const listEnvHard = () => {
return new Promise<IEnvHard[]>((resolve) => {
get({
get<IEnvHard[]>({
messageApi,
url: '/api/proj-env-hard/list',
onSuccess({data}) {
@ -57,7 +57,7 @@ export default function ProjEditStep3() {
}
const listEnvSoft = () => {
return new Promise<IEnvSoft[]>((resolve) => {
get({
get<IEnvSoft[]>({
messageApi,
url: '/api/proj-env-soft/list',
onSuccess({data}) {
@ -68,7 +68,7 @@ export default function ProjEditStep3() {
}
const listEnvLang = () => {
return new Promise<IEnvLang[]>((resolve) => {
get({
get<IEnvLang[]>({
messageApi,
url: '/api/proj/env/lang/list',
onSuccess({data}) {
@ -79,7 +79,7 @@ export default function ProjEditStep3() {
}
const listEnvTechnical = () => {
return new Promise<IEnvTechnical[]>((resolve) => {
get({
get<IEnvTechnical[]>({
messageApi,
url: '/api/proj/env/technical/list',
onSuccess({data}) {

View File

@ -43,7 +43,7 @@ export default function ProjEditStep3Show() {
const listEnvHard = () => {
return new Promise<IEnvHard[]>((resolve) => {
get({
get<IEnvHard[]>({
messageApi,
url: '/api/proj-env-hard/list',
onSuccess({data}) {
@ -54,7 +54,7 @@ export default function ProjEditStep3Show() {
}
const listEnvSoft = () => {
return new Promise<IEnvSoft[]>((resolve) => {
get({
get<IEnvSoft[]>({
messageApi,
url: '/api/proj-env-soft/list',
onSuccess({data}) {
@ -65,7 +65,7 @@ export default function ProjEditStep3Show() {
}
const listEnvLang = () => {
return new Promise<IEnvLang[]>((resolve) => {
get({
get<IEnvLang[]>({
messageApi,
url: '/api/proj/env/lang/list',
onSuccess({data}) {
@ -76,7 +76,7 @@ export default function ProjEditStep3Show() {
}
const listEnvTechnical = () => {
return new Promise<IEnvTechnical[]>((resolve) => {
get({
get<IEnvTechnical[]>({
messageApi,
url: '/api/proj/env/technical/list',
onSuccess({data}) {

View File

@ -40,7 +40,7 @@ export default function ProjEditStep4() {
const listArea = (pId: string) => {
return new Promise<ITree[]>((resolve) => {
get({
get<ITree[]>({
messageApi,
url: '/api/area/list-area-ztree',
config: {

View File

@ -37,7 +37,7 @@ export default function ProjEditStep4Show() {
const listArea = (pId: string) => {
return new Promise<ITree[]>((resolve) => {
get({
get<ITree[]>({
messageApi,
url: '/api/area/list-area-ztree',
config: {

View File

@ -26,7 +26,7 @@ export default function ProjEditStep6() {
const listLoginpage = () => {
return new Promise<ILoginpageList[]>(resolve => {
get({
get<ILoginpageList[]>({
messageApi,
url: '/api/loginpage/list',
onSuccess({data}) {

View File

@ -22,7 +22,7 @@ export default function ProjEditStep6Show() {
const listLoginpage = () => {
return new Promise<ILoginpageList[]>(resolve => {
get({
get<any>({
messageApi,
url: '/api/loginpage/list',
onSuccess({data}) {

View File

@ -1,4 +1,4 @@
import {createBrowserRouter} from "react-router-dom";
import {createHashRouter} from "react-router-dom";
import Index from "../route/index/Index.tsx";
import ProjCreate from "./proj/ProjCreate.tsx";
import ProjNew from "./proj/ProjNew.tsx";
@ -29,7 +29,7 @@ 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 = createHashRouter([
{
path: '/',
element: <Index />
@ -146,4 +146,5 @@ export const router = createBrowserRouter([
path: '/agent-result/:orderId',
element: <AgentResult />
}
// ], {basename: import.meta.env.BASE_URL})
])

View File

@ -2,7 +2,11 @@ import axios, {AxiosRequestConfig, AxiosResponse} from "axios";
import type {MessageInstance} from "antd/es/message/interface";
export const Axios = axios;
axios.defaults.baseURL = 'http://127.0.0.1:7025/copyright';
// axios.defaults.baseURL = '/copyright';
export const DevUserId: string = '80d3365e-0597-4988-979e-18ef1c3ec671';
// export const DevUserId: string = '';
type Req<T> = {
messageApi: MessageInstance;
@ -14,8 +18,6 @@ type Req<T> = {
onFinally?(): void;
}
axios.defaults.baseURL = 'http://127.0.0.1:7025/copyright';
axios.interceptors.request.use(config => {
if (config.method === 'get') {
config.data = {unused: 0} // 这个是关键点解决get 请求添加不上content_type

View File

@ -6,5 +6,6 @@ export default defineConfig({
plugins: [react()],
server: {
host: '0.0.0.0'
}
},
base: './'
})