diff --git a/web/src/pages/assets/index.tsx b/web/src/pages/assets/index.tsx index 5835b1092..1d8526400 100644 --- a/web/src/pages/assets/index.tsx +++ b/web/src/pages/assets/index.tsx @@ -1,4 +1,4 @@ -import { Copy, Download, FileUp, MoreHorizontal, PencilLine, Plus, Search, Trash2, Upload } from "lucide-react"; +import { CheckCheck, Copy, Download, FileUp, MoreHorizontal, PencilLine, Plus, Search, Trash2, Upload } from "lucide-react"; import { useEffect, useMemo, useRef, useState } from "react"; import { App, Button, Drawer, Dropdown, Form, Input, Modal, Select, Space, Tag, Typography } from "antd"; @@ -95,6 +95,8 @@ export default function AssetsPage() { return assetSearchText(asset).includes(query); }); }, [validAssets, keyword, kindFilter, categoryFilter]); + const filteredAssetIds = useMemo(() => filteredAssets.map((asset) => asset.id), [filteredAssets]); + const allFilteredSelected = filteredAssetIds.length > 0 && filteredAssetIds.every((id) => selectedIds.includes(id)); const visibleAssets = useMemo(() => { const start = (page - 1) * pageSize; @@ -283,6 +285,7 @@ export default function AssetsPage() { {selectedAssets.length ? (
已选择 {selectedAssets.length} 个素材 + diff --git a/web/src/styles/globals.css b/web/src/styles/globals.css index 67c18eccd..5ea8093d9 100644 --- a/web/src/styles/globals.css +++ b/web/src/styles/globals.css @@ -3419,17 +3419,19 @@ .library-page .app-page-header p { max-width: 560px; } /* 页面头部按钮优化 - 去除重边框 */ - .library-page .app-page-header .ant-btn, - .canvas-library-page .app-page-header .ant-btn { + html:not(.dark) .library-page .app-page-header .ant-btn, + html:not(.dark) .canvas-library-page .app-page-header .ant-btn { border: 1px solid rgba(17, 17, 17, 0.08) !important; background: #ffffff !important; + color: #171717 !important; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04) !important; transition: all 180ms ease-out !important; } - .library-page .app-page-header .ant-btn:hover, - .canvas-library-page .app-page-header .ant-btn:hover { + html:not(.dark) .library-page .app-page-header .ant-btn:hover, + html:not(.dark) .canvas-library-page .app-page-header .ant-btn:hover { border-color: rgba(17, 17, 17, 0.12) !important; background: #ffffff !important; + color: #171717 !important; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08) !important; transform: translateY(-1px); } diff --git a/web/test/asset-header-button-style.test.ts b/web/test/asset-header-button-style.test.ts new file mode 100644 index 000000000..0b9ad39e9 --- /dev/null +++ b/web/test/asset-header-button-style.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +describe("asset library header buttons", () => { + test("only forces white header buttons in light mode", () => { + const css = readFileSync(resolve(import.meta.dir, "../src/styles/globals.css"), "utf8"); + const headerButtonRule = css.match(/html:not\(\.dark\) \.library-page \.app-page-header \.ant-btn,[\s\S]*?transition: all 180ms ease-out !important;\s*}/)?.[0] || ""; + const headerButtonHoverRule = css.match(/html:not\(\.dark\) \.library-page \.app-page-header \.ant-btn:hover,[\s\S]*?transform: translateY\(-1px\);\s*}/)?.[0] || ""; + + expect(headerButtonRule).toContain("background: #ffffff !important;"); + expect(headerButtonRule).toContain("color: #171717 !important;"); + expect(headerButtonHoverRule).toContain("background: #ffffff !important;"); + expect(headerButtonHoverRule).toContain("color: #171717 !important;"); + expect(css).not.toContain("\n .library-page .app-page-header .ant-btn,\n"); + }); +}); diff --git a/web/test/assets-page-batch-toolbar.test.ts b/web/test/assets-page-batch-toolbar.test.ts new file mode 100644 index 000000000..479b67499 --- /dev/null +++ b/web/test/assets-page-batch-toolbar.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +describe("assets page batch toolbar", () => { + test("places select all before cancel selection", () => { + const source = readFileSync(resolve(import.meta.dir, "../src/pages/assets/index.tsx"), "utf8"); + const selectAllIndex = source.indexOf(">全选"); + const clearSelectionIndex = source.indexOf(">取消选择"); + + expect(selectAllIndex).toBeGreaterThanOrEqual(0); + expect(clearSelectionIndex).toBeGreaterThanOrEqual(0); + expect(selectAllIndex).toBeLessThan(clearSelectionIndex); + }); +});