Add batch download feature, support collecting selected images from other pages for download and archiving
parent
d3b3599b73
commit
3692b2e6c3
|
|
@ -13,4 +13,5 @@ build/**/*
|
||||||
dist/**/*
|
dist/**/*
|
||||||
*.spec
|
*.spec
|
||||||
out/**/*
|
out/**/*
|
||||||
venv/**/*
|
venv/**/*
|
||||||
|
zip_temp/*.zip
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ Promise.resolve().then(async () => {
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Infinite Image Browsing</title>
|
<title>Infinite Image Browsing</title>
|
||||||
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-23e5bc7c.js"></script>
|
<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-618900f2.css">
|
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-90388ea3.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ from scripts.iib.tool import (
|
||||||
open_folder,
|
open_folder,
|
||||||
get_img_geninfo_txt_path,
|
get_img_geninfo_txt_path,
|
||||||
unique_by,
|
unique_by,
|
||||||
|
create_zip_file
|
||||||
)
|
)
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
@ -442,6 +443,10 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
|
||||||
def index_bd():
|
def index_bd():
|
||||||
return FileResponse(index_html_path)
|
return FileResponse(index_html_path)
|
||||||
|
|
||||||
|
|
||||||
|
class PathsReq(BaseModel):
|
||||||
|
paths: List[str]
|
||||||
|
|
||||||
class OpenFolderReq(BaseModel):
|
class OpenFolderReq(BaseModel):
|
||||||
path: str
|
path: str
|
||||||
|
|
||||||
|
|
@ -458,6 +463,16 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
|
||||||
raise HTTPException(status_code=403, detail="Shutdown is disabled.")
|
raise HTTPException(status_code=403, detail="Shutdown is disabled.")
|
||||||
os.kill(os.getpid(), 9)
|
os.kill(os.getpid(), 9)
|
||||||
return {"message": "Application is shutting down."}
|
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"
|
db_pre = pre + "/db"
|
||||||
|
|
||||||
|
|
@ -532,9 +547,6 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
|
||||||
# tags = Tag.get_all_custom_tag()
|
# tags = Tag.get_all_custom_tag()
|
||||||
return ImageTag.get_tags_for_image(conn, img.id, type="custom")
|
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)])
|
@app.post(db_pre + "/get_image_tags", dependencies=[Depends(get_token)])
|
||||||
async def get_img_tags(req: PathsReq):
|
async def get_img_tags(req: PathsReq):
|
||||||
conn = DataBase.get_conn()
|
conn = DataBase.get_conn()
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,12 @@ import re
|
||||||
import tempfile
|
import tempfile
|
||||||
import imghdr
|
import imghdr
|
||||||
import subprocess
|
import subprocess
|
||||||
from typing import Dict
|
from typing import Dict, List
|
||||||
import sys
|
import sys
|
||||||
import piexif
|
import piexif
|
||||||
import piexif.helper
|
import piexif.helper
|
||||||
import json
|
import json
|
||||||
|
import zipfile
|
||||||
|
|
||||||
sd_img_dirs = [
|
sd_img_dirs = [
|
||||||
"outdir_txt2img_samples",
|
"outdir_txt2img_samples",
|
||||||
|
|
@ -145,6 +146,28 @@ def is_valid_image_path(path):
|
||||||
return True
|
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():
|
def get_temp_path():
|
||||||
"""获取跨平台的临时文件目录路径"""
|
"""获取跨平台的临时文件目录路径"""
|
||||||
temp_path = None
|
temp_path = None
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ declare module '@vue/runtime-core' {
|
||||||
ATabs: typeof import('ant-design-vue/es')['Tabs']
|
ATabs: typeof import('ant-design-vue/es')['Tabs']
|
||||||
ATag: typeof import('ant-design-vue/es')['Tag']
|
ATag: typeof import('ant-design-vue/es')['Tag']
|
||||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
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']
|
NumInput: typeof import('./src/components/numInput.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -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};
|
||||||
|
|
@ -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};
|
||||||
|
|
@ -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%}
|
|
||||||
|
|
@ -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};
|
|
||||||
|
|
@ -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%}
|
||||||
|
|
@ -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%}
|
||||||
|
|
@ -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};
|
||||||
|
|
@ -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};
|
|
||||||
|
|
@ -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
|
|
@ -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}
|
||||||
|
|
@ -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};
|
||||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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};
|
|
||||||
|
|
@ -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
|
|
@ -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};
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -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
|
|
@ -7,8 +7,8 @@
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Infinite Image Browsing</title>
|
<title>Infinite Image Browsing</title>
|
||||||
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-23e5bc7c.js"></script>
|
<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-618900f2.css">
|
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-90388ea3.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ const tags = computed(() => {
|
||||||
<a-menu-item key="send2outpaint">openOutpaint</a-menu-item>
|
<a-menu-item key="send2outpaint">openOutpaint</a-menu-item>
|
||||||
</a-sub-menu>
|
</a-sub-menu>
|
||||||
</template>
|
</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="send2savedDir">{{ $t('send2savedDir') }}</a-menu-item>
|
||||||
<a-menu-divider />
|
<a-menu-divider />
|
||||||
<a-sub-menu key="toggle-tag" :title="$t('toggleTag')">
|
<a-sub-menu key="toggle-tag" :title="$t('toggleTag')">
|
||||||
|
|
@ -4,11 +4,12 @@ import { useGlobalStore } from '@/store/useGlobalStore'
|
||||||
import { fallbackImage } from 'vue3-ts-util'
|
import { fallbackImage } from 'vue3-ts-util'
|
||||||
import type { FileNodeInfo } from '@/api/files'
|
import type { FileNodeInfo } from '@/api/files'
|
||||||
import { isImageFile } from '@/util'
|
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 type { MenuInfo } from 'ant-design-vue/lib/menu/src/interface'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import ContextMenu from './ContextMenu.vue'
|
import ContextMenu from './ContextMenu.vue'
|
||||||
import { useTagStore } from '@/store/useTagStore'
|
import { useTagStore } from '@/store/useTagStore'
|
||||||
|
import { CloseCircleOutlined } from '@/icon'
|
||||||
|
|
||||||
const global = useGlobalStore()
|
const global = useGlobalStore()
|
||||||
const tagStore = useTagStore()
|
const tagStore = useTagStore()
|
||||||
|
|
@ -20,17 +21,20 @@ const props = withDefaults(
|
||||||
showMenuIdx?: number
|
showMenuIdx?: number
|
||||||
cellWidth: number
|
cellWidth: number
|
||||||
fullScreenPreviewImageUrl?: string
|
fullScreenPreviewImageUrl?: string
|
||||||
|
enableRightClickMenu: boolean,
|
||||||
|
enableCloseIcon: boolean
|
||||||
}>(),
|
}>(),
|
||||||
{ selected: false }
|
{ selected: false, enableRightClickMenu: true, enableCloseIcon: false }
|
||||||
)
|
)
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(type: 'update:showMenuIdx', v: number): void
|
'update:showMenuIdx': [v: number],
|
||||||
(type: 'fileItemClick', event: MouseEvent, file: FileNodeInfo, idx: number): void
|
'fileItemClick': [event: MouseEvent, file: FileNodeInfo, idx: number],
|
||||||
(type: 'dragstart', event: DragEvent, idx: number): void
|
'dragstart': [event: DragEvent, idx: number],
|
||||||
(type: 'dragend', event: DragEvent, idx: number): void
|
'dragend': [event: DragEvent, idx: number],
|
||||||
(type: 'previewVisibleChange', value: boolean, last: boolean): void
|
'previewVisibleChange': [value: boolean, last: boolean],
|
||||||
(type: 'contextMenuClick', e: MenuInfo, file: FileNodeInfo, idx: number): void
|
'contextMenuClick': [e: MenuInfo, file: FileNodeInfo, idx: number],
|
||||||
|
'close-icon-click': []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const customTags = computed(() => {
|
const customTags = computed(() => {
|
||||||
|
|
@ -52,7 +56,10 @@ const imageSrc = computed(() => {
|
||||||
@dragend="emit('dragend', $event, idx)" @click.capture="emit('fileItemClick', $event, file, idx)">
|
@dragend="emit('dragend', $event, idx)" @click.capture="emit('fileItemClick', $event, file, idx)">
|
||||||
|
|
||||||
<div>
|
<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">
|
<div class="more">
|
||||||
<ellipsis-outlined />
|
<ellipsis-outlined />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -95,7 +102,7 @@ const imageSrc = computed(() => {
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<template #overlay>
|
<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)" />
|
@context-menu-click="(e, f, i) => emit('contextMenuClick', e, f, i)" />
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</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 {
|
.file {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
|
|
@ -132,7 +152,6 @@ const imageSrc = computed(() => {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 0 4px var(--zp-secondary-variant-background);
|
box-shadow: 0 0 4px var(--zp-secondary-variant-background);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&:hover .more {
|
&:hover .more {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
@ -220,6 +239,7 @@ const imageSrc = computed(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.basic-info {
|
.basic-info {
|
||||||
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
|
|
@ -51,6 +51,7 @@ const zh = {
|
||||||
sendToImg2img: '发送到图生图',
|
sendToImg2img: '发送到图生图',
|
||||||
sendToInpaint: '发送到局部重绘',
|
sendToInpaint: '发送到局部重绘',
|
||||||
sendToControlNet: '发送到ControlNet',
|
sendToControlNet: '发送到ControlNet',
|
||||||
|
sendToBatchDownload: '发送到批量下载',
|
||||||
sendToExtraFeatures: '发送到附加功能',
|
sendToExtraFeatures: '发送到附加功能',
|
||||||
loadNextPage: '加载下一页',
|
loadNextPage: '加载下一页',
|
||||||
localFile: '本地文件',
|
localFile: '本地文件',
|
||||||
|
|
@ -185,11 +186,21 @@ const zh = {
|
||||||
inputFolderName: '输入文件夹名',
|
inputFolderName: '输入文件夹名',
|
||||||
createFolder: '创建文件夹',
|
createFolder: '创建文件夹',
|
||||||
sendToThirdPartyExtension: '发送到第三方拓展',
|
sendToThirdPartyExtension: '发送到第三方拓展',
|
||||||
lyco: 'LyCORIS'
|
lyco: 'LyCORIS',
|
||||||
|
batchDownloaDDragAndDropHint:
|
||||||
|
'使用拖拽或者右键菜单中的“发送到批量下载”将其他页面的图片添加到这里,支持多选',
|
||||||
|
zipDownload: '打包成zip下载',
|
||||||
|
archive: '归档',
|
||||||
|
batchDownload: '批量下载'
|
||||||
}
|
}
|
||||||
const en: Record<keyof typeof zh, string> = {
|
const en: Record<keyof typeof zh, string> = {
|
||||||
//! MissingTranslations: "Mark missing translations like this""shortcutKey": "Keyboard Shortcuts",
|
//! MissingTranslations: "Mark missing translations like this""shortcutKey": "Keyboard Shortcuts",
|
||||||
//! MissingTranslations
|
//! 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',
|
lyco: 'LyCORIS',
|
||||||
sendToThirdPartyExtension: 'Send to third-party extension',
|
sendToThirdPartyExtension: 'Send to third-party extension',
|
||||||
createFolder: 'Create Folder',
|
createFolder: 'Create Folder',
|
||||||
|
|
@ -354,6 +365,7 @@ const en: Record<keyof typeof zh, string> = {
|
||||||
sendToTxt2img: 'Send to txt2img',
|
sendToTxt2img: 'Send to txt2img',
|
||||||
sendToImg2img: 'Send to img2img',
|
sendToImg2img: 'Send to img2img',
|
||||||
sendToInpaint: 'Send to Inpaint',
|
sendToInpaint: 'Send to Inpaint',
|
||||||
|
sendToBatchDownload: 'Send to BatchDownload',
|
||||||
sendToExtraFeatures: 'Send to Extra',
|
sendToExtraFeatures: 'Send to Extra',
|
||||||
sendToControlNet: 'Send to ControlNet',
|
sendToControlNet: 'Send to ControlNet',
|
||||||
loadNextPage: 'Load next page',
|
loadNextPage: 'Load next page',
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useImgSliStore } from '@/store/useImgSli'
|
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 { CloseCircleOutlined } from '@/icon'
|
||||||
import { isImageFile } from '@/util'
|
import { isImageFile } from '@/util'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
@ -14,8 +14,8 @@ const g = useGlobalStore()
|
||||||
const { left, right } = storeToRefs(sliStore)
|
const { left, right } = storeToRefs(sliStore)
|
||||||
|
|
||||||
const onImageDrop = async (e: DragEvent, side: 'left' | 'right') => {
|
const onImageDrop = async (e: DragEvent, side: 'left' | 'right') => {
|
||||||
const data = JSON.parse(e.dataTransfer?.getData('text') ?? '{}')
|
const data = getFileTransferDataFromDragEvent(e)
|
||||||
if (isFileTransferData(data)) {
|
if (data) {
|
||||||
const img = data.nodes[0]
|
const img = data.nodes[0]
|
||||||
if (!isImageFile(img.name)) {
|
if (!isImageFile(img.name)) {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import ImgSliSide from './ImgSliSide.vue'
|
||||||
import { asyncComputed, useElementSize } from '@vueuse/core'
|
import { asyncComputed, useElementSize } from '@vueuse/core'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { FileNodeInfo } from '@/api/files'
|
import { FileNodeInfo } from '@/api/files'
|
||||||
import { toRawFileUrl } from '../fileTransfer/util'
|
import { toRawFileUrl } from '@/util/file'
|
||||||
import { createImage } from '@/util'
|
import { createImage } from '@/util'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { toRawFileUrl } from '../fileTransfer/util'
|
import { toRawFileUrl } from '@/util/file'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { FileNodeInfo } from '@/api/files'
|
import { FileNodeInfo } from '@/api/files'
|
||||||
const props = defineProps<{ side: 'left' | 'right', containerWidth: number, img: FileNodeInfo, maxEdge: 'width' | 'height', percent: number }>()
|
const props = defineProps<{ side: 'left' | 'right', containerWidth: number, img: FileNodeInfo, maxEdge: 'width' | 'height', percent: number }>()
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ const compMap: Record<TabPane['type'], ReturnType<typeof defineAsyncComponent>>
|
||||||
'tag-search': defineAsyncComponent(() => import('@/page/TagSearch/TagSearch.vue')),
|
'tag-search': defineAsyncComponent(() => import('@/page/TagSearch/TagSearch.vue')),
|
||||||
'fuzzy-search': defineAsyncComponent(() => import('@/page/TagSearch/SubstrSearch.vue')),
|
'fuzzy-search': defineAsyncComponent(() => import('@/page/TagSearch/SubstrSearch.vue')),
|
||||||
'img-sli': defineAsyncComponent(() => import('@/page/ImgSli/ImgSliPagePane.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 onEdit = (idx: number, targetKey: any, action: string) => {
|
||||||
const tab = global.tabList[idx]
|
const tab = global.tabList[idx]
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ const compCnMap: Partial<Record<TabPane['type'], string>> = {
|
||||||
local: t('local'),
|
local: t('local'),
|
||||||
'tag-search': t('imgSearch'),
|
'tag-search': t('imgSearch'),
|
||||||
'fuzzy-search': t('fuzzy-search'),
|
'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) => {
|
const openInCurrentTab = (type: TabPane['type'], path?: string, walkMode = false) => {
|
||||||
let pane: TabPane
|
let pane: TabPane
|
||||||
|
|
@ -30,6 +31,7 @@ const openInCurrentTab = (type: TabPane['type'], path?: string, walkMode = false
|
||||||
return
|
return
|
||||||
case 'global-setting':
|
case 'global-setting':
|
||||||
case 'tag-search':
|
case 'tag-search':
|
||||||
|
case 'batch-download':
|
||||||
case 'fuzzy-search':
|
case 'fuzzy-search':
|
||||||
case 'empty':
|
case 'empty':
|
||||||
pane = { type, name: compCnMap[type]!, key: Date.now() + uniqueId() }
|
pane = { type, name: compCnMap[type]!, key: Date.now() + uniqueId() }
|
||||||
|
|
@ -162,7 +164,9 @@ const addToSearchScanPathAndQuickMove = async () => {
|
||||||
<h2>{{ $t('launchFromQuickMove') }}</h2>
|
<h2>{{ $t('launchFromQuickMove') }}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li @click="addToSearchScanPathAndQuickMove" class="item" style="text-align: ;">
|
<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>
|
||||||
<li v-for="dir in global.quickMovePaths" :key="dir.key" class="item"
|
<li v-for="dir in global.quickMovePaths" :key="dir.key" class="item"
|
||||||
@click.prevent="openInCurrentTab('local', dir.dir)">
|
@click.prevent="openInCurrentTab('local', dir.dir)">
|
||||||
|
|
@ -177,7 +181,7 @@ const addToSearchScanPathAndQuickMove = async () => {
|
||||||
@click.prevent="openInCurrentTab(comp)">
|
@click.prevent="openInCurrentTab(comp)">
|
||||||
<span class="text line-clamp-1">{{ compCnMap[comp] }}</span>
|
<span class="text line-clamp-1">{{ compCnMap[comp] }}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="item" @click="imgsli.opened = true">
|
<li class="item" @click="imgsli.opened = true">
|
||||||
<span class="text line-clamp-1">{{ $t('imgCompare') }}</span>
|
<span class="text line-clamp-1">{{ $t('imgCompare') }}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="item" v-if="canpreviewInNewWindow" @click="previewInNewWindow">
|
<li class="item" v-if="canpreviewInNewWindow" @click="previewInNewWindow">
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<script lang="ts" setup>
|
<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'
|
import '@zanllp/vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { RecycleScroller } from '@zanllp/vue-virtual-scroller'
|
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 { getImagesByTags, type MatchImageByTagsReq } from '@/api/db'
|
||||||
import { nextTick, watch } from 'vue'
|
import { nextTick, watch } from 'vue'
|
||||||
import { copy2clipboardI18n } from '@/util'
|
import { copy2clipboardI18n } from '@/util'
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted, ref } from 'vue'
|
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'
|
import '@zanllp/vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { RecycleScroller } from '@zanllp/vue-virtual-scroller'
|
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 { getDbBasicInfo, getExpiredDirs, getImagesBySubstr, updateImageData, type DataBaseBasicInfo } from '@/api/db'
|
||||||
import { copy2clipboardI18n, makeAsyncFunctionSingle, useGlobalEventListen } from '@/util'
|
import { copy2clipboardI18n, makeAsyncFunctionSingle, useGlobalEventListen } from '@/util'
|
||||||
import fullScreenContextMenu from '@/page/fileTransfer/fullScreenContextMenu.vue'
|
import fullScreenContextMenu from '@/page/fileTransfer/fullScreenContextMenu.vue'
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,10 @@ export const useImageSearch = () => {
|
||||||
const images = ref<FileNodeInfo[]>()
|
const images = ref<FileNodeInfo[]>()
|
||||||
const queue = createReactiveQueue()
|
const queue = createReactiveQueue()
|
||||||
const tagStore = useTagStore()
|
const tagStore = useTagStore()
|
||||||
const propsMock = { tabIdx: -1, target: 'local', paneIdx: -1, walkMode: false } as const
|
|
||||||
const { stackViewEl, multiSelectedIdxs, stack, scroller } = useHookShareState({ images }).toRefs()
|
const { stackViewEl, multiSelectedIdxs, stack, scroller } = useHookShareState({ images }).toRefs()
|
||||||
const { itemSize, gridItems, cellWidth } = useFilesDisplay(propsMock)
|
const { itemSize, gridItems, cellWidth } = useFilesDisplay()
|
||||||
const { showMenuIdx } = useMobileOptimization()
|
const { showMenuIdx } = useMobileOptimization()
|
||||||
useLocation(propsMock)
|
useLocation()
|
||||||
const { onFileDragStart, onFileDragEnd } = useFileTransfer()
|
const { onFileDragStart, onFileDragEnd } = useFileTransfer()
|
||||||
const {
|
const {
|
||||||
showGenInfo,
|
showGenInfo,
|
||||||
|
|
@ -31,8 +30,8 @@ export const useImageSearch = () => {
|
||||||
q: genInfoQueue,
|
q: genInfoQueue,
|
||||||
onContextMenuClick,
|
onContextMenuClick,
|
||||||
onFileItemClick
|
onFileItemClick
|
||||||
} = useFileItemActions(propsMock, { openNext: identity })
|
} = useFileItemActions({ openNext: identity })
|
||||||
const { previewIdx, previewing, onPreviewVisibleChange, previewImgMove, canPreview } = usePreview(propsMock)
|
const { previewIdx, previewing, onPreviewVisibleChange, previewImgMove, canPreview } = usePreview()
|
||||||
|
|
||||||
const onContextMenuClickU: typeof onContextMenuClick = async (e, file, idx) => {
|
const onContextMenuClickU: typeof onContextMenuClick = async (e, file, idx) => {
|
||||||
stack.value = [{ curr: '', files: images.value! }] // hack,for delete multi files
|
stack.value = [{ curr: '', files: images.value! }] // hack,for delete multi files
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -21,8 +21,8 @@ import {
|
||||||
import { t } from '@/i18n'
|
import { t } from '@/i18n'
|
||||||
import { type Tag } from '@/api/db'
|
import { type Tag } from '@/api/db'
|
||||||
import { createReactiveQueue } from '@/util'
|
import { createReactiveQueue } from '@/util'
|
||||||
import { toRawFileUrl } from './hook'
|
import { toRawFileUrl } from '@/util/file'
|
||||||
import ContextMenu from './ContextMenu.vue'
|
import ContextMenu from '@/components/ContextMenu.vue'
|
||||||
import { useWatchDocument } from 'vue3-ts-util'
|
import { useWatchDocument } from 'vue3-ts-util'
|
||||||
import { useTagStore } from '@/store/useTagStore'
|
import { useTagStore } from '@/store/useTagStore'
|
||||||
|
|
||||||
|
|
@ -156,6 +156,7 @@ const baseInfoTags = computed(() => {
|
||||||
<a-menu-item key="send2outpaint">openOutpaint</a-menu-item>
|
<a-menu-item key="send2outpaint">openOutpaint</a-menu-item>
|
||||||
</a-sub-menu>
|
</a-sub-menu>
|
||||||
</template>
|
</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="send2savedDir">{{ $t('send2savedDir') }}</a-menu-item>
|
||||||
<a-menu-item key="deleteFiles" :disabled="toRawFileUrl(file) === global.fullscreenPreviewInitialUrl">
|
<a-menu-item key="deleteFiles" :disabled="toRawFileUrl(file) === global.fullscreenPreviewInitialUrl">
|
||||||
{{ $t('deleteSelected') }}
|
{{ $t('deleteSelected') }}
|
||||||
|
|
|
||||||
|
|
@ -30,16 +30,16 @@ import type { MenuInfo } from 'ant-design-vue/lib/menu/src/interface'
|
||||||
import { t } from '@/i18n'
|
import { t } from '@/i18n'
|
||||||
import { DatabaseOutlined } from '@/icon'
|
import { DatabaseOutlined } from '@/icon'
|
||||||
import { addScannedPath, removeScannedPath, toggleCustomTagToImg } from '@/api/db'
|
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 { getShortcutStrFromEvent } from '@/util/shortcut'
|
||||||
import { openCreateFlodersModal, MultiSelectTips } from './functionalCallableComp'
|
import { openCreateFlodersModal, MultiSelectTips } from './functionalCallableComp'
|
||||||
import { useTagStore } from '@/store/useTagStore'
|
import { useTagStore } from '@/store/useTagStore'
|
||||||
export * from './util'
|
import { useBatchDownloadStore } from '@/store/useBatchDownloadStore'
|
||||||
|
|
||||||
export const stackCache = new Map<string, Page[]>()
|
export const stackCache = new Map<string, Page[]>()
|
||||||
|
|
||||||
const global = useGlobalStore()
|
const global = useGlobalStore()
|
||||||
|
const batchDownload = useBatchDownloadStore()
|
||||||
const tagStore = useTagStore()
|
const tagStore = useTagStore()
|
||||||
const sli = useImgSliStore()
|
const sli = useImgSliStore()
|
||||||
const imgTransferBus = new BroadcastChannel('iib-image-transfer-bus')
|
const imgTransferBus = new BroadcastChannel('iib-image-transfer-bus')
|
||||||
|
|
@ -143,14 +143,15 @@ export interface Page {
|
||||||
* @param props
|
* @param props
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function usePreview (props: Props) {
|
export function usePreview () {
|
||||||
const {
|
const {
|
||||||
previewIdx,
|
previewIdx,
|
||||||
eventEmitter,
|
eventEmitter,
|
||||||
canLoadNext,
|
canLoadNext,
|
||||||
previewing,
|
previewing,
|
||||||
sortedFiles: files,
|
sortedFiles: files,
|
||||||
scroller
|
scroller,
|
||||||
|
props
|
||||||
} = useHookShareState().toRefs()
|
} = useHookShareState().toRefs()
|
||||||
const { state } = useHookShareState()
|
const { state } = useHookShareState()
|
||||||
let waitScrollTo = null as number | null
|
let waitScrollTo = null as number | null
|
||||||
|
|
@ -164,7 +165,7 @@ export function usePreview (props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadNextIfNeeded = () => {
|
const loadNextIfNeeded = () => {
|
||||||
if (props.walkModePath) {
|
if (props.value.walkModePath) {
|
||||||
if (!canPreview('next') && canLoadNext) {
|
if (!canPreview('next') && canLoadNext) {
|
||||||
message.info(t('loadingNextFolder'))
|
message.info(t('loadingNextFolder'))
|
||||||
eventEmitter.value.emit('loadNextDir', true) // 如果在全屏预览时外面scroller可能还停留在很久之前,使用全屏预览的索引
|
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 np = ref<Progress.NProgress>()
|
||||||
const {
|
const {
|
||||||
scroller,
|
scroller,
|
||||||
|
|
@ -272,7 +273,8 @@ export function useLocation (props: Props) {
|
||||||
eventEmitter,
|
eventEmitter,
|
||||||
getPane,
|
getPane,
|
||||||
multiSelectedIdxs,
|
multiSelectedIdxs,
|
||||||
sortedFiles
|
sortedFiles,
|
||||||
|
props
|
||||||
} = useHookShareState().toRefs()
|
} = useHookShareState().toRefs()
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
@ -286,7 +288,7 @@ export function useLocation (props: Props) {
|
||||||
|
|
||||||
const handleWalkModeTo = async (path: string) => {
|
const handleWalkModeTo = async (path: string) => {
|
||||||
await to(path)
|
await to(path)
|
||||||
if (props.walkModePath) {
|
if (props.value.walkModePath) {
|
||||||
await delay()
|
await delay()
|
||||||
const [firstDir] = sortFiles(currPage.value!.files, sortMethod.value).filter(
|
const [firstDir] = sortFiles(currPage.value!.files, sortMethod.value).filter(
|
||||||
(v) => v.type === 'dir'
|
(v) => v.type === 'dir'
|
||||||
|
|
@ -309,8 +311,8 @@ export function useLocation (props: Props) {
|
||||||
}
|
}
|
||||||
np.value = new NProgress()
|
np.value = new NProgress()
|
||||||
np.value!.configure({ parent: stackViewEl.value as any })
|
np.value!.configure({ parent: stackViewEl.value as any })
|
||||||
if (props.path && props.path !== '/') {
|
if (props.value.path && props.value.path !== '/') {
|
||||||
await handleWalkModeTo(props.walkModePath ?? props.path)
|
await handleWalkModeTo(props.value.walkModePath ?? props.value.path)
|
||||||
} else {
|
} else {
|
||||||
global.conf?.home && to(global.conf.home)
|
global.conf?.home && to(global.conf.home)
|
||||||
}
|
}
|
||||||
|
|
@ -326,7 +328,7 @@ export function useLocation (props: Props) {
|
||||||
pane.path = loc
|
pane.path = loc
|
||||||
const filename = pane.path!.split('/').pop()
|
const filename = pane.path!.split('/').pop()
|
||||||
const getTitle = () => {
|
const getTitle = () => {
|
||||||
if (!props.walkModePath) {
|
if (!props.value.walkModePath) {
|
||||||
const np = Path.normalize(loc)
|
const np = Path.normalize(loc)
|
||||||
for (const [k, v] of Object.entries(global.pathAliasMap)) {
|
for (const [k, v] of Object.entries(global.pathAliasMap)) {
|
||||||
if (np.startsWith(v)) {
|
if (np.startsWith(v)) {
|
||||||
|
|
@ -430,9 +432,9 @@ export function useLocation (props: Props) {
|
||||||
const refresh = makeAsyncFunctionSingle(async () => {
|
const refresh = makeAsyncFunctionSingle(async () => {
|
||||||
try {
|
try {
|
||||||
np.value?.start()
|
np.value?.start()
|
||||||
if (props.walkModePath) {
|
if (props.value.walkModePath) {
|
||||||
back(0)
|
back(0)
|
||||||
await handleWalkModeTo(props.walkModePath)
|
await handleWalkModeTo(props.value.walkModePath)
|
||||||
} else {
|
} else {
|
||||||
const { files } = await getTargetFolderFiles(
|
const { files } = await getTargetFolderFiles(
|
||||||
stack.value.length === 1 ? '/' : currLocation.value
|
stack.value.length === 1 ? '/' : currLocation.value
|
||||||
|
|
@ -449,7 +451,7 @@ export function useLocation (props: Props) {
|
||||||
useGlobalEventListen(
|
useGlobalEventListen(
|
||||||
'returnToIIB',
|
'returnToIIB',
|
||||||
makeAsyncFunctionSingle(async () => {
|
makeAsyncFunctionSingle(async () => {
|
||||||
if (!props.walkModePath) {
|
if (!props.value.walkModePath) {
|
||||||
try {
|
try {
|
||||||
np.value?.start()
|
np.value?.start()
|
||||||
const { files } = await getTargetFolderFiles(
|
const { files } = await getTargetFolderFiles(
|
||||||
|
|
@ -470,7 +472,7 @@ export function useLocation (props: Props) {
|
||||||
useEventListen.value('refresh', refresh)
|
useEventListen.value('refresh', refresh)
|
||||||
|
|
||||||
const quickMoveTo = (path: string) => {
|
const quickMoveTo = (path: string) => {
|
||||||
if (props.walkModePath) {
|
if (props.value.walkModePath) {
|
||||||
getPane.value().walkModePath = path
|
getPane.value().walkModePath = path
|
||||||
}
|
}
|
||||||
handleWalkModeTo(path)
|
handleWalkModeTo(path)
|
||||||
|
|
@ -564,7 +566,7 @@ export function useLocation (props: Props) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFilesDisplay (props: Props) {
|
export function useFilesDisplay () {
|
||||||
const {
|
const {
|
||||||
scroller,
|
scroller,
|
||||||
sortedFiles,
|
sortedFiles,
|
||||||
|
|
@ -574,7 +576,8 @@ export function useFilesDisplay (props: Props) {
|
||||||
currPage,
|
currPage,
|
||||||
stackViewEl,
|
stackViewEl,
|
||||||
canLoadNext,
|
canLoadNext,
|
||||||
previewIdx
|
previewIdx,
|
||||||
|
props
|
||||||
} = useHookShareState().toRefs()
|
} = useHookShareState().toRefs()
|
||||||
const { state } = useHookShareState()
|
const { state } = useHookShareState()
|
||||||
const moreActionsDropdownShow = ref(false)
|
const moreActionsDropdownShow = ref(false)
|
||||||
|
|
@ -597,7 +600,7 @@ export function useFilesDisplay (props: Props) {
|
||||||
const loadNextDirLoading = ref(false)
|
const loadNextDirLoading = ref(false)
|
||||||
|
|
||||||
const loadNextDir = async () => {
|
const loadNextDir = async () => {
|
||||||
if (loadNextDirLoading.value || !props.walkModePath || !canLoadNext.value) {
|
if (loadNextDirLoading.value || !props.value.walkModePath || !canLoadNext.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
@ -763,7 +766,6 @@ export function useFileTransfer () {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFileItemActions (
|
export function useFileItemActions (
|
||||||
props: Props,
|
|
||||||
{ openNext }: { openNext: (file: FileNodeInfo) => Promise<void> }
|
{ openNext }: { openNext: (file: FileNodeInfo) => Promise<void> }
|
||||||
) {
|
) {
|
||||||
const showGenInfo = ref(false)
|
const showGenInfo = ref(false)
|
||||||
|
|
@ -777,7 +779,8 @@ export function useFileItemActions (
|
||||||
spinning,
|
spinning,
|
||||||
previewing,
|
previewing,
|
||||||
stackViewEl,
|
stackViewEl,
|
||||||
eventEmitter
|
eventEmitter,
|
||||||
|
props
|
||||||
} = useHookShareState().toRefs()
|
} = useHookShareState().toRefs()
|
||||||
const nor = Path.normalize
|
const nor = Path.normalize
|
||||||
useEventListen('removeFiles', ({ paths, loc }) => {
|
useEventListen('removeFiles', ({ paths, loc }) => {
|
||||||
|
|
@ -944,7 +947,7 @@ export function useFileItemActions (
|
||||||
}
|
}
|
||||||
case 'openWithWalkMode': {
|
case 'openWithWalkMode': {
|
||||||
stackCache.set(path, stack.value)
|
stackCache.set(path, stack.value)
|
||||||
const tab = global.tabList[props.tabIdx]
|
const tab = global.tabList[props.value.tabIdx]
|
||||||
const pane: FileTransferTabPane = {
|
const pane: FileTransferTabPane = {
|
||||||
type: 'local',
|
type: 'local',
|
||||||
key: uniqueId(),
|
key: uniqueId(),
|
||||||
|
|
@ -959,7 +962,7 @@ export function useFileItemActions (
|
||||||
}
|
}
|
||||||
case 'openInNewTab': {
|
case 'openInNewTab': {
|
||||||
stackCache.set(path, stack.value)
|
stackCache.set(path, stack.value)
|
||||||
const tab = global.tabList[props.tabIdx]
|
const tab = global.tabList[props.value.tabIdx]
|
||||||
const pane: FileTransferTabPane = {
|
const pane: FileTransferTabPane = {
|
||||||
type: 'local',
|
type: 'local',
|
||||||
key: uniqueId(),
|
key: uniqueId(),
|
||||||
|
|
@ -973,10 +976,10 @@ export function useFileItemActions (
|
||||||
}
|
}
|
||||||
case 'openOnTheRight': {
|
case 'openOnTheRight': {
|
||||||
stackCache.set(path, stack.value)
|
stackCache.set(path, stack.value)
|
||||||
let tab = global.tabList[props.tabIdx + 1]
|
let tab = global.tabList[props.value.tabIdx + 1]
|
||||||
if (!tab) {
|
if (!tab) {
|
||||||
tab = { panes: [], key: '', id: uniqueId() }
|
tab = { panes: [], key: '', id: uniqueId() }
|
||||||
global.tabList[props.tabIdx + 1] = tab
|
global.tabList[props.value.tabIdx + 1] = tab
|
||||||
}
|
}
|
||||||
const pane: FileTransferTabPane = {
|
const pane: FileTransferTabPane = {
|
||||||
type: 'local',
|
type: 'local',
|
||||||
|
|
@ -989,6 +992,10 @@ export function useFileItemActions (
|
||||||
tab.key = pane.key
|
tab.key = pane.key
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'send2BatchDownload': {
|
||||||
|
batchDownload.addFiles(getSelectedImg())
|
||||||
|
break
|
||||||
|
}
|
||||||
case 'viewGenInfo': {
|
case 'viewGenInfo': {
|
||||||
showGenInfo.value = true
|
showGenInfo.value = true
|
||||||
imageGenInfo.value = await q.pushAction(() => getImageGenerationInfo(file.fullpath)).res
|
imageGenInfo.value = await q.pushAction(() => getImageGenerationInfo(file.fullpath)).res
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,18 @@ import {
|
||||||
useLocation,
|
useLocation,
|
||||||
usePreview,
|
usePreview,
|
||||||
useFileItemActions,
|
useFileItemActions,
|
||||||
toRawFileUrl,
|
|
||||||
stackCache,
|
stackCache,
|
||||||
useMobileOptimization
|
useMobileOptimization
|
||||||
} from './hook'
|
} from './hook'
|
||||||
import { SearchSelect } from 'vue3-ts-util'
|
import { SearchSelect } from 'vue3-ts-util'
|
||||||
|
import { toRawFileUrl } from '@/util/file'
|
||||||
|
|
||||||
import 'multi-nprogress/nprogress.css'
|
import 'multi-nprogress/nprogress.css'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { RecycleScroller } from '@zanllp/vue-virtual-scroller'
|
import { RecycleScroller } from '@zanllp/vue-virtual-scroller'
|
||||||
import '@zanllp/vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
import '@zanllp/vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||||
import { watch } from 'vue'
|
import { watch } from 'vue'
|
||||||
import FileItem from './FileItem.vue'
|
import FileItem from '@/components/FileItem.vue'
|
||||||
import fullScreenContextMenu from './fullScreenContextMenu.vue'
|
import fullScreenContextMenu from './fullScreenContextMenu.vue'
|
||||||
import { copy2clipboardI18n } from '@/util'
|
import { copy2clipboardI18n } from '@/util'
|
||||||
import { openFolder } from '@/api'
|
import { openFolder } from '@/api'
|
||||||
|
|
@ -50,7 +50,7 @@ const {
|
||||||
const { currLocation, currPage, refresh, copyLocation, back, openNext, stack, quickMoveTo,
|
const { currLocation, currPage, refresh, copyLocation, back, openNext, stack, quickMoveTo,
|
||||||
addToSearchScanPathAndQuickMove, searchPathInfo, locInputValue, isLocationEditing,
|
addToSearchScanPathAndQuickMove, searchPathInfo, locInputValue, isLocationEditing,
|
||||||
onLocEditEnter, onEditBtnClick, share, selectAll, onCreateFloderBtnClick
|
onLocEditEnter, onEditBtnClick, share, selectAll, onCreateFloderBtnClick
|
||||||
} = useLocation(props)
|
} = useLocation()
|
||||||
const {
|
const {
|
||||||
gridItems,
|
gridItems,
|
||||||
sortMethodConv,
|
sortMethodConv,
|
||||||
|
|
@ -63,14 +63,10 @@ const {
|
||||||
canLoadNext,
|
canLoadNext,
|
||||||
onScroll,
|
onScroll,
|
||||||
cellWidth
|
cellWidth
|
||||||
} = useFilesDisplay(props)
|
} = useFilesDisplay()
|
||||||
const { onDrop, onFileDragStart, onFileDragEnd } = useFileTransfer()
|
const { onDrop, onFileDragStart, onFileDragEnd } = useFileTransfer()
|
||||||
const { onFileItemClick, onContextMenuClick, showGenInfo, imageGenInfo, q } = useFileItemActions(
|
const { onFileItemClick, onContextMenuClick, showGenInfo, imageGenInfo, q } = useFileItemActions({ openNext })
|
||||||
props,
|
const { previewIdx, onPreviewVisibleChange, previewing, previewImgMove, canPreview } = usePreview()
|
||||||
{ openNext }
|
|
||||||
)
|
|
||||||
const { previewIdx, onPreviewVisibleChange, previewing, previewImgMove, canPreview } =
|
|
||||||
usePreview(props)
|
|
||||||
const { showMenuIdx } = useMobileOptimization()
|
const { showMenuIdx } = useMobileOptimization()
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -19,7 +19,7 @@ interface TabPaneBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OtherTabPane extends TabPaneBase {
|
interface OtherTabPane extends TabPaneBase {
|
||||||
type: 'empty' | 'global-setting' | 'tag-search' | 'fuzzy-search'
|
type: 'empty' | 'global-setting' | 'tag-search' | 'fuzzy-search' | 'batch-download'
|
||||||
}
|
}
|
||||||
// logDetailId
|
// logDetailId
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
|
|
||||||
export const useTagSearch = defineStore('useTagSearch', () => {
|
|
||||||
return {}
|
|
||||||
})
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import type { FileNodeInfo } from '@/api/files'
|
import type { FileNodeInfo } from '@/api/files'
|
||||||
import { apiBase } from '@/api'
|
import { apiBase } from '@/api'
|
||||||
|
import { uniqBy } from 'lodash-es'
|
||||||
|
|
||||||
const encode = encodeURIComponent
|
const encode = encodeURIComponent
|
||||||
export const toRawFileUrl = (file: FileNodeInfo, download = false) =>
|
export const toRawFileUrl = (file: FileNodeInfo, download = false) =>
|
||||||
|
|
@ -25,4 +26,15 @@ export const isFileTransferData = (v: any): v is FileTransferData =>
|
||||||
export const getFileTransferDataFromDragEvent = (e: DragEvent) => {
|
export const getFileTransferDataFromDragEvent = (e: DragEvent) => {
|
||||||
const data = JSON.parse(e.dataTransfer?.getData('text') ?? '{}')
|
const data = JSON.parse(e.dataTransfer?.getData('text') ?? '{}')
|
||||||
return isFileTransferData(data) ? data : null
|
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}`)
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import { t } from '@/i18n'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
import { FetchQueue, idKey, typedEventEmitter, type UniqueId } from 'vue3-ts-util'
|
import { FetchQueue, idKey, typedEventEmitter, type UniqueId } from 'vue3-ts-util'
|
||||||
|
export * from './file'
|
||||||
|
|
||||||
export const parentWindow = () => {
|
export const parentWindow = () => {
|
||||||
return parent.window as any as Window & {
|
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\>
|
* ReturnTypeAsync\<typeof fn\>
|
||||||
*/
|
*/
|
||||||
export type ReturnTypeAsync<T extends (...arg: any) => Promise<any>> = Awaited<ReturnType<T>>
|
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 createReactiveQueue = () => reactive(new FetchQueue(-1, 0, -1, 'throw'))
|
||||||
|
|
||||||
export const copy2clipboardI18n = async (text: string, msg?: string) => {
|
export const copy2clipboardI18n = async (text: string, msg?: string) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue