You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
6.3 KiB
164 lines
6.3 KiB
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
import { Message } from "@arco-design/web-react";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
import { getGatewayApikeyList, setGatewayApikeyStatus } from "@/api/api";
|
|
import { useCopyText } from "@/hooks/useCopyText";
|
|
import "./index.scss";
|
|
|
|
interface GatewayApikeyItem {
|
|
id: number;
|
|
code: string;
|
|
apikey: string;
|
|
status: number;
|
|
createTime?: string | number;
|
|
}
|
|
|
|
function formatDateTime(value?: string | number): string {
|
|
if (value == null) return "-";
|
|
const date = new Date(value);
|
|
if (Number.isNaN(date.getTime())) return String(value);
|
|
const y = date.getFullYear();
|
|
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
const d = String(date.getDate()).padStart(2, "0");
|
|
const hh = String(date.getHours()).padStart(2, "0");
|
|
const mm = String(date.getMinutes()).padStart(2, "0");
|
|
return `${String(y)}-${m}-${d} ${hh}:${mm}`;
|
|
}
|
|
|
|
function maskApikey(value: string): string {
|
|
if (!value) return "-";
|
|
if (value.length <= 10) return value;
|
|
return `${value.slice(0, 5)}...${value.slice(-4)}`;
|
|
}
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return value != null && typeof value === "object";
|
|
}
|
|
|
|
function getString(record: Record<string, unknown>, key: string): string {
|
|
const value = record[key];
|
|
if (typeof value === "string") return value;
|
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
return "";
|
|
}
|
|
|
|
function getNumber(record: Record<string, unknown>, key: string): number | null {
|
|
const value = record[key];
|
|
const numericValue = typeof value === "number" ? value : typeof value === "string" ? Number(value) : null;
|
|
return numericValue != null && Number.isFinite(numericValue) ? numericValue : null;
|
|
}
|
|
|
|
function normalizeGatewayApikeyItem(value: unknown): GatewayApikeyItem | null {
|
|
if (!isRecord(value)) return null;
|
|
const id = getNumber(value, "id");
|
|
if (id == null) return null;
|
|
return {
|
|
id,
|
|
code: getString(value, "code"),
|
|
apikey: getString(value, "apikey"),
|
|
status: getNumber(value, "status") ?? 2,
|
|
createTime: getString(value, "createTime") || getString(value, "create_time") || undefined,
|
|
};
|
|
}
|
|
|
|
export default function GatewayApikeyDialog() {
|
|
const { t } = useTranslation();
|
|
const copyText = useCopyText();
|
|
const [loading, setLoading] = useState(false);
|
|
const [list, setList] = useState<GatewayApikeyItem[]>([]);
|
|
const [updatingId, setUpdatingId] = useState<number | null>(null);
|
|
|
|
const loadList = useCallback(async () => {
|
|
setLoading(true);
|
|
try {
|
|
const res = await getGatewayApikeyList({ page: 1, pageSize: 20 });
|
|
const rows = Array.isArray(res.data?.list) ? res.data.list.map(normalizeGatewayApikeyItem).filter((item): item is GatewayApikeyItem => item != null) : [];
|
|
setList(rows);
|
|
} catch {
|
|
setList([]);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
void loadList();
|
|
}, [loadList]);
|
|
|
|
const hasData = useMemo(() => list.length > 0, [list]);
|
|
|
|
const handleToggle = useCallback(
|
|
async (item: GatewayApikeyItem) => {
|
|
if (updatingId != null) return;
|
|
const nextEnabled = item.status !== 1;
|
|
setUpdatingId(item.id);
|
|
try {
|
|
await setGatewayApikeyStatus({
|
|
id: item.id,
|
|
enabled: nextEnabled,
|
|
});
|
|
setList((prev) =>
|
|
prev.map((row) => {
|
|
if (row.id !== item.id) return row;
|
|
return {
|
|
...row,
|
|
status: nextEnabled ? 1 : 2,
|
|
};
|
|
}),
|
|
);
|
|
} catch {
|
|
Message.error(t("gatewayApiKey.updateStatusFailed"));
|
|
} finally {
|
|
setUpdatingId(null);
|
|
}
|
|
},
|
|
[t, updatingId],
|
|
);
|
|
|
|
return (
|
|
<div className="gateway_apikey_dialog">
|
|
<h2 className="gateway_apikey_dialog__title">{t("gatewayApiKey.title")}</h2>
|
|
{loading ? <div className="gateway_apikey_dialog__statusText">{t("common.loading")}</div> : null}
|
|
{!loading && !hasData ? <div className="gateway_apikey_dialog__statusText">{t("gatewayApiKey.empty")}</div> : null}
|
|
{list.map((item) => (
|
|
<div className="gateway_apikey_dialog__card" key={item.id}>
|
|
<div className="gateway_apikey_dialog__row gateway_apikey_dialog__row_header">
|
|
<span className="gateway_apikey_dialog__headerText">Apikey</span>
|
|
</div>
|
|
|
|
<div className="gateway_apikey_dialog__row gateway_apikey_dialog__row_user">
|
|
<span className="gateway_apikey_dialog__userCode">{item.code}</span>
|
|
{/* <span
|
|
className={["gateway_apikey_dialog__switch", item.status === 1 ? "gateway_apikey_dialog__switch_on" : "gateway_apikey_dialog__switch_off", updatingId === item.id ? "gateway_apikey_dialog__switch_loading" : ""].join(" ")}
|
|
role="switch"
|
|
aria-checked={item.status === 1}
|
|
aria-label={t("gatewayApiKey.statusAria")}
|
|
onClick={() => { void handleToggle(item); }}
|
|
>
|
|
<span className="gateway_apikey_dialog__switchLabel">{item.status === 1 ? "ON" : "OFF"}</span>
|
|
<span className="gateway_apikey_dialog__switchKnob" />
|
|
</span> */}
|
|
</div>
|
|
|
|
<div className="gateway_apikey_dialog__detail">
|
|
<span className="gateway_apikey_dialog__detailLabel">Key</span>
|
|
<span className="gateway_apikey_dialog__detailValue gateway_apikey_dialog__detailValue_key">
|
|
{maskApikey(item.apikey)}
|
|
<button type="button" className="gateway_apikey_dialog__copyBtn" onClick={() => { void copyText(item.apikey); }} aria-label={t("gatewayApiKey.copyKey")}>
|
|
<img className="gateway_apikey_dialog__copyIcon" src={new URL("@/assets/images/ic_copy.png", import.meta.url).href} />
|
|
</button>
|
|
</span>
|
|
</div>
|
|
|
|
<div className="gateway_apikey_dialog__divider" />
|
|
|
|
<div className="gateway_apikey_dialog__detail gateway_apikey_dialog__detail_last">
|
|
<span className="gateway_apikey_dialog__detailLabel">{t("gatewayApiKey.createdAt")}</span>
|
|
<span className="gateway_apikey_dialog__detailValue">{formatDateTime(item.createTime)}</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|