hyapp-admin-platform/finance/src/components/FinanceMyApplicationList.jsx
2026-07-02 10:54:00 +08:00

157 lines
6.7 KiB
JavaScript

import AddOutlined from "@mui/icons-material/AddOutlined";
import RefreshOutlined from "@mui/icons-material/RefreshOutlined";
import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import TextField from "@mui/material/TextField";
import { APPLICATION_STATUS } from "../constants.js";
import { formatAmount, formatTime, operationLabel, statusLabel, walletIdentityLabel } from "../format.js";
export function FinanceMyApplicationList({ applications, error, filters, loading, onCreate, onFiltersChange, onReload, operations, options }) {
const items = applications.items || [];
const page = Number(applications.page || filters.page || 1);
const pageSize = Number(applications.pageSize || 50);
const total = Number(applications.total || 0);
const hasNextPage = page * pageSize < total;
return (
<section className="finance-panel finance-list">
<div className="finance-toolbar finance-toolbar--with-action">
<TextField label="APP" select value={filters.appCode} onChange={(event) => onFiltersChange({ appCode: event.target.value })}>
<MenuItem value="">全部 APP</MenuItem>
{options.apps.map((app) => (
<MenuItem key={app.appCode} value={app.appCode}>
{app.appName || app.appCode}
</MenuItem>
))}
</TextField>
<TextField label="操作内容" select value={filters.operation} onChange={(event) => onFiltersChange({ operation: event.target.value })}>
<MenuItem value="">全部操作</MenuItem>
{operations.map((operation) => (
<MenuItem key={operation.value} value={operation.value}>
{operation.label}
</MenuItem>
))}
</TextField>
<TextField label="状态" select value={filters.status} onChange={(event) => onFiltersChange({ status: event.target.value })}>
<MenuItem value="">全部状态</MenuItem>
{APPLICATION_STATUS.map(([value, label]) => (
<MenuItem key={value} value={value}>
{label}
</MenuItem>
))}
</TextField>
<TextField label="搜索" value={filters.keyword} onChange={(event) => onFiltersChange({ keyword: event.target.value })} />
<Button startIcon={<RefreshOutlined fontSize="small" />} variant="outlined" onClick={onReload}>
刷新列表
</Button>
<Button startIcon={<AddOutlined fontSize="small" />} variant="contained" onClick={onCreate}>
添加
</Button>
</div>
{error ? <div className="finance-error">{error}</div> : null}
{loading && !items.length ? <div className="finance-skeleton" /> : null}
{!loading || items.length ? (
<TableContainer className="finance-table-wrap">
<Table className="finance-table" size="small" stickyHeader>
<TableHead>
<TableRow>
<TableCell>APP</TableCell>
<TableCell>操作</TableCell>
<TableCell>目标用户</TableCell>
<TableCell align="right">金币数量</TableCell>
<TableCell align="right">充值金额</TableCell>
<TableCell>凭证</TableCell>
<TableCell>状态</TableCell>
<TableCell className="finance-time-cell">发起申请时间</TableCell>
<TableCell>审批人</TableCell>
<TableCell className="finance-time-cell">审核时间</TableCell>
</TableRow>
</TableHead>
<TableBody>
{items.length ? (
items.map((item) => (
<TableRow key={item.id}>
<TableCell>{item.appName || item.appCode || "-"}</TableCell>
<TableCell>
<Stacked primary={operationLabel(item.operation, operations)} secondary={walletIdentityLabel(item.walletIdentity)} />
</TableCell>
<TableCell>{item.targetUserId || "-"}</TableCell>
<TableCell align="right">{formatAmount(item.coinAmount)}</TableCell>
<TableCell align="right">{formatAmount(item.rechargeAmount)}</TableCell>
<TableCell>
<Evidence item={item} />
</TableCell>
<TableCell>
<StatusBadge status={item.status} />
</TableCell>
<TableCell className="finance-time-cell">{formatTime(item.createdAtMs)}</TableCell>
<TableCell>
<Stacked primary={item.auditorName || "-"} secondary={item.auditorUserId || ""} />
</TableCell>
<TableCell className="finance-time-cell">{formatTime(item.auditedAtMs)}</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell align="center" colSpan={10}>
当前无数据
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
) : null}
{total > pageSize ? (
<div className="finance-pagination">
<Button disabled={page <= 1 || loading} variant="outlined" onClick={() => onFiltersChange({ page: page - 1 })}>
上一页
</Button>
<span>
{page} / {total}
</span>
<Button disabled={!hasNextPage || loading} variant="outlined" onClick={() => onFiltersChange({ page: page + 1 })}>
下一页
</Button>
</div>
) : null}
</section>
);
}
function Evidence({ item }) {
if (!item.credentialImageUrl && !item.credentialText) {
return "-";
}
return (
<span className="finance-evidence">
{item.credentialImageUrl ? (
<a href={item.credentialImageUrl} rel="noreferrer" target="_blank">
图片
</a>
) : null}
{item.credentialText ? <span title={item.credentialText}>{item.credentialText}</span> : null}
</span>
);
}
function Stacked({ primary, secondary }) {
return (
<span className="finance-stack">
<span>{primary || "-"}</span>
<small>{secondary || ""}</small>
</span>
);
}
function StatusBadge({ status }) {
const tone = status === "approved" ? "success" : status === "rejected" ? "danger" : "warning";
return <span className={`finance-status finance-status--${tone}`}>{statusLabel(status)}</span>;
}