diff --git a/video-gen-app/src/api/mock.ts b/video-gen-app/src/api/mock.ts
index bea7a751..5e55cefb 100644
--- a/video-gen-app/src/api/mock.ts
+++ b/video-gen-app/src/api/mock.ts
@@ -27,6 +27,18 @@ const MOCK_USER: User = {
username: 'videomaker',
email: 'demo@videogen.ai',
credits: 2680,
+ resourceCapacity: {
+ enabled: true,
+ source: 'user',
+ hasUserConfig: true,
+ usedBytes: 298161654,
+ availableBytes: 300349549066,
+ totalBytes: 300647710720,
+ usagePercent: 0.1,
+ exceeded: false,
+ limitValue: '280.000',
+ limitUnit: 'GB',
+ },
};
const MOCK_CREDIT_RECORDS: CreditRecord[] = [
diff --git a/video-gen-app/src/components/Layout/AppLayout.tsx b/video-gen-app/src/components/Layout/AppLayout.tsx
index bc11a447..04eb97c1 100644
--- a/video-gen-app/src/components/Layout/AppLayout.tsx
+++ b/video-gen-app/src/components/Layout/AppLayout.tsx
@@ -61,10 +61,146 @@ import {
} from '@ant-design/icons';
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
import { useAuthStore } from '../../store/useAuthStore';
-import { getMenuConfigs, getRechargePackages, getPaymentMethods, createRechargeOrder, getPaymentOrder, cancelPaymentOrder, getSiteInfo, getUnreadCount, createContactRequest } from '../../api';
+import { getMenuConfigs, getRechargePackages, getPaymentMethods, createRechargeOrder, getPaymentOrder, cancelPaymentOrder, getSiteInfo, getUnreadCount, createContactRequest, getUser } from '../../api';
import NotificationPopup from '../NotificationPopup';
import './AppLayout.css';
+// ── ResourceCapacity 类型(与 src/types/index.ts 保持一致) ──
+type ResourceCapacityData = {
+ enabled: boolean;
+ usedBytes: number;
+ totalBytes: number;
+ availableBytes: number;
+ usagePercent: number;
+ exceeded: boolean;
+ limitValue: string;
+ limitUnit: string;
+};
+
+/**
+ * 资源存储展示卡片
+ * - enabled === true:显示「已用 / 总额」+ 进度条(超额时变红并提示)
+ * - enabled === false:按 usedBytes 大小自动选 KB / MB / GB / TB 单位显示「当前使用」
+ */
+const StorageCard: React.FC<{ data: ResourceCapacityData | null }> = ({ data }) => {
+ if (!data) return null;
+
+ // 单位:后端返回的 limitUnit(KB / MB / GB)
+ const unit = (data.limitUnit || 'GB').toUpperCase();
+ const divisor = unit === 'GB' ? 1024 ** 3 : unit === 'MB' ? 1024 ** 2 : 1024;
+
+ // 百分比(可能 > 100)
+ const rawPercent = Number(data.usagePercent) || 0;
+ // 进度条宽度最多 100%
+ const barPercent = Math.min(100, Math.max(0, rawPercent));
+
+ // 总额:优先用后端 limitValue,兜底 totalBytes / divisor
+ const total = data.limitValue
+ ? parseFloat(data.limitValue)
+ : data.totalBytes / divisor;
+
+ // 已用 = 总额 × 百分比 / 100(保证数字与 percent 自洽)
+ const used = (total * rawPercent) / 100;
+ // 剩余 = 总额 - 已用
+ const available = total - used;
+
+ // 超额判断
+ const isOver = data.exceeded || rawPercent >= 100;
+
+ // enabled === false 时按 usedBytes 自动选单位
+ const KB = 1024;
+ const MB = 1024 ** 2;
+ const GB = 1024 ** 3;
+ const TB = 1024 ** 4;
+ const formatUsedAuto = (bytes: number) => {
+ const b = Number(bytes) || 0;
+ if (b >= TB) return { val: (b / TB).toFixed(2), unit: 'TB' };
+ if (b >= GB) return { val: (b / GB).toFixed(2), unit: 'GB' };
+ if (b >= MB) return { val: (b / MB).toFixed(2), unit: 'MB' };
+ return { val: (b / KB).toFixed(2), unit: 'KB' };
+ };
+ const usedAuto = formatUsedAuto(data.usedBytes);
+
+ return (
+
+
+
+
+ 资源存储
+
+ {data.enabled ? (
+
+ {used.toFixed(2)} / {total.toFixed(2)} {unit}
+
+ ) : (
+
+ 当前使用 {usedAuto.val} {usedAuto.unit}
+
+ )}
+
+ {data.enabled && (
+ <>
+
+
+
+ {isOver
+ ? `存储超额 · 超用 ${Math.abs(available).toFixed(2)} ${unit}`
+ : `剩余 ${available.toFixed(2)} ${unit}`}
+
+ {rawPercent.toFixed(1)}%
+
+ >
+ )}
+
+ );
+};
+
interface MenuConfig {
id: string;
key?: string;
@@ -179,6 +315,18 @@ const AppLayout: React.FC = () => {
const [contactHovered, setContactHovered] = useState(false);
const [submittingContact, setSubmittingContact] = useState(false);
+ // 资源存储容量(从 getUser().resource_capacity 获取)
+ const [resourceCapacity, setResourceCapacity] = useState<{
+ enabled: boolean;
+ usedBytes: number;
+ totalBytes: number;
+ availableBytes: number;
+ usagePercent: number;
+ exceeded: boolean;
+ limitValue: string;
+ limitUnit: string;
+ } | null>(null);
+
const PENDING_ORDER_KEY = 'pending_payment_order';
useEffect(() => {
@@ -207,6 +355,31 @@ const AppLayout: React.FC = () => {
}).catch(() => { });
}, []);
+ // 拉取用户资源容量信息
+ useEffect(() => {
+ getUser().then((res: any) => {
+ console.log('[Storage] getUser 返回:', res);
+
+ const rc = res?.resourceCapacity;
+ if (rc) {
+ setResourceCapacity({
+ enabled: !!rc.enabled,
+ usedBytes: Number(rc.usedBytes) || 0,
+ totalBytes: Number(rc.totalBytes) || 0,
+ availableBytes: Number(rc.availableBytes) || 0,
+ usagePercent: Number(rc.usagePercent) || 0,
+ exceeded: !!rc.exceeded,
+ limitValue: rc.limitValue ?? '',
+ limitUnit: rc.limitUnit || 'GB',
+ });
+ }
+ // 没数据时不显示
+ }).catch((err: any) => {
+ console.error('[Storage] getUser 失败:', err);
+ // 接口失败也不显示
+ });
+ }, []);
+
const loadUnreadCount = () => {
getUnreadCount().then(count => {
setUnreadCount(count);
@@ -620,6 +793,9 @@ const AppLayout: React.FC = () => {
+
+ {/* 资源存储容量展示(来自 getUser.resourceCapacity) */}
+
@@ -714,6 +890,9 @@ const AppLayout: React.FC = () => {
+ {/* 资源存储容量展示(与桌面端共用 StorageCard) */}
+
+
{topLevelItems.map(item => {
const menuType = item.menu_type ?? item.menuType;
const hasChildren = childMap[item.id] && childMap[item.id].length > 0;
diff --git a/video-gen-app/src/index.css b/video-gen-app/src/index.css
index 2c679d73..24078d5d 100644
--- a/video-gen-app/src/index.css
+++ b/video-gen-app/src/index.css
@@ -3,20 +3,26 @@
:root {
--font-sans: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--nav-bg: #f8f9fb;
- --nav-surface: rgba(0,0,0,0.02);
+ --nav-surface: rgba(0, 0, 0, 0.02);
--nav-border: #e5e7eb;
--nav-text: #1f2937;
--nav-text-muted: #6b7280;
- --nav-hover: rgba(0,0,0,0.04);
- --nav-active: rgba(99,102,241,0.08);
+ --nav-hover: rgba(0, 0, 0, 0.04);
+ --nav-active: rgba(99, 102, 241, 0.08);
}
*,
*::before,
-*::after { box-sizing: border-box; }
+*::after {
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+}
-html, body {
- margin: 0; padding: 0;
+html,
+body {
+ margin: 0;
+ padding: 0;
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@@ -24,23 +30,68 @@ html, body {
color: #1a1a2e;
letter-spacing: -0.01em;
}
-#root { min-height: 100vh; }
+
+
+#root {
+ min-height: 100vh;
+}
/* ── Scrollbar ─────────────────────────── */
-::-webkit-scrollbar { width: 5px; height: 5px; }
-::-webkit-scrollbar-track { background: transparent; }
-::-webkit-scrollbar-thumb { background: #d1d5db; border-radius: 3px; }
-::-webkit-scrollbar-thumb:hover { background: #9ca3af; }
+::-webkit-scrollbar {
+ width: 5px;
+ height: 5px;
+}
+
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+ background: #d1d5db;
+ border-radius: 3px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: #9ca3af;
+}
/* ── Electric Border Animation ─────────── */
@keyframes electric-pulse {
- 0%, 100% { opacity: 0.4; filter: blur(1px); }
- 50% { opacity: 1; filter: blur(0); }
+
+ 0%,
+ 100% {
+ opacity: 0.4;
+ filter: blur(1px);
+ }
+
+ 50% {
+ opacity: 1;
+ filter: blur(0);
+ }
}
+
@keyframes electric-flow {
- 0% { background-position: 0% 50%; }
- 50% { background-position: 100% 50%; }
- 100% { background-position: 0% 50%; }
+ 0% {
+ background-position: 0% 50%;
+ }
+
+ 50% {
+ background-position: 100% 50%;
+ }
+
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+
+
+.content_box {
+ margin: -24px -32px -32px;
+ border-radius: 0 0 16px 16px;
+ height: calc(100vh - 34px);
+ overflow: auto;
+ scrollbar-width: none;
+ padding: 20px;
}
.electric-border-card {
@@ -51,16 +102,14 @@ html, body {
position: absolute;
inset: -2px;
border-radius: 16px;
- background: linear-gradient(
- 90deg,
- #6366f1,
- #8b5cf6,
- #a855f7,
- #6366f1,
- #8b5cf6,
- #a855f7,
- #6366f1
- );
+ background: linear-gradient(90deg,
+ #6366f1,
+ #8b5cf6,
+ #a855f7,
+ #6366f1,
+ #8b5cf6,
+ #a855f7,
+ #6366f1);
background-size: 300% 100%;
animation: electric-flow 3s linear infinite, electric-pulse 2s ease-in-out infinite;
z-index: 0;
@@ -85,6 +134,7 @@ html, body {
0% {
background-position: -200% center;
}
+
100% {
background-position: 200% center;
}
@@ -97,16 +147,14 @@ html, body {
.shiny-text {
font-size: 24px;
font-weight: 700;
- background: linear-gradient(
- 90deg,
- #6366f1 0%,
- #8b5cf6 20%,
- #a855f7 40%,
- #c084fc 50%,
- #a855f7 60%,
- #8b5cf6 80%,
- #6366f1 100%
- );
+ background: linear-gradient(90deg,
+ #6366f1 0%,
+ #8b5cf6 20%,
+ #a855f7 40%,
+ #c084fc 50%,
+ #a855f7 60%,
+ #8b5cf6 80%,
+ #6366f1 100%);
background-size: 200% auto;
-webkit-background-clip: text;
background-clip: text;
@@ -118,92 +166,225 @@ html, body {
/* ── Keyframe Animations ───────────────── */
@keyframes fadeInUp {
- from { opacity: 0; transform: translateY(20px); }
- to { opacity: 1; transform: translateY(0); }
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
}
+
@keyframes fadeInLeft {
- from { opacity: 0; transform: translateX(-20px); }
- to { opacity: 1; transform: translateX(0); }
+ from {
+ opacity: 0;
+ transform: translateX(-20px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
}
+
@keyframes fadeInRight {
- from { opacity: 0; transform: translateX(20px); }
- to { opacity: 1; transform: translateX(0); }
+ from {
+ opacity: 0;
+ transform: translateX(20px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
}
+
@keyframes float1 {
- 0%, 100% { transform: translate(0, 0) scale(1); }
- 50% { transform: translate(20px, -30px) scale(1.05); }
+
+ 0%,
+ 100% {
+ transform: translate(0, 0) scale(1);
+ }
+
+ 50% {
+ transform: translate(20px, -30px) scale(1.05);
+ }
}
+
@keyframes float2 {
- 0%, 100% { transform: translate(0, 0) scale(1); }
- 50% { transform: translate(-15px, 20px) scale(0.95); }
+
+ 0%,
+ 100% {
+ transform: translate(0, 0) scale(1);
+ }
+
+ 50% {
+ transform: translate(-15px, 20px) scale(0.95);
+ }
}
+
@keyframes float3 {
- 0%, 100% { transform: translate(0, 0) rotate(0deg); }
- 50% { transform: translate(10px, -15px) rotate(3deg); }
+
+ 0%,
+ 100% {
+ transform: translate(0, 0) rotate(0deg);
+ }
+
+ 50% {
+ transform: translate(10px, -15px) rotate(3deg);
+ }
}
+
@keyframes slideInCard {
- from { opacity: 0; transform: translateY(14px) scale(0.97); }
- to { opacity: 1; transform: translateY(0) scale(1); }
+ from {
+ opacity: 0;
+ transform: translateY(14px) scale(0.97);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
}
+
@keyframes spinSlow {
- from { transform: rotate(0deg); }
- to { transform: rotate(360deg); }
+ from {
+ transform: rotate(0deg);
+ }
+
+ to {
+ transform: rotate(360deg);
+ }
}
-.animate-fadeInUp { animation: fadeInUp 0.45s ease-out both; }
-.animate-fadeInLeft { animation: fadeInLeft 0.55s ease-out both; }
-.animate-fadeInRight { animation: fadeInRight 0.55s ease-out both; }
-.animate-slideInCard { animation: slideInCard 0.4s ease-out both; }
-.animate-float1 { animation: float1 8s ease-in-out infinite; }
-.animate-float2 { animation: float2 10s ease-in-out infinite; }
-.animate-float3 { animation: float3 12s ease-in-out infinite; }
+.animate-fadeInUp {
+ animation: fadeInUp 0.45s ease-out both;
+}
-.ref-thumb:hover .ref-delete { opacity: 1 !important; }
+.animate-fadeInLeft {
+ animation: fadeInLeft 0.55s ease-out both;
+}
-.stagger-children > *:nth-child(1) { animation-delay: 0.04s; }
-.stagger-children > *:nth-child(2) { animation-delay: 0.10s; }
-.stagger-children > *:nth-child(3) { animation-delay: 0.16s; }
-.stagger-children > *:nth-child(4) { animation-delay: 0.22s; }
-.stagger-children > *:nth-child(5) { animation-delay: 0.28s; }
-.stagger-children > *:nth-child(6) { animation-delay: 0.34s; }
+.animate-fadeInRight {
+ animation: fadeInRight 0.55s ease-out both;
+}
+
+.animate-slideInCard {
+ animation: slideInCard 0.4s ease-out both;
+}
+
+.animate-float1 {
+ animation: float1 8s ease-in-out infinite;
+}
+
+.animate-float2 {
+ animation: float2 10s ease-in-out infinite;
+}
+
+.animate-float3 {
+ animation: float3 12s ease-in-out infinite;
+}
+
+.ref-thumb:hover .ref-delete {
+ opacity: 1 !important;
+}
+
+.stagger-children>*:nth-child(1) {
+ animation-delay: 0.04s;
+}
+
+.stagger-children>*:nth-child(2) {
+ animation-delay: 0.10s;
+}
+
+.stagger-children>*:nth-child(3) {
+ animation-delay: 0.16s;
+}
+
+.stagger-children>*:nth-child(4) {
+ animation-delay: 0.22s;
+}
+
+.stagger-children>*:nth-child(5) {
+ animation-delay: 0.28s;
+}
+
+.stagger-children>*:nth-child(6) {
+ animation-delay: 0.34s;
+}
/* ── Ant Design Overrides ──────────────── */
-.ant-input, .ant-input-affix-wrapper, .ant-select-selector, .ant-input-number {
+.ant-input,
+.ant-input-affix-wrapper,
+.ant-select-selector,
+.ant-input-number {
border-radius: 10px !important;
transition: all 0.25s ease !important;
}
-.ant-input:focus, .ant-input-affix-wrapper:focus, .ant-input-affix-wrapper-focused,
+
+.ant-input:focus,
+.ant-input-affix-wrapper:focus,
+.ant-input-affix-wrapper-focused,
.ant-select-focused .ant-select-selector {
- box-shadow: 0 0 0 3px rgba(99,102,241,0.12) !important;
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.12) !important;
border-color: #6366f1 !important;
}
+
.ant-btn {
border-radius: 10px !important;
transition: all 0.25s ease !important;
font-weight: 500 !important;
}
-.ant-btn:hover { transform: none; }
-.ant-btn:active { transform: translateY(0); }
-.ant-card { border-radius: 16px !important; transition: all 0.3s ease !important; }
-.ant-modal-content { border-radius: 20px !important; overflow: hidden; }
-.ant-tag { font-size: 12px; border-radius: 8px !important; }
+
+.ant-btn:hover {
+ transform: none;
+}
+
+.ant-btn:active {
+ transform: translateY(0);
+}
+
+.ant-card {
+ border-radius: 16px !important;
+ transition: all 0.3s ease !important;
+}
+
+.ant-modal-content {
+ border-radius: 20px !important;
+ overflow: hidden;
+}
+
+.ant-tag {
+ font-size: 12px;
+ border-radius: 8px !important;
+}
/* ── Recharge card hover ───────────────── */
.recharge-card {
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1) !important;
cursor: pointer;
}
+
.recharge-card:hover {
transform: translateY(-8px) scale(1.02) !important;
- box-shadow: 0 20px 40px rgba(99,102,241,0.15) !important;
+ box-shadow: 0 20px 40px rgba(99, 102, 241, 0.15) !important;
+}
+
+.recharge-card .recharge-price {
+ transition: all 0.3s ease;
+}
+
+.recharge-card:hover .recharge-price {
+ transform: scale(1.08);
}
-.recharge-card .recharge-price { transition: all 0.3s ease; }
-.recharge-card:hover .recharge-price { transform: scale(1.08); }
/* ── Homepage tabs ────────────────────── */
.homepage-tabs .ant-tabs-nav {
margin-bottom: 8px !important;
}
+
.homepage-tabs .ant-tabs-tab {
padding: 8px 16px !important;
font-size: 13px !important;
@@ -211,13 +392,16 @@ html, body {
color: #6b7280 !important;
transition: all 0.25s !important;
}
+
.homepage-tabs .ant-tabs-tab:hover {
color: #6366f1 !important;
}
+
.homepage-tabs .ant-tabs-tab-active .ant-tabs-tab-btn {
color: #6366f1 !important;
font-weight: 600 !important;
}
+
.homepage-tabs .ant-tabs-ink-bar {
background: linear-gradient(90deg, #6366f1, #a855f7) !important;
height: 3px !important;
@@ -230,108 +414,249 @@ html, body {
position: relative;
overflow: visible;
}
+
.project-card:hover {
transform: translateY(-4px) !important;
- box-shadow: 0 16px 40px rgba(99,102,241,0.12) !important;
+ box-shadow: 0 16px 40px rgba(99, 102, 241, 0.12) !important;
border-color: #6366f1 !important;
}
/* ── Mobile Bottom Nav ─────────────────── */
.mobile-bottom-nav {
display: none;
- position: fixed; bottom: 0; left: 0; right: 0; z-index: 200;
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 200;
height: 60px;
- background: rgba(255,255,255,0.95);
+ background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(24px);
- border-top: 1px solid rgba(0,0,0,0.05);
- box-shadow: 0 -4px 20px rgba(0,0,0,0.06);
+ border-top: 1px solid rgba(0, 0, 0, 0.05);
+ box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.06);
}
+
.mobile-bottom-nav .nav-item {
- flex: 1; display: flex; flex-direction: column;
- align-items: center; justify-content: center; gap: 2px;
- cursor: pointer; transition: all 0.2s;
- color: #8b8fa3; font-size: 9px; font-weight: 400;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 2px;
+ cursor: pointer;
+ transition: all 0.2s;
+ color: #8b8fa3;
+ font-size: 9px;
+ font-weight: 400;
letter-spacing: 0;
}
+
.mobile-bottom-nav .nav-item.active {
color: #08080c;
}
+
.mobile-bottom-nav .nav-item .nav-icon {
- font-size: 18px; transition: transform 0.2s;
+ font-size: 18px;
+ transition: transform 0.2s;
}
+
.mobile-bottom-nav .nav-item.active .nav-icon {
transform: scale(1.05);
}
/* ── Responsive Breakpoints ────────────── */
@media (max-width: 768px) {
- .desktop-sidebar { display: none !important; }
- .desktop-content { margin-left: 0 !important; padding: 16px !important; padding-bottom: 80px !important; overflow-x: hidden !important; }
- .desktop-sidebar-toggle-zone { display: none !important; }
- .mobile-bottom-nav { display: flex !important; }
+ .desktop-sidebar {
+ display: none !important;
+ }
+
+ .desktop-content {
+ margin-left: 0 !important;
+ padding: 16px !important;
+ padding-bottom: 80px !important;
+ overflow-x: hidden !important;
+ }
+
+ .desktop-sidebar-toggle-zone {
+ display: none !important;
+ }
+
+ .mobile-bottom-nav {
+ display: flex !important;
+ }
/* Prevent horizontal overflow globally */
- html, body { overflow-x: hidden !important; }
+ html,
+ body {
+ overflow-x: hidden !important;
+ }
- .mobile-hide { display: none !important; }
- .mobile-full { width: 100% !important; max-width: 100% !important; }
- .mobile-stack { flex-direction: column !important; }
- .mobile-p-16 { padding: 16px !important; }
- .mobile-gap-12 { gap: 12px !important; }
+ .mobile-hide {
+ display: none !important;
+ }
+
+ .mobile-full {
+ width: 100% !important;
+ max-width: 100% !important;
+ }
+
+ .mobile-stack {
+ flex-direction: column !important;
+ }
+
+ .mobile-p-16 {
+ padding: 16px !important;
+ }
+
+ .mobile-gap-12 {
+ gap: 12px !important;
+ }
/* Login page */
- .login-page { flex-direction: column !important; }
- .login-left { display: none !important; }
- .login-right { padding: 24px 16px !important; }
- .login-card { width: 100% !important; }
+ .login-page {
+ flex-direction: column !important;
+ }
+
+ .login-left {
+ display: none !important;
+ }
+
+ .login-right {
+ padding: 24px 16px !important;
+ }
+
+ .login-card {
+ width: 100% !important;
+ }
/* Project list */
- .project-item { flex-direction: column !important; align-items: flex-start !important; }
- .project-item .project-actions { margin-left: 0 !important; margin-top: 12px; width: 100%; justify-content: flex-end; }
+ .project-item {
+ flex-direction: column !important;
+ align-items: flex-start !important;
+ }
+
+ .project-item .project-actions {
+ margin-left: 0 !important;
+ margin-top: 12px;
+ width: 100%;
+ justify-content: flex-end;
+ }
/* Generate page header */
- .gen-header { flex-direction: column !important; gap: 12px !important; padding: 16px 18px !important; }
+ .gen-header {
+ flex-direction: column !important;
+ gap: 12px !important;
+ padding: 16px 18px !important;
+ }
/* Operation flow bar */
- .gen-flow-bar { flex-direction: column !important; gap: 14px !important; padding: 16px !important; }
- .gen-flow-divider { display: none !important; }
+ .gen-flow-bar {
+ flex-direction: column !important;
+ gap: 14px !important;
+ padding: 16px !important;
+ }
+
+ .gen-flow-divider {
+ display: none !important;
+ }
/* Config form params row */
- .gen-form-row { flex-direction: column !important; gap: 0 !important; }
+ .gen-form-row {
+ flex-direction: column !important;
+ gap: 0 !important;
+ }
/* Credits estimate bar */
- .gen-credits-bar { flex-direction: column !important; gap: 8px !important; text-align: center; }
+ .gen-credits-bar {
+ flex-direction: column !important;
+ gap: 8px !important;
+ text-align: center;
+ }
/* Prompt rows */
- .gen-prompt-row { flex-direction: column !important; }
- .gen-prompt-row > * { width: 100% !important; }
- .gen-params { flex-wrap: wrap !important; gap: 8px !important; }
- .gen-params > div { flex: 1 1 45% !important; min-width: 0; }
+ .gen-prompt-row {
+ flex-direction: column !important;
+ }
+
+ .gen-prompt-row>* {
+ width: 100% !important;
+ }
+
+ .gen-params {
+ flex-wrap: wrap !important;
+ gap: 8px !important;
+ }
+
+ .gen-params>div {
+ flex: 1 1 45% !important;
+ min-width: 0;
+ }
/* Expanded detail left+right stacks */
- .gen-detail-row { flex-direction: column !important; }
- .gen-detail-row > * { width: 100% !important; min-width: 0 !important; flex: 1 1 auto !important; }
+ .gen-detail-row {
+ flex-direction: column !important;
+ }
+
+ .gen-detail-row>* {
+ width: 100% !important;
+ min-width: 0 !important;
+ flex: 1 1 auto !important;
+ }
/* Record filter bar stacks */
- .record-filter-bar { flex-direction: column !important; }
- .record-filter-bar .ant-space { flex-wrap: wrap !important; }
- .record-filter-bar .ant-select { width: 100% !important; }
+ .record-filter-bar {
+ flex-direction: column !important;
+ }
+
+ .record-filter-bar .ant-space {
+ flex-wrap: wrap !important;
+ }
+
+ .record-filter-bar .ant-select {
+ width: 100% !important;
+ }
/* Record summary row stacks */
- .record-item { flex-direction: column !important; align-items: flex-start !important; gap: 8px !important; overflow: hidden !important; }
- .record-item .record-actions { width: 100% !important; justify-content: flex-end; }
- .record-item > span[style*="textOverflow"] { white-space: normal !important; overflow: visible !important; text-overflow: unset !important; }
+ .record-item {
+ flex-direction: column !important;
+ align-items: flex-start !important;
+ gap: 8px !important;
+ overflow: hidden !important;
+ }
+
+ .record-item .record-actions {
+ width: 100% !important;
+ justify-content: flex-end;
+ }
+
+ .record-item>span[style*="textOverflow"] {
+ white-space: normal !important;
+ overflow: visible !important;
+ text-overflow: unset !important;
+ }
/* Hide less important meta on mobile */
- .mobile-meta { display: none !important; }
+ .mobile-meta {
+ display: none !important;
+ }
/* Credits page */
- .credits-balance { flex-direction: column !important; }
- .recharge-grid { flex-direction: column !important; }
- .recharge-grid > * { width: 100% !important; }
+ .credits-balance {
+ flex-direction: column !important;
+ }
+
+ .recharge-grid {
+ flex-direction: column !important;
+ }
+
+ .recharge-grid>* {
+ width: 100% !important;
+ }
/* Video player fits mobile */
- video { max-width: 100% !important; }
+ video {
+ max-width: 100% !important;
+ }
/* InitialReplication page */
.replication-container {
@@ -340,17 +665,20 @@ html, body {
padding: 16px !important;
height: calc(100vh - 64px) !important;
}
+
.replication-preview {
width: 100% !important;
height: 30vh !important;
border-radius: 12px !important;
}
+
.replication-form {
width: 100% !important;
height: auto !important;
max-height: calc(70vh - 32px) !important;
border-radius: 12px !important;
}
+
.replication-form-content {
padding: 16px !important;
max-height: calc(70vh - 64px) !important;
@@ -358,56 +686,92 @@ html, body {
}
/* ── Date Display ──────────────────────── */
-.date-display { font-family: 'SF Mono', 'Menlo', 'Consolas', monospace; }
+.date-display {
+ font-family: 'SF Mono', 'Menlo', 'Consolas', monospace;
+}
/* ── Small Mobile Breakpoint (max-width: 480px) ────────────── */
@media (max-width: 480px) {
+
/* Base styles */
- html, body { font-size: 14px !important; }
+ html,
+ body {
+ font-size: 14px !important;
+ }
/* Login page */
- .login-card { width: 100% !important; }
- .login-right { padding: 16px !important; }
+ .login-card {
+ width: 100% !important;
+ }
+
+ .login-right {
+ padding: 16px !important;
+ }
/* Header adjustments */
- .gen-header { padding: 12px 14px !important; }
+ .gen-header {
+ padding: 12px 14px !important;
+ }
/* Form elements */
- .gen-form-row { gap: 8px !important; }
-
+ .gen-form-row {
+ gap: 8px !important;
+ }
+
/* Button adjustments */
- .ant-btn { height: 36px !important; font-size: 13px !important; padding: 0 12px !important; }
-
+ .ant-btn {
+ height: 36px !important;
+ font-size: 13px !important;
+ padding: 0 12px !important;
+ }
+
/* Input adjustments */
- .ant-input, .ant-input-affix-wrapper, .ant-select-selector {
- height: 36px !important;
+ .ant-input,
+ .ant-input-affix-wrapper,
+ .ant-select-selector {
+ height: 36px !important;
font-size: 13px !important;
}
/* Card padding */
- .ant-card { padding: 12px !important; }
+ .ant-card {
+ padding: 12px !important;
+ }
/* Mobile bottom nav adjustments */
- .mobile-bottom-nav { height: 56px !important; }
- .mobile-bottom-nav .nav-item .nav-icon { font-size: 18px !important; }
- .mobile-bottom-nav .nav-item { font-size: 9px !important; gap: 2px !important; }
+ .mobile-bottom-nav {
+ height: 56px !important;
+ }
+
+ .mobile-bottom-nav .nav-item .nav-icon {
+ font-size: 18px !important;
+ }
+
+ .mobile-bottom-nav .nav-item {
+ font-size: 9px !important;
+ gap: 2px !important;
+ }
/* InitialReplication page - Small Mobile */
.replication-container {
padding: 12px !important;
gap: 12px !important;
}
+
.replication-preview {
height: 25vh !important;
border-radius: 10px !important;
}
+
.replication-form-content {
padding: 12px !important;
}
+
.replication-form-content .ant-btn {
height: 36px !important;
font-size: 13px !important;
}
+
.replication-form-content .ant-input,
.replication-form-content .ant-input-affix-wrapper {
height: 36px !important;
@@ -415,30 +779,56 @@ html, body {
}
/* Video and image containers */
- video, img {
- max-height: 160px !important;
+ video,
+ img {
+ max-height: 160px !important;
border-radius: 6px !important;
}
/* Upload areas */
- .upload-area { padding: 12px !important; }
+ .upload-area {
+ padding: 12px !important;
+ }
/* Spacing adjustments */
- .mobile-p-16 { padding: 12px !important; }
- .mobile-gap-12 { gap: 8px !important; }
+ .mobile-p-16 {
+ padding: 12px !important;
+ }
+
+ .mobile-gap-12 {
+ gap: 8px !important;
+ }
/* Font size adjustments */
- h1 { font-size: 18px !important; }
- h2 { font-size: 16px !important; }
- h3 { font-size: 14px !important; }
- p { font-size: 12px !important; }
+ h1 {
+ font-size: 18px !important;
+ }
+
+ h2 {
+ font-size: 16px !important;
+ }
+
+ h3 {
+ font-size: 14px !important;
+ }
+
+ p {
+ font-size: 12px !important;
+ }
/* Button text */
- .btn-text-sm { font-size: 12px !important; }
+ .btn-text-sm {
+ font-size: 12px !important;
+ }
/* Margin adjustments */
- .mobile-mb-8 { margin-bottom: 8px !important; }
- .mobile-mb-12 { margin-bottom: 12px !important; }
+ .mobile-mb-8 {
+ margin-bottom: 8px !important;
+ }
+
+ .mobile-mb-12 {
+ margin-bottom: 12px !important;
+ }
}
/* ── Tablet Breakpoint (769px to 1024px) ────────────── */
@@ -450,33 +840,49 @@ html, body {
*/
/* Card max-width */
- .ant-card { max-width: calc(50% - 8px) !important; }
+ .ant-card {
+ max-width: calc(50% - 8px) !important;
+ }
/* Font adjustments */
- html, body { font-size: 15px !important; }
+ html,
+ body {
+ font-size: 15px !important;
+ }
}
/* ── Large Desktop Breakpoint (min-width: 1200px) ────────────── */
@media (min-width: 1200px) {
+
/* Max width container */
- .max-w-container { max-width: 1400px; margin: 0 auto; }
+ .max-w-container {
+ max-width: 1400px;
+ margin: 0 auto;
+ }
/* Card grid */
- .card-grid { grid-template-columns: repeat(3, 1fr) !important; }
+ .card-grid {
+ grid-template-columns: repeat(3, 1fr) !important;
+ }
}
/* ── HomePage Tabs ────────────── */
-.homepage-tabs .ant-tabs-nav { margin-bottom: 0; }
+.homepage-tabs .ant-tabs-nav {
+ margin-bottom: 0;
+}
+
.homepage-tabs .ant-tabs-tab {
font-size: 14px;
font-weight: 500;
padding: 8px 20px;
margin-right: 4px;
}
+
.homepage-tabs .ant-tabs-tab-active {
color: #6366f1;
font-weight: 600;
}
+
.homepage-tabs .ant-tabs-ink-bar {
background: linear-gradient(135deg, #6366f1, #8b5cf6);
-}
+}
\ No newline at end of file
diff --git a/video-gen-app/src/pages/CreativePlazaPage.tsx b/video-gen-app/src/pages/CreativePlazaPage.tsx
index 4d58436c..0f34b088 100644
--- a/video-gen-app/src/pages/CreativePlazaPage.tsx
+++ b/video-gen-app/src/pages/CreativePlazaPage.tsx
@@ -8,127 +8,326 @@ import {
PlayCircleOutlined,
CheckCircleOutlined,
HeartOutlined,
+ EyeOutlined,
+ ArrowRightOutlined,
+ CrownOutlined,
} from '@ant-design/icons';
const CreativePlazaPage: React.FC = () => {
const handleUnlock = () => {
- message.info('请联系客服开通');
+ message.info('请联系客服开通会员');
};
-
+ // 顶部特性卡片
const features = [
{
- icon: ,
- title: 'AI智能生成',
- description: '先进的AI技术,一键生成高质量视频内容',
+ icon: ,
+ title: 'AI 智能生成',
+ description: '先进 AI 技术一键产出高质量视频内容',
},
{
- icon: ,
+ icon: ,
title: '多格式输出',
- description: '支持多种视频格式,满足不同场景需求',
+ description: '支持多种视频格式,满足不同场景需求',
},
{
- icon: ,
+ icon: ,
title: '品质保证',
- description: '专业级画质,细节清晰,色彩鲜艳',
+ description: '专业级画质,细节清晰,色彩鲜艳',
},
{
- icon: ,
+ icon: ,
title: '创意无限',
- description: '丰富的模板和风格,激发创作灵感',
+ description: '丰富模板与风格,激发创作灵感',
},
];
+ // 精选案例占位
+ const caseList = [
+ { title: '美妆直播切片', tag: '热门', tagColor: '#ef4444' },
+ { title: '3C 数码测评', tag: '推荐', tagColor: '#6366f1' },
+ { title: '美食探店 vlog', tag: '推荐', tagColor: '#6366f1' },
+ { title: '服饰穿搭合集', tag: '热门', tagColor: '#ef4444' },
+ { title: '家居场景展示', tag: '推荐', tagColor: '#6366f1' },
+ { title: '母婴亲子内容', tag: '热门', tagColor: '#ef4444' },
+ ];
+
return (
-
-
-
-
-
+
+ {/* 顶部 Hero 区域 */}
+
+
+
+
-
- 创意素材案例
-
- 探索AI创作无限可能
+
+
+ 创意素材案例
-
+
+
+
+ 探索 AI 创作无限可能
+
-
- 精选AI生成的优秀素材案例,展示AI创作的无限潜力与创意灵感
+
+ 精选 AI 生成的优秀素材案例,展示 AI 创作的无限潜力与创意灵感
-
+ {/* 特性卡片 */}
+ {/*
{features.map((feature, index) => (
-
-
{feature.icon}
-
+
+
+ {feature.icon}
+
+
{feature.title}
-
-
+
+
{feature.description}
-
+
))}
-
+ */}
-
-
-
- 精选案例展示
-
-
-
-
-
-
- 敬请期待更多创意案例
+
+
+ {/*
*/}
-
+
+ {/* 案例网格 */}
+
+
+
+ {/*
+
会员可查看 */}
+
+
+ {caseList.map((item, index) => (
+
+
+ {/* 模糊视频缩略图占位 */}
+
+ {/* 标签 */}
+
+ {item.tag}
+
+
+
+
+
+ {item.title}
+
+
+
+
+
+ ))}
+
+ {/* 锁定遮罩 */}
+
+
+
);
};
-export default CreativePlazaPage;
\ No newline at end of file
+export default CreativePlazaPage;
diff --git a/video-gen-app/src/pages/HomePage.tsx b/video-gen-app/src/pages/HomePage.tsx
index 7e5ced8e..a4fc513f 100644
--- a/video-gen-app/src/pages/HomePage.tsx
+++ b/video-gen-app/src/pages/HomePage.tsx
@@ -72,7 +72,7 @@ const HomePage: React.FC = () => {
setActiveTab(key);
};
-
+
const aiEntries = [
{
@@ -120,21 +120,43 @@ const HomePage: React.FC = () => {
];
return (
-
+
+
+
首页工作台
+
如需进行账户素材推送:
+ {
+ navigate('/authorization')
+ }}>
+ 一键推送
+
+
+
+
+
+
{/* ========== 顶部工作台引导区域(三步流程) ========== */}
@@ -151,7 +173,7 @@ const HomePage: React.FC = () => {
-
+ {/*
{
onClick={() => {
navigate('/authorization')
}}
- >一键授权
+ >如需进行账户素材推送 一键推送
-
+
*/}
@@ -553,7 +575,7 @@ const HomePage: React.FC = () => {
近期作品
-
+
{/* Tab切换 */}
@@ -739,7 +761,7 @@ const HomePage: React.FC = () => {
{/* ========== 素材案例区域 ========== */}
-
{
alt={`素材案例 ${index + 1}`}
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
/>
- {/* 案例悬停遮罩(由父级 hover 触发) */}
+ 案例悬停遮罩(由父级 hover 触发)
{
))}
-
+
*/}
);
};
diff --git a/video-gen-app/src/pages/InitialInfo.tsx b/video-gen-app/src/pages/InitialInfo.tsx
index 715f4440..f0532b95 100644
--- a/video-gen-app/src/pages/InitialInfo.tsx
+++ b/video-gen-app/src/pages/InitialInfo.tsx
@@ -31,6 +31,8 @@ function InitialInfo() {
// 引擎和视频参数相关状态
const [enginesele, setEnginesele] = useState({});
const [countType, setCountType] = useState('');
+ // 图片引擎 id:取自 getEngine().engine.image[0].id
+ const [imageEngineId, setImageEngineId] = useState('');
const [showEngineModal, setShowEngineModal] = useState(false);
const [showVideoSettingsModal, setShowVideoSettingsModal] = useState(false);
const [videoDuration, setVideoDuration] = useState(5);
@@ -160,6 +162,10 @@ function InitialInfo() {
durations: firstEngine.supportedDurations || [5, 8, 10, 12, 15],
});
}
+ // 默认选中第一个图片引擎
+ if (data.engine?.image && data.engine.image.length > 0) {
+ setImageEngineId(data.engine.image[0].id);
+ }
})
.catch((error: any) => {
});
@@ -335,7 +341,7 @@ function InitialInfo() {
const createimage = (stepId: number) => {
let params = {
- engine_id: "0019e3dac0b795b925b",
+ engine_id: imageEngineId,
image_proportion: "1:1",
image_px: "2048x2048",
image_size: "2K"
@@ -349,7 +355,7 @@ function InitialInfo() {
const newcreateimage = () => {
// console.log('下一步:', stepId);
let params = {
- engine_id: "0019e3dac0b795b925b",
+ engine_id: imageEngineId,
image_proportion: "1:1",
image_px: "2048x2048",
image_size: "2K"
diff --git a/video-gen-app/src/pages/InitialReplication.tsx b/video-gen-app/src/pages/InitialReplication.tsx
index dafea04e..832afc4c 100644
--- a/video-gen-app/src/pages/InitialReplication.tsx
+++ b/video-gen-app/src/pages/InitialReplication.tsx
@@ -850,6 +850,7 @@ const GenerateConver: React.FC = () => {
) : (
{
) : (
{
const handleUnlock = () => {
- message.info('请联系客服开通');
+ message.info('请联系客服开通会员');
};
+ // 顶部统计 KPI
+ const stats = [
+ {
+ icon: ,
+ label: '今日爆款',
+ value: '1,286',
+ unit: '条',
+ trend: '+12.5%',
+ color: '#f59e0b',
+ },
+ {
+ icon: ,
+ label: '总浏览量',
+ value: '428',
+ unit: '万',
+ trend: '+8.2%',
+ color: '#6366f1',
+ },
+ {
+ icon: ,
+ label: '总互动数',
+ value: '76.4',
+ unit: '万',
+ trend: '+15.7%',
+ color: '#ec4899',
+ },
+ {
+ icon: ,
+ label: '上升最快',
+ value: '美妆',
+ unit: '赛道',
+ trend: '+28.3%',
+ color: '#10b981',
+ },
+ ];
+
+ // 行业热度排行(假数据)
+ const industryRank = [
+ { rank: 1, name: '美妆个护', hot: 98.5, change: 'up' as const, value: 98234 },
+ { rank: 2, name: '服饰穿搭', hot: 95.2, change: 'up' as const, value: 87643 },
+ { rank: 3, name: '美食探店', hot: 91.7, change: 'up' as const, value: 78921 },
+ { rank: 4, name: '3C 数码', hot: 87.3, change: 'down' as const, value: 65432 },
+ { rank: 5, name: '家居生活', hot: 82.6, change: 'up' as const, value: 54321 },
+ { rank: 6, name: '母婴亲子', hot: 78.1, change: 'flat' as const, value: 43210 },
+ { rank: 7, name: '运动健身', hot: 72.4, change: 'up' as const, value: 38765 },
+ ];
+
+ // 爆款素材榜单
+ const popularList = [
+ {
+ rank: 1,
+ title: '夏日美妆教程:3 步打造奶油肌',
+ author: '美妆博主·小雅',
+ views: '128.4w',
+ likes: '12.6w',
+ comments: '8,234',
+ tag: 'NO.1',
+ tagColor: '#f59e0b',
+ },
+ {
+ rank: 2,
+ title: '平价穿搭 | 学生党一周不重样',
+ author: '穿搭达人·Miki',
+ views: '96.2w',
+ likes: '9.8w',
+ comments: '6,128',
+ tag: 'NO.2',
+ tagColor: '#a78bfa',
+ },
+ {
+ rank: 3,
+ title: '探店 vlog | 网红咖啡馆测评',
+ author: '美食探店·阿伦',
+ views: '78.5w',
+ likes: '7.4w',
+ comments: '4,892',
+ tag: 'NO.3',
+ tagColor: '#6366f1',
+ },
+ {
+ rank: 4,
+ title: 'iPhone 16 Pro 一周深度体验',
+ author: '数码评测·老王',
+ views: '65.7w',
+ likes: '6.1w',
+ comments: '3,847',
+ tag: 'NO.4',
+ tagColor: '#94a3b8',
+ },
+ ];
+
return (
-
-
-
-
-
+
+ {/* Hero 区域 - 橙金主题(区别创意素材案例的紫粉) */}
+
+
+
+
-
- 行业爆款大盘
-
- 发现热门素材趋势
+
+
+ 行业爆款大盘
-
+
+
+
+ 实时追踪全网热门素材趋势
+
-
- 实时追踪各行业爆款素材,把握创作风向,打造热门内容
+
+ 实时追踪各行业爆款素材,把握创作风向,打造热门内容
-
-
-
- 行业热度排行
-
-
-
- 敬请期待行业热度排行
+ {stats.map((item, index) => (
+
+
+
+ {item.icon}
+
+
+
+ {item.trend}
+
+
+
+ {item.value}
+ {item.unit}
+
+
{item.label}
-
-
+ ))}
+
*/}
+ {/* 行业热度排行 */}
-
-
-
- 爆款素材榜单
-
-
-
-
- 敬请期待更多爆款素材
-
-
);
};
-export default PopularPage;
\ No newline at end of file
+export default PopularPage;
diff --git a/video-gen-app/src/pages/RemoveRw.tsx b/video-gen-app/src/pages/RemoveRw.tsx
index 3478c8b9..d4e76c10 100644
--- a/video-gen-app/src/pages/RemoveRw.tsx
+++ b/video-gen-app/src/pages/RemoveRw.tsx
@@ -31,6 +31,8 @@ function InitialInfo() {
// 引擎和视频参数相关状态
const [enginesele, setEnginesele] = useState
({});
const [countType, setCountType] = useState('');
+ // 图片引擎 id:取自 getEngine().engine.image[0].id
+ const [imageEngineId, setImageEngineId] = useState('');
const [showEngineModal, setShowEngineModal] = useState(false);
const [showVideoSettingsModal, setShowVideoSettingsModal] = useState(false);
const [videoDuration, setVideoDuration] = useState(5);
@@ -155,6 +157,10 @@ function InitialInfo() {
durations: firstEngine.supportedDurations || [5, 8, 10, 12, 15],
});
}
+ // 默认选中第一个图片引擎
+ if (data.engine?.image && data.engine.image.length > 0) {
+ setImageEngineId(data.engine.image[0].id);
+ }
})
.catch((error: any) => {
});
@@ -341,7 +347,7 @@ function InitialInfo() {
const createimage = (stepId: number) => {
let params = {
- engine_id: "0019e3dac0b795b925b",
+ engine_id: imageEngineId,
image_proportion: "1:1",
image_px: "2048x2048",
image_size: "2K"
@@ -355,7 +361,7 @@ function InitialInfo() {
const newcreateimage = () => {
// console.log('下一步:', stepId);
let params = {
- engine_id: "0019e3dac0b795b925b",
+ engine_id: imageEngineId,
image_proportion: "1:1",
image_px: "2048x2048",
image_size: "2K"
diff --git a/video-gen-app/src/types/index.ts b/video-gen-app/src/types/index.ts
index 84615a10..2d88c4e9 100644
--- a/video-gen-app/src/types/index.ts
+++ b/video-gen-app/src/types/index.ts
@@ -1,10 +1,27 @@
+export interface ResourceCapacity {
+ enabled: boolean;
+ source?: string;
+ hasUserConfig?: boolean;
+ usedBytes: number;
+ availableBytes: number;
+ totalBytes: number;
+ usagePercent: number;
+ exceeded: boolean;
+ limitValue?: string;
+ limitUnit: string; // 'KB' | 'MB' | 'GB' 等
+}
+
export interface User {
id: string;
username: string;
email: string;
avatar?: string;
credits: number;
+ isAdmin?: boolean;
+ phone?: string;
allowedMenus?: string[] | null;
+ mustSetPassword?: boolean;
+ resourceCapacity?: ResourceCapacity;
}
export interface CreditRecord {