Compare commits
6 Commits
39bc0c5c7b
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 511ce4ff65 | |||
| 64fe23f877 | |||
| 09c5216f60 | |||
| 4d84095af2 | |||
| d66b664198 | |||
| a7d89acc7f |
@@ -54,6 +54,10 @@ function toUserSubscribePayload(values: SubscriptionFormValues) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolvePlanSpeed(row: API.UserSubscribe) {
|
||||||
|
return row.plan_speed_limit ?? row.subscribe?.speed_limit ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
export default function UserSubscription({ userId }: { userId: number }) {
|
export default function UserSubscription({ userId }: { userId: number }) {
|
||||||
const { t } = useTranslation("user");
|
const { t } = useTranslation("user");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -315,10 +319,11 @@ export default function UserSubscription({ userId }: { userId: number }) {
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const userSpeed = row.original.speed_limit ?? 0;
|
const userSpeed = row.original.speed_limit ?? 0;
|
||||||
const effectiveSpeed = row.original.effective_speed ?? 0;
|
const effectiveSpeed = row.original.effective_speed ?? 0;
|
||||||
const planSpeed = row.original.subscribe?.speed_limit ?? 0;
|
const planSpeed = resolvePlanSpeed(row.original);
|
||||||
const speed = effectiveSpeed || userSpeed || planSpeed;
|
const speed = effectiveSpeed || userSpeed || planSpeed;
|
||||||
const source =
|
const source = row.original.is_throttled
|
||||||
userSpeed > 0
|
? t("effectiveSpeed", "Effective Speed")
|
||||||
|
: userSpeed > 0
|
||||||
? t("userOverride", "User Override")
|
? t("userOverride", "User Override")
|
||||||
: t("subscriptionDefault", "Subscription Default");
|
: t("subscriptionDefault", "Subscription Default");
|
||||||
|
|
||||||
@@ -335,6 +340,16 @@ export default function UserSubscription({ userId }: { userId: number }) {
|
|||||||
<span className="text-muted-foreground text-xs">
|
<span className="text-muted-foreground text-xs">
|
||||||
{source}
|
{source}
|
||||||
</span>
|
</span>
|
||||||
|
<span className="text-muted-foreground text-xs">
|
||||||
|
{t("userOverride", "User Override")}:{" "}
|
||||||
|
<span className="inline-block">
|
||||||
|
<Display type="trafficSpeed" value={userSpeed} />
|
||||||
|
</span>{" "}
|
||||||
|
/ {t("subscriptionDefault", "Subscription Default")}:{" "}
|
||||||
|
<span className="inline-block">
|
||||||
|
<Display type="trafficSpeed" value={planSpeed} />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -87,11 +87,12 @@ function SpeedLimitCard({ subscriptionId }: { subscriptionId: number }) {
|
|||||||
|
|
||||||
if (detail.status !== 1) return null;
|
if (detail.status !== 1) return null;
|
||||||
|
|
||||||
const baseSpeed = detail.subscribe?.speed_limit ?? 0;
|
const baseSpeed =
|
||||||
|
detail.plan_speed_limit ?? detail.subscribe?.speed_limit ?? 0;
|
||||||
const userSpeed = detail.speed_limit ?? 0;
|
const userSpeed = detail.speed_limit ?? 0;
|
||||||
const effectiveSpeed = detail.effective_speed ?? 0;
|
const effectiveSpeed = detail.effective_speed ?? 0;
|
||||||
const isThrottled = detail.is_throttled;
|
const isThrottled = detail.is_throttled;
|
||||||
const rules = detail.traffic_limit || [];
|
const rules = detail.user_traffic_limit || detail.traffic_limit || [];
|
||||||
const hasLimitConfig =
|
const hasLimitConfig =
|
||||||
baseSpeed > 0 || userSpeed > 0 || effectiveSpeed > 0 || rules.length > 0;
|
baseSpeed > 0 || userSpeed > 0 || effectiveSpeed > 0 || rules.length > 0;
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ interface Props {
|
|||||||
|
|
||||||
type SubscriptionTrafficLimitRule = API.UserSubscribeTrafficLimitRule;
|
type SubscriptionTrafficLimitRule = API.UserSubscribeTrafficLimitRule;
|
||||||
|
|
||||||
|
function getUserTrafficLimitRules(
|
||||||
|
data?: Pick<API.UserSubscribe, "traffic_limit" | "user_traffic_limit">
|
||||||
|
) {
|
||||||
|
return data?.user_traffic_limit || data?.traffic_limit || [];
|
||||||
|
}
|
||||||
|
|
||||||
const defaultLimitRule: SubscriptionTrafficLimitRule = {
|
const defaultLimitRule: SubscriptionTrafficLimitRule = {
|
||||||
stat_type: "day",
|
stat_type: "day",
|
||||||
stat_value: 1,
|
stat_value: 1,
|
||||||
@@ -98,7 +104,7 @@ export function SubscriptionForm({
|
|||||||
traffic: initialData?.traffic || 0,
|
traffic: initialData?.traffic || 0,
|
||||||
speed_limit: initialData?.speed_limit || 0,
|
speed_limit: initialData?.speed_limit || 0,
|
||||||
device_limit: initialData?.device_limit || 0,
|
device_limit: initialData?.device_limit || 0,
|
||||||
traffic_limit: initialData?.traffic_limit || [],
|
traffic_limit: getUserTrafficLimitRules(initialData),
|
||||||
upload: initialData?.upload || 0,
|
upload: initialData?.upload || 0,
|
||||||
download: initialData?.download || 0,
|
download: initialData?.download || 0,
|
||||||
expired_at: initialData?.expire_time || 0,
|
expired_at: initialData?.expire_time || 0,
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const WITHDRAWAL_METHOD_MAP: Record<number, string> = {
|
|||||||
0: "其他",
|
0: "其他",
|
||||||
1: "支付宝",
|
1: "支付宝",
|
||||||
2: "微信",
|
2: "微信",
|
||||||
3: "银行卡",
|
3: "USDT(TRC20)",
|
||||||
};
|
};
|
||||||
|
|
||||||
const rejectSchema = z.object({
|
const rejectSchema = z.object({
|
||||||
@@ -143,7 +143,7 @@ export default function WithdrawalPage() {
|
|||||||
const methodOptions = [
|
const methodOptions = [
|
||||||
{ value: "1", label: "支付宝" },
|
{ value: "1", label: "支付宝" },
|
||||||
{ value: "2", label: "微信" },
|
{ value: "2", label: "微信" },
|
||||||
{ value: "3", label: "银行卡" },
|
{ value: "3", label: "USDT(TRC20)" },
|
||||||
{ value: "0", label: "其他" },
|
{ value: "0", label: "其他" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ import { useGlobalStore } from "@/stores/global";
|
|||||||
const WITHDRAWAL_METHODS = [
|
const WITHDRAWAL_METHODS = [
|
||||||
{ value: 1, label: "支付宝" },
|
{ value: 1, label: "支付宝" },
|
||||||
{ value: 2, label: "微信" },
|
{ value: 2, label: "微信" },
|
||||||
{ value: 3, label: "银行卡" },
|
{ value: 3, label: "USDT(TRC20)" },
|
||||||
{ value: 0, label: "其他" },
|
{ value: 0, label: "其他" },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
@@ -111,13 +111,21 @@ function QrCodeView({ url }: { url: string }) {
|
|||||||
<img
|
<img
|
||||||
alt="收款码"
|
alt="收款码"
|
||||||
className="h-16 w-16 cursor-zoom-in object-cover"
|
className="h-16 w-16 cursor-zoom-in object-cover"
|
||||||
|
height={64}
|
||||||
src={url}
|
src={url}
|
||||||
|
width={64}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
{zoomOpen && (
|
{zoomOpen && (
|
||||||
<Dialog onOpenChange={setZoomOpen} open={zoomOpen}>
|
<Dialog onOpenChange={setZoomOpen} open={zoomOpen}>
|
||||||
<DialogContent className="max-w-sm">
|
<DialogContent className="max-w-sm">
|
||||||
<img alt="收款码" className="w-full" src={url} />
|
<img
|
||||||
|
alt="收款码"
|
||||||
|
className="w-full"
|
||||||
|
height={320}
|
||||||
|
src={url}
|
||||||
|
width={320}
|
||||||
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
@@ -153,19 +161,17 @@ const withdrawalFormSchema = z
|
|||||||
if (!data.account) {
|
if (!data.account) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
code: z.ZodIssueCode.custom,
|
code: z.ZodIssueCode.custom,
|
||||||
message: "银行卡收款账号为必填",
|
message: "USDT(TRC20) 地址为必填",
|
||||||
path: ["account"],
|
path: ["account"],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (data.method === 0) {
|
} else if (data.method === 0 && !(data.account || data.content)) {
|
||||||
if (!data.account && !data.content) {
|
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
code: z.ZodIssueCode.custom,
|
code: z.ZodIssueCode.custom,
|
||||||
message: "收款账号和备注至少填一项",
|
message: "收款账号和备注至少填一项",
|
||||||
path: ["account"],
|
path: ["account"],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type WithdrawalFormValues = z.infer<typeof withdrawalFormSchema>;
|
type WithdrawalFormValues = z.infer<typeof withdrawalFormSchema>;
|
||||||
@@ -421,9 +427,7 @@ export default function Affiliate() {
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
收款码
|
收款码
|
||||||
<span className="ml-1 text-destructive">
|
<span className="ml-1 text-destructive">*</span>
|
||||||
*
|
|
||||||
</span>
|
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
{watchedQrCodeUrl ? (
|
{watchedQrCodeUrl ? (
|
||||||
<div className="relative inline-block">
|
<div className="relative inline-block">
|
||||||
@@ -446,7 +450,9 @@ export default function Affiliate() {
|
|||||||
returnType="file"
|
returnType="file"
|
||||||
>
|
>
|
||||||
<div className="flex h-24 w-24 flex-col items-center justify-center rounded border-2 border-dashed text-muted-foreground text-xs">
|
<div className="flex h-24 w-24 flex-col items-center justify-center rounded border-2 border-dashed text-muted-foreground text-xs">
|
||||||
{qrUploading ? "上传中..." : "点击上传\n收款码"}
|
{qrUploading
|
||||||
|
? "上传中..."
|
||||||
|
: "点击上传\n收款码"}
|
||||||
</div>
|
</div>
|
||||||
</UploadImage>
|
</UploadImage>
|
||||||
)}
|
)}
|
||||||
@@ -474,7 +480,7 @@ export default function Affiliate() {
|
|||||||
<Input
|
<Input
|
||||||
placeholder={
|
placeholder={
|
||||||
watchedMethod === 3
|
watchedMethod === 3
|
||||||
? "请输入银行卡号或账户信息"
|
? "请输入 USDT(TRC20) 收款地址"
|
||||||
: "请输入收款账号(与备注至少填一项)"
|
: "请输入收款账号(与备注至少填一项)"
|
||||||
}
|
}
|
||||||
{...field}
|
{...field}
|
||||||
@@ -519,9 +525,7 @@ export default function Affiliate() {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={
|
disabled={
|
||||||
submitting ||
|
submitting || qrUploading || availableCommission <= 0
|
||||||
qrUploading ||
|
|
||||||
availableCommission <= 0
|
|
||||||
}
|
}
|
||||||
form="withdrawal-form"
|
form="withdrawal-form"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
+4
@@ -2664,11 +2664,13 @@ declare namespace API {
|
|||||||
download: number;
|
download: number;
|
||||||
upload: number;
|
upload: number;
|
||||||
speed_limit?: number;
|
speed_limit?: number;
|
||||||
|
plan_speed_limit?: number;
|
||||||
device_limit?: number;
|
device_limit?: number;
|
||||||
effective_speed?: number;
|
effective_speed?: number;
|
||||||
is_throttled?: boolean;
|
is_throttled?: boolean;
|
||||||
throttle_rule?: string;
|
throttle_rule?: string;
|
||||||
traffic_limit?: UserSubscribeTrafficLimitRule[];
|
traffic_limit?: UserSubscribeTrafficLimitRule[];
|
||||||
|
user_traffic_limit?: UserSubscribeTrafficLimitRule[];
|
||||||
token: string;
|
token: string;
|
||||||
status: number;
|
status: number;
|
||||||
short: string;
|
short: string;
|
||||||
@@ -2699,8 +2701,10 @@ declare namespace API {
|
|||||||
download: number;
|
download: number;
|
||||||
upload: number;
|
upload: number;
|
||||||
speed_limit?: number;
|
speed_limit?: number;
|
||||||
|
plan_speed_limit?: number;
|
||||||
device_limit?: number;
|
device_limit?: number;
|
||||||
traffic_limit?: UserSubscribeTrafficLimitRule[];
|
traffic_limit?: UserSubscribeTrafficLimitRule[];
|
||||||
|
user_traffic_limit?: UserSubscribeTrafficLimitRule[];
|
||||||
token: string;
|
token: string;
|
||||||
status: number;
|
status: number;
|
||||||
effective_speed: number;
|
effective_speed: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user