Browse Source

fix: 暂存

createUI
leiyuee 5 days ago
parent
commit
b26f4a1584
  1. BIN
      src/assets/images/create/tabs/create_tab_all.png
  2. BIN
      src/assets/images/ic_dui.png
  3. 52
      src/components/TabPill/index.scss
  4. 21
      src/components/TabPill/index.tsx
  5. 12
      src/pages/Create/createTabIcons.ts
  6. 43
      src/pages/Create/index.scss
  7. 53
      src/pages/Create/panels/InspirationLibraryPanel/index.scss
  8. 28
      src/pages/Create/panels/InspirationLibraryPanel/index.tsx
  9. 6
      src/pages/Create/panels/MyCreationsPanel/index.scss
  10. 30
      src/pages/Create/panels/MyCreationsPanel/index.tsx
  11. 48
      src/pages/Create/panels/MyFavoritePanel/index.tsx

BIN
src/assets/images/create/tabs/create_tab_all.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
src/assets/images/ic_dui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

52
src/components/TabPill/index.scss

@ -0,0 +1,52 @@
.tabPill {
width: 91px;
height: 28px;
margin: 0;
padding: 4px 20px;
border: none;
border-radius: 18px;
background: #ffffff;
color: #333333;
font-size: 14px;
font-weight: 400;
line-height: normal;
cursor: pointer;
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 3px;
white-space: nowrap;
flex-shrink: 0;
}
.tabPill_wide {
width: 97px;
}
.tabPill__icon {
flex-shrink: 0;
width: 20px;
height: 20px;
display: block;
object-fit: contain;
filter: brightness(0) saturate(100%) invert(17%) sepia(0%) saturate(0%) hue-rotate(187deg) brightness(95%) contrast(91%);
}
.tabPill__label {
flex-shrink: 0;
}
.tabPill_active {
background: #fbf0ff;
color: #6f47f5;
}
.tabPill_active .tabPill__icon {
filter: brightness(0) saturate(100%) invert(41%) sepia(56%) saturate(6861%) hue-rotate(241deg) brightness(93%) contrast(106%);
}
.tabPill:focus-visible {
outline: 2px solid rgba(111, 71, 245, 0.45);
outline-offset: 2px;
}

21
src/components/TabPill/index.tsx

@ -0,0 +1,21 @@
import type { ButtonHTMLAttributes, ReactNode } from "react";
import "./index.scss";
export type TabPillProps = ButtonHTMLAttributes<HTMLButtonElement> & {
active?: boolean;
children: ReactNode;
iconSrc?: string;
wide?: boolean;
};
export function TabPill({ active, children, className, iconSrc, type = "button", wide, ...props }: TabPillProps) {
const tabPillClassName = ["tabPill", active ? "tabPill_active" : null, wide ? "tabPill_wide" : null, className].filter(Boolean).join(" ");
return (
<button className={tabPillClassName} type={type} {...props}>
{iconSrc ? <img className="tabPill__icon" src={iconSrc} alt="" aria-hidden /> : null}
<span className="tabPill__label">{children}</span>
</button>
);
}

12
src/pages/Create/createTabIcons.ts

@ -0,0 +1,12 @@
export type CreateTabIconName = "all" | "image" | "video" | "voice" | "template" | "character" | "background" | "characterAssets";
export const CREATE_TAB_ICON_SRC: Record<CreateTabIconName, string> = {
all: new URL("/src/assets/images/create/tabs/create_tab_all.png", import.meta.url).href,
image: new URL("/src/assets/images/create/tabs/create_tab_image.png", import.meta.url).href,
video: new URL("/src/assets/images/create/tabs/create_tab_video.png", import.meta.url).href,
voice: new URL("/src/assets/images/create/tabs/create_tab_voice.png", import.meta.url).href,
template: new URL("/src/assets/images/create/tabs/create_tab_template.png", import.meta.url).href,
character: new URL("/src/assets/images/create/tabs/create_tab_character.png", import.meta.url).href,
background: new URL("/src/assets/images/create/tabs/create_tab_background.png", import.meta.url).href,
characterAssets: new URL("/src/assets/images/create/tabs/create_tab_character_assets.png", import.meta.url).href,
};

43
src/pages/Create/index.scss

@ -131,49 +131,6 @@
box-sizing: border-box;
}
.createPage__filterPill {
width: 91px;
height: 28px;
margin: 0;
padding: 4px 20px;
border: none;
border-radius: 18px;
background: #ffffff;
color: #333333;
font-size: 14px;
font-weight: 400;
line-height: normal;
cursor: pointer;
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 3px;
white-space: nowrap;
}
.createPage__filterPillIcon {
flex-shrink: 0;
width: 20px;
height: 20px;
display: block;
object-fit: contain;
}
.createPage__filterPillIcon_placeholder {
visibility: hidden;
}
.createPage__filterPill_active {
background: #fbf0ff;
color: #6f47f5;
}
.createPage__filterPill:focus-visible {
outline: 2px solid rgba(111, 71, 245, 0.45);
outline-offset: 2px;
}
.createPage__batchBtn {
width: 91px;
height: 36px;

53
src/pages/Create/panels/InspirationLibraryPanel/index.scss

@ -25,59 +25,6 @@
gap: 10px;
}
.inspirationPanel__filterPill {
flex-shrink: 0;
width: 91px;
height: 28px;
margin: 0;
padding: 4px 20px;
border: none;
border-radius: 18px;
background: #ffffff;
color: #333333;
font-size: 14px;
font-weight: 400;
line-height: normal;
cursor: pointer;
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 3px;
white-space: nowrap;
}
.inspirationPanel__filterPill_wide {
width: 97px;
}
.inspirationPanel__filterPillIcon {
width: 20px;
height: 20px;
flex-shrink: 0;
display: block;
object-fit: contain;
filter: brightness(0) saturate(100%) invert(17%) sepia(0%) saturate(0%) hue-rotate(187deg) brightness(95%) contrast(91%);
}
.inspirationPanel__filterPill_active .inspirationPanel__filterPillIcon {
filter: brightness(0) saturate(100%) invert(39%) sepia(93%) saturate(2844%) hue-rotate(240deg) brightness(98%) contrast(96%);
}
.inspirationPanel__filterPillLabel {
flex-shrink: 0;
}
.inspirationPanel__filterPill_active {
background: #fbf0ff;
color: #6f47f5;
}
.inspirationPanel__filterPill:focus-visible {
outline: 2px solid rgba(111, 71, 245, 0.45);
outline-offset: 2px;
}
/* 设计稿:分类行底到「我的角色」标题区约 13px */
.inspirationPanel__content {
margin-top: 20px;

28
src/pages/Create/panels/InspirationLibraryPanel/index.tsx

@ -3,8 +3,10 @@ import { useTranslation } from "react-i18next";
import { AssetLibraryToolbar } from "./AssetLibraryToolbar";
import { BackgroundAssetsPanel, useBackgroundAssetsModel } from "./BackgroundAssets/BackgroundAssets";
import { TemplateAssetsPanel, useTemplateAssetsModel } from "./TemplateAssets/TemplateAssets";
import { TabPill } from "@/components/TabPill";
import { CharacterLibrarySections } from "@/pages/Characters";
import { CharacterAssetsPanel, useCharacterAssetsModel } from "./CharacterAssetsPanel/CharacterAssetsPanel";
import { CREATE_TAB_ICON_SRC, type CreateTabIconName } from "../../createTabIcons";
import "./index.scss";
export interface InspirationLibraryPanelProps {
@ -24,20 +26,13 @@ function useInspirationLibraryToolbar(initialFilter: InspirationFilterId = "back
return { filter, selectFilter };
}
const FILTER_CONFIG: readonly InspirationFilterId[] = [
"background", // 背景素材
"character", // 角色
"characterAssets", // 角色素材
"templates", // 模板
const FILTER_CONFIG: readonly { iconName: CreateTabIconName; id: InspirationFilterId; wide?: boolean }[] = [
{ iconName: "background", id: "background" }, // 背景素材
{ iconName: "character", id: "character" }, // 角色
{ iconName: "characterAssets", id: "characterAssets", wide: true }, // 角色素材
{ iconName: "template", id: "templates" }, // 模板
];
const INSPIRATION_FILTER_ICON_SRC: Record<InspirationFilterId, string> = {
background: new URL("/src/assets/images/create/tabs/create_tab_background.png", import.meta.url).href,
character: new URL("/src/assets/images/create/tabs/create_tab_character.png", import.meta.url).href,
characterAssets: new URL("/src/assets/images/create/tabs/create_tab_character_assets.png", import.meta.url).href,
templates: new URL("/src/assets/images/create/tabs/create_tab_template.png", import.meta.url).href,
};
export default function InspirationLibraryPanel({ tabIdPrefix, setToolbarExtra }: InspirationLibraryPanelProps) {
const { t } = useTranslation();
const { filter, selectFilter } = useInspirationLibraryToolbar("background");
@ -52,11 +47,10 @@ export default function InspirationLibraryPanel({ tabIdPrefix, setToolbarExtra }
() => (
<div className="createPage__subRow inspirationPanel__toolbar inspirationPanel__toolbarExtra">
<div className="inspirationPanel__filters" role="group" aria-label={t("pages.create.inspiration.filtersAria")} data-active-tab={filter}>
{FILTER_CONFIG.map((id) => (
<button key={id} type="button" className={["inspirationPanel__filterPill", id === "characterAssets" ? "inspirationPanel__filterPill_wide" : "", filter === id ? "inspirationPanel__filterPill_active" : ""].filter(Boolean).join(" ")} onClick={() => selectFilter(id)}>
<img className="inspirationPanel__filterPillIcon" src={INSPIRATION_FILTER_ICON_SRC[id]} alt="" aria-hidden />
<span className="inspirationPanel__filterPillLabel">{t(`pages.create.inspiration.filters.${id}`)}</span>
</button>
{FILTER_CONFIG.map(({ iconName, id, wide }) => (
<TabPill key={id} iconSrc={CREATE_TAB_ICON_SRC[iconName]} active={filter === id} wide={wide} onClick={() => selectFilter(id)}>
{t(`pages.create.inspiration.filters.${id}`)}
</TabPill>
))}
</div>
{showAssetToolbar ? (

6
src/pages/Create/panels/MyCreationsPanel/index.scss

@ -108,10 +108,8 @@
}
.createPage__myCreationsCheckboxIcon {
position: absolute;
inset: -2px;
width: 25px;
height: 25px;
width: 21px;
height: 21px;
display: block;
}

30
src/pages/Create/panels/MyCreationsPanel/index.tsx

@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
import { useSearchParams } from "react-router-dom";
import { getTaskAssetForAsset, getTaskList, getTemplateTaskList, getAssetDetail, addPostLike, delPostLike, deleteAsset, deleteTemplateAsset, getPostDownloadUrls } from "@/api/api";
import { AssetGalleryTile, getAssetDragUrls, resolveAssetGalleryTile, useAssetGalleryVoicePlayback } from "@/components/AssetGalleryTile";
import { TabPill } from "@/components/TabPill";
import { MY_CREATIONS_REFRESH_QUERY } from "@/constants";
import { formatLocalDate } from "@/utils";
import { openAssetDetailsDialog } from "@/dialog";
@ -13,6 +14,7 @@ import { useImageDragTransfer, useWarmupImageDragUrls } from "@/hooks/useImageDr
import { VoiceMusicDetailModal } from "@/pages/Voice/components/music/VoiceMusicDetailModal";
import { VOICE_MUSIC_SUB_TYPE } from "@/pages/Voice/mockData";
import { getVoiceMusicDetailNeighborId, parseVoiceMusicAssetDetail, type VoiceMusicDetailNavigation } from "@/pages/Voice/utils/voiceGalleryTaskUtils";
import { CREATE_TAB_ICON_SRC, type CreateTabIconName } from "../../createTabIcons";
import "./index.scss";
const PAGE_SIZE = 20;
@ -27,24 +29,16 @@ export interface MyCreationsPanelProps {
}
/** 筛选项:title 对应文案 key,type 对应接口参数 */
const MY_CREATIONS_FILTER_OPTIONS: { title: string; type: number }[] = [
{ title: "all", type: 0 },
{ title: "image", type: 1 },
{ title: "video", type: 2 },
{ title: "voice", type: 3 },
{ title: "template", type: 4 },
const MY_CREATIONS_FILTER_OPTIONS: { iconName: CreateTabIconName; title: string; type: number }[] = [
{ iconName: "all", title: "all", type: 0 },
{ iconName: "image", title: "image", type: 1 },
{ iconName: "video", title: "video", type: 2 },
{ iconName: "voice", title: "voice", type: 3 },
{ iconName: "template", title: "template", type: 4 },
// { title: "character", type: 5 },
];
const MY_CREATIONS_FILTER_ICON_SRC: Record<string, string> = {
all: new URL("/src/assets/images/create/tabs/create_tab_all.png", import.meta.url).href,
image: new URL("/src/assets/images/create/tabs/create_tab_image.png", import.meta.url).href,
video: new URL("/src/assets/images/create/tabs/create_tab_video.png", import.meta.url).href,
voice: new URL("/src/assets/images/create/tabs/create_tab_voice.png", import.meta.url).href,
template: new URL("/src/assets/images/create/tabs/create_tab_template.png", import.meta.url).href,
};
const MY_CREATIONS_BATCH_CHECKED_SRC = "https://www.figma.com/api/mcp/asset/73956270-6032-4730-94ec-3115dfde0970";
const MY_CREATIONS_BATCH_CHECKED_SRC = new URL("@/assets/images/ic_dui.png", import.meta.url).href;
function toMs(raw: any): number {
if (raw == null || raw === "") return 0;
@ -883,12 +877,10 @@ export default function MyCreationsPanel({ tabIdPrefix, setToolbarExtra }: MyCre
<div className="createPage__filters" role="group" aria-label={t("pages.create.filtersAria")} data-active-tab={selectedType}>
{MY_CREATIONS_FILTER_OPTIONS.map((option) => {
const selected = selectedType === option.type;
const filterIconSrc = MY_CREATIONS_FILTER_ICON_SRC[option.title];
return (
<button key={option.type} type="button" data-type={option.type} data-selected={selected} aria-pressed={selected} className={selected ? "createPage__filterPill createPage__filterPill_active" : "createPage__filterPill"} onClick={() => selectFilterType(option.type)}>
{filterIconSrc ? <img className="createPage__filterPillIcon" src={filterIconSrc} alt="" aria-hidden /> : <span className="createPage__filterPillIcon createPage__filterPillIcon_placeholder" aria-hidden />}
<TabPill key={option.type} iconSrc={CREATE_TAB_ICON_SRC[option.iconName]} active={selected} data-type={option.type} data-selected={selected} aria-pressed={selected} onClick={() => selectFilterType(option.type)}>
{t(`pages.create.filters.${option.title}`)}
</button>
</TabPill>
);
})}
</div>

48
src/pages/Create/panels/MyFavoritePanel/index.tsx

@ -3,8 +3,10 @@ import { useTranslation } from "react-i18next";
import { useSearchParams } from "react-router-dom";
import { getPostLikeList, getUserLikeCharacterList } from "@/api/api";
import { AssetGalleryTile, getAssetDragUrls, resolveAssetGalleryTile, useAssetGalleryVoicePlayback } from "@/components/AssetGalleryTile";
import { TabPill } from "@/components/TabPill";
import { openAssetDetailsDialog, openCharacterDetailDialog } from "@/dialog";
import { useImageDragTransfer } from "@/hooks/useImageDragTransfer";
import { CREATE_TAB_ICON_SRC, type CreateTabIconName } from "../../createTabIcons";
import "./index.scss";
const PAGE_SIZE = 20;
@ -14,34 +16,21 @@ export interface MyFavoritePanelProps {
}
/** 筛选项:title 对应文案 key,assetType 对应接口参数(与「我的创造」一致) */
const MY_FAVORITE_FILTER_OPTIONS: { title: string; assetType: string }[] = [
{ title: "all", assetType: "0" },
{ title: "image", assetType: "1" },
{ title: "video", assetType: "2" },
{ title: "voice", assetType: "3" },
{ title: "template", assetType: "4" },
{ title: "character", assetType: "5" },
const MY_FAVORITE_FILTER_OPTIONS: { assetType: string; iconName: CreateTabIconName; title: string }[] = [
{ assetType: "0", iconName: "all", title: "all" },
{ assetType: "1", iconName: "image", title: "image" },
{ assetType: "2", iconName: "video", title: "video" },
{ assetType: "3", iconName: "voice", title: "voice" },
{ assetType: "4", iconName: "template", title: "template" },
{ assetType: "5", iconName: "character", title: "character" },
];
const MY_FAVORITE_FILTER_ICON_SRC: Record<string, string> = {
all: new URL("/src/assets/images/create/tabs/create_tab_all.png", import.meta.url).href,
image: new URL("/src/assets/images/create/tabs/create_tab_image.png", import.meta.url).href,
video: new URL("/src/assets/images/create/tabs/create_tab_video.png", import.meta.url).href,
voice: new URL("/src/assets/images/create/tabs/create_tab_voice.png", import.meta.url).href,
template: new URL("/src/assets/images/create/tabs/create_tab_template.png", import.meta.url).href,
character: new URL("/src/assets/images/create/tabs/create_tab_character.png", import.meta.url).href,
};
function buildFavoriteListParams(assetType: string, pageNum: number): { page: number; pageSize: number; assetType?: string } {
const params: { page: number; pageSize: number; assetType?: string } = { page: pageNum, pageSize: PAGE_SIZE };
if (assetType !== "0") params.assetType = assetType;
return params;
}
function joinClassNames(...parts: Array<string | false | null | undefined>) {
return parts.filter(Boolean).join(" ");
}
function findScrollContainer(node: HTMLElement | null): HTMLElement | null {
let current = node?.parentElement ?? null;
while (current != null) {
@ -210,9 +199,9 @@ export default function MyFavoritePanel({ tabIdPrefix, setToolbarExtra }: MyFavo
const translatedFilters = useMemo(
() =>
MY_FAVORITE_FILTER_OPTIONS.map(({ title, assetType }) => ({
MY_FAVORITE_FILTER_OPTIONS.map(({ assetType, iconName, title }) => ({
assetType,
iconSrc: MY_FAVORITE_FILTER_ICON_SRC[title],
iconName,
label: t(`pages.create.filters.${title}`),
})),
[t],
@ -221,19 +210,10 @@ export default function MyFavoritePanel({ tabIdPrefix, setToolbarExtra }: MyFavo
const toolbarExtra = useMemo(
() => (
<div className="createPage__filters" role="group" aria-label={t("pages.create.filtersAria")} data-active-tab={selectedType}>
{translatedFilters.map(({ assetType, iconSrc, label }) => (
<button
key={assetType}
type="button"
data-type={assetType}
data-selected={selectedType === assetType}
aria-pressed={selectedType === assetType}
className={joinClassNames("createPage__filterPill", selectedType === assetType && "createPage__filterPill_active")}
onClick={() => selectFilterType(assetType)}
>
<img className="createPage__filterPillIcon" src={iconSrc} alt="" aria-hidden />
{translatedFilters.map(({ assetType, iconName, label }) => (
<TabPill key={assetType} iconSrc={CREATE_TAB_ICON_SRC[iconName]} active={selectedType === assetType} data-type={assetType} data-selected={selectedType === assetType} aria-pressed={selectedType === assetType} onClick={() => selectFilterType(assetType)}>
{label}
</button>
</TabPill>
))}
</div>
),

Loading…
Cancel
Save