- {renderList()}
+ <>
+ {contextHolder}
+
+
+
-
-
+
+
+ {renderList()}
+
+
+
{
+ setPage(page);
+ reqData(page);
+ }}/>
+
-
+ >
)
}
\ No newline at end of file
diff --git a/src/interfaces/proj/ICardProj.ts b/src/interfaces/proj/ICardProj.ts
index acfe14a..da83efe 100644
--- a/src/interfaces/proj/ICardProj.ts
+++ b/src/interfaces/proj/ICardProj.ts
@@ -1,18 +1,24 @@
+
export interface ICardProjBodyLine {
title: string;
contents: string[];
}
export interface ICardProjChargeLine {
+ id: string;
title: string;
price: number;
}
export interface ICardProjBuy {
+ id: string;
label?: string,
price: number;
- handleClick(title: string, price: number): void;
+ handleClick(title: string, additional: {
+ pkg: boolean,
+ videoDemo: boolean
+ }): void;
}
export interface ICardProj {
diff --git a/src/interfaces/proj/IProj.ts b/src/interfaces/proj/IProj.ts
index b4490d3..1eefa8e 100644
--- a/src/interfaces/proj/IProj.ts
+++ b/src/interfaces/proj/IProj.ts
@@ -20,6 +20,34 @@ export enum GenerateStatus {
ERROR = 'ERROR'
}
+export enum ProjChargeType {
+ ALL = 'ALL',
+ MATERIAL_AGENT = 'MATERIAL_AGENT',
+ MATERIAL_AGENT_URGENT = 'MATERIAL_AGENT_URGENT',
+ MATERIAL = 'MATERIAL',
+ FREE = 'FREE',
+}
+
+export enum ProjAdditionalType {
+ PKG = 'PKG',
+ VIDEO_DEMO = 'VIDEO_DEMO'
+}
+
+export interface IProjCharge {
+ proj: {
+ all: number;
+ materialAgent: number;
+ materialAgentUrgent: number;
+ material: number;
+ free: number;
+ },
+ additional: {
+ pkg: number;
+ videoDemo: number;
+ }
+}
+
+
export interface IProj {
projId: string,
@@ -33,5 +61,4 @@ export interface IProj {
gmtCreate: string,
generateStatus: GenerateStatus
-
-}
\ No newline at end of file
+}
diff --git a/src/layout/body/Body.tsx b/src/layout/body/Body.tsx
index 9f0a804..e296284 100644
--- a/src/layout/body/Body.tsx
+++ b/src/layout/body/Body.tsx
@@ -16,11 +16,11 @@ const router = createBrowserRouter([
element:
},
{
- path: '/proj-new/:type/:price',
+ path: '/proj-new/:projChargeType',
element:
},
{
- path: '/proj-edit',
+ path: '/proj-edit/:projId',
element:
},
{
diff --git a/src/main.tsx b/src/main.tsx
index 8224efa..08b21dc 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,4 +1,3 @@
-import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
diff --git a/src/route/proj/ProjCreate.tsx b/src/route/proj/ProjCreate.tsx
index 8255557..2ac2f3e 100644
--- a/src/route/proj/ProjCreate.tsx
+++ b/src/route/proj/ProjCreate.tsx
@@ -1,13 +1,42 @@
import './proj-create.css'
import {Link, useNavigate} from "react-router-dom";
-import {Breadcrumb} from "antd";
+import {Breadcrumb, message} from "antd";
import CardProjType from "../../components/card/CardProjType.tsx";
+import {IProjCharge, ProjAdditionalType, ProjChargeType} from "../../interfaces/proj/IProj.ts";
+import {useEffect, useState} from "react";
+import {get} from "../../util/AjaxUtils.ts";
export default function ProjCreate() {
+ const [messageApi, contextHolder] = message.useMessage();
+ const [charge, setCharge] = useState
({
+ proj: {
+ all: 0,
+ materialAgent: 0,
+ materialAgentUrgent: 0,
+ material: 0,
+ free: 0,
+ },
+ additional: {
+ pkg: 0,
+ videoDemo: 0
+ }
+ });
const nav = useNavigate();
const height = window.innerHeight - 150;
+
+ useEffect(() => {
+ get({
+ messageApi: messageApi,
+ url: '/api/proj/charge/get',
+ onSuccess({data}) {
+ setCharge(data);
+ }
+ })
+ }, [])
+
return (
<>
+ {contextHolder}
首页},
@@ -15,7 +44,7 @@ export default function ProjCreate() {
]}
/>
-
+
{
- nav(`/proj-new/q/${price}`)
+ id: ProjChargeType.ALL,
+ price: charge.proj.all,
+ handleClick: () => {
+ nav(`/proj-new/${ProjChargeType.ALL}`)
}
}
]}
@@ -72,27 +102,31 @@ export default function ProjCreate() {
]}
chargeLineArray={[
{
- price: 100,
- title: '安装包100元'
+ id: ProjAdditionalType.PKG,
+ price: charge.additional.pkg,
+ title: `安装包 ${charge.additional.pkg / 100} 元`
},
{
- price: 50,
- title: '系统演示视频文件50元'
+ id: ProjAdditionalType.VIDEO_DEMO,
+ price: charge.additional.videoDemo,
+ title: `系统演示视频文件 ${charge.additional.videoDemo / 100} 元`
}
]}
buyArray={[
{
+ id: ProjChargeType.MATERIAL_AGENT,
label: '普件:',
- price: 200,
- handleClick: (_title, price) => {
- nav(`/proj-new/xdp/${price}`)
+ price: charge.proj.materialAgent,
+ handleClick: (_title, additional) => {
+ nav(`/proj-new/${ProjChargeType.MATERIAL_AGENT}?pkg=${additional.pkg}&videoDemo=${additional.videoDemo}`)
}
},
{
+ id: ProjChargeType.MATERIAL_AGENT_URGENT,
label: '加急:',
- price: 300,
- handleClick: (_title, price) => {
- nav(`/proj-new/xdj/${price}`)
+ price: charge.proj.materialAgentUrgent,
+ handleClick: (_title, additional) => {
+ nav(`/proj-new/${ProjChargeType.MATERIAL_AGENT_URGENT}?pkg=${additional.pkg}&videoDemo=${additional.videoDemo}`)
}
}
]}
@@ -123,19 +157,22 @@ export default function ProjCreate() {
]}
chargeLineArray={[
{
- price: 100,
- title: '安装包100元'
+ id: ProjAdditionalType.PKG,
+ price: charge.additional.pkg,
+ title: `安装包 ${charge.additional.pkg / 100} 元`
},
{
- price: 50,
- title: '系统演示视频文件50元'
+ id: ProjAdditionalType.VIDEO_DEMO,
+ price: charge.additional.videoDemo,
+ title: `系统演示视频文件 ${charge.additional.videoDemo / 100} 元`
}
]}
buyArray={[
{
- price: 200,
- handleClick: (_title, price) => {
- nav(`/proj-new/x/${price}`)
+ id: ProjChargeType.MATERIAL,
+ price: charge.proj.material,
+ handleClick: () => {
+ nav(`/proj-new/${ProjChargeType.MATERIAL}`)
}
}
]}
@@ -159,9 +196,10 @@ export default function ProjCreate() {
]}
buyArray={[
{
- price: 0,
- handleClick: (_title, price) => {
- nav(`/proj-new/m/${price}`)
+ id: ProjChargeType.FREE,
+ price: charge.proj.free,
+ handleClick: () => {
+ nav(`/proj-new/${ProjChargeType.FREE}`)
}
}
]}
diff --git a/src/route/proj/ProjNew.tsx b/src/route/proj/ProjNew.tsx
index c0848db..8ce38b9 100644
--- a/src/route/proj/ProjNew.tsx
+++ b/src/route/proj/ProjNew.tsx
@@ -1,38 +1,76 @@
import './proj-new.css';
-import {Link, useNavigate, useParams} from "react-router-dom";
-import {Breadcrumb, Button, Flex, Form, type FormProps, Input, Modal} from "antd";
-import {useState} from "react";
+import {Link, useNavigate, useParams, useSearchParams} from "react-router-dom";
+import {Breadcrumb, Button, Flex, Form, Input, message, Modal, Spin} from "antd";
+import {useEffect, useState} from "react";
+import {get, post} from "../../util/AjaxUtils.ts";
+import {IProjCharge, ProjAdditionalType, ProjChargeType} from "../../interfaces/proj/IProj.ts";
const {TextArea} = Input;
-type FieldType = {
- projTitle: string;
- projDesc: string;
+type ProjInfo = {
+ projName: string;
+ projIntroduction: string;
};
export default function ProjNew() {
- const height = window.innerHeight - 150;
- const [isModalOpen, setIsModalOpen] = useState(false);
const nav = useNavigate();
- const params = useParams();
+ const pathParams = useParams();
+ const [queryParams] = useSearchParams();
- const onFinish: FormProps["onFinish"] = (values) => {
- console.log('Success:', values);
- setIsModalOpen(true);
- };
+ const [messageApi, contextHolder] = message.useMessage();
+ const [loading, setLoading] = useState(false);
+ const height = window.innerHeight - 150;
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
+ const [isEditModalOpen, setIsEditModalOpen] = useState(false);
+ const [chargePrice, setChargePrice] = useState(0);
+ const [projInfo, setProjInfo] = useState({
+ projName: '',
+ projIntroduction: '',
+ });
+ const listProjChargeAdditional: string[] = [];
+ let createProjId = '';
- const onFinishFailed: FormProps["onFinishFailed"] = (errorInfo) => {
- console.log('Failed:', errorInfo);
- };
-
- const handleConfirmOk = () => {
- // 扣款
- nav('/proj-edit');
- }
-
- const handleConfirmCancel = () => {
- setIsModalOpen(false);
- }
+ useEffect(() => {
+ get({
+ messageApi: messageApi,
+ url: '/api/proj/charge/get',
+ onSuccess({data}) {
+ const charge = data as IProjCharge;
+ let price = 0;
+ switch (pathParams.projChargeType) {
+ case ProjChargeType.ALL:
+ price = charge.proj.all;
+ break;
+ case ProjChargeType.MATERIAL_AGENT:
+ price = charge.proj.materialAgent;
+ break;
+ case ProjChargeType.MATERIAL_AGENT_URGENT:
+ price = charge.proj.materialAgentUrgent;
+ break;
+ case ProjChargeType.MATERIAL:
+ price = charge.proj.material;
+ break;
+ case ProjChargeType.FREE:
+ price = charge.proj.free;
+ break;
+ default:
+ messageApi.error({
+ type: 'error',
+ content: '价格类型错误',
+ })
+ }
+ if (queryParams.get('pkg')) {
+ price += charge.additional.pkg;
+ listProjChargeAdditional.push(ProjAdditionalType.PKG);
+ }
+ if (queryParams.get('videoDemo')) {
+ price += charge.additional.videoDemo;
+ listProjChargeAdditional.push(ProjAdditionalType.VIDEO_DEMO);
+ }
+ setChargePrice(price);
+ }
+ })
+ }, []);
const onBack = () => {
nav(-1);
@@ -40,6 +78,7 @@ export default function ProjNew() {
return (
<>
+ {contextHolder}
首页},
@@ -58,21 +97,26 @@ export default function ProjNew() {
wrapperCol={{span: 24}}
style={{width: 500}}
initialValues={{remember: true}}
- onFinish={onFinish}
- onFinishFailed={onFinishFailed}
+ onFinish={(formData) => {
+ setIsCreateModalOpen(true);
+ setProjInfo({
+ projName: formData.projName,
+ projIntroduction: formData.projIntroduction
+ })
+ }}
autoComplete="off"
>
-
+
label="系统标题"
- name="projTitle"
+ name="projName"
rules={[{required: true, message: '请输入系统标题'}]}
>
-
+
label="简单描述"
- name="projDesc"
+ name="projIntroduction"
rules={[{required: true, message: '请输入简单描述'}]}
>
@@ -80,7 +124,8 @@ export default function ProjNew() {
-
-
- 该操作会扣除{params.price}元,确定操作码?
+ {
+ setIsCreateModalOpen(false);
+ post({
+ messageApi,
+ url: '/api/proj/create',
+ body: {
+ projName: projInfo.projName,
+ projIntroduction: projInfo.projIntroduction,
+ projChargeType: pathParams.projChargeType,
+ listProjChargeAdditional: listProjChargeAdditional
+ },
+ onBefore() {
+ setLoading(true);
+ },
+ onSuccess({data}) {
+ setIsEditModalOpen(true);
+ createProjId = data.data;
+ },
+ onFinally() {
+ setLoading(false);
+ }
+ })
+ }}
+ onCancel={() => {
+ setIsCreateModalOpen(false);
+ }}>
+ 该操作会扣除 {chargePrice / 100} 元,确定操作码?
+ {
+ nav(`/proj-edit/${createProjId}`);
+ }}
+ onCancel={() => {
+ setIsEditModalOpen(false);
+ }}>
+ 项目创建成功,开始编辑项目?
+
+
>
)
}
\ No newline at end of file
diff --git a/src/util/AjaxUtils.ts b/src/util/AjaxUtils.ts
index 5cc39ce..3b9e660 100644
--- a/src/util/AjaxUtils.ts
+++ b/src/util/AjaxUtils.ts
@@ -1,6 +1,100 @@
-import axios from "axios";
+import axios, {AxiosRequestConfig, AxiosResponse} from "axios";
+import type {MessageInstance} from "antd/es/message/interface";
+
+type Req = {
+ messageApi: MessageInstance;
+ url: string;
+ body?: any;
+ config?: AxiosRequestConfig;
+ onBefore?(): void;
+ onSuccess(data: AxiosResponse): void;
+ onFinally?(): void;
+}
axios.defaults.baseURL = 'http://127.0.0.1:7025/copyright';
-axios.defaults.headers['X-USER-ID'] = '80d3365e-0597-4988-979e-18ef1c3ec671';
+axios.interceptors.request.use(config => {
+ if (config.method === 'get') {
+ config.data = {unused: 0} // 这个是关键点,解决get 请求添加不上content_type
+ }
+ config.headers['X-USER-ID'] = '80d3365e-0597-4988-979e-18ef1c3ec671';
+ return config
+ }
+);
-export const Axios = axios;
\ No newline at end of file
+export const Axios = axios;
+
+export function get(req: Req) {
+ req.onBefore?.();
+ Axios.get(req.url, req.config).then(res => {
+ req.onSuccess(res);
+ }).catch(error => {
+ if (error.response) {
+ const data = error.response.data;
+ req.messageApi.open({
+ type: 'error',
+ content: data.msg ? data.msg : `${data.path}(${data.status})`,
+ });
+ } else {
+ console.error(error)
+ }
+ }).finally(() => {
+ req.onFinally?.();
+ })
+}
+
+export function post(req: Req) {
+ req.onBefore?.();
+ Axios.post(req.url, req.body, req.config).then(res => {
+ req.onSuccess(res);
+ }).catch(error => {
+ if (error.response) {
+ const data = error.response.data;
+ req.messageApi.open({
+ type: 'error',
+ content: data.msg ? data.msg : `${data.path}(${data.status})`,
+ });
+ } else {
+ console.error(error)
+ }
+ }).finally(() => {
+ req.onFinally?.();
+ })
+}
+
+export function put(req: Req) {
+ req.onBefore?.();
+ Axios.put(req.url, req.config).then(res => {
+ req.onSuccess(res);
+ }).catch(error => {
+ if (error.response) {
+ const data = error.response.data;
+ req.messageApi.open({
+ type: 'error',
+ content: data.msg ? data.msg : `${data.path}(${data.status})`,
+ });
+ } else {
+ console.error(error)
+ }
+ }).finally(() => {
+ req.onFinally?.();
+ })
+}
+
+export function del(req: Req) {
+ req.onBefore?.();
+ Axios.delete(req.url, req.config).then(res => {
+ req.onSuccess(res);
+ }).catch(error => {
+ if (error.response) {
+ const data = error.response.data;
+ req.messageApi.open({
+ type: 'error',
+ content: data.msg ? data.msg : `${data.path}(${data.status})`,
+ });
+ } else {
+ console.error(error)
+ }
+ }).finally(() => {
+ req.onFinally?.();
+ })
+}
\ No newline at end of file