提交 f0e00dd8 作者: yueyang.lv

feat: 接入费用管理

上级 499847b0
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -67,7 +67,7 @@
"yarn": "^1.22.17"
},
"scripts": {
"start": "PORT=5005 react-app-rewired start",
"start": "HTTPS=true PORT=5005 react-app-rewired start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
......@@ -87,11 +87,11 @@
"last 1 safari version"
]
},
"proxy": "https://open-fdf34379-134d-4e0f-a3c4-8f2a2f5a2bc7-console.kube.ucas/",
"devDependencies": {
"@types/react-window": "^1.8.5",
"@types/react-window-infinite-loader": "^1.0.5",
"@types/recharts": "^1.8.22",
"http-proxy-middleware": "^2.0.6",
"prettier": "2.5.1",
"react-app-rewire-hot-loader": "^2.0.1",
"react-app-rewired": "^2.1.6",
......
......@@ -19,3 +19,23 @@ export const PATH = {
/** 合同管理 */
CONTRACT_MANAGE: createPath("/contract"),
};
/** 排序方式 */
export const SORT = {
/** 升序 */
ASC: {
value: 1,
code: "asc",
},
/** 降序 */
DESC: {
value: -1,
code: "desc",
},
} as const;
type SortType = typeof SORT;
/** 排序方式 */
export type SORT_TYPE = SortType[keyof SortType]["value"];
export type SORT_CODE_TYPE = SortType[keyof SortType]["code"];
......@@ -6,30 +6,30 @@ import { PATH } from "./constants";
import { IMenuItem } from "../Menu/types";
const menu: IMenuItem[] = [
// {
// name: "用户中心",
// id: "saturnUser",
// icon: UsersMenuIcon,
// forceDisplay: true,
// children: [
// // {
// // component: NavLink,
// // id: "saturnUserInfo",
// // name: "用户信息",
// // to: PATH.USERINFO,
// // icon: UsersMenuIcon,
// // forceDisplay: true,
// // },
// {
// component: NavLink,
// id: "costManage",
// name: "费用管理",
// to: PATH.COST_MANAGE,
// icon: AccountBalanceWallet,
// forceDisplay: true,
// },
// ],
// },
{
name: "用户中心",
id: "saturnUser",
icon: UsersMenuIcon,
forceDisplay: true,
children: [
// {
// component: NavLink,
// id: "saturnUserInfo",
// name: "用户信息",
// to: PATH.USERINFO,
// icon: UsersMenuIcon,
// forceDisplay: true,
// },
{
component: NavLink,
id: "costManage",
name: "费用管理",
to: PATH.COST_MANAGE,
icon: AccountBalanceWallet,
forceDisplay: true,
},
],
},
];
export default menu;
import React from "react";
import { DataGrid, GridColDef, GridValueGetterParams } from "@mui/x-data-grid";
import React, { useEffect, useState } from "react";
import { DataGrid, GridColDef } from "@mui/x-data-grid";
import PageLayout from "../../../Common/Layout/PageLayout";
import PageHeader from "../../../Common/PageHeader/PageHeader";
import { getCostList } from "../../services";
import { CostItem } from "../../services/interface";
import { SORT, SORT_TYPE } from "../../constants";
import { sortTypeCode2Value } from "../../utils";
import type { Pagination, Sort } from "../../types";
const columns: GridColDef[] = [
{
field: "id",
headerName: "账单号",
width: 220,
disableColumnMenu: true,
},
{
field: "date",
headerName: "交易时间",
width: 130,
disableColumnMenu: true,
},
{
field: "transaction_type",
headerName: "交易类型",
sortable: false,
width: 130,
disableColumnMenu: true,
},
{
field: "transaction_source",
headerName: "交易渠道",
sortable: false,
width: 130,
disableColumnMenu: true,
},
{
field: "transaction_money",
headerName: "交易金额/元",
sortable: false,
width: 130,
disableColumnMenu: true,
},
{
field: "account_balance",
headerName: "可用余额/元",
sortable: false,
// width: 130,
disableColumnMenu: true,
},
];
const CostHistory: React.FC = () => {
const columns: GridColDef[] = [
{
field: "accountId",
headerName: "账单号",
sortable: false,
width: 130,
disableColumnMenu: true,
},
{
field: "trade_type",
headerName: "交易类型",
sortable: false,
width: 130,
disableColumnMenu: true,
},
{
field: "trade_channel",
headerName: "交易渠道",
sortable: false,
width: 130,
disableColumnMenu: true,
},
{
field: "created_at",
headerName: "交易时间",
width: 130,
disableColumnMenu: true,
},
{
field: "trade_money",
headerName: "充值/元",
sortable: false,
width: 130,
disableColumnMenu: true,
},
{
field: "expenditure",
headerName: "支出/元",
sortable: false,
width: 130,
disableColumnMenu: true,
},
{
field: "account_money",
headerName: "可用余额/元",
sortable: false,
width: 130,
disableColumnMenu: true,
},
];
const [searchForm, setSearchForm] = useState<Pagination & Sort>({
page: 1,
pageSize: 2,
total: 0,
sort: SORT.DESC.value,
});
const [list, setList] = useState<CostItem[]>([]);
const getList = () => {
getCostList(searchForm).then((res) => {
setList(res.data.list || []);
setSearchForm((state) => ({ ...state, total: res.data.total }));
});
};
const rows = [
{ id: 1, lastName: "Snow", firstName: "Jon", age: 35 },
{ id: 2, lastName: "Lannister", firstName: "Cersei", age: 42 },
{ id: 3, lastName: "Lannister", firstName: "Jaime", age: 45 },
{ id: 4, lastName: "Stark", firstName: "Arya", age: 16 },
{ id: 5, lastName: "Targaryen", firstName: "Daenerys", age: null },
{ id: 6, lastName: "Melisandre", firstName: null, age: 150 },
{ id: 7, lastName: "Clifford", firstName: "Ferrara", age: 44 },
{ id: 8, lastName: "Frances", firstName: "Rossini", age: 36 },
{ id: 9, lastName: "Roxie", firstName: "Harvey", age: 65 },
];
useEffect(() => {
getList();
}, [searchForm.page, searchForm.sort]);
const pageChangeHandler = (page: number) => {
setSearchForm((state) => ({ ...state, page: page + 1 }));
};
const sortChangeHandler = (sorts) => {
const item = sorts.find((item) => item.field === "date");
let sort: SORT_TYPE = SORT.DESC.value;
if (item) {
sort = sortTypeCode2Value(item.sort);
}
setSearchForm((state) => ({
...state,
sort,
}));
};
return (
<>
<PageHeader label="交易记录"></PageHeader>
<PageLayout>
<div style={{ height: 400, width: "100%" }}>
<DataGrid
rows={rows}
rows={list}
columns={columns}
pageSize={10}
rowsPerPageOptions={[10]}
paginationMode="server"
isRowSelectable={() => false}
page={searchForm.page - 1}
pageSize={searchForm.pageSize}
rowCount={searchForm.total}
rowsPerPageOptions={[searchForm.pageSize - 1]}
autoHeight
onPageChange={pageChangeHandler}
onSortModelChange={sortChangeHandler}
/>
</div>
</PageLayout>
......
export default {};
import request from "./request";
import { ResultListPromise, CostItem } from "./interface";
import { Pagination, Sort } from "../types";
import { SORT } from "../constants";
/** 账单列表 */
export const getCostList = (
options?: Pagination & Sort
): ResultListPromise<CostItem> => {
return request.post("/api/user/bill/list", {
page: options?.page || 1,
page_size: options?.pageSize || 10,
sort: options?.sort || SORT.DESC.value,
});
};
export interface ListResult<T extends Record<string, any>> {
total: number;
page: number;
list: T[];
}
export interface ResultBody<T> {
code: number;
msg: string;
data: T;
}
export type ResultListPromise<T> = Promise<ResultBody<ListResult<T>>>;
/** 账单 */
export interface CostItem {
id: string;
date: string;
transaction_type: string;
transaction_source: string;
transaction_money: number;
account_balance: number;
}
import superagent from "superagent";
import message from "../../../../utils/message";
import { toLoginPage } from "../../../../utils/toLogin";
enum METHOD {
POST = "POST",
GET = "GET",
PUT = "PUT",
DELETE = "DELETE",
}
interface Options {
baseURL: string;
}
interface RequestHandlerOptions {
url: string;
methods: METHOD;
data: {
[key: string]: any;
params?: Record<string, any>;
};
}
class Request {
baseURL: string = "";
constructor(options: Options) {
this.baseURL = options.baseURL;
}
formateUrl(url) {
return this.baseURL + url;
}
requestHandler({ url, methods, data }: RequestHandlerOptions) {
const req = superagent(methods, this.formateUrl(url)).withCredentials();
const resultSuccessCallback = (res) => {
const body = res.body;
const code = body.code;
if (code !== 1000) {
message.error(body.msg);
if (code === 40001) {
toLoginPage();
}
return Promise.reject(res);
}
return body;
};
if (data) {
if (data.params && methods === METHOD.GET) {
return req.query(data.params).then(resultSuccessCallback);
}
return req.send(data).then(resultSuccessCallback);
}
return req;
}
get(url, data?) {
return this.requestHandler({ url, methods: METHOD.GET, data });
}
post(url, data?) {
return this.requestHandler({ url, methods: METHOD.POST, data });
}
put(url, data?) {
return this.requestHandler({ url, methods: METHOD.PUT, data });
}
delete(url, data?) {
return this.requestHandler({ url, methods: METHOD.POST, data });
}
}
let baseURL = "https://test-www.kube.ucas";
if (process.env.NODE_ENV === "development") {
baseURL = "/saturn";
}
const request = new Request({ baseURL });
export default request;
import { SORT_TYPE } from "./constants";
/** 分页 */
export interface Pagination {
/** 当前页 */
page?: number;
/** 每页条数 */
pageSize?: number;
/** 总条数 */
total?: number;
}
/** 排序 */
export interface Sort {
/** 排序方式 */
sort?: SORT_TYPE;
}
import { SORT, SORT_TYPE, SORT_CODE_TYPE } from "../constants";
/** 将 sort 的值 变成 desc / asc */
export const sortValue2TypeCode = (sort: SORT_TYPE) => {
return Object.values(SORT).find((s) => s.value === sort).code;
};
/** 将 desc / asc 变成 后端需要的 sort 的值 */
export const sortTypeCode2Value = (sort: SORT_CODE_TYPE) => {
return Object.values(SORT).find((s) => s.code === sort).value;
};
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api/v1/',
createProxyMiddleware({
target: 'http://open-fdf34379-134d-4e0f-a3c4-8f2a2f5a2bc7-console.kube.ucas/',
changeOrigin: true,
pathRewrite: { "": "" },
})
);
app.use(
'/saturn/api/',
createProxyMiddleware({
target: 'https://test-www.kube.ucas/',
changeOrigin: true,
pathRewrite: {
'^/saturn/api/': '/api/'
},
secure: false
})
);
};
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论