feat: 完善多云资产与腾讯云账单同步

- 接入 AWS、腾讯云、华为云和 Azure 区域及主机发现
- 接入腾讯云账单分页、限流与组件费用汇总
- 批量账单仅同步已支持厂商并优化云账号操作
This commit is contained in:
Logan.Li committed 2026-07-25 14:45:19 +08:00
1 parent 74fa122319
commit 0b785da8ae
10 files changed
+821 -20

No files matched your search

@@ -50,6 +50,17 @@ function getProviderLabels(t: (key: string) => string): Record<string, string> {
};
}
// getCredentialHints 按厂商提示凭据格式,Azure 使用服务主体三段标识。 // Logan.Li
function getCredentialHints(provider: string) {
if (provider === 'azure') {
return { accessKey: '租户ID:订阅ID:客户端ID', secretKey: '客户端密钥' };
}
if (provider === 'aws') return { accessKey: 'AKIA...', secretKey: 'AWS Secret Access Key' };
if (provider === 'tencent') return { accessKey: '腾讯云 SecretId', secretKey: '腾讯云 SecretKey' };
if (provider === 'huawei') return { accessKey: '华为云 Access Key', secretKey: '华为云 Secret Access Key' };
return { accessKey: 'LTAI...', secretKey: '阿里云 AccessKey Secret' };
}
export function CloudAccountManager() {
const { t } = useTranslation();
const [accounts, setAccounts] = useState<JsonData[]>([]);
@@ -76,6 +87,7 @@ export function CloudAccountManager() {
const [actionLoading, setActionLoading] = useState<Record<string, string>>({});
const providerLabels = getProviderLabels(t);
const credentialHints = getCredentialHints(formData.provider);
const setCardAction = (accountId: string | number, action: string | null) => {
setActionLoading((prev) => {
@@ -475,11 +487,11 @@ export function CloudAccountManager() {
<div className="space-y-1.5">
<Label>
AccessKey
{formData.provider === 'azure' ? 'Azure 标识' : 'AccessKey'}
{editingAccount && <span className="text-xs dg-text-muted ml-2">{t('cloud.keepBlank')}</span>}
</Label>
<Input
placeholder="LTAI..."
placeholder={credentialHints.accessKey}
value={formData.access_key}
onChange={(e) => setFormData({ ...formData, access_key: e.target.value })}
className="dg-input font-mono text-sm"
@@ -488,18 +500,22 @@ export function CloudAccountManager() {
<div className="space-y-1.5">
<Label>
SecretKey
{formData.provider === 'azure' ? 'Azure 客户端密钥' : 'SecretKey'}
{editingAccount && <span className="text-xs dg-text-muted ml-2">{t('cloud.keepBlank')}</span>}
</Label>
<Input
type="password"
placeholder="••••••••"
placeholder={credentialHints.secretKey}
value={formData.secret_key}
onChange={(e) => setFormData({ ...formData, secret_key: e.target.value })}
className="dg-input font-mono text-sm"
/>
</div>
{formData.provider === 'azure' && (
<p className="text-xs dg-text-muted">Azure 同步使用服务主体:标识填写“租户ID:订阅ID:客户端ID”,密钥填写客户端密钥。</p>
)}
<div className="grid grid-cols-2 gap-4">
<div className="space-y-1.5">
<Label>{t('cloud.monthlyFeeUnit')}</Label>
@@ -294,7 +294,6 @@ export function CloudServerImport({ open, onClose, initialAccountId }: CloudServ
{ id: 1, title: t('server.cloudProvider'), icon: Cloud },
{ id: 2, title: t('server.cloudAccount'), icon: Key },
{ id: 3, title: t('server.selectRegion'), icon: MapPin },
{ id: 4, title: t('server.importConfirm'), icon: CheckCircle },
];
return (