Skip to content

Commit

Permalink
feat(mis): 管理系统仪表盘账户信息显示优化 (#1242)
Browse files Browse the repository at this point in the history
仪表盘中账户信息展示当前用户所在的账户信息,具体调整如下:

1. 左上角为账户名,右上角为上锁图标,下方为可用额度;
2. 当账户或者用户状态均为正常时:
   1. 右上角隐藏锁的图标;
   2. 有设置限额,可用额度=用户限额-已用额度;
   3. 未设置限额且账户在白名单中,可用额度无限,显示为"不限";
   4. 未设置限额且账户不在白名单中,可用额度=账户余额-封锁阈值;
3. 当账户或者用户有一个非正常状态时(封锁/欠费/限额):
   1. 右上角用锁表示不可用,避免用文字误导;
   2. 无可用额度,显示为“-”;

原设计示意:

![Image](https://github.com/PKUHPC/scow-internal-dev/assets/111728204/59365140-08dd-41c2-9a8a-cb8b5b68ed91)
兼容账户名超出的情况:
<img width="1116" alt="fe048395c30084236836df3698a613a"
src="https://github.com/PKUHPC/scow-internal-dev/assets/25954437/8deacdaa-7267-45a8-bab4-e0da53adb292">
实际完成示意:

![QQ截图20240509161310](https://github.com/PKUHPC/SCOW/assets/78541912/1d7e5a12-3c34-4f44-aaa9-84a5cc53cb4a)
  • Loading branch information
cuvalign committed May 10, 2024
1 parent a53bcad commit 875fe29
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 33 deletions.
7 changes: 7 additions & 0 deletions .changeset/grumpy-scissors-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@scow/grpc-api": minor
"@scow/mis-server": patch
"@scow/mis-web": patch
---

管理系统仪表盘账户信息显示卡片中可用余额逻辑和 UI 优化
8 changes: 8 additions & 0 deletions apps/mis-server/src/services/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ export const userServiceServer = plugin((server) => {
};
}

const tenant = await em.findOne(Tenant, { name: tenantName });
if (!tenant) {
throw <ServiceError>{ code:Status.NOT_FOUND, message: `Tenant ${tenantName} is not found.` };
}

return [{
accountStatuses: user.accounts.getItems().reduce((prev, curr) => {
const account = curr.account.getEntity();
Expand All @@ -115,6 +120,9 @@ export const userServiceServer = plugin((server) => {
jobChargeLimit: curr.jobChargeLimit ? decimalToMoney(curr.jobChargeLimit) : undefined,
usedJobCharge: curr.usedJobCharge ? decimalToMoney(curr.usedJobCharge) : undefined,
balance: decimalToMoney(curr.account.getEntity().balance),
isInWhitelist: Boolean(account.whitelist),
blockThresholdAmount:account.blockThresholdAmount ?
decimalToMoney(account.blockThresholdAmount) : decimalToMoney(tenant.defaultAccountBlockThreshold),
} as AccountStatus;
return prev;
}, {}),
Expand Down
33 changes: 33 additions & 0 deletions apps/mis-web/src/components/StatCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ import { styled } from "styled-components";

type Props = React.PropsWithChildren<{
title: React.ReactNode;
icon?: React.ReactNode;
}>;

const Title = styled.h3`
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;

const ChildrenContainer = styled.div`
Expand All @@ -29,7 +33,18 @@ const ChildrenContainer = styled.div`
flex: 1;
`;

const Header = styled.header`
display: flex;
justify-content: flex-start;
align-items: baseline;
`;

const IconContainer = styled.div`
flex-shrink: 0;
margin-left: clamp(1em, 12%, 5em);
`;

// 仅StorageSection使用,但StorageSection已注释
export const StatCard: React.FC<Props> = ({ children, title }) => {
return (
<Card
Expand All @@ -46,3 +61,21 @@ export const StatCard: React.FC<Props> = ({ children, title }) => {
);
};

export const AccountStatCard: React.FC<Props> = ({ children, title, icon }) => {
return (
<Card
style={{ height: "100%" }}
bodyStyle={{ display: "flex", flexDirection: "column", height: "100%" }}
>
<Header>
<Title>
{title}
</Title>
<IconContainer>{icon}</IconContainer>
</Header>
<ChildrenContainer>
{children}
</ChildrenContainer>
</Card>
);
};
6 changes: 1 addition & 5 deletions apps/mis-web/src/i18n/zh_cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,7 @@ export default {
account: {
title: "账户信息",
state: "状态",
balance: "可用余额",
status: {
blocked: "封锁",
normal: "正常",
},
balance: "可用额度",
alert: "您不属于任何一个账户。",
},
job: {
Expand Down
2 changes: 2 additions & 0 deletions apps/mis-web/src/models/UserSchemaModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ export const AccountStatus = Type.Object({
jobChargeLimit: Type.Optional(Money),
usedJobCharge: Type.Optional(Money),
balance: Type.Optional(Money),
isInWhitelist: Type.Optional(Type.Boolean()),
blockThresholdAmount:Type.Optional(Money),
});
export type AccountStatus = Static<typeof AccountStatus>;

Expand Down
42 changes: 17 additions & 25 deletions apps/mis-web/src/pageComponents/dashboard/AccountInfoSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { moneyToNumber } from "@scow/lib-decimal";
import { Alert, Col, Row, Statistic, StatisticProps } from "antd";
import React from "react";
import { Section } from "src/components/Section";
import { StatCard } from "src/components/StatCard";
import { AccountStatCard } from "src/components/StatCard";
import { useI18nTranslateToString } from "src/i18n";
import { UserStatus } from "src/models/User";
import type { AccountInfo } from "src/pages/dashboard";
Expand All @@ -27,6 +27,7 @@ interface Props {
info: Record<string, AccountInfo>;
}

// max-width: calc(100%/2 - 8px); 考虑换行后限制最大宽度
const CardContainer = styled.div`
flex: 1;
min-width: 300px;
Expand All @@ -39,7 +40,7 @@ const Container = styled.div`
`;

const Info: React.FC<StatisticProps> = (props) => (
<Col span={8}>
<Col span={24}>
<Statistic {...props} />
</Col>
);
Expand All @@ -52,9 +53,8 @@ export const AccountInfoSection: React.FC<Props> = ({ info }) => {
const t = useI18nTranslateToString();

const statusTexts = {
blocked: [t("dashboard.account.status.blocked"), "red", LockOutlined],
normal: [t("dashboard.account.status.normal"), "green", UnlockOutlined],

blocked: ["red", LockOutlined, "1"],
normal: ["green", UnlockOutlined, "0"],
} as const;

return (
Expand All @@ -67,37 +67,29 @@ export const AccountInfoSection: React.FC<Props> = ({ info }) => {
{
accounts.map(([accountName, {
accountBlocked, userStatus, balance,
jobChargeLimit, usedJobCharge,
jobChargeLimit, usedJobCharge, isInWhitelist, blockThresholdAmount,
}]) => {

const [text, textColor, Icon] = accountBlocked || userStatus === UserStatus.BLOCKED
? statusTexts.blocked
: statusTexts.normal;

const isBlocked = accountBlocked || userStatus === UserStatus.BLOCKED;
const [ textColor, Icon, opacity] = isBlocked ? statusTexts.blocked : statusTexts.normal;
const availableLimit = jobChargeLimit && usedJobCharge
? moneyToNumber(jobChargeLimit) - moneyToNumber(usedJobCharge)
? (moneyToNumber(jobChargeLimit) - moneyToNumber(usedJobCharge)).toFixed(2)
: undefined;

const minOne = availableLimit ? Math.min(availableLimit, balance) : balance;

const whitelistCharge = isInWhitelist ? "不限" : undefined;
const normalCharge = (balance - blockThresholdAmount).toFixed(2);
const showAvailableBalance = availableLimit ?? whitelistCharge ?? normalCharge;
return (
<CardContainer key={accountName}>
<StatCard title={`${accountName}`}>
<AccountStatCard title={`${accountName}`} icon={<Icon style={{ color:textColor, opacity }} />}>
<Row style={{ flex: 1, width: "100%" }}>
<Info
title={t("dashboard.account.state")}
valueStyle={{ color: textColor }}
prefix={<Icon />}
value={text}
/>
<Info
title={t("dashboard.account.balance")}
valueStyle={{ color: minOne < 0 ? "red" : undefined }}
prefix={"¥"}
value={minOne.toFixed(2)}
valueStyle={{ color:textColor }}
prefix={isBlocked ? "" : <span></span>}
value={isBlocked ? "-" : showAvailableBalance}
/>
</Row>
</StatCard>
</AccountStatCard>
</CardContainer>
);
})
Expand Down
9 changes: 6 additions & 3 deletions apps/mis-web/src/pages/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ import { UserStore } from "src/stores/UserStore";
import { ensureNotUndefined } from "src/utils/checkNull";


export type AccountInfo = Omit<AccountStatus, "balance" | "jobChargeLimit" | "usedJobCharge"> & {
export type AccountInfo = Omit<AccountStatus, "balance" | "jobChargeLimit" | "usedJobCharge"
| "blockThresholdAmount" > & {
balance: number;
jobChargeLimit: Money | null;
usedJobCharge: Money | null;
blockThresholdAmount: number
}

type Props = {
Expand Down Expand Up @@ -106,15 +108,16 @@ export const getServerSideProps: GetServerSideProps<Props> = async ({ req }) =>

const accounts = Object.entries(status.accountStatuses).reduce((prev, [accountName, info]) => {

const { balance, ...validated } = ensureNotUndefined(info, ["balance"]);
const { balance, blockThresholdAmount, ...validated }
= ensureNotUndefined(info, ["balance", "blockThresholdAmount"]);

prev[accountName] = {
...validated,
balance: moneyToNumber(balance),

// 不能使用undefined,NextJs中:`undefined` cannot be serialized as JSON
jobChargeLimit: validated.jobChargeLimit ?? null,
usedJobCharge: validated.usedJobCharge ?? null,
blockThresholdAmount:moneyToNumber(blockThresholdAmount),
};

return prev;
Expand Down
2 changes: 2 additions & 0 deletions protos/server/user.proto
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ message AccountStatus {
optional common.Money job_charge_limit = 3;
optional common.Money used_job_charge = 4;
common.Money balance = 5;
optional bool is_in_whitelist = 6;
common.Money block_threshold_amount = 7;
}

message GetUserStatusResponse {
Expand Down

0 comments on commit 875fe29

Please sign in to comment.