feat: enhance ecommerce UI with typewriter animation, icon buttons, responsive layout, and refined design tokens
- EcommercePage.tsx: Add typewriter animation (useTypewriter hook) for slogan text with blinking cursor; replace text-based add/delete buttons with Ant Design icons (CloudUploadOutlined, DeleteOutlined); make command history panel responsive to window width (auto-collapse ≤1180px); update button labels from generic "添加" to context-specific "上传商品图" / "上传素材" - New useTypewriter.ts hook: Character-by-character typewriter animation with configurable speed and pause-before-loop, auto-resets on text change - reset.css: Add comprehensive HTML/body typography baseline (font-size, text-rendering, font-smoothing, line-height); extend reset coverage to select/canvas/svg elements; add overflow-wrap:anywhere for text-bearing elements; add min-width:0 to prevent form element overflow - primitives.css: Add reusable typography utility classes (ui-page-title, ui-section-title, ui-body-copy) with design token references - tokens.css: Expand design token set for typography scales, font weights, and leading values - ecommerce-standalone.css: Add 689 lines of standalone ecommerce page styles - Page CSS (ecommerce, image-workbench, more-tools, more, script-tokens, script-tokens-v5, studio-layout): Enhanced visual styles and layout refinements across all pages - app-shell.css: Shell-level layout and styling improvements
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
AppstoreOutlined,
|
||||
CloudUploadOutlined,
|
||||
CloseOutlined,
|
||||
DeleteOutlined,
|
||||
FileImageOutlined,
|
||||
FolderOpenOutlined,
|
||||
FrownOutlined,
|
||||
@@ -16,6 +17,7 @@
|
||||
TableOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useEffect, useMemo, useRef, useState, type CSSProperties, type ChangeEvent, type DragEvent, type MouseEvent as ReactMouseEvent, type PointerEvent as ReactPointerEvent, type ReactNode } from "react";
|
||||
import { useTypewriter } from "../../hooks/useTypewriter";
|
||||
import "../../styles/pages/ecommerce.css";
|
||||
import "../../styles/pages/local-theme-parity.css";
|
||||
import { ossAssets } from "../../data/ossAssets";
|
||||
@@ -1187,7 +1189,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
const [visibleComposerMenu, setVisibleComposerMenu] = useState<ComposerMenuKey | null>(null);
|
||||
const [isComposerMenuClosing, setIsComposerMenuClosing] = useState(false);
|
||||
const [composerPopoverLeft, setComposerPopoverLeft] = useState(0);
|
||||
const [isCommandHistoryCollapsed, setIsCommandHistoryCollapsed] = useState(false);
|
||||
const [isCommandHistoryCollapsed, setIsCommandHistoryCollapsed] = useState(() => (typeof window !== "undefined" ? window.innerWidth <= 1180 : false));
|
||||
const [openCloneModelSelect, setOpenCloneModelSelect] = useState<CloneModelSelectKey | null>(null);
|
||||
const [cloneModelSelectDropUp, setCloneModelSelectDropUp] = useState(false);
|
||||
const [cloneReferenceMode, setCloneReferenceMode] = useState<CloneReferenceMode>("upload");
|
||||
@@ -1223,6 +1225,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
offsetY: 0,
|
||||
});
|
||||
const [isCommandComposerCompact, setIsCommandComposerCompact] = useState(false);
|
||||
const typewriterText = useTypewriter("万物皆可AI,广告素材一键生成");
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -1238,6 +1241,16 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
previewOffsetRef.current = previewOffset;
|
||||
}, [previewOffset]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return undefined;
|
||||
const syncHistoryPanel = () => {
|
||||
setIsCommandHistoryCollapsed(window.innerWidth <= 1180);
|
||||
};
|
||||
syncHistoryPanel();
|
||||
window.addEventListener("resize", syncHistoryPanel);
|
||||
return () => window.removeEventListener("resize", syncHistoryPanel);
|
||||
}, []);
|
||||
|
||||
const previewTransformStyle = useMemo<CSSProperties>(
|
||||
() => ({
|
||||
transform: `translate3d(${previewOffset.x}px, ${previewOffset.y}px, 0) scale(${previewZoom})`,
|
||||
@@ -3729,7 +3742,10 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
if (isCommandComposerCompact) setIsCommandComposerCompact(false);
|
||||
}}
|
||||
>
|
||||
<h1 className={`ecom-command-title${status === "done" ? " is-after-generate" : ""}`}>万物皆可AI,广告素材一键生成</h1>
|
||||
<h1 className={`ecom-command-title${status === "done" ? " is-after-generate" : ""}`}>
|
||||
{typewriterText}
|
||||
<span className="typewriter-cursor" aria-hidden="true">|</span>
|
||||
</h1>
|
||||
<input
|
||||
ref={cloneReferenceInputRef}
|
||||
type="file"
|
||||
@@ -3772,8 +3788,8 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
if (files.length) addComposerAssets(files);
|
||||
}}
|
||||
>
|
||||
<span aria-hidden="true">+</span>
|
||||
<strong>添加</strong>
|
||||
<span aria-hidden="true"><CloudUploadOutlined /></span>
|
||||
<strong>上传商品图</strong>
|
||||
</button>
|
||||
{productImages.length || videoOutfitVideoFile ? (
|
||||
<div className="ecom-command-asset-popover" aria-label="宸蹭笂浼犵礌鏉?">
|
||||
@@ -3783,7 +3799,9 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
<span className="ecom-command-asset-zoom" aria-hidden="true">
|
||||
<img src={image.src} alt="" />
|
||||
</span>
|
||||
<button type="button" onClick={() => removeProductImage(image.id)} aria-label="删除图片">×</button>
|
||||
<button type="button" onClick={() => removeProductImage(image.id)} aria-label="删除图片">
|
||||
<DeleteOutlined />
|
||||
</button>
|
||||
</figure>
|
||||
))}
|
||||
{videoOutfitVideoFile && videoOutfitPreviewUrl ? (
|
||||
@@ -3792,7 +3810,9 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
<span className="ecom-command-asset-zoom" aria-hidden="true">
|
||||
<video src={videoOutfitPreviewUrl} muted playsInline />
|
||||
</span>
|
||||
<button type="button" onClick={() => setVideoOutfitVideoFile(null)} aria-label="删除视频">×</button>
|
||||
<button type="button" onClick={() => setVideoOutfitVideoFile(null)} aria-label="删除视频">
|
||||
<DeleteOutlined />
|
||||
</button>
|
||||
</figure>
|
||||
) : null}
|
||||
<button type="button" className="ecom-command-asset-add" onClick={() => productInputRef.current?.click()} aria-label="继续上传">+</button>
|
||||
@@ -3836,8 +3856,8 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
if (files.length) addComposerAssets(files);
|
||||
}}
|
||||
>
|
||||
<span aria-hidden="true">+</span>
|
||||
<strong>添加</strong>
|
||||
<span aria-hidden="true"><CloudUploadOutlined /></span>
|
||||
<strong>上传素材</strong>
|
||||
</button>
|
||||
<button type="button" className={composerMenu === "mode" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("mode", event)}>
|
||||
<span className="ecom-command-option-icon" aria-hidden="true"><AppstoreOutlined /></span>
|
||||
|
||||
Reference in New Issue
Block a user