Add batch download feature, support collecting selected images from other pages for download and archiving

pull/329/head
zanllp 2023-07-22 11:41:00 +08:00
parent d3b3599b73
commit 3692b2e6c3
58 changed files with 324 additions and 135 deletions

3
.gitignore vendored
View File

@ -13,4 +13,5 @@ build/**/*
dist/**/*
*.spec
out/**/*
venv/**/*
venv/**/*
zip_temp/*.zip

View File

@ -12,8 +12,8 @@ Promise.resolve().then(async () => {
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Infinite Image Browsing</title>
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-23e5bc7c.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-618900f2.css">
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-af270b30.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-90388ea3.css">
</head>
<body>

View File

@ -18,6 +18,7 @@ from scripts.iib.tool import (
open_folder,
get_img_geninfo_txt_path,
unique_by,
create_zip_file
)
from fastapi import FastAPI, HTTPException
from fastapi.staticfiles import StaticFiles
@ -442,6 +443,10 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
def index_bd():
return FileResponse(index_html_path)
class PathsReq(BaseModel):
paths: List[str]
class OpenFolderReq(BaseModel):
path: str
@ -458,6 +463,16 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
raise HTTPException(status_code=403, detail="Shutdown is disabled.")
os.kill(os.getpid(), 9)
return {"message": "Application is shutting down."}
@app.post(pre + "/zip", dependencies=[Depends(get_token)])
def zip_files(req: PathsReq):
now = datetime.now()
timestamp = now.strftime("%Y-%m-%d-%H-%M-%S")
zip_temp_dir = os.path.join(cwd, "zip_temp")
os.makedirs(zip_temp_dir, exist_ok=True)
file_path = os.path.join(zip_temp_dir, f"iib_batch_download_{timestamp}.zip")
create_zip_file(req.paths, file_path)
return FileResponse(file_path, media_type="application/zip")
db_pre = pre + "/db"
@ -532,9 +547,6 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
# tags = Tag.get_all_custom_tag()
return ImageTag.get_tags_for_image(conn, img.id, type="custom")
class PathsReq(BaseModel):
paths: List[str]
@app.post(db_pre + "/get_image_tags", dependencies=[Depends(get_token)])
async def get_img_tags(req: PathsReq):
conn = DataBase.get_conn()

View File

@ -5,11 +5,12 @@ import re
import tempfile
import imghdr
import subprocess
from typing import Dict
from typing import Dict, List
import sys
import piexif
import piexif.helper
import json
import zipfile
sd_img_dirs = [
"outdir_txt2img_samples",
@ -145,6 +146,28 @@ def is_valid_image_path(path):
return True
def create_zip_file(file_paths: List[str], zip_file_name: str):
"""
将文件打包成一个压缩包
Args:
file_paths: 文件路径的列表
zip_file_name: 压缩包的文件名
Returns:
无返回值
"""
with zipfile.ZipFile(zip_file_name, 'w', zipfile.ZIP_DEFLATED) as zip_file:
for file_path in file_paths:
if os.path.isfile(file_path):
zip_file.write(file_path, os.path.basename(file_path))
elif os.path.isdir(file_path):
for root, _, files in os.walk(file_path):
for file in files:
full_path = os.path.join(root, file)
zip_file.write(full_path, os.path.relpath(full_path, file_path))
def get_temp_path():
"""获取跨平台的临时文件目录路径"""
temp_path = None

2
vue/components.d.ts vendored
View File

@ -37,6 +37,8 @@ declare module '@vue/runtime-core' {
ATabs: typeof import('ant-design-vue/es')['Tabs']
ATag: typeof import('ant-design-vue/es')['Tag']
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
ContextMenu: typeof import('./src/components/ContextMenu.vue')['default']
FileItem: typeof import('./src/components/FileItem.vue')['default']
NumInput: typeof import('./src/components/numInput.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']

4
vue/dist/assets/FileItem-a4055f0b.js vendored Normal file

File diff suppressed because one or more lines are too long

1
vue/dist/assets/FileItem-b09f7869.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{d as t,o as a,m as r,cI as n}from"./index-23e5bc7c.js";const p=t({__name:"ImgSliPagePane",props:{paneIdx:{},tabIdx:{},left:{},right:{}},setup(o){return(e,s)=>(a(),r(n,{left:e.left,right:e.right},null,8,["left","right"]))}});export{p as default};
import{d as t,o as a,m as r,b$ as n}from"./index-af270b30.js";const p=t({__name:"ImgSliPagePane",props:{paneIdx:{},tabIdx:{},left:{},right:{}},setup(o){return(e,s)=>(a(),r(n,{left:e.left,right:e.right},null,8,["left","right"]))}});export{p as default};

View File

@ -0,0 +1 @@
import{d as q,l as Q,ax as W,o as r,y as _,c as s,n as a,r as e,s as h,p as y,t as X,v as b,x as j,m as M,L as H,E as m,N as S,Q as J,R as K,X as Y}from"./index-af270b30.js";import{L as Z,R as ee,f as te,S as ie}from"./fullScreenContextMenu-2284d97e.js";import{g as le,F as se}from"./FileItem-a4055f0b.js";import{g as ne}from"./db-dbaa937e.js";import{u as ae}from"./hook-c56860bd.js";import"./shortcut-c1bb7547.js";const oe={class:"hint"},re={key:1,class:"preview-switch"},de=q({__name:"MatchedImageGrid",props:{tabIdx:{},paneIdx:{},selectedTagIds:{},id:{}},setup(T){const u=T,{queue:p,images:i,onContextMenuClickU:g,stackViewEl:V,previewIdx:n,previewing:v,onPreviewVisibleChange:D,previewImgMove:f,canPreview:w,itemSize:I,gridItems:F,showGenInfo:o,imageGenInfo:k,q:z,multiSelectedIdxs:$,onFileItemClick:B,scroller:x,showMenuIdx:d,onFileDragStart:E,onFileDragEnd:G,cellWidth:N,onScroll:R,updateImageTag:A}=ae();return Q(()=>u.selectedTagIds,async()=>{const{res:c}=p.pushAction(()=>ne(u.selectedTagIds));i.value=await c,await W(),A(),x.value.scrollToItem(0)},{immediate:!0}),(c,t)=>{const P=J,U=K,L=ie;return r(),_("div",{class:"container",ref_key:"stackViewEl",ref:V},[s(L,{size:"large",spinning:!e(p).isIdle},{default:a(()=>[s(U,{visible:e(o),"onUpdate:visible":t[1]||(t[1]=l=>h(o)?o.value=l:null),width:"70vw","mask-closable":"",onOk:t[2]||(t[2]=l=>o.value=!1)},{cancelText:a(()=>[]),default:a(()=>[s(P,{active:"",loading:!e(z).isIdle},{default:a(()=>[y("div",{style:{width:"100%","word-break":"break-all","white-space":"pre-line","max-height":"70vh",overflow:"auto"},onDblclick:t[0]||(t[0]=l=>e(X)(e(k)))},[y("div",oe,b(c.$t("doubleClickToCopy")),1),j(" "+b(e(k)),1)],32)]),_:1},8,["loading"])]),_:1},8,["visible"]),e(i)?(r(),M(e(le),{key:0,ref_key:"scroller",ref:x,class:"file-list",items:e(i),"item-size":e(I).first,"key-field":"fullpath","item-secondary-size":e(I).second,gridItems:e(F),onScroll:e(R)},{default:a(({item:l,index:C})=>[s(se,{idx:C,file:l,"cell-width":e(N),"show-menu-idx":e(d),"onUpdate:showMenuIdx":t[3]||(t[3]=O=>h(d)?d.value=O:null),onDragstart:e(E),onDragend:e(G),onFileItemClick:e(B),"full-screen-preview-image-url":e(i)[e(n)]?e(H)(e(i)[e(n)]):"",selected:e($).includes(C),onContextMenuClick:e(g),onPreviewVisibleChange:e(D)},null,8,["idx","file","cell-width","show-menu-idx","onDragstart","onDragend","onFileItemClick","full-screen-preview-image-url","selected","onContextMenuClick","onPreviewVisibleChange"])]),_:1},8,["items","item-size","item-secondary-size","gridItems","onScroll"])):m("",!0),e(v)?(r(),_("div",re,[s(e(Z),{onClick:t[4]||(t[4]=l=>e(f)("prev")),class:S({disable:!e(w)("prev")})},null,8,["class"]),s(e(ee),{onClick:t[5]||(t[5]=l=>e(f)("next")),class:S({disable:!e(w)("next")})},null,8,["class"])])):m("",!0)]),_:1},8,["spinning"]),e(v)&&e(i)&&e(i)[e(n)]?(r(),M(te,{key:0,file:e(i)[e(n)],idx:e(n),onContextMenuClick:e(g)},null,8,["file","idx","onContextMenuClick"])):m("",!0)],512)}}});const fe=Y(de,[["__scopeId","data-v-d698e678"]]);export{fe as default};

View File

@ -1 +0,0 @@
.preview-switch[data-v-3c251729]{position:fixed;top:0;bottom:0;left:0;right:0;display:flex;align-items:center;justify-content:space-between;z-index:11111;pointer-events:none}.preview-switch>*[data-v-3c251729]{color:#fff;margin:16px;font-size:4em;pointer-events:all;cursor:pointer}.preview-switch>*.disable[data-v-3c251729]{opacity:0;pointer-events:none;cursor:none}.container[data-v-3c251729]{background:var(--zp-secondary-background)}.container .file-list[data-v-3c251729]{list-style:none;padding:8px;height:100%;overflow:auto;height:var(--pane-max-height);width:100%}

View File

@ -1 +0,0 @@
import{d as q,l as Q,ax as j,o as r,y as _,c as s,n as a,r as e,s as h,p as y,t as W,v as b,x as X,m as M,L as H,E as u,N as S,Q as J,R as K,X as Y}from"./index-23e5bc7c.js";import{h as Z,i as ee,L as te,R as ie,j as le,S as se}from"./fullScreenContextMenu-c5f9ce74.js";import{g as ne}from"./db-52d8ead8.js";import{u as ae}from"./hook-13bccbae.js";import"./shortcut-98354a10.js";const oe={class:"hint"},re={key:1,class:"preview-switch"},de=q({__name:"MatchedImageGrid",props:{tabIdx:{},paneIdx:{},selectedTagIds:{},id:{}},setup(T){const m=T,{queue:p,images:i,onContextMenuClickU:g,stackViewEl:V,previewIdx:n,previewing:v,onPreviewVisibleChange:D,previewImgMove:f,canPreview:w,itemSize:I,gridItems:z,showGenInfo:o,imageGenInfo:k,q:F,multiSelectedIdxs:$,onFileItemClick:B,scroller:x,showMenuIdx:d,onFileDragStart:E,onFileDragEnd:G,cellWidth:N,onScroll:R,updateImageTag:A}=ae();return Q(()=>m.selectedTagIds,async()=>{const{res:c}=p.pushAction(()=>ne(m.selectedTagIds));i.value=await c,await j(),A(),x.value.scrollToItem(0)},{immediate:!0}),(c,t)=>{const P=J,U=K,L=se;return r(),_("div",{class:"container",ref_key:"stackViewEl",ref:V},[s(L,{size:"large",spinning:!e(p).isIdle},{default:a(()=>[s(U,{visible:e(o),"onUpdate:visible":t[1]||(t[1]=l=>h(o)?o.value=l:null),width:"70vw","mask-closable":"",onOk:t[2]||(t[2]=l=>o.value=!1)},{cancelText:a(()=>[]),default:a(()=>[s(P,{active:"",loading:!e(F).isIdle},{default:a(()=>[y("div",{style:{width:"100%","word-break":"break-all","white-space":"pre-line","max-height":"70vh",overflow:"auto"},onDblclick:t[0]||(t[0]=l=>e(W)(e(k)))},[y("div",oe,b(c.$t("doubleClickToCopy")),1),X(" "+b(e(k)),1)],32)]),_:1},8,["loading"])]),_:1},8,["visible"]),e(i)?(r(),M(e(Z),{key:0,ref_key:"scroller",ref:x,class:"file-list",items:e(i),"item-size":e(I).first,"key-field":"fullpath","item-secondary-size":e(I).second,gridItems:e(z),onScroll:e(R)},{default:a(({item:l,index:C})=>[s(ee,{idx:C,file:l,"cell-width":e(N),"show-menu-idx":e(d),"onUpdate:showMenuIdx":t[3]||(t[3]=O=>h(d)?d.value=O:null),onDragstart:e(E),onDragend:e(G),onFileItemClick:e(B),"full-screen-preview-image-url":e(i)[e(n)]?e(H)(e(i)[e(n)]):"",selected:e($).includes(C),onContextMenuClick:e(g),onPreviewVisibleChange:e(D)},null,8,["idx","file","cell-width","show-menu-idx","onDragstart","onDragend","onFileItemClick","full-screen-preview-image-url","selected","onContextMenuClick","onPreviewVisibleChange"])]),_:1},8,["items","item-size","item-secondary-size","gridItems","onScroll"])):u("",!0),e(v)?(r(),_("div",re,[s(e(te),{onClick:t[4]||(t[4]=l=>e(f)("prev")),class:S({disable:!e(w)("prev")})},null,8,["class"]),s(e(ie),{onClick:t[5]||(t[5]=l=>e(f)("next")),class:S({disable:!e(w)("next")})},null,8,["class"])])):u("",!0)]),_:1},8,["spinning"]),e(v)&&e(i)&&e(i)[e(n)]?(r(),M(le,{key:0,file:e(i)[e(n)],idx:e(n),onContextMenuClick:e(g)},null,8,["file","idx","onContextMenuClick"])):u("",!0)],512)}}});const ve=Y(de,[["__scopeId","data-v-3c251729"]]);export{ve as default};

View File

@ -0,0 +1 @@
.preview-switch[data-v-d698e678]{position:fixed;top:0;bottom:0;left:0;right:0;display:flex;align-items:center;justify-content:space-between;z-index:11111;pointer-events:none}.preview-switch>*[data-v-d698e678]{color:#fff;margin:16px;font-size:4em;pointer-events:all;cursor:pointer}.preview-switch>*.disable[data-v-d698e678]{opacity:0;pointer-events:none;cursor:none}.container[data-v-d698e678]{background:var(--zp-secondary-background)}.container .file-list[data-v-d698e678]{list-style:none;padding:8px;height:100%;overflow:auto;height:var(--pane-max-height);width:100%}

View File

@ -0,0 +1 @@
.search-bar[data-v-bb005cb9]{padding:8px;display:flex}.preview-switch[data-v-bb005cb9]{position:fixed;top:0;bottom:0;left:0;right:0;display:flex;align-items:center;justify-content:space-between;z-index:11111;pointer-events:none}.preview-switch>*[data-v-bb005cb9]{color:#fff;margin:16px;font-size:4em;pointer-events:all;cursor:pointer}.preview-switch>*.disable[data-v-bb005cb9]{opacity:0;pointer-events:none;cursor:none}.container[data-v-bb005cb9]{background:var(--zp-secondary-background)}.container .file-list[data-v-bb005cb9]{list-style:none;padding:8px;height:100%;overflow:auto;height:var(--pane-max-height);width:100%}

View File

@ -0,0 +1 @@
import{d as Y,$,aw as Z,bQ as ee,bP as B,o,y as k,c as r,r as e,bT as ne,m,n as u,x as w,v,E as g,s as V,p as F,t as te,L as ae,N as A,ax as se,ar as le,ai as ie,U as oe,V as re,Q as ue,R as de,X as ce}from"./index-af270b30.js";import{L as pe,R as me,f as ve,S as ge}from"./fullScreenContextMenu-2284d97e.js";/* empty css */import{g as fe,F as ke}from"./FileItem-a4055f0b.js";import{b as E,c as we,e as be,u as ye}from"./db-dbaa937e.js";import{u as Ie}from"./hook-c56860bd.js";import"./shortcut-c1bb7547.js";const xe={key:0,class:"search-bar"},Ce={class:"hint"},_e={key:1,class:"preview-switch"},he=Y({__name:"SubstrSearch",setup(Se){const{queue:l,images:a,onContextMenuClickU:b,stackViewEl:T,previewIdx:d,previewing:y,onPreviewVisibleChange:U,previewImgMove:I,canPreview:x,itemSize:C,gridItems:R,showGenInfo:c,imageGenInfo:_,q:N,multiSelectedIdxs:P,onFileItemClick:L,scroller:h,showMenuIdx:f,onFileDragStart:q,onFileDragEnd:G,cellWidth:K,onScroll:O,updateImageTag:Q}=Ie(),p=$(""),t=$();Z(async()=>{t.value=await E(),t.value.img_count&&t.value.expired&&S()});const S=ee(()=>l.pushAction(async()=>(await ye(),t.value=await E(),t.value)).res),M=async()=>{a.value=await l.pushAction(()=>be(p.value)).res,await se(),Q(),h.value.scrollToItem(0),a.value.length||le.info(ie("fuzzy-search-noResults"))};return B("returnToIIB",async()=>{const i=await l.pushAction(we).res;t.value.expired=i.expired}),B("searchIndexExpired",()=>t.value&&(t.value.expired=!0)),(i,n)=>{const H=oe,z=re,W=ue,X=de,j=ge;return o(),k("div",{class:"container",ref_key:"stackViewEl",ref:T},[t.value?(o(),k("div",xe,[r(H,{value:p.value,"onUpdate:value":n[0]||(n[0]=s=>p.value=s),placeholder:i.$t("fuzzy-search-placeholder"),disabled:!e(l).isIdle,onKeydown:ne(M,["enter"])},null,8,["value","placeholder","disabled","onKeydown"]),t.value.expired||!t.value.img_count?(o(),m(z,{key:0,onClick:e(S),loading:!e(l).isIdle,type:"primary"},{default:u(()=>[w(v(t.value.img_count===0?i.$t("generateIndexHint"):i.$t("UpdateIndex")),1)]),_:1},8,["onClick","loading"])):(o(),m(z,{key:1,type:"primary",onClick:M,loading:!e(l).isIdle,disabled:!p.value},{default:u(()=>[w(v(i.$t("search")),1)]),_:1},8,["loading","disabled"]))])):g("",!0),r(j,{size:"large",spinning:!e(l).isIdle},{default:u(()=>[r(X,{visible:e(c),"onUpdate:visible":n[2]||(n[2]=s=>V(c)?c.value=s:null),width:"70vw","mask-closable":"",onOk:n[3]||(n[3]=s=>c.value=!1)},{cancelText:u(()=>[]),default:u(()=>[r(W,{active:"",loading:!e(N).isIdle},{default:u(()=>[F("div",{style:{width:"100%","word-break":"break-all","white-space":"pre-line","max-height":"70vh",overflow:"auto"},onDblclick:n[1]||(n[1]=s=>e(te)(e(_)))},[F("div",Ce,v(i.$t("doubleClickToCopy")),1),w(" "+v(e(_)),1)],32)]),_:1},8,["loading"])]),_:1},8,["visible"]),e(a)?(o(),m(e(fe),{key:0,ref_key:"scroller",ref:h,class:"file-list",items:e(a),"item-size":e(C).first,"key-field":"fullpath","item-secondary-size":e(C).second,gridItems:e(R),onScroll:e(O)},{default:u(({item:s,index:D})=>[r(ke,{idx:D,file:s,"show-menu-idx":e(f),"onUpdate:showMenuIdx":n[4]||(n[4]=J=>V(f)?f.value=J:null),onFileItemClick:e(L),"full-screen-preview-image-url":e(a)[e(d)]?e(ae)(e(a)[e(d)]):"","cell-width":e(K),selected:e(P).includes(D),onContextMenuClick:e(b),onDragstart:e(q),onDragend:e(G),onPreviewVisibleChange:e(U)},null,8,["idx","file","show-menu-idx","onFileItemClick","full-screen-preview-image-url","cell-width","selected","onContextMenuClick","onDragstart","onDragend","onPreviewVisibleChange"])]),_:1},8,["items","item-size","item-secondary-size","gridItems","onScroll"])):g("",!0),e(y)?(o(),k("div",_e,[r(e(pe),{onClick:n[5]||(n[5]=s=>e(I)("prev")),class:A({disable:!e(x)("prev")})},null,8,["class"]),r(e(me),{onClick:n[6]||(n[6]=s=>e(I)("next")),class:A({disable:!e(x)("next")})},null,8,["class"])])):g("",!0)]),_:1},8,["spinning"]),e(y)&&e(a)&&e(a)[e(d)]?(o(),m(ve,{key:1,file:e(a)[e(d)],idx:e(d),onContextMenuClick:e(b)},null,8,["file","idx","onContextMenuClick"])):g("",!0)],512)}}});const Ae=ce(he,[["__scopeId","data-v-bb005cb9"]]);export{Ae as default};

View File

@ -1 +0,0 @@
import{d as Y,$,aw as Z,bQ as ee,bP as B,o,y as k,c as r,r as e,bT as ae,m,n as d,x as w,v,E as f,s as V,p as A,t as ne,L as te,N as E,ax as le,ar as se,ai as ie,U as oe,V as re,Q as de,R as ue,X as ce}from"./index-23e5bc7c.js";import{h as pe,i as me,L as ve,R as fe,j as ge,S as ke}from"./fullScreenContextMenu-c5f9ce74.js";/* empty css */import{b as T,c as we,e as ye,u as Ie}from"./db-52d8ead8.js";import{u as xe}from"./hook-13bccbae.js";import"./shortcut-98354a10.js";const be={key:0,class:"search-bar"},Ce={class:"hint"},he={key:1,class:"preview-switch"},_e=Y({__name:"SubstrSearch",setup(Se){const{queue:s,images:t,onContextMenuClickU:y,stackViewEl:U,previewIdx:u,previewing:I,onPreviewVisibleChange:F,previewImgMove:x,canPreview:b,itemSize:C,gridItems:R,showGenInfo:c,imageGenInfo:h,q:N,multiSelectedIdxs:P,onFileItemClick:L,scroller:_,showMenuIdx:g,onFileDragStart:q,onFileDragEnd:G,cellWidth:K,onScroll:O,updateImageTag:Q}=xe(),p=$(""),n=$();Z(async()=>{n.value=await T(),n.value.img_count&&n.value.expired&&S()});const S=ee(()=>s.pushAction(async()=>(await Ie(),n.value=await T(),n.value)).res),M=async()=>{t.value=await s.pushAction(()=>ye(p.value)).res,await le(),Q(),_.value.scrollToItem(0),t.value.length||se.info(ie("fuzzy-search-noResults"))};return B("returnToIIB",async()=>{const i=await s.pushAction(we).res;n.value.expired=i.expired}),B("searchIndexExpired",()=>n.value&&(n.value.expired=!0)),(i,a)=>{const j=oe,z=re,H=de,W=ue,X=ke;return o(),k("div",{class:"container",ref_key:"stackViewEl",ref:U},[n.value?(o(),k("div",be,[r(j,{value:p.value,"onUpdate:value":a[0]||(a[0]=l=>p.value=l),placeholder:i.$t("fuzzy-search-placeholder"),disabled:!e(s).isIdle,onKeydown:ae(M,["enter"])},null,8,["value","placeholder","disabled","onKeydown"]),n.value.expired||!n.value.img_count?(o(),m(z,{key:0,onClick:e(S),loading:!e(s).isIdle,type:"primary"},{default:d(()=>[w(v(n.value.img_count===0?i.$t("generateIndexHint"):i.$t("UpdateIndex")),1)]),_:1},8,["onClick","loading"])):(o(),m(z,{key:1,type:"primary",onClick:M,loading:!e(s).isIdle,disabled:!p.value},{default:d(()=>[w(v(i.$t("search")),1)]),_:1},8,["loading","disabled"]))])):f("",!0),r(X,{size:"large",spinning:!e(s).isIdle},{default:d(()=>[r(W,{visible:e(c),"onUpdate:visible":a[2]||(a[2]=l=>V(c)?c.value=l:null),width:"70vw","mask-closable":"",onOk:a[3]||(a[3]=l=>c.value=!1)},{cancelText:d(()=>[]),default:d(()=>[r(H,{active:"",loading:!e(N).isIdle},{default:d(()=>[A("div",{style:{width:"100%","word-break":"break-all","white-space":"pre-line","max-height":"70vh",overflow:"auto"},onDblclick:a[1]||(a[1]=l=>e(ne)(e(h)))},[A("div",Ce,v(i.$t("doubleClickToCopy")),1),w(" "+v(e(h)),1)],32)]),_:1},8,["loading"])]),_:1},8,["visible"]),e(t)?(o(),m(e(pe),{key:0,ref_key:"scroller",ref:_,class:"file-list",items:e(t),"item-size":e(C).first,"key-field":"fullpath","item-secondary-size":e(C).second,gridItems:e(R),onScroll:e(O)},{default:d(({item:l,index:D})=>[r(me,{idx:D,file:l,"show-menu-idx":e(g),"onUpdate:showMenuIdx":a[4]||(a[4]=J=>V(g)?g.value=J:null),onFileItemClick:e(L),"full-screen-preview-image-url":e(t)[e(u)]?e(te)(e(t)[e(u)]):"","cell-width":e(K),selected:e(P).includes(D),onContextMenuClick:e(y),onDragstart:e(q),onDragend:e(G),onPreviewVisibleChange:e(F)},null,8,["idx","file","show-menu-idx","onFileItemClick","full-screen-preview-image-url","cell-width","selected","onContextMenuClick","onDragstart","onDragend","onPreviewVisibleChange"])]),_:1},8,["items","item-size","item-secondary-size","gridItems","onScroll"])):f("",!0),e(I)?(o(),k("div",he,[r(e(ve),{onClick:a[5]||(a[5]=l=>e(x)("prev")),class:E({disable:!e(b)("prev")})},null,8,["class"]),r(e(fe),{onClick:a[6]||(a[6]=l=>e(x)("next")),class:E({disable:!e(b)("next")})},null,8,["class"])])):f("",!0)]),_:1},8,["spinning"]),e(I)&&e(t)&&e(t)[e(u)]?(o(),m(ge,{key:1,file:e(t)[e(u)],idx:e(u),onContextMenuClick:e(y)},null,8,["file","idx","onContextMenuClick"])):f("",!0)],512)}}});const Ae=ce(_e,[["__scopeId","data-v-905bf6da"]]);export{Ae as default};

View File

@ -1 +0,0 @@
.search-bar[data-v-905bf6da]{padding:8px;display:flex}.preview-switch[data-v-905bf6da]{position:fixed;top:0;bottom:0;left:0;right:0;display:flex;align-items:center;justify-content:space-between;z-index:11111;pointer-events:none}.preview-switch>*[data-v-905bf6da]{color:#fff;margin:16px;font-size:4em;pointer-events:all;cursor:pointer}.preview-switch>*.disable[data-v-905bf6da]{opacity:0;pointer-events:none;cursor:none}.container[data-v-905bf6da]{background:var(--zp-secondary-background)}.container .file-list[data-v-905bf6da]{list-style:none;padding:8px;height:100%;overflow:auto;height:var(--pane-max-height);width:100%}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.container[data-v-aab31da2]{background:var(--zp-secondary-background);height:100%;overflow:auto;display:flex;flex-direction:column}.container .actions-panel[data-v-aab31da2]{padding:8px;background-color:var(--zp-primary-background)}.container .file-list[data-v-aab31da2]{flex:1;list-style:none;padding:8px;height:var(--pane-max-height);width:100%}.container .file-list .hint[data-v-aab31da2]{text-align:center;font-size:2em;padding:30vh 128px 0}

View File

@ -0,0 +1 @@
import{d as v,c0 as C,bO as I,o as i,y as _,p as f,c,n as r,x as h,v as d,r as e,m as F,L as x,c1 as z,c2 as B,V as $,X as R}from"./index-af270b30.js";import{u as S,b as V,m as E,F as A,g as L}from"./FileItem-a4055f0b.js";import"./db-dbaa937e.js";import"./shortcut-c1bb7547.js";const T={class:"actions-panel actions"},N={key:0,class:"file-list"},U={class:"hint"},H=v({__name:"batchDownload",props:{tabIdx:{},paneIdx:{},id:{}},setup(O){const{stackViewEl:w}=S().toRefs(),{itemSize:p,gridItems:k,cellWidth:b}=V(),l=E(),{selectdFiles:n}=C(l),m=I(),y=async t=>{const s=z(t);s&&l.addFiles(s.nodes)},D=async()=>{m.pushAction(async()=>{const t=await B.value.post("/zip",{paths:n.value.map(o=>o.fullpath)},{responseType:"blob"}),s=window.URL.createObjectURL(new Blob([t.data])),a=document.createElement("a");a.href=s,a.setAttribute("download",`iib_${new Date().toLocaleString()}.zip`),document.body.appendChild(a),a.click()})},g=t=>{n.value.splice(t,1)};return(t,s)=>{const a=$;return i(),_("div",{class:"container",ref_key:"stackViewEl",ref:w,onDrop:y},[f("div",T,[c(a,{onClick:s[0]||(s[0]=o=>e(l).selectdFiles=[])},{default:r(()=>[h(d(t.$t("clear")),1)]),_:1}),c(a,{onClick:D,type:"primary",loading:!e(m).isIdle},{default:r(()=>[h(d(t.$t("zipDownload")),1)]),_:1},8,["loading"])]),e(n).length?(i(),F(e(L),{key:1,ref:"scroller",class:"file-list",items:e(n).slice(),"item-size":e(p).first,"key-field":"fullpath","item-secondary-size":e(p).second,gridItems:e(k)},{default:r(({item:o,index:u})=>[c(A,{idx:u,file:o,"cell-width":e(b),"enable-close-icon":"",onCloseIconClick:j=>g(u),"full-screen-preview-image-url":e(x)(o),"enable-right-click-menu":!1},null,8,["idx","file","cell-width","onCloseIconClick","full-screen-preview-image-url"])]),_:1},8,["items","item-size","item-secondary-size","gridItems"])):(i(),_("div",N,[f("p",U,d(t.$t("batchDownloaDDragAndDropHint")),1)]))],544)}}});const G=R(H,[["__scopeId","data-v-aab31da2"]]);export{G as default};

View File

@ -1 +1 @@
import{c6 as t}from"./index-23e5bc7c.js";const o=async()=>(await t.value.get("/db/basic_info")).data,c=async()=>(await t.value.get("/db/expired_dirs")).data,r=async()=>{await t.value.post("/db/update_image_data",{},{timeout:1/0})},d=async a=>(await t.value.post("/db/match_images_by_tags",a)).data,g=async a=>(await t.value.post("/db/add_custom_tag",a)).data,u=async a=>(await t.value.post("/db/toggle_custom_tag_to_img",a)).data,p=async a=>{await t.value.post("/db/remove_custom_tag",a)},i=async a=>(await t.value.get("/db/search_by_substr",{params:{substr:a}})).data,e="/db/scanned_paths",m=async a=>{await t.value.post(e,{path:a})},_=async a=>{await t.value.delete(e,{data:{path:a}})},b=async a=>(await t.value.post("/db/get_image_tags",{paths:a})).data;export{m as a,o as b,c,g as d,i as e,b as f,d as g,_ as h,p as r,u as t,r as u};
import{c2 as t}from"./index-af270b30.js";const o=async()=>(await t.value.get("/db/basic_info")).data,c=async()=>(await t.value.get("/db/expired_dirs")).data,r=async()=>{await t.value.post("/db/update_image_data",{},{timeout:1/0})},d=async a=>(await t.value.post("/db/match_images_by_tags",a)).data,g=async a=>(await t.value.post("/db/add_custom_tag",a)).data,u=async a=>(await t.value.post("/db/toggle_custom_tag_to_img",a)).data,p=async a=>{await t.value.post("/db/remove_custom_tag",a)},i=async a=>(await t.value.get("/db/search_by_substr",{params:{substr:a}})).data,e="/db/scanned_paths",m=async a=>{await t.value.post(e,{path:a})},_=async a=>{await t.value.delete(e,{data:{path:a}})},b=async a=>(await t.value.post("/db/get_image_tags",{paths:a})).data;export{m as a,o as b,c,g as d,i as e,b as f,d as g,_ as h,p as r,u as t,r as u};

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
.ant-alert{box-sizing:border-box;margin:0;color:#000000d9;font-size:14px;font-variant:tabular-nums;line-height:1.5715;list-style:none;font-feature-settings:"tnum";position:relative;display:flex;align-items:center;padding:8px 15px;word-wrap:break-word;border-radius:2px}.ant-alert-content{flex:1;min-width:0}.ant-alert-icon{margin-right:8px}.ant-alert-description{display:none;font-size:14px;line-height:22px}.ant-alert-success{background-color:#f6ffed;border:1px solid #b7eb8f}.ant-alert-success .ant-alert-icon{color:#52c41a}.ant-alert-info{background-color:#fff1e6;border:1px solid #f7ae83}.ant-alert-info .ant-alert-icon{color:#d03f0a}.ant-alert-warning{background-color:#fffbe6;border:1px solid #ffe58f}.ant-alert-warning .ant-alert-icon{color:#faad14}.ant-alert-error{background-color:#fff2f0;border:1px solid #ffccc7}.ant-alert-error .ant-alert-icon{color:#ff4d4f}.ant-alert-error .ant-alert-description>pre{margin:0;padding:0}.ant-alert-action{margin-left:8px}.ant-alert-close-icon{margin-left:8px;padding:0;overflow:hidden;font-size:12px;line-height:12px;background-color:transparent;border:none;outline:none;cursor:pointer}.ant-alert-close-icon .anticon-close{color:#00000073;transition:color .3s}.ant-alert-close-icon .anticon-close:hover{color:#000000bf}.ant-alert-close-text{color:#00000073;transition:color .3s}.ant-alert-close-text:hover{color:#000000bf}.ant-alert-with-description{align-items:flex-start;padding:15px 15px 15px 24px}.ant-alert-with-description.ant-alert-no-icon{padding:15px}.ant-alert-with-description .ant-alert-icon{margin-right:15px;font-size:24px}.ant-alert-with-description .ant-alert-message{display:block;margin-bottom:4px;color:#000000d9;font-size:16px}.ant-alert-message{color:#000000d9}.ant-alert-with-description .ant-alert-description{display:block}.ant-alert.ant-alert-motion-leave{overflow:hidden;opacity:1;transition:max-height .3s cubic-bezier(.78,.14,.15,.86),opacity .3s cubic-bezier(.78,.14,.15,.86),padding-top .3s cubic-bezier(.78,.14,.15,.86),padding-bottom .3s cubic-bezier(.78,.14,.15,.86),margin-bottom .3s cubic-bezier(.78,.14,.15,.86)}.ant-alert.ant-alert-motion-leave-active{max-height:0;margin-bottom:0!important;padding-top:0;padding-bottom:0;opacity:0}.ant-alert-banner{margin-bottom:0;border:0;border-radius:0}.ant-alert.ant-alert-rtl{direction:rtl}.ant-alert-rtl .ant-alert-icon{margin-right:auto;margin-left:8px}.ant-alert-rtl .ant-alert-action,.ant-alert-rtl .ant-alert-close-icon{margin-right:8px;margin-left:auto}.ant-alert-rtl.ant-alert-with-description{padding-right:24px;padding-left:15px}.ant-alert-rtl.ant-alert-with-description .ant-alert-icon{margin-right:auto;margin-left:15px}.access-mode-message[data-v-903b3fda]{display:flex;flex-direction:row;align-items:center}.access-mode-message a[data-v-903b3fda]{margin-left:16px}.container[data-v-903b3fda]{padding:20px;background-color:var(--zp-secondary-background);height:100%;overflow:auto}.header[data-v-903b3fda]{display:flex;justify-content:space-between;align-items:center}.header h1[data-v-903b3fda]{font-size:28px;font-weight:700;color:var(--zp-primary);margin:0}.last-record[data-v-903b3fda]{margin-left:16px;font-size:14px;color:var(--zp-secondary)}.last-record a[data-v-903b3fda]{text-decoration:none;color:var(--zp-secondary)}.last-record a[data-v-903b3fda]:hover{color:var(--zp-primary)}.content[data-v-903b3fda]{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));grid-gap:20px;margin-top:16px}.feature-item[data-v-903b3fda]{background-color:var(--zp-primary-background);border-radius:8px;box-shadow:0 1px 2px #0000001a;padding:20px}.feature-item ul[data-v-903b3fda]{list-style:none;padding:4px;max-height:70vh;overflow-y:auto}.feature-item .item[data-v-903b3fda]{margin-bottom:10px;padding:4px 8px;display:flex;align-items:center}.feature-item .item[data-v-903b3fda]:hover{background:var(--zp-secondary-background);border-radius:4px;color:var(--primary-color);cursor:pointer}.feature-item .icon[data-v-903b3fda]{margin-right:8px}.feature-item h2[data-v-903b3fda]{margin-top:0;margin-bottom:20px;font-size:20px;font-weight:700;color:var(--zp-primary)}.text[data-v-903b3fda]{flex:1;font-size:16px}
.ant-alert{box-sizing:border-box;margin:0;color:#000000d9;font-size:14px;font-variant:tabular-nums;line-height:1.5715;list-style:none;font-feature-settings:"tnum";position:relative;display:flex;align-items:center;padding:8px 15px;word-wrap:break-word;border-radius:2px}.ant-alert-content{flex:1;min-width:0}.ant-alert-icon{margin-right:8px}.ant-alert-description{display:none;font-size:14px;line-height:22px}.ant-alert-success{background-color:#f6ffed;border:1px solid #b7eb8f}.ant-alert-success .ant-alert-icon{color:#52c41a}.ant-alert-info{background-color:#fff1e6;border:1px solid #f7ae83}.ant-alert-info .ant-alert-icon{color:#d03f0a}.ant-alert-warning{background-color:#fffbe6;border:1px solid #ffe58f}.ant-alert-warning .ant-alert-icon{color:#faad14}.ant-alert-error{background-color:#fff2f0;border:1px solid #ffccc7}.ant-alert-error .ant-alert-icon{color:#ff4d4f}.ant-alert-error .ant-alert-description>pre{margin:0;padding:0}.ant-alert-action{margin-left:8px}.ant-alert-close-icon{margin-left:8px;padding:0;overflow:hidden;font-size:12px;line-height:12px;background-color:transparent;border:none;outline:none;cursor:pointer}.ant-alert-close-icon .anticon-close{color:#00000073;transition:color .3s}.ant-alert-close-icon .anticon-close:hover{color:#000000bf}.ant-alert-close-text{color:#00000073;transition:color .3s}.ant-alert-close-text:hover{color:#000000bf}.ant-alert-with-description{align-items:flex-start;padding:15px 15px 15px 24px}.ant-alert-with-description.ant-alert-no-icon{padding:15px}.ant-alert-with-description .ant-alert-icon{margin-right:15px;font-size:24px}.ant-alert-with-description .ant-alert-message{display:block;margin-bottom:4px;color:#000000d9;font-size:16px}.ant-alert-message{color:#000000d9}.ant-alert-with-description .ant-alert-description{display:block}.ant-alert.ant-alert-motion-leave{overflow:hidden;opacity:1;transition:max-height .3s cubic-bezier(.78,.14,.15,.86),opacity .3s cubic-bezier(.78,.14,.15,.86),padding-top .3s cubic-bezier(.78,.14,.15,.86),padding-bottom .3s cubic-bezier(.78,.14,.15,.86),margin-bottom .3s cubic-bezier(.78,.14,.15,.86)}.ant-alert.ant-alert-motion-leave-active{max-height:0;margin-bottom:0!important;padding-top:0;padding-bottom:0;opacity:0}.ant-alert-banner{margin-bottom:0;border:0;border-radius:0}.ant-alert.ant-alert-rtl{direction:rtl}.ant-alert-rtl .ant-alert-icon{margin-right:auto;margin-left:8px}.ant-alert-rtl .ant-alert-action,.ant-alert-rtl .ant-alert-close-icon{margin-right:8px;margin-left:auto}.ant-alert-rtl.ant-alert-with-description{padding-right:24px;padding-left:15px}.ant-alert-rtl.ant-alert-with-description .ant-alert-icon{margin-right:auto;margin-left:15px}.access-mode-message[data-v-81c5212e]{display:flex;flex-direction:row;align-items:center}.access-mode-message a[data-v-81c5212e]{margin-left:16px}.container[data-v-81c5212e]{padding:20px;background-color:var(--zp-secondary-background);height:100%;overflow:auto}.header[data-v-81c5212e]{display:flex;justify-content:space-between;align-items:center}.header h1[data-v-81c5212e]{font-size:28px;font-weight:700;color:var(--zp-primary);margin:0}.last-record[data-v-81c5212e]{margin-left:16px;font-size:14px;color:var(--zp-secondary)}.last-record a[data-v-81c5212e]{text-decoration:none;color:var(--zp-secondary)}.last-record a[data-v-81c5212e]:hover{color:var(--zp-primary)}.content[data-v-81c5212e]{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));grid-gap:20px;margin-top:16px}.feature-item[data-v-81c5212e]{background-color:var(--zp-primary-background);border-radius:8px;box-shadow:0 1px 2px #0000001a;padding:20px}.feature-item ul[data-v-81c5212e]{list-style:none;padding:4px;max-height:70vh;overflow-y:auto}.feature-item .item[data-v-81c5212e]{margin-bottom:10px;padding:4px 8px;display:flex;align-items:center}.feature-item .item[data-v-81c5212e]:hover{background:var(--zp-secondary-background);border-radius:4px;color:var(--primary-color);cursor:pointer}.feature-item .icon[data-v-81c5212e]{margin-right:8px}.feature-item h2[data-v-81c5212e]{margin-top:0;margin-bottom:20px;font-size:20px;font-weight:700;color:var(--zp-primary)}.text[data-v-81c5212e]{flex:1;font-size:16px}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.ant-spin{box-sizing:border-box;margin:0;padding:0;color:#000000d9;font-size:14px;font-variant:tabular-nums;line-height:1.5715;list-style:none;font-feature-settings:"tnum";position:absolute;display:none;color:#d03f0a;text-align:center;vertical-align:middle;opacity:0;transition:transform .3s cubic-bezier(.78,.14,.15,.86)}.ant-spin-spinning{position:static;display:inline-block;opacity:1}.ant-spin-nested-loading{position:relative}.ant-spin-nested-loading>div>.ant-spin{position:absolute;top:0;left:0;z-index:4;display:block;width:100%;height:100%;max-height:400px}.ant-spin-nested-loading>div>.ant-spin .ant-spin-dot{position:absolute;top:50%;left:50%;margin:-10px}.ant-spin-nested-loading>div>.ant-spin .ant-spin-text{position:absolute;top:50%;width:100%;padding-top:5px;text-shadow:0 1px 2px #fff}.ant-spin-nested-loading>div>.ant-spin.ant-spin-show-text .ant-spin-dot{margin-top:-20px}.ant-spin-nested-loading>div>.ant-spin-sm .ant-spin-dot{margin:-7px}.ant-spin-nested-loading>div>.ant-spin-sm .ant-spin-text{padding-top:2px}.ant-spin-nested-loading>div>.ant-spin-sm.ant-spin-show-text .ant-spin-dot{margin-top:-17px}.ant-spin-nested-loading>div>.ant-spin-lg .ant-spin-dot{margin:-16px}.ant-spin-nested-loading>div>.ant-spin-lg .ant-spin-text{padding-top:11px}.ant-spin-nested-loading>div>.ant-spin-lg.ant-spin-show-text .ant-spin-dot{margin-top:-26px}.ant-spin-container{position:relative;transition:opacity .3s}.ant-spin-container:after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;display:none \ ;width:100%;height:100%;background:#fff;opacity:0;transition:all .3s;content:"";pointer-events:none}.ant-spin-blur{clear:both;opacity:.5;user-select:none;pointer-events:none}.ant-spin-blur:after{opacity:.4;pointer-events:auto}.ant-spin-tip{color:#00000073}.ant-spin-dot{position:relative;display:inline-block;font-size:20px;width:1em;height:1em}.ant-spin-dot-item{position:absolute;display:block;width:9px;height:9px;background-color:#d03f0a;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.ant-spin-dot-item:nth-child(1){top:0;left:0}.ant-spin-dot-item:nth-child(2){top:0;right:0;animation-delay:.4s}.ant-spin-dot-item:nth-child(3){right:0;bottom:0;animation-delay:.8s}.ant-spin-dot-item:nth-child(4){bottom:0;left:0;animation-delay:1.2s}.ant-spin-dot-spin{transform:rotate(45deg);animation:antRotate 1.2s infinite linear}.ant-spin-sm .ant-spin-dot{font-size:14px}.ant-spin-sm .ant-spin-dot i{width:6px;height:6px}.ant-spin-lg .ant-spin-dot{font-size:32px}.ant-spin-lg .ant-spin-dot i{width:14px;height:14px}.ant-spin.ant-spin-show-text .ant-spin-text{display:block}@media all and (-ms-high-contrast: none),(-ms-high-contrast: active){.ant-spin-blur{background:#fff;opacity:.5}}@keyframes antSpinMove{to{opacity:1}}@keyframes antRotate{to{transform:rotate(405deg)}}.ant-spin-rtl{direction:rtl}.ant-spin-rtl .ant-spin-dot-spin{transform:rotate(-45deg);animation-name:antRotateRtl}@keyframes antRotateRtl{to{transform:rotate(-405deg)}}.full-screen-menu[data-v-c968315f]{position:fixed;z-index:99999;background:var(--zp-primary-background);padding:8px 16px;box-shadow:0 0 4px var(--zp-secondary);border-radius:4px}.full-screen-menu .tags-container>*[data-v-c968315f]{margin-right:4px;font-size:14px;line-height:1.6}.full-screen-menu .container[data-v-c968315f]{height:100%;display:flex;overflow:hidden;flex-direction:column}.full-screen-menu .gen-info[data-v-c968315f]{flex:1;word-break:break-all;white-space:pre-line;overflow:auto;z-index:1;padding-top:4px;position:relative}.full-screen-menu .gen-info .tags .tag[data-v-c968315f]{display:inline-block;overflow:hidden;border-radius:4px;margin-right:8px;border:2px solid var(--zp-primary)}.full-screen-menu .gen-info .tags .name[data-v-c968315f]{background-color:var(--zp-primary);color:var(--zp-primary-background);padding:4px}.full-screen-menu .gen-info .tags .value[data-v-c968315f]{padding:4px}.full-screen-menu.unset-size[data-v-c968315f]{width:unset!important;height:unset!important}.full-screen-menu .mouse-sensor[data-v-c968315f]{position:absolute;bottom:0;right:0;transform:rotate(90deg);cursor:se-resize;z-index:1;background:var(--zp-primary-background);border-radius:2px}.full-screen-menu .mouse-sensor>*[data-v-c968315f]{font-size:18px;padding:4px}.full-screen-menu .action-bar[data-v-c968315f]{display:flex;align-items:center;user-select:none}.full-screen-menu .action-bar .icon[data-v-c968315f]{font-size:1.5em;padding:2px 4px;border-radius:4px}.full-screen-menu .action-bar .icon[data-v-c968315f]:hover{background:var(--zp-secondary-variant-background)}.full-screen-menu .action-bar>*[data-v-c968315f]{flex-wrap:wrap}.full-screen-menu .action-bar>*[data-v-c968315f]:not(:last-child){margin-right:8px}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
import{$ as D,bO as E,bd as P,aC as $}from"./index-23e5bc7c.js";import{k as z,u as G,b as L,f as O,a as Q,c as R,d as V,e as _,l as A}from"./fullScreenContextMenu-c5f9ce74.js";const U=()=>{const e=D(),c=E(),u=z(),n={tabIdx:-1,target:"local",paneIdx:-1,walkMode:!1},{stackViewEl:r,multiSelectedIdxs:d,stack:m,scroller:o}=G({images:e}).toRefs(),{itemSize:g,gridItems:p,cellWidth:v}=L(n),{showMenuIdx:I}=O();Q(n);const{onFileDragStart:f,onFileDragEnd:x}=R(),{showGenInfo:h,imageGenInfo:w,q:k,onContextMenuClick:i,onFileItemClick:S}=V(n,{openNext:P}),{previewIdx:M,previewing:b,onPreviewVisibleChange:C,previewImgMove:F,canPreview:y}=_(n),T=async(s,a,t)=>{m.value=[{curr:"",files:e.value}],await i(s,a,t)};A("removeFiles",async({paths:s})=>{var a;e.value=(a=e.value)==null?void 0:a.filter(t=>!s.includes(t.fullpath))});const l=()=>{const s=o.value;if(s&&e.value){const a=e.value.slice(Math.max(s.$_startIndex-10,0),s.$_endIndex+10).map(t=>t.fullpath);u.fetchImageTags(a)}},q=$(l,300);return{scroller:o,queue:c,images:e,onContextMenuClickU:T,stackViewEl:r,previewIdx:M,previewing:b,onPreviewVisibleChange:C,previewImgMove:F,canPreview:y,itemSize:g,gridItems:p,showGenInfo:h,imageGenInfo:w,q:k,onContextMenuClick:i,onFileItemClick:S,showMenuIdx:I,multiSelectedIdxs:d,onFileDragStart:f,onFileDragEnd:x,cellWidth:v,onScroll:q,updateImageTag:l}};export{U as u};

1
vue/dist/assets/hook-c56860bd.js vendored Normal file
View File

@ -0,0 +1 @@
import{$ as q,bO as D,bd as E,aC as P}from"./index-af270b30.js";import{h as $,u as z,b as G,f as L,a as O,c as Q,d as R,e as V,i as _}from"./FileItem-a4055f0b.js";const N=()=>{const e=q(),c=D(),l=$(),{stackViewEl:u,multiSelectedIdxs:r,stack:m,scroller:n}=z({images:e}).toRefs(),{itemSize:v,gridItems:d,cellWidth:g}=G(),{showMenuIdx:f}=L();O();const{onFileDragStart:I,onFileDragEnd:p}=Q(),{showGenInfo:h,imageGenInfo:w,q:x,onContextMenuClick:o,onFileItemClick:S}=R({openNext:E}),{previewIdx:C,previewing:F,onPreviewVisibleChange:b,previewImgMove:k,canPreview:M}=V(),y=async(s,t,a)=>{m.value=[{curr:"",files:e.value}],await o(s,t,a)};_("removeFiles",async({paths:s})=>{var t;e.value=(t=e.value)==null?void 0:t.filter(a=>!s.includes(a.fullpath))});const i=()=>{const s=n.value;if(s&&e.value){const t=e.value.slice(Math.max(s.$_startIndex-10,0),s.$_endIndex+10).map(a=>a.fullpath);l.fetchImageTags(t)}},T=P(i,300);return{scroller:n,queue:c,images:e,onContextMenuClickU:y,stackViewEl:u,previewIdx:C,previewing:F,onPreviewVisibleChange:b,previewImgMove:k,canPreview:M,itemSize:v,gridItems:d,showGenInfo:h,imageGenInfo:w,q:x,onContextMenuClick:o,onFileItemClick:S,showMenuIdx:f,multiSelectedIdxs:r,onFileDragStart:I,onFileDragEnd:p,cellWidth:g,onScroll:T,updateImageTag:i}};export{N as u};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{cH as s}from"./index-23e5bc7c.js";var r=1/0,i=17976931348623157e292;function e(t){if(!t)return t===0?t:0;if(t=s(t),t===r||t===-r){var n=t<0?-1:1;return n*i}return t===t?t:0}function f(t){var n=t==null?0:t.length;return n?t[n-1]:void 0}const h=t=>{const n=[];return t.shiftKey&&n.push("Shift"),t.ctrlKey&&n.push("Ctrl"),t.metaKey&&n.push("Cmd"),(t.code.startsWith("Key")||t.code.startsWith("Digit"))&&n.push(t.code),n.join(" + ")};export{h as g,f as l,e as t};
import{cK as s}from"./index-af270b30.js";var r=1/0,i=17976931348623157e292;function e(t){if(!t)return t===0?t:0;if(t=s(t),t===r||t===-r){var n=t<0?-1:1;return n*i}return t===t?t:0}function f(t){var n=t==null?0:t.length;return n?t[n-1]:void 0}const h=t=>{const n=[];return t.shiftKey&&n.push("Shift"),t.ctrlKey&&n.push("Ctrl"),t.metaKey&&n.push("Cmd"),(t.code.startsWith("Key")||t.code.startsWith("Digit"))&&n.push(t.code),n.join(" + ")};export{h as g,f as l,e as t};

1
vue/dist/assets/stackView-5134a008.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
.ant-breadcrumb{box-sizing:border-box;margin:0;padding:0;color:#000000d9;font-variant:tabular-nums;line-height:1.5715;list-style:none;font-feature-settings:"tnum";color:#00000073;font-size:14px}.ant-breadcrumb .anticon{font-size:14px}.ant-breadcrumb a{color:#00000073;transition:color .3s}.ant-breadcrumb a:hover{color:#de632f}.ant-breadcrumb>span:last-child{color:#000000d9}.ant-breadcrumb>span:last-child a{color:#000000d9}.ant-breadcrumb>span:last-child .ant-breadcrumb-separator{display:none}.ant-breadcrumb-separator{margin:0 8px;color:#00000073}.ant-breadcrumb-link>.anticon+span,.ant-breadcrumb-link>.anticon+a{margin-left:4px}.ant-breadcrumb-overlay-link>.anticon{margin-left:4px}.ant-breadcrumb-rtl{direction:rtl}.ant-breadcrumb-rtl:before{display:table;content:""}.ant-breadcrumb-rtl:after{display:table;clear:both;content:""}.ant-breadcrumb-rtl>span{float:right}.ant-breadcrumb-rtl .ant-breadcrumb-link>.anticon+span,.ant-breadcrumb-rtl .ant-breadcrumb-link>.anticon+a{margin-right:4px;margin-left:0}.ant-breadcrumb-rtl .ant-breadcrumb-overlay-link>.anticon{margin-right:4px;margin-left:0}.nprogress{pointer-events:none}.nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}.nprogress .peg{display:block;position:absolute;right:0px;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;-webkit-transform:rotate(3deg) translate(0px,-4px);-ms-transform:rotate(3deg) translate(0px,-4px);transform:rotate(3deg) translateY(-4px)}.nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}.nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:solid 2px transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent .nprogress .spinner,.nprogress-custom-parent .nprogress .bar{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.preview-switch[data-v-43659a67]{position:fixed;top:0;bottom:0;left:0;right:0;display:flex;align-items:center;justify-content:space-between;z-index:11111;pointer-events:none}.preview-switch>*[data-v-43659a67]{color:#fff;margin:16px;font-size:4em;pointer-events:all;cursor:pointer}.preview-switch>*.disable[data-v-43659a67]{opacity:0;pointer-events:none;cursor:none}.breadcrumb[data-v-43659a67]{display:flex;align-items:center}.breadcrumb>*[data-v-43659a67]{margin-right:4px}.container[data-v-43659a67]{background:var(--zp-secondary-background);height:var(--pane-max-height)}.location-bar[data-v-43659a67]{padding:4px 16px;background:var(--zp-primary-background);border-bottom:1px solid var(--zp-border);display:flex;align-items:center;justify-content:space-between}.location-bar .actions[data-v-43659a67]{display:flex;align-items:center;flex-shrink:0}.location-bar a.opt[data-v-43659a67]{margin-left:8px}.view[data-v-43659a67]{padding:8px;height:calc(100vh - 48px)}.view .file-list[data-v-43659a67]{list-style:none;padding:8px;height:100%;overflow:auto}.hint[data-v-43659a67]{padding:4px;border:4px;background:var(--zp-secondary-background);border:1px solid var(--zp-border)}
.ant-breadcrumb{box-sizing:border-box;margin:0;padding:0;color:#000000d9;font-variant:tabular-nums;line-height:1.5715;list-style:none;font-feature-settings:"tnum";color:#00000073;font-size:14px}.ant-breadcrumb .anticon{font-size:14px}.ant-breadcrumb a{color:#00000073;transition:color .3s}.ant-breadcrumb a:hover{color:#de632f}.ant-breadcrumb>span:last-child{color:#000000d9}.ant-breadcrumb>span:last-child a{color:#000000d9}.ant-breadcrumb>span:last-child .ant-breadcrumb-separator{display:none}.ant-breadcrumb-separator{margin:0 8px;color:#00000073}.ant-breadcrumb-link>.anticon+span,.ant-breadcrumb-link>.anticon+a{margin-left:4px}.ant-breadcrumb-overlay-link>.anticon{margin-left:4px}.ant-breadcrumb-rtl{direction:rtl}.ant-breadcrumb-rtl:before{display:table;content:""}.ant-breadcrumb-rtl:after{display:table;clear:both;content:""}.ant-breadcrumb-rtl>span{float:right}.ant-breadcrumb-rtl .ant-breadcrumb-link>.anticon+span,.ant-breadcrumb-rtl .ant-breadcrumb-link>.anticon+a{margin-right:4px;margin-left:0}.ant-breadcrumb-rtl .ant-breadcrumb-overlay-link>.anticon{margin-right:4px;margin-left:0}.nprogress{pointer-events:none}.nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}.nprogress .peg{display:block;position:absolute;right:0px;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;-webkit-transform:rotate(3deg) translate(0px,-4px);-ms-transform:rotate(3deg) translate(0px,-4px);transform:rotate(3deg) translateY(-4px)}.nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}.nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:solid 2px transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent .nprogress .spinner,.nprogress-custom-parent .nprogress .bar{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.preview-switch[data-v-4d3aae06]{position:fixed;top:0;bottom:0;left:0;right:0;display:flex;align-items:center;justify-content:space-between;z-index:11111;pointer-events:none}.preview-switch>*[data-v-4d3aae06]{color:#fff;margin:16px;font-size:4em;pointer-events:all;cursor:pointer}.preview-switch>*.disable[data-v-4d3aae06]{opacity:0;pointer-events:none;cursor:none}.breadcrumb[data-v-4d3aae06]{display:flex;align-items:center}.breadcrumb>*[data-v-4d3aae06]{margin-right:4px}.container[data-v-4d3aae06]{background:var(--zp-secondary-background);height:var(--pane-max-height)}.location-bar[data-v-4d3aae06]{padding:4px 16px;background:var(--zp-primary-background);border-bottom:1px solid var(--zp-border);display:flex;align-items:center;justify-content:space-between}.location-bar .actions[data-v-4d3aae06]{display:flex;align-items:center;flex-shrink:0}.location-bar a.opt[data-v-4d3aae06]{margin-left:8px}.view[data-v-4d3aae06]{padding:8px;height:calc(100vh - 48px)}.view .file-list[data-v-4d3aae06]{list-style:none;padding:8px;height:100%;overflow:auto}.hint[data-v-4d3aae06]{padding:4px;border:4px;background:var(--zp-secondary-background);border:1px solid var(--zp-border)}

File diff suppressed because one or more lines are too long

4
vue/dist/index.html vendored
View File

@ -7,8 +7,8 @@
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Infinite Image Browsing</title>
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-23e5bc7c.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-618900f2.css">
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-af270b30.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-90388ea3.css">
</head>
<body>

View File

@ -46,6 +46,7 @@ const tags = computed(() => {
<a-menu-item key="send2outpaint">openOutpaint</a-menu-item>
</a-sub-menu>
</template>
<a-menu-item key="send2BatchDownload">{{ $t('sendToBatchDownload') }}</a-menu-item>
<a-menu-item key="send2savedDir">{{ $t('send2savedDir') }}</a-menu-item>
<a-menu-divider />
<a-sub-menu key="toggle-tag" :title="$t('toggleTag')">

View File

@ -4,11 +4,12 @@ import { useGlobalStore } from '@/store/useGlobalStore'
import { fallbackImage } from 'vue3-ts-util'
import type { FileNodeInfo } from '@/api/files'
import { isImageFile } from '@/util'
import { toImageThumbnailUrl, toRawFileUrl } from './hook'
import { toImageThumbnailUrl, toRawFileUrl } from '@/util/file'
import type { MenuInfo } from 'ant-design-vue/lib/menu/src/interface'
import { computed } from 'vue'
import ContextMenu from './ContextMenu.vue'
import { useTagStore } from '@/store/useTagStore'
import { CloseCircleOutlined } from '@/icon'
const global = useGlobalStore()
const tagStore = useTagStore()
@ -20,17 +21,20 @@ const props = withDefaults(
showMenuIdx?: number
cellWidth: number
fullScreenPreviewImageUrl?: string
enableRightClickMenu: boolean,
enableCloseIcon: boolean
}>(),
{ selected: false }
{ selected: false, enableRightClickMenu: true, enableCloseIcon: false }
)
const emit = defineEmits<{
(type: 'update:showMenuIdx', v: number): void
(type: 'fileItemClick', event: MouseEvent, file: FileNodeInfo, idx: number): void
(type: 'dragstart', event: DragEvent, idx: number): void
(type: 'dragend', event: DragEvent, idx: number): void
(type: 'previewVisibleChange', value: boolean, last: boolean): void
(type: 'contextMenuClick', e: MenuInfo, file: FileNodeInfo, idx: number): void
'update:showMenuIdx': [v: number],
'fileItemClick': [event: MouseEvent, file: FileNodeInfo, idx: number],
'dragstart': [event: DragEvent, idx: number],
'dragend': [event: DragEvent, idx: number],
'previewVisibleChange': [value: boolean, last: boolean],
'contextMenuClick': [e: MenuInfo, file: FileNodeInfo, idx: number],
'close-icon-click': []
}>()
const customTags = computed(() => {
@ -52,7 +56,10 @@ const imageSrc = computed(() => {
@dragend="emit('dragend', $event, idx)" @click.capture="emit('fileItemClick', $event, file, idx)">
<div>
<a-dropdown>
<div class="close-icon" v-if="enableCloseIcon" @click="emit('close-icon-click')">
<close-circle-outlined />
</div>
<a-dropdown v-if="enableRightClickMenu">
<div class="more">
<ellipsis-outlined />
</div>
@ -95,7 +102,7 @@ const imageSrc = computed(() => {
</div>
</li>
<template #overlay>
<context-menu :file="file" :idx="idx" :selected-tag="customTags"
<context-menu :file="file" :idx="idx" :selected-tag="customTags" v-if="enableRightClickMenu"
@context-menu-click="(e, f, i) => emit('contextMenuClick', e, f, i)" />
</template>
</a-dropdown>
@ -123,6 +130,19 @@ const imageSrc = computed(() => {
}
}
.close-icon {
position: absolute;
top: 0;
right: 0;
transform: translate(50%, -50%) scale(1.5);
cursor: pointer;
z-index: 100;
border-radius: 100%;
overflow: hidden;
line-height: 1;
background-color: var(--zp-primary-background);
}
.file {
padding: 8px 16px;
margin: 8px;
@ -132,7 +152,6 @@ const imageSrc = computed(() => {
border-radius: 8px;
box-shadow: 0 0 4px var(--zp-secondary-variant-background);
position: relative;
overflow: hidden;
&:hover .more {
opacity: 1;
@ -220,6 +239,7 @@ const imageSrc = computed(() => {
}
.basic-info {
overflow: hidden;
display: flex;
flex-direction: column;
align-items: flex-end;

View File

@ -51,6 +51,7 @@ const zh = {
sendToImg2img: '发送到图生图',
sendToInpaint: '发送到局部重绘',
sendToControlNet: '发送到ControlNet',
sendToBatchDownload: '发送到批量下载',
sendToExtraFeatures: '发送到附加功能',
loadNextPage: '加载下一页',
localFile: '本地文件',
@ -185,11 +186,21 @@ const zh = {
inputFolderName: '输入文件夹名',
createFolder: '创建文件夹',
sendToThirdPartyExtension: '发送到第三方拓展',
lyco: 'LyCORIS'
lyco: 'LyCORIS',
batchDownloaDDragAndDropHint:
'使用拖拽或者右键菜单中的“发送到批量下载”将其他页面的图片添加到这里,支持多选',
zipDownload: '打包成zip下载',
archive: '归档',
batchDownload: '批量下载'
}
const en: Record<keyof typeof zh, string> = {
//! MissingTranslations: "Mark missing translations like this""shortcutKey": "Keyboard Shortcuts",
//! MissingTranslations
batchDownload: 'Batch Download',
archive: 'Archive',
zipDownload: 'Download as ZIP',
batchDownloaDDragAndDropHint:
"Use drag and drop or the 'Send to Batch Download' option in the right-click menu to add images from other pages here. Multiple selections are supported.",
lyco: 'LyCORIS',
sendToThirdPartyExtension: 'Send to third-party extension',
createFolder: 'Create Folder',
@ -354,6 +365,7 @@ const en: Record<keyof typeof zh, string> = {
sendToTxt2img: 'Send to txt2img',
sendToImg2img: 'Send to img2img',
sendToInpaint: 'Send to Inpaint',
sendToBatchDownload: 'Send to BatchDownload',
sendToExtraFeatures: 'Send to Extra',
sendToControlNet: 'Send to ControlNet',
loadNextPage: 'Load next page',

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { useImgSliStore } from '@/store/useImgSli'
import { isFileTransferData, toImageThumbnailUrl, toRawFileUrl } from '@/page/fileTransfer/util'
import { toImageThumbnailUrl, toRawFileUrl, getFileTransferDataFromDragEvent } from '@/util/file'
import { CloseCircleOutlined } from '@/icon'
import { isImageFile } from '@/util'
import { storeToRefs } from 'pinia'
@ -14,8 +14,8 @@ const g = useGlobalStore()
const { left, right } = storeToRefs(sliStore)
const onImageDrop = async (e: DragEvent, side: 'left' | 'right') => {
const data = JSON.parse(e.dataTransfer?.getData('text') ?? '{}')
if (isFileTransferData(data)) {
const data = getFileTransferDataFromDragEvent(e)
if (data) {
const img = data.nodes[0]
if (!isImageFile(img.name)) {
return

View File

@ -5,7 +5,7 @@ import ImgSliSide from './ImgSliSide.vue'
import { asyncComputed, useElementSize } from '@vueuse/core'
import { ref } from 'vue'
import { FileNodeInfo } from '@/api/files'
import { toRawFileUrl } from '../fileTransfer/util'
import { toRawFileUrl } from '@/util/file'
import { createImage } from '@/util'
const props = defineProps<{

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { toRawFileUrl } from '../fileTransfer/util'
import { toRawFileUrl } from '@/util/file'
import { computed } from 'vue'
import { FileNodeInfo } from '@/api/files'
const props = defineProps<{ side: 'left' | 'right', containerWidth: number, img: FileNodeInfo, maxEdge: 'width' | 'height', percent: number }>()

View File

@ -24,6 +24,7 @@ const compMap: Record<TabPane['type'], ReturnType<typeof defineAsyncComponent>>
'tag-search': defineAsyncComponent(() => import('@/page/TagSearch/TagSearch.vue')),
'fuzzy-search': defineAsyncComponent(() => import('@/page/TagSearch/SubstrSearch.vue')),
'img-sli': defineAsyncComponent(() => import('@/page/ImgSli/ImgSliPagePane.vue')),
'batch-download': defineAsyncComponent(() => import('@/page/batchDownload/batchDownload.vue'))
}
const onEdit = (idx: number, targetKey: any, action: string) => {
const tab = global.tabList[idx]

View File

@ -20,7 +20,8 @@ const compCnMap: Partial<Record<TabPane['type'], string>> = {
local: t('local'),
'tag-search': t('imgSearch'),
'fuzzy-search': t('fuzzy-search'),
'global-setting': t('globalSettings')
'global-setting': t('globalSettings'),
'batch-download': t('batchDownload') + ' / ' + t('archive')
}
const openInCurrentTab = (type: TabPane['type'], path?: string, walkMode = false) => {
let pane: TabPane
@ -30,6 +31,7 @@ const openInCurrentTab = (type: TabPane['type'], path?: string, walkMode = false
return
case 'global-setting':
case 'tag-search':
case 'batch-download':
case 'fuzzy-search':
case 'empty':
pane = { type, name: compCnMap[type]!, key: Date.now() + uniqueId() }
@ -162,7 +164,9 @@ const addToSearchScanPathAndQuickMove = async () => {
<h2>{{ $t('launchFromQuickMove') }}</h2>
<ul>
<li @click="addToSearchScanPathAndQuickMove" class="item" style="text-align: ;">
<span class="text line-clamp-1"> <PlusOutlined/> {{ $t('add') }}</span>
<span class="text line-clamp-1">
<PlusOutlined /> {{ $t('add') }}
</span>
</li>
<li v-for="dir in global.quickMovePaths" :key="dir.key" class="item"
@click.prevent="openInCurrentTab('local', dir.dir)">
@ -177,7 +181,7 @@ const addToSearchScanPathAndQuickMove = async () => {
@click.prevent="openInCurrentTab(comp)">
<span class="text line-clamp-1">{{ compCnMap[comp] }}</span>
</li>
<li class="item" @click="imgsli.opened = true">
<li class="item" @click="imgsli.opened = true">
<span class="text line-clamp-1">{{ $t('imgCompare') }}</span>
</li>
<li class="item" v-if="canpreviewInNewWindow" @click="previewInNewWindow">

View File

@ -1,9 +1,9 @@
<script lang="ts" setup>
import fileItemCell from '@/page/fileTransfer/FileItem.vue'
import fileItemCell from '@/components/FileItem.vue'
import '@zanllp/vue-virtual-scroller/dist/vue-virtual-scroller.css'
// @ts-ignore
import { RecycleScroller } from '@zanllp/vue-virtual-scroller'
import { toRawFileUrl } from '@/page/fileTransfer/hook'
import { toRawFileUrl } from '@/util/file'
import { getImagesByTags, type MatchImageByTagsReq } from '@/api/db'
import { nextTick, watch } from 'vue'
import { copy2clipboardI18n } from '@/util'

View File

@ -1,10 +1,10 @@
<script lang="ts" setup>
import { nextTick, onMounted, ref } from 'vue'
import fileItemCell from '@/page/fileTransfer/FileItem.vue'
import fileItemCell from '@/components/FileItem.vue'
import '@zanllp/vue-virtual-scroller/dist/vue-virtual-scroller.css'
// @ts-ignore
import { RecycleScroller } from '@zanllp/vue-virtual-scroller'
import { toRawFileUrl } from '@/page/fileTransfer/hook'
import { toRawFileUrl } from '@/util/file'
import { getDbBasicInfo, getExpiredDirs, getImagesBySubstr, updateImageData, type DataBaseBasicInfo } from '@/api/db'
import { copy2clipboardI18n, makeAsyncFunctionSingle, useGlobalEventListen } from '@/util'
import fullScreenContextMenu from '@/page/fileTransfer/fullScreenContextMenu.vue'

View File

@ -19,11 +19,10 @@ export const useImageSearch = () => {
const images = ref<FileNodeInfo[]>()
const queue = createReactiveQueue()
const tagStore = useTagStore()
const propsMock = { tabIdx: -1, target: 'local', paneIdx: -1, walkMode: false } as const
const { stackViewEl, multiSelectedIdxs, stack, scroller } = useHookShareState({ images }).toRefs()
const { itemSize, gridItems, cellWidth } = useFilesDisplay(propsMock)
const { itemSize, gridItems, cellWidth } = useFilesDisplay()
const { showMenuIdx } = useMobileOptimization()
useLocation(propsMock)
useLocation()
const { onFileDragStart, onFileDragEnd } = useFileTransfer()
const {
showGenInfo,
@ -31,8 +30,8 @@ export const useImageSearch = () => {
q: genInfoQueue,
onContextMenuClick,
onFileItemClick
} = useFileItemActions(propsMock, { openNext: identity })
const { previewIdx, previewing, onPreviewVisibleChange, previewImgMove, canPreview } = usePreview(propsMock)
} = useFileItemActions({ openNext: identity })
const { previewIdx, previewing, onPreviewVisibleChange, previewImgMove, canPreview } = usePreview()
const onContextMenuClickU: typeof onContextMenuClick = async (e, file, idx) => {
stack.value = [{ curr: '', files: images.value! }] // hackfor delete multi files

View File

@ -0,0 +1,93 @@
<script lang="ts" setup>
// @ts-ignore
import { RecycleScroller } from '@zanllp/vue-virtual-scroller'
import '@zanllp/vue-virtual-scroller/dist/vue-virtual-scroller.css'
import FileItem from '@/components/FileItem.vue'
import { useBatchDownloadStore } from '@/store/useBatchDownloadStore'
import { storeToRefs } from 'pinia'
import { useFilesDisplay, useHookShareState } from '@/page/fileTransfer/hook'
import { getFileTransferDataFromDragEvent, toRawFileUrl } from '@/util/file'
import { axiosInst } from '@/api'
import { createReactiveQueue } from '@/util'
const { stackViewEl } = useHookShareState().toRefs()
const { itemSize, gridItems, cellWidth } = useFilesDisplay()
const store = useBatchDownloadStore()
const { selectdFiles } = storeToRefs(store)
const q = createReactiveQueue()
defineProps<{
tabIdx: number
paneIdx: number
id: string
}>()
const onDrop = async (e: DragEvent) => {
const data = getFileTransferDataFromDragEvent(e)
if (data) {
store.addFiles(data.nodes)
}
}
const onDownloadClick = async () => {
q.pushAction(async () => {
const resp = await axiosInst.value.post('/zip', { paths: selectdFiles.value.map(v => v.fullpath) }, {
responseType: 'blob',
})
const url = window.URL.createObjectURL(new Blob([resp.data]))
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `iib_${new Date().toLocaleString()}.zip`)
document.body.appendChild(link)
link.click()
})
}
const onDeleteClick = (idx: number) => {
selectdFiles.value.splice(idx, 1)
}
</script>
<template>
<div class="container" ref="stackViewEl" @drop="onDrop">
<div class="actions-panel actions">
<AButton @click="store.selectdFiles = []">{{ $t('clear') }}</AButton>
<AButton @click="onDownloadClick" type="primary" :loading="!q.isIdle">{{ $t('zipDownload') }}</AButton>
</div>
<div v-if="!selectdFiles.length" class="file-list">
<p class="hint">{{ $t('batchDownloaDDragAndDropHint') }}</p>
</div>
<RecycleScroller ref="scroller" v-else class="file-list" :items="selectdFiles.slice()" :item-size="itemSize.first"
key-field="fullpath" :item-secondary-size="itemSize.second" :gridItems="gridItems">
<template v-slot="{ item: file, index: idx }">
<file-item :idx="idx" :file="file" :cell-width="cellWidth" enable-close-icon
@close-icon-click="onDeleteClick(idx)" :full-screen-preview-image-url="toRawFileUrl(file)"
:enable-right-click-menu="false" />
</template>
</RecycleScroller>
</div>
</template>
<style scoped lang="scss">
.container {
background: var(--zp-secondary-background);
height: 100%;
overflow: auto;
display: flex;
flex-direction: column;
.actions-panel {
padding: 8px;
background-color: var(--zp-primary-background);
}
.file-list {
flex: 1;
list-style: none;
padding: 8px;
height: var(--pane-max-height);
width: 100%;
.hint {
text-align: center;
font-size: 2em;
padding: 30vh 128px 0;
}
}
}
</style>

View File

@ -21,8 +21,8 @@ import {
import { t } from '@/i18n'
import { type Tag } from '@/api/db'
import { createReactiveQueue } from '@/util'
import { toRawFileUrl } from './hook'
import ContextMenu from './ContextMenu.vue'
import { toRawFileUrl } from '@/util/file'
import ContextMenu from '@/components/ContextMenu.vue'
import { useWatchDocument } from 'vue3-ts-util'
import { useTagStore } from '@/store/useTagStore'
@ -156,6 +156,7 @@ const baseInfoTags = computed(() => {
<a-menu-item key="send2outpaint">openOutpaint</a-menu-item>
</a-sub-menu>
</template>
<a-menu-item key="send2BatchDownload">{{ $t('sendToBatchDownload') }}</a-menu-item>
<a-menu-item key="send2savedDir">{{ $t('send2savedDir') }}</a-menu-item>
<a-menu-item key="deleteFiles" :disabled="toRawFileUrl(file) === global.fullscreenPreviewInitialUrl">
{{ $t('deleteSelected') }}

View File

@ -30,16 +30,16 @@ import type { MenuInfo } from 'ant-design-vue/lib/menu/src/interface'
import { t } from '@/i18n'
import { DatabaseOutlined } from '@/icon'
import { addScannedPath, removeScannedPath, toggleCustomTagToImg } from '@/api/db'
import { FileTransferData, getFileTransferDataFromDragEvent, toRawFileUrl } from './util'
import { FileTransferData, getFileTransferDataFromDragEvent, toRawFileUrl } from '../../util/file'
import { getShortcutStrFromEvent } from '@/util/shortcut'
import { openCreateFlodersModal, MultiSelectTips } from './functionalCallableComp'
import { useTagStore } from '@/store/useTagStore'
export * from './util'
import { useBatchDownloadStore } from '@/store/useBatchDownloadStore'
export const stackCache = new Map<string, Page[]>()
const global = useGlobalStore()
const batchDownload = useBatchDownloadStore()
const tagStore = useTagStore()
const sli = useImgSliStore()
const imgTransferBus = new BroadcastChannel('iib-image-transfer-bus')
@ -143,14 +143,15 @@ export interface Page {
* @param props
* @returns
*/
export function usePreview (props: Props) {
export function usePreview () {
const {
previewIdx,
eventEmitter,
canLoadNext,
previewing,
sortedFiles: files,
scroller
scroller,
props
} = useHookShareState().toRefs()
const { state } = useHookShareState()
let waitScrollTo = null as number | null
@ -164,7 +165,7 @@ export function usePreview (props: Props) {
}
const loadNextIfNeeded = () => {
if (props.walkModePath) {
if (props.value.walkModePath) {
if (!canPreview('next') && canLoadNext) {
message.info(t('loadingNextFolder'))
eventEmitter.value.emit('loadNextDir', true) // 如果在全屏预览时外面scroller可能还停留在很久之前使用全屏预览的索引
@ -259,7 +260,7 @@ export function usePreview (props: Props) {
/**
*
*/
export function useLocation (props: Props) {
export function useLocation () {
const np = ref<Progress.NProgress>()
const {
scroller,
@ -272,7 +273,8 @@ export function useLocation (props: Props) {
eventEmitter,
getPane,
multiSelectedIdxs,
sortedFiles
sortedFiles,
props
} = useHookShareState().toRefs()
watch(
@ -286,7 +288,7 @@ export function useLocation (props: Props) {
const handleWalkModeTo = async (path: string) => {
await to(path)
if (props.walkModePath) {
if (props.value.walkModePath) {
await delay()
const [firstDir] = sortFiles(currPage.value!.files, sortMethod.value).filter(
(v) => v.type === 'dir'
@ -309,8 +311,8 @@ export function useLocation (props: Props) {
}
np.value = new NProgress()
np.value!.configure({ parent: stackViewEl.value as any })
if (props.path && props.path !== '/') {
await handleWalkModeTo(props.walkModePath ?? props.path)
if (props.value.path && props.value.path !== '/') {
await handleWalkModeTo(props.value.walkModePath ?? props.value.path)
} else {
global.conf?.home && to(global.conf.home)
}
@ -326,7 +328,7 @@ export function useLocation (props: Props) {
pane.path = loc
const filename = pane.path!.split('/').pop()
const getTitle = () => {
if (!props.walkModePath) {
if (!props.value.walkModePath) {
const np = Path.normalize(loc)
for (const [k, v] of Object.entries(global.pathAliasMap)) {
if (np.startsWith(v)) {
@ -430,9 +432,9 @@ export function useLocation (props: Props) {
const refresh = makeAsyncFunctionSingle(async () => {
try {
np.value?.start()
if (props.walkModePath) {
if (props.value.walkModePath) {
back(0)
await handleWalkModeTo(props.walkModePath)
await handleWalkModeTo(props.value.walkModePath)
} else {
const { files } = await getTargetFolderFiles(
stack.value.length === 1 ? '/' : currLocation.value
@ -449,7 +451,7 @@ export function useLocation (props: Props) {
useGlobalEventListen(
'returnToIIB',
makeAsyncFunctionSingle(async () => {
if (!props.walkModePath) {
if (!props.value.walkModePath) {
try {
np.value?.start()
const { files } = await getTargetFolderFiles(
@ -470,7 +472,7 @@ export function useLocation (props: Props) {
useEventListen.value('refresh', refresh)
const quickMoveTo = (path: string) => {
if (props.walkModePath) {
if (props.value.walkModePath) {
getPane.value().walkModePath = path
}
handleWalkModeTo(path)
@ -564,7 +566,7 @@ export function useLocation (props: Props) {
}
}
export function useFilesDisplay (props: Props) {
export function useFilesDisplay () {
const {
scroller,
sortedFiles,
@ -574,7 +576,8 @@ export function useFilesDisplay (props: Props) {
currPage,
stackViewEl,
canLoadNext,
previewIdx
previewIdx,
props
} = useHookShareState().toRefs()
const { state } = useHookShareState()
const moreActionsDropdownShow = ref(false)
@ -597,7 +600,7 @@ export function useFilesDisplay (props: Props) {
const loadNextDirLoading = ref(false)
const loadNextDir = async () => {
if (loadNextDirLoading.value || !props.walkModePath || !canLoadNext.value) {
if (loadNextDirLoading.value || !props.value.walkModePath || !canLoadNext.value) {
return
}
try {
@ -763,7 +766,6 @@ export function useFileTransfer () {
}
export function useFileItemActions (
props: Props,
{ openNext }: { openNext: (file: FileNodeInfo) => Promise<void> }
) {
const showGenInfo = ref(false)
@ -777,7 +779,8 @@ export function useFileItemActions (
spinning,
previewing,
stackViewEl,
eventEmitter
eventEmitter,
props
} = useHookShareState().toRefs()
const nor = Path.normalize
useEventListen('removeFiles', ({ paths, loc }) => {
@ -944,7 +947,7 @@ export function useFileItemActions (
}
case 'openWithWalkMode': {
stackCache.set(path, stack.value)
const tab = global.tabList[props.tabIdx]
const tab = global.tabList[props.value.tabIdx]
const pane: FileTransferTabPane = {
type: 'local',
key: uniqueId(),
@ -959,7 +962,7 @@ export function useFileItemActions (
}
case 'openInNewTab': {
stackCache.set(path, stack.value)
const tab = global.tabList[props.tabIdx]
const tab = global.tabList[props.value.tabIdx]
const pane: FileTransferTabPane = {
type: 'local',
key: uniqueId(),
@ -973,10 +976,10 @@ export function useFileItemActions (
}
case 'openOnTheRight': {
stackCache.set(path, stack.value)
let tab = global.tabList[props.tabIdx + 1]
let tab = global.tabList[props.value.tabIdx + 1]
if (!tab) {
tab = { panes: [], key: '', id: uniqueId() }
global.tabList[props.tabIdx + 1] = tab
global.tabList[props.value.tabIdx + 1] = tab
}
const pane: FileTransferTabPane = {
type: 'local',
@ -989,6 +992,10 @@ export function useFileItemActions (
tab.key = pane.key
break
}
case 'send2BatchDownload': {
batchDownload.addFiles(getSelectedImg())
break
}
case 'viewGenInfo': {
showGenInfo.value = true
imageGenInfo.value = await q.pushAction(() => getImageGenerationInfo(file.fullpath)).res

View File

@ -8,18 +8,18 @@ import {
useLocation,
usePreview,
useFileItemActions,
toRawFileUrl,
stackCache,
useMobileOptimization
} from './hook'
import { SearchSelect } from 'vue3-ts-util'
import { toRawFileUrl } from '@/util/file'
import 'multi-nprogress/nprogress.css'
// @ts-ignore
import { RecycleScroller } from '@zanllp/vue-virtual-scroller'
import '@zanllp/vue-virtual-scroller/dist/vue-virtual-scroller.css'
import { watch } from 'vue'
import FileItem from './FileItem.vue'
import FileItem from '@/components/FileItem.vue'
import fullScreenContextMenu from './fullScreenContextMenu.vue'
import { copy2clipboardI18n } from '@/util'
import { openFolder } from '@/api'
@ -50,7 +50,7 @@ const {
const { currLocation, currPage, refresh, copyLocation, back, openNext, stack, quickMoveTo,
addToSearchScanPathAndQuickMove, searchPathInfo, locInputValue, isLocationEditing,
onLocEditEnter, onEditBtnClick, share, selectAll, onCreateFloderBtnClick
} = useLocation(props)
} = useLocation()
const {
gridItems,
sortMethodConv,
@ -63,14 +63,10 @@ const {
canLoadNext,
onScroll,
cellWidth
} = useFilesDisplay(props)
} = useFilesDisplay()
const { onDrop, onFileDragStart, onFileDragEnd } = useFileTransfer()
const { onFileItemClick, onContextMenuClick, showGenInfo, imageGenInfo, q } = useFileItemActions(
props,
{ openNext }
)
const { previewIdx, onPreviewVisibleChange, previewing, previewImgMove, canPreview } =
usePreview(props)
const { onFileItemClick, onContextMenuClick, showGenInfo, imageGenInfo, q } = useFileItemActions({ openNext })
const { previewIdx, onPreviewVisibleChange, previewing, previewImgMove, canPreview } = usePreview()
const { showMenuIdx } = useMobileOptimization()
watch(

View File

@ -0,0 +1,15 @@
import { FileNodeInfo } from '@/api/files'
import { uniqueFile } from '@/util'
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useBatchDownloadStore = defineStore('useBatchDownloadStore', () => {
const selectdFiles = ref<FileNodeInfo[]>([])
const addFiles = (files: FileNodeInfo[]) => {
selectdFiles.value = uniqueFile([...selectdFiles.value,...files])
}
return {
selectdFiles,
addFiles
}
})

View File

@ -19,7 +19,7 @@ interface TabPaneBase {
}
interface OtherTabPane extends TabPaneBase {
type: 'empty' | 'global-setting' | 'tag-search' | 'fuzzy-search'
type: 'empty' | 'global-setting' | 'tag-search' | 'fuzzy-search' | 'batch-download'
}
// logDetailId

View File

@ -1,5 +0,0 @@
import { defineStore } from 'pinia'
export const useTagSearch = defineStore('useTagSearch', () => {
return {}
})

View File

@ -1,5 +1,6 @@
import type { FileNodeInfo } from '@/api/files'
import { apiBase } from '@/api'
import { uniqBy } from 'lodash-es'
const encode = encodeURIComponent
export const toRawFileUrl = (file: FileNodeInfo, download = false) =>
@ -25,4 +26,15 @@ export const isFileTransferData = (v: any): v is FileTransferData =>
export const getFileTransferDataFromDragEvent = (e: DragEvent) => {
const data = JSON.parse(e.dataTransfer?.getData('text') ?? '{}')
return isFileTransferData(data) ? data : null
}
}
export const uniqueFile = (files: FileNodeInfo[]) => uniqBy(files, 'fullpath')
export function isImageFile(filename: string): boolean {
if (typeof filename !== 'string') {
return false
}
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
const extension = filename.split('.').pop()?.toLowerCase()
return extension !== undefined && imageExtensions.includes(`.${extension}`)
}

View File

@ -2,6 +2,7 @@ import { t } from '@/i18n'
import { message } from 'ant-design-vue'
import { reactive } from 'vue'
import { FetchQueue, idKey, typedEventEmitter, type UniqueId } from 'vue3-ts-util'
export * from './file'
export const parentWindow = () => {
return parent.window as any as Window & {
@ -74,16 +75,6 @@ export const pick = <T extends Dict, keys extends Array<keyof T>>(v: T, ...keys:
* ReturnTypeAsync\<typeof fn\>
*/
export type ReturnTypeAsync<T extends (...arg: any) => Promise<any>> = Awaited<ReturnType<T>>
export function isImageFile(filename: string): boolean {
if (typeof filename !== 'string') {
return false
}
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
const extension = filename.split('.').pop()?.toLowerCase()
return extension !== undefined && imageExtensions.includes(`.${extension}`)
}
export const createReactiveQueue = () => reactive(new FetchQueue(-1, 0, -1, 'throw'))
export const copy2clipboardI18n = async (text: string, msg?: string) => {

0
zip_temp/.gitkeep Normal file
View File