Change image search to use pagination based on cursor instead of fixed maximum of 500 images.
parent
a9f1406803
commit
bc6c3ef205
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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};
|
||||
|
|
@ -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%}
|
||||
|
|
@ -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};
|
||||
|
|
@ -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};
|
||||
|
|
@ -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%}
|
||||
|
|
@ -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%}
|
||||
|
|
@ -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};
|
||||
|
|
@ -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};
|
||||
|
|
@ -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
|
|
@ -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};
|
||||
|
|
@ -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};
|
||||
|
|
@ -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
File diff suppressed because one or more lines are too long
|
|
@ -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};
|
||||
|
|
@ -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
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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'))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue