Change image search to use pagination based on cursor instead of fixed maximum of 500 images.

pull/390/head
zanllp 2023-09-01 03:16:57 +08:00
parent a9f1406803
commit bc6c3ef205
32 changed files with 158 additions and 105 deletions

View File

@ -12,7 +12,7 @@ Promise.resolve().then(async () => {
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Infinite Image Browsing</title>
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-f1d763a2.js"></script>
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-1537dba6.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-04bf0fce.css">
</head>

View File

@ -598,14 +598,22 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
and_tags: List[int]
or_tags: List[int]
not_tags: List[int]
cursor: str
size = 200
@app.post(db_pre + "/match_images_by_tags", dependencies=[Depends(verify_secret)])
async def match_image_by_tags(req: MatchImagesByTagsReq):
conn = DataBase.get_conn()
imgs = ImageTag.get_images_by_tags(
conn, {"and": req.and_tags, "or": req.or_tags, "not": req.not_tags}
imgs, next_cursor = ImageTag.get_images_by_tags(
conn=conn,
tag_dict={"and": req.and_tags, "or": req.or_tags, "not": req.not_tags},
cursor=req.cursor,
limit=req.size
)
return filter_allowed_files([x.to_file_info() for x in imgs])
return {
"files": filter_allowed_files([x.to_file_info() for x in imgs]),
"cursor": next_cursor
}
@app.get(db_pre + "/img_selected_custom_tag", dependencies=[Depends(verify_secret)])
async def get_img_selected_custom_tag(path: str):
@ -711,10 +719,13 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
ImageTag.remove(conn, image_id=req.img_id, tag_id=req.tag_id)
@app.get(db_pre + "/search_by_substr", dependencies=[Depends(verify_secret)])
async def search_by_substr(substr: str):
async def search_by_substr(substr: str, cursor: str = '', size = 200):
conn = DataBase.get_conn()
imgs = DbImg.find_by_substring(conn=conn, substring=substr)
return filter_allowed_files([x.to_file_info() for x in imgs])
imgs, next_cursor = DbImg.find_by_substring(conn=conn, substring=substr, cursor=cursor, limit=size)
return {
"files": filter_allowed_files([x.to_file_info() for x in imgs]),
"cursor": next_cursor
}
class ScannedPathModel(BaseModel):
path: str

View File

@ -21,6 +21,12 @@ class FileInfoDict(TypedDict):
bytes: bytes
created_time: float
fullpath: str
class Cursor:
def __init__(self, has_next = True, next = ''):
self.has_next = has_next
self.next = next
class DataBase:
local = threading.local()
@ -74,6 +80,7 @@ class Image:
"date": self.date,
"created_date": self.date,
"size": human_readable_size(self.size),
"is_under_scanned_path": True,
"bytes": self.size,
"name": os.path.basename(self.path),
"fullpath": self.path,
@ -163,6 +170,8 @@ class Image:
@classmethod
def safe_batch_remove(cls, conn: Connection, image_ids: List[int]) -> None:
if not(image_ids):
return
with closing(conn.cursor()) as cur:
try:
placeholders = ",".join("?" * len(image_ids))
@ -174,12 +183,18 @@ class Image:
conn.commit()
@classmethod
def find_by_substring(cls, conn: Connection, substring: str, limit: int = 500) -> List["Image"]:
def find_by_substring(cls, conn: Connection, substring: str, limit: int = 500, cursor = '') -> tuple[List["Image"], Cursor]:
api_cur = Cursor()
with closing(conn.cursor()) as cur:
cur.execute("SELECT * FROM image WHERE path LIKE ? OR exif LIKE ? ORDER BY date DESC LIMIT ?",
(f"%{substring}%", f"%{substring}%", limit))
if cursor:
sql = f"SELECT * FROM image WHERE (path LIKE ? OR exif LIKE ?) AND (date < ?) ORDER BY date DESC LIMIT ?"
cur.execute(sql, (f"%{substring}%", f"%{substring}%", cursor, limit))
else:
sql = "SELECT * FROM image WHERE path LIKE ? OR exif LIKE ? ORDER BY date DESC LIMIT ?"
cur.execute(sql, (f"%{substring}%", f"%{substring}%", limit))
rows = cur.fetchall()
api_cur.has_next = len(rows) >= limit
images = []
deleted_ids = []
for row in rows:
@ -188,8 +203,10 @@ class Image:
images.append(img)
else:
deleted_ids.append(img.id)
cls.safe_batch_remove(conn, deleted_ids)
return images
cls.safe_batch_remove(conn, deleted_ids)
if images:
api_cur.next = str(images[-1].date)
return images, api_cur
class Tag:
@ -352,8 +369,8 @@ class ImageTag:
@classmethod
def get_images_by_tags(
cls, conn: Connection, tag_dict: Dict[str, List[int]], limit: int = 500
) -> List[Image]:
cls, conn: Connection, tag_dict: Dict[str, List[int]], limit: int = 500, cursor = ''
) -> tuple[List[Image], Cursor]:
query = """
SELECT image.id, image.path, image.size,image.date
FROM image
@ -392,6 +409,9 @@ class ImageTag:
)""".format(",".join("?" * len(tag_ids)))
)
params.extend(tag_ids)
if cursor:
where_clauses.append("(image.date < ?)")
params.append(cursor)
if where_clauses:
query += " WHERE " + " AND ".join(where_clauses)
@ -404,6 +424,7 @@ class ImageTag:
query += " ORDER BY date DESC LIMIT ?"
params.append(limit)
api_cur = Cursor()
with closing(conn.cursor()) as cur:
cur.execute(query, params)
rows = cur.fetchall()
@ -416,7 +437,10 @@ class ImageTag:
else:
deleted_ids.append(img.id)
Image.safe_batch_remove(conn, deleted_ids)
return images
api_cur.has_next = len(rows) >= limit
if images:
api_cur.next = str(images[-1].date)
return images, api_cur
@classmethod
def batch_get_tags_by_path(cls, conn: Connection, paths: List[str], type = "custom") -> Dict[str, List[Tag]]:

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{d as t,o as a,m as r,b$ as n}from"./index-f1d763a2.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,c1 as n}from"./index-1537dba6.js";const p=t({__name:"ImgSliPagePane",props:{paneIdx:{},tabIdx:{},left:{},right:{}},setup(o){return(e,s)=>(a(),r(n,{left:e.left,right:e.right},null,8,["left","right"]))}});export{p as default};

View File

@ -0,0 +1 @@
.preview-switch[data-v-6cc08968]{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-6cc08968]{color:#fff;margin:16px;font-size:4em;pointer-events:all;cursor:pointer}.preview-switch>*.disable[data-v-6cc08968]{opacity:0;pointer-events:none;cursor:none}.container[data-v-6cc08968]{background:var(--zp-secondary-background)}.container .file-list[data-v-6cc08968]{list-style:none;padding:8px;height:100%;overflow:auto;height:var(--pane-max-height);width:100%}

View File

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

View File

@ -1 +0,0 @@
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,C as m,N as S,Q as J,R as K,X as Y}from"./index-f1d763a2.js";import{L as Z,R as ee,f as te,S as ie}from"./fullScreenContextMenu-c371385f.js";import{g as le,F as se}from"./FileItem-a4a4ada7.js";import{g as ne}from"./db-328ec51d.js";import{u as ae}from"./hook-cd0f6c09.js";import"./shortcut-6ab7aba6.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:G,onFileDragEnd:N,cellWidth:R,onScroll:A,updateImageTag:E}=ae();return Q(()=>u.selectedTagIds,async()=>{const{res:c}=p.pushAction(()=>ne(u.selectedTagIds));i.value=await c,await W(),E(),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(A)},{default:a(({item:l,index:C})=>[s(se,{idx:C,file:l,"cell-width":e(R),"show-menu-idx":e(d),"onUpdate:showMenuIdx":t[3]||(t[3]=O=>h(d)?d.value=O:null),onDragstart:e(G),onDragend:e(N),onFileItemClick:e(B),"full-screen-preview-image-url":e(i)[e(n)]?e(H)(e(i)[e(n)]):"",selected:e($).includes(C),onContextMenuClick:e(g),onPreviewVisibleChange:e(D)},null,8,["idx","file","cell-width","show-menu-idx","onDragstart","onDragend","onFileItemClick","full-screen-preview-image-url","selected","onContextMenuClick","onPreviewVisibleChange"])]),_:1},8,["items","item-size","item-secondary-size","gridItems","onScroll"])):m("",!0),e(v)?(r(),_("div",re,[s(e(Z),{onClick:t[4]||(t[4]=l=>e(f)("prev")),class:S({disable:!e(w)("prev")})},null,8,["class"]),s(e(ee),{onClick:t[5]||(t[5]=l=>e(f)("next")),class:S({disable:!e(w)("next")})},null,8,["class"])])):m("",!0)]),_:1},8,["spinning"]),e(v)&&e(i)&&e(i)[e(n)]?(r(),M(te,{key:0,file:e(i)[e(n)],idx:e(n),onContextMenuClick:e(g)},null,8,["file","idx","onContextMenuClick"])):m("",!0)],512)}}});const fe=Y(de,[["__scopeId","data-v-d698e678"]]);export{fe as default};

View File

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

View File

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

View File

@ -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 ne,m,n as u,x as w,v,C 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-f1d763a2.js";import{L as pe,R as me,f as ve,S as ge}from"./fullScreenContextMenu-c371385f.js";/* empty css */import{g as fe,F as ke}from"./FileItem-a4a4ada7.js";import{b as T,c as we,f as be,u as ye}from"./db-328ec51d.js";import{u as Ie}from"./hook-cd0f6c09.js";import"./shortcut-6ab7aba6.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:U,previewIdx:d,previewing:y,onPreviewVisibleChange:E,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 T(),t.value.img_count&&t.value.expired&&S()});const S=ee(()=>l.pushAction(async()=>(await ye(),t.value=await T(),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:U},[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(E)},null,8,["idx","file","show-menu-idx","onFileItemClick","full-screen-preview-image-url","cell-width","selected","onContextMenuClick","onDragstart","onDragend","onPreviewVisibleChange"])]),_:1},8,["items","item-size","item-secondary-size","gridItems","onScroll"])):g("",!0),e(y)?(o(),k("div",_e,[r(e(pe),{onClick:n[5]||(n[5]=s=>e(I)("prev")),class:A({disable:!e(x)("prev")})},null,8,["class"]),r(e(me),{onClick:n[6]||(n[6]=s=>e(I)("next")),class:A({disable:!e(x)("next")})},null,8,["class"])])):g("",!0)]),_:1},8,["spinning"]),e(y)&&e(a)&&e(a)[e(d)]?(o(),m(ve,{key:1,file:e(a)[e(d)],idx:e(d),onContextMenuClick:e(b)},null,8,["file","idx","onContextMenuClick"])):g("",!0)],512)}}});const Ae=ce(he,[["__scopeId","data-v-bb005cb9"]]);export{Ae as default};

View File

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

View File

@ -0,0 +1 @@
.search-bar[data-v-d4ab92e4]{padding:8px;display:flex}.preview-switch[data-v-d4ab92e4]{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-d4ab92e4]{color:#fff;margin:16px;font-size:4em;pointer-events:all;cursor:pointer}.preview-switch>*.disable[data-v-d4ab92e4]{opacity:0;pointer-events:none;cursor:none}.container[data-v-d4ab92e4]{background:var(--zp-secondary-background)}.container .file-list[data-v-d4ab92e4]{list-style:none;padding:8px;height:100%;overflow:auto;height:var(--pane-max-height);width:100%}

File diff suppressed because one or more lines are too long

View File

@ -1 +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-f1d763a2.js";import{u as S,b as V,k as E,F as A,g as L}from"./FileItem-a4a4ada7.js";import"./db-328ec51d.js";import"./shortcut-6ab7aba6.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),u=I(),y=async t=>{const s=z(t);s&&l.addFiles(s.nodes)},D=async()=>{u.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(u).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:m})=>[c(A,{idx:m,file:o,"cell-width":e(b),"enable-close-icon":"",onCloseIconClick:j=>g(m),"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};
import{d as v,c2 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,c3 as z,c4 as B,V as $,X as R}from"./index-1537dba6.js";import{u as S,b as V,k as E,F as A,g as L}from"./FileItem-dbdcf20b.js";import"./db-925e828e.js";import"./shortcut-ec395f0e.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),u=I(),y=async t=>{const s=z(t);s&&l.addFiles(s.nodes)},D=async()=>{u.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(u).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:m})=>[c(A,{idx:m,file:o,"cell-width":e(b),"enable-close-icon":"",onCloseIconClick:j=>g(m),"full-screen-preview-image-url":e(x)(o),"enable-right-click-menu":!1},null,8,["idx","file","cell-width","onCloseIconClick","full-screen-preview-image-url"])]),_:1},8,["items","item-size","item-secondary-size","gridItems"])):(i(),_("div",N,[f("p",U,d(t.$t("batchDownloaDDragAndDropHint")),1)]))],544)}}});const G=R(H,[["__scopeId","data-v-aab31da2"]]);export{G as default};

View File

@ -1 +0,0 @@
import{c2 as t}from"./index-f1d763a2.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,p as e,i as f,d as g,b as h,_ as r,u as t,r as u};

1
vue/dist/assets/db-925e828e.js vendored Normal file
View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

@ -0,0 +1 @@
import{bn as A,$ as g,bU as q,bV as x,am as k,aj as D,bO as j,bd as z}from"./index-1537dba6.js";import{u as G,b as N,f as O,c as Q,d as U,e as H,h as L}from"./FileItem-dbdcf20b.js";let T=0;const V=()=>++T,W=(r,l,{dataUpdateStrategy:c="replace"}={})=>{const s=A([""]),u=g(!1),t=g(),a=g(!1);let f=g(-1);const v=new Set,b=e=>{var n;c==="replace"?t.value=e:c==="merge"&&(k((Array.isArray(t.value)||typeof t.value>"u")&&Array.isArray(e),"数据更新策略为合并时仅可用于值为数组的情况"),t.value=[...(n=t==null?void 0:t.value)!==null&&n!==void 0?n:[],...e])},d=e=>x(void 0,void 0,void 0,function*(){if(a.value||u.value&&typeof e>"u")return!1;a.value=!0;const n=V();f.value=n;try{let o;if(typeof e=="number"){if(o=s[e],typeof o!="string")return!1}else o=s[s.length-1];const p=yield r(o);if(v.has(n))return v.delete(n),!1;b(l(p));const i=p.cursor;if((e===s.length-1||typeof e!="number")&&(u.value=!i.has_next,i.has_next)){const I=i.next_cursor||i.next;k(typeof I=="string"),s.push(I)}}finally{f.value===n&&(a.value=!1)}return!0}),h=()=>{v.add(f.value),a.value=!1},w=(e=!1)=>x(void 0,void 0,void 0,function*(){const{refetch:n,force:o}=typeof e=="object"?e:{refetch:e};o&&h(),k(!a.value),s.splice(0,s.length,""),a.value=!1,t.value=void 0,u.value=!1,n&&(yield d())}),m=()=>({next:()=>x(void 0,void 0,void 0,function*(){if(a.value)throw new Error("不允许同时迭代");return{done:!(yield d()),value:t.value}})});return q({abort:h,load:u,next:d,res:t,loading:a,cursorStack:s,reset:w,[Symbol.asyncIterator]:m,iter:{[Symbol.asyncIterator]:m}})},J=r=>A(W(r,l=>l.files,{dataUpdateStrategy:"merge"})),K=r=>{const l=A(new Set),c=D(()=>(r.res??[]).filter(y=>!l.has(y.fullpath))),s=j(),{stackViewEl:u,multiSelectedIdxs:t,stack:a,scroller:f}=G({images:c}).toRefs(),{itemSize:v,gridItems:b,cellWidth:d,onScroll:h}=N({fetchNext:()=>r.next()}),{showMenuIdx:w}=O(),{onFileDragStart:m,onFileDragEnd:e}=Q(),{showGenInfo:n,imageGenInfo:o,q:p,onContextMenuClick:i,onFileItemClick:I}=U({openNext:z}),{previewIdx:C,previewing:F,onPreviewVisibleChange:_,previewImgMove:E,canPreview:M}=H(),P=async(y,S,R)=>{a.value=[{curr:"",files:c.value}],await i(y,S,R)};return L("removeFiles",async({paths:y})=>{y.forEach(S=>l.add(S))}),{images:c,scroller:f,queue:s,iter:r,onContextMenuClickU:P,stackViewEl:u,previewIdx:C,previewing:F,onPreviewVisibleChange:_,previewImgMove:E,canPreview:M,itemSize:v,gridItems:b,showGenInfo:n,imageGenInfo:o,q:p,onContextMenuClick:i,onFileItemClick:I,showMenuIdx:w,multiSelectedIdxs:t,onFileDragStart:m,onFileDragEnd:e,cellWidth:d,onScroll:h}};export{J as c,K as u};

View File

@ -1 +0,0 @@
import{$ as q,bO as D,bd as E,aC as P}from"./index-f1d763a2.js";import{h as $,u as z,b as G,f as O,c as Q,d as R,e as V,i as _}from"./FileItem-a4a4ada7.js";const L=()=>{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}=O(),{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{L 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

File diff suppressed because one or more lines are too long

2
vue/dist/index.html vendored
View File

@ -7,7 +7,7 @@
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Infinite Image Browsing</title>
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-f1d763a2.js"></script>
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-1537dba6.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-04bf0fce.css">
</head>

View File

@ -1,6 +1,7 @@
import { Dict } from '@/util'
import type { FileNodeInfo } from './files'
import { axiosInst } from './index'
import { PageCursor } from 'vue3-ts-util'
export interface Tag {
name: string
@ -37,9 +38,15 @@ export interface MatchImageByTagsReq {
not_tags: number[]
}
export const getImagesByTags = async (req: MatchImageByTagsReq) => {
const resp = await axiosInst.value.post('/db/match_images_by_tags', req)
return resp.data as FileNodeInfo[]
export const getImagesByTags = async (req: MatchImageByTagsReq, cursor: string) => {
const resp = await axiosInst.value.post('/db/match_images_by_tags', {
...req,
cursor
})
return resp.data as {
files: FileNodeInfo[],
cursor: PageCursor
}
}
export const addCustomTag = async (req: { tag_name: string }) => {
@ -65,9 +72,12 @@ export const getImageSelectedCustomTag = async (path: string) => {
return resp.data as Tag[]
}
export const getImagesBySubstr = async (substr: string) => {
const resp = await axiosInst.value.get('/db/search_by_substr', { params: { substr } })
return resp.data as FileNodeInfo[]
export const getImagesBySubstr = async (substr: string, cursor: string) => {
const resp = await axiosInst.value.get('/db/search_by_substr', { params: { substr,cursor } })
return resp.data as {
files: FileNodeInfo[],
cursor: PageCursor
}
}
const scannedPaths = `/db/scanned_paths`

View File

@ -9,7 +9,16 @@ import { nextTick, watch } from 'vue'
import { copy2clipboardI18n } from '@/util'
import fullScreenContextMenu from '@/page/fileTransfer/fullScreenContextMenu.vue'
import { LeftCircleOutlined, RightCircleOutlined } from '@/icon'
import { useImageSearch } from './hook'
import { useImageSearch, createImageSearchIter } from './hook'
const props = defineProps<{
tabIdx: number
paneIdx: number
selectedTagIds: MatchImageByTagsReq
id: string
}>()
const iter = createImageSearchIter(cursor => getImagesByTags(props.selectedTagIds, cursor))
const {
queue,
images,
@ -33,24 +42,15 @@ const {
onFileDragEnd,
cellWidth,
onScroll,
updateImageTag
} = useImageSearch()
const props = defineProps<{
tabIdx: number
paneIdx: number
selectedTagIds: MatchImageByTagsReq
id: string
}>()
} = useImageSearch(iter)
watch(
() => props.selectedTagIds,
async () => {
const { res } = queue.pushAction(() => getImagesByTags(props.selectedTagIds))
images.value = await res
await iter.reset()
await nextTick()
updateImageTag()
scroller.value!.scrollToItem(0)
onScroll() //
},
{ immediate: true }
)

View File

@ -11,7 +11,10 @@ import fullScreenContextMenu from '@/page/fileTransfer/fullScreenContextMenu.vue
import { LeftCircleOutlined, RightCircleOutlined } from '@/icon'
import { message } from 'ant-design-vue'
import { t } from '@/i18n'
import { useImageSearch } from './hook'
import { createImageSearchIter, useImageSearch } from './hook'
const substr = ref('')
const iter = createImageSearchIter(cursor => getImagesBySubstr(substr.value, cursor))
const {
queue,
images,
@ -34,10 +37,8 @@ const {
onFileDragStart,
onFileDragEnd,
cellWidth,
onScroll,
updateImageTag
} = useImageSearch()
const substr = ref('')
onScroll
} = useImageSearch(iter)
const info = ref<DataBaseBasicInfo>()
@ -57,9 +58,9 @@ const onUpdateBtnClick = makeAsyncFunctionSingle(
}).res
)
const query = async () => {
images.value = await queue.pushAction(() => getImagesBySubstr(substr.value)).res
await iter.reset({ refetch: true })
await nextTick()
updateImageTag()
onScroll()
scroller.value!.scrollToItem(0)
if (!images.value.length) {
message.info(t('fuzzy-search-noResults'))

View File

@ -1,7 +1,6 @@
import type { FileNodeInfo } from '@/api/files'
import { createReactiveQueue } from '@/util'
import { identity } from 'lodash-es'
import { ref } from 'vue'
import { reactive, computed } from 'vue'
import {
useHookShareState,
useFilesDisplay,
@ -11,15 +10,25 @@ import {
usePreview,
useEventListen
} from '../fileTransfer/hook'
import { useTagStore } from '@/store/useTagStore'
import { debounce } from 'lodash-es'
import { makeAsyncIterator } from 'vue3-ts-util'
import { getImagesByTags } from '@/api/db'
export const useImageSearch = () => {
const images = ref<FileNodeInfo[]>()
export const createImageSearchIter = (
fetchfn: (cursor: string) => ReturnType<typeof getImagesByTags>
) => {
return reactive(makeAsyncIterator(fetchfn, (v) => v.files, {
dataUpdateStrategy: 'merge'
}))
}
export const useImageSearch = (iter: ReturnType<typeof createImageSearchIter>) => {
const deletedImagePahts = reactive(new Set<String>())
const images = computed(() => (iter.res ?? []).filter((v) => !deletedImagePahts.has(v.fullpath)))
const queue = createReactiveQueue()
const tagStore = useTagStore()
const { stackViewEl, multiSelectedIdxs, stack, scroller } = useHookShareState({ images }).toRefs()
const { itemSize, gridItems, cellWidth } = useFilesDisplay()
const { stackViewEl, multiSelectedIdxs, stack, scroller } = useHookShareState({
images: images as any
}).toRefs()
const { itemSize, gridItems, cellWidth, onScroll } = useFilesDisplay({ fetchNext: () => iter.next() })
const { showMenuIdx } = useMobileOptimization()
const { onFileDragStart, onFileDragEnd } = useFileTransfer()
const {
@ -37,25 +46,15 @@ export const useImageSearch = () => {
}
useEventListen('removeFiles', async ({ paths }) => {
images.value = images.value?.filter((v) => !paths.includes(v.fullpath))
paths.forEach((v) => deletedImagePahts.add(v))
})
const updateImageTag = () => {
const s = scroller.value
if (s && images.value) {
const paths = images.value
.slice(Math.max(s.$_startIndex - 10, 0), s.$_endIndex + 10)
.map((v) => v.fullpath)
tagStore.fetchImageTags(paths)
}
}
const onScroll = debounce(updateImageTag, 300)
return {
images,
scroller,
queue,
images,
iter,
onContextMenuClickU,
stackViewEl,
previewIdx,
@ -75,7 +74,6 @@ export const useImageSearch = () => {
onFileDragStart,
onFileDragEnd,
cellWidth,
onScroll,
updateImageTag
onScroll
}
}

View File

@ -589,7 +589,7 @@ export function useLocation () {
}
}
export function useFilesDisplay () {
export function useFilesDisplay ({ fetchNext }: {fetchNext?: () => Promise<any>} = { }) {
const {
scroller,
sortedFiles,
@ -633,23 +633,33 @@ export function useFilesDisplay () {
}
}
const fill = async (isFullScreenPreview = false) => {
const s = scroller.value
// 填充够一页,直到不行为止
const fetchDataUntilViewFilled = async (isFullScreenPreview = false) => {
const s = scroller.value
const currIdx = () => (isFullScreenPreview ? previewIdx.value : s?.$_endIndex ?? 0)
while (
!sortedFiles.value.length ||
(currIdx() > sortedFiles.value.length - 20 && canLoadNext.value)
) {
const needLoad = () => {
const len = sortedFiles.value.length
if (!len) {
return true
}
if (fetchNext) {
return currIdx() > len - 20
}
return currIdx() > len - 20 && canLoadNext.value // canLoadNext 是walker的表示加载完成
}
while (needLoad()) {
const ret = await (fetchNext ?? loadNextDir)()
if (typeof ret === 'boolean' && !ret) {
return // 返回false同样表示加载完成
}
await delay(30)
await loadNextDir()
}
}
state.useEventListen('loadNextDir', fill)
state.useEventListen('loadNextDir', fetchDataUntilViewFilled)
const onViewedImagesChange = () => {
const updateViewingImagesTag = () => {
const s = scroller.value
if (s) {
const paths = sortedFiles.value.slice(Math.max(s.$_startIndex - 10, 0), s.$_endIndex + 10)
@ -659,12 +669,12 @@ export function useFilesDisplay () {
}
}
watch(currLocation, debounce(onViewedImagesChange, 150))
watch(currLocation, debounce(updateViewingImagesTag, 150))
const onScroll = debounce(() => {
fill()
onViewedImagesChange()
}, 300)
const onScroll = debounce(async () => {
await fetchDataUntilViewFilled()
updateViewingImagesTag()
}, 150)
return {
gridItems,
@ -678,8 +688,7 @@ export function useFilesDisplay () {
loadNextDirLoading,
canLoadNext,
itemSize,
cellWidth,
onViewedImagesChange
cellWidth
}
}