@@ -2,7 +2,6 @@ import { Button } from "@/components/ui/button";
22import { ContextMenu , ContextMenuContent , ContextMenuItem , ContextMenuTrigger } from "@/components/ui/context-menu" ;
33import { Dialog } from "@/components/ui/dialog" ;
44import { Popover } from "@/components/ui/popover" ;
5- import { Separator } from "@/components/ui/separator" ;
65import { SubWindow } from "@/core/service/SubWindow" ;
76import { activeProjectAtom } from "@/state" ;
87import { Vector } from "@graphif/data-structures" ;
@@ -44,7 +43,7 @@ export default function AttachmentsWindow() {
4443 } , [ attachments ] ) ;
4544
4645 return (
47- < div className = "flex flex-col gap-4 p-4 " >
46+ < div className = "bg-background flex flex-col gap-2 p-2 " >
4847 < div className = "flex gap-3" >
4948 < Button
5049 onClick = { async ( ) => {
@@ -71,17 +70,21 @@ export default function AttachmentsWindow() {
7170 title = "清理附件"
7271 description = "删除所有未被实体引用的附件,且此操作不可撤销,是否继续?"
7372 onConfirm = { async ( ) => {
73+ let deletedCount = 0 ;
7474 const referencedAttachmentIds = project . stageManager
7575 . getEntities ( )
7676 . map ( ( it ) => ( "attachmentId" in it ? ( it . attachmentId as string ) : "" ) )
7777 . filter ( Boolean ) ;
7878 for ( const id of project . attachments . keys ( ) ) {
7979 if ( ! referencedAttachmentIds . includes ( id ) ) {
8080 project . attachments . delete ( id ) ;
81+ deletedCount ++ ;
8182 }
8283 }
83- refresh ( ) ;
84- toast . success ( "ok" ) ;
84+ toast . success ( `已清理 ${ deletedCount } 个未被引用的附件` ) ;
85+ setTimeout ( ( ) => {
86+ refresh ( ) ;
87+ } , 500 ) ; // TODO: 在windows上未生效
8588 } }
8689 destructive
8790 >
@@ -91,56 +94,66 @@ export default function AttachmentsWindow() {
9194 </ Button >
9295 </ Popover . Confirm >
9396 </ div >
94- { attachments . entries ( ) . map ( ( [ id , blob ] ) => (
95- < ContextMenu key = { id } >
96- < ContextMenuTrigger >
97- < div className = "flex flex-col gap-2" >
98- < Separator />
99- < div className = "flex flex-col gap-0.5" >
100- < span className = "text-xs opacity-50" > { id } </ span >
101- < div className = "flex flex-wrap gap-x-2" >
102- < span > { blob . type } </ span >
103- < span > { formatBytes ( blob . size ) } </ span >
97+ < div >
98+ < span className = "text-xs opacity-50" > 提示:对着附件右键可进行操作</ span >
99+ </ div >
100+
101+ { /* 一个又一个的附件展示 */ }
102+ < div className = "flex flex-wrap gap-1" >
103+ { attachments . entries ( ) . map ( ( [ id , blob ] ) => (
104+ < ContextMenu key = { id } >
105+ { /* 非右键的直接展示部分 */ }
106+ < ContextMenuTrigger >
107+ < div className = "bg-card hover:bg-primary text-primary hover:text-primary-foreground flex flex-col gap-2 rounded-sm p-1 transition-colors hover:ring" >
108+ { /* <Separator /> */ }
109+ { blob . type . startsWith ( "image" ) && (
110+ < img src = { urls . get ( id ) } alt = { id } className = "max-h-12 max-w-full object-contain" />
111+ ) }
112+ < div className = "flex flex-col gap-0.5" >
113+ < span className = "text-[6px] opacity-50" > { id } </ span >
114+ < div className = "flex flex-wrap gap-x-2 text-xs" >
115+ < span > { blob . type } </ span >
116+ < span > { formatBytes ( blob . size ) } </ span >
117+ </ div >
104118 </ div >
105119 </ div >
106- { blob . type . startsWith ( "image" ) && (
107- < img src = { urls . get ( id ) } alt = { id } className = "max-h-64 max-w-full object-contain" />
108- ) }
109- </ div >
110- </ ContextMenuTrigger >
111- < ContextMenuContent >
112- < ContextMenuItem
113- onClick = { async ( ) => {
114- const path = await save ( {
115- filters : [
116- {
117- name : blob . type ,
118- extensions : [ ...( mime . getAllExtensions ( blob . type ) ?? [ ] ) ] ,
119- } ,
120- ] ,
121- } ) ;
122- if ( ! path ) return ;
123- await writeFile ( path , new Uint8Array ( await blob . arrayBuffer ( ) ) ) ;
124- } }
125- >
126- < FileOutput />
127- 导出
128- </ ContextMenuItem >
129- < ContextMenuItem
130- variant = "destructive"
131- onClick = { async ( ) => {
132- if ( await Dialog . confirm ( "删除附件" , "所有引用了此附件的实体将无法正常渲染" , { destructive : true } ) ) {
133- project . attachments . delete ( id ) ;
134- refresh ( ) ;
135- }
136- } }
137- >
138- < Trash />
139- 删除
140- </ ContextMenuItem >
141- </ ContextMenuContent >
142- </ ContextMenu >
143- ) ) }
120+ </ ContextMenuTrigger >
121+
122+ { /* 右键内容 */ }
123+ < ContextMenuContent >
124+ < ContextMenuItem
125+ onClick = { async ( ) => {
126+ const path = await save ( {
127+ filters : [
128+ {
129+ name : blob . type ,
130+ extensions : [ ...( mime . getAllExtensions ( blob . type ) ?? [ ] ) ] ,
131+ } ,
132+ ] ,
133+ } ) ;
134+ if ( ! path ) return ;
135+ await writeFile ( path , new Uint8Array ( await blob . arrayBuffer ( ) ) ) ;
136+ } }
137+ >
138+ < FileOutput />
139+ 导出
140+ </ ContextMenuItem >
141+ < ContextMenuItem
142+ variant = "destructive"
143+ onClick = { async ( ) => {
144+ if ( await Dialog . confirm ( "删除附件" , "所有引用了此附件的实体将无法正常渲染" , { destructive : true } ) ) {
145+ project . attachments . delete ( id ) ;
146+ refresh ( ) ;
147+ }
148+ } }
149+ >
150+ < Trash />
151+ 删除
152+ </ ContextMenuItem >
153+ </ ContextMenuContent >
154+ </ ContextMenu >
155+ ) ) }
156+ </ div >
144157 </ div >
145158 ) ;
146159}
0 commit comments