Add support for fuzzy search within specified scope

pull/471/head
zanllp 2023-11-26 02:58:05 +08:00
parent 8de7f929eb
commit b9332d3525
32 changed files with 149 additions and 77 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-dda7184d.js"></script>
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-7014c21f.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-896679b3.css">
</head>

View File

@ -46,6 +46,7 @@ from scripts.iib.db.datamodel import (
ImageTag,
ExtraPath,
FileInfoDict,
Cursor
)
from scripts.iib.db.update_image_data import update_image_data, rebuild_image_index
from scripts.iib.logger import logger
@ -597,22 +598,51 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
finally:
DataBase._initing = False
class SearchBySubstrReq(BaseModel):
surstr: str
cursor: str
regexp: str
folder_paths: List[str] = None
size: Optional[int] = 200
@app.post(db_pre + "/search_by_substr", dependencies=[Depends(verify_secret)])
async def search_by_substr(req: SearchBySubstrReq):
conn = DataBase.get_conn()
folder_paths=normalize_paths(req.folder_paths, os.getcwd())
if(not folder_paths and req.folder_paths):
return { "files": [], "cursor": Cursor(has_next=False) }
imgs, next_cursor = DbImg.find_by_substring(
conn=conn,
substring=req.surstr,
cursor=req.cursor,
limit=req.size,
regexp=req.regexp,
folder_paths=folder_paths
)
return {
"files": filter_allowed_files([x.to_file_info() for x in imgs]),
"cursor": next_cursor
}
class MatchImagesByTagsReq(BaseModel):
and_tags: List[int]
or_tags: List[int]
not_tags: List[int]
cursor: str
folder_paths: List[str] = None,
folder_paths: List[str] = None
size: Optional[int] = 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()
folder_paths=normalize_paths(req.folder_paths, os.getcwd())
if(not folder_paths and req.folder_paths):
return { "files": [], "cursor": Cursor(has_next=False) }
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,
folder_paths=normalize_paths(req.folder_paths, os.getcwd()),
folder_paths=folder_paths,
limit=req.size
)
return {
@ -767,20 +797,7 @@ def infinite_image_browsing_api(app: FastAPI, **kwargs):
conn = DataBase.get_conn()
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 = '', cursor: str = '', size = 200, regexp: str = ''):
conn = DataBase.get_conn()
imgs, next_cursor = DbImg.find_by_substring(
conn=conn,
substring=substr,
cursor=cursor,
limit=size,
regexp=regexp
)
return {
"files": filter_allowed_files([x.to_file_info() for x in imgs]),
"cursor": next_cursor
}
class ExtraPathModel(BaseModel):

View File

@ -201,33 +201,35 @@ class Image:
@classmethod
def find_by_substring(
cls, conn: Connection, substring: str, limit: int = 500, cursor="", regexp=""
cls, conn: Connection, substring: str, limit: int = 500, cursor="", regexp="",
folder_paths: List[str] = []
) -> tuple[List["Image"], Cursor]:
api_cur = Cursor()
with closing(conn.cursor()) as cur:
params = []
where_clauses = []
if regexp:
if cursor:
sql = f"SELECT * FROM image WHERE (exif REGEXP ?) and (date < ?) ORDER BY date DESC LIMIT ?"
cur.execute(sql, (regexp, cursor, limit))
else:
sql = "SELECT * FROM image WHERE (exif REGEXP ?) ORDER BY date DESC LIMIT ?"
cur.execute(
sql,
(
regexp,
limit,
),
)
where_clauses.append("(exif REGEXP ?)")
params.append(regexp)
else:
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))
where_clauses.append("(path LIKE ? OR exif LIKE ?)")
params.extend((f"%{substring}%", f"%{substring}%"))
if cursor:
where_clauses.append("(date < ?)")
params.append(cursor)
if folder_paths:
folder_clauses = []
for folder_path in folder_paths:
folder_clauses.append("(image.path LIKE ?)")
params.append(os.path.join(folder_path, "%"))
where_clauses.append("(" + " OR ".join(folder_clauses) + ")")
sql = "SELECT * FROM image"
if where_clauses:
sql += " WHERE "
sql += " AND ".join(where_clauses)
sql += " ORDER BY date DESC LIMIT ? "
params.append(limit)
cur.execute(sql, params)
rows = cur.fetchall()
api_cur.has_next = len(rows) >= limit

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,c2 as n}from"./index-dda7184d.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,c2 as n}from"./index-7014c21f.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

@ -1 +1 @@
import{d as j,l as H,ax as J,o as r,y as p,c as n,n as o,r as e,s as V,p as c,t as K,v as m,x as $,m as D,L as Y,C as f,N as F,Q as Z,R as ee,V as te,X as se}from"./index-dda7184d.js";import{L as ie,R as le,f as ne,S as oe}from"./fullScreenContextMenu-217e4fe7.js";import{g as ae,F as re}from"./FileItem-ba4545b5.js";import{g as de}from"./db-3650b2a5.js";import{c as ce,u as me}from"./hook-12226230.js";import{o as ue}from"./functionalCallableComp-9395a4a4.js";const ge={class:"hint"},pe={key:1},fe={class:"no-res-hint"},ve={class:"hint"},Ie={key:2,class:"preview-switch"},ke=j({__name:"MatchedImageGrid",props:{tabIdx:{},paneIdx:{},selectedTagIds:{},id:{}},setup(z){const v=z,u=ce(s=>de(v.selectedTagIds,s)),{queue:B,images:l,onContextMenuClickU:I,stackViewEl:R,previewIdx:a,previewing:k,onPreviewVisibleChange:N,previewImgMove:w,canPreview:C,itemSize:_,gridItems:G,showGenInfo:d,imageGenInfo:h,q:A,multiSelectedIdxs:x,onFileItemClick:E,scroller:y,showMenuIdx:g,onFileDragStart:P,onFileDragEnd:U,cellWidth:L,onScroll:b}=me(u);return H(()=>v.selectedTagIds,async()=>{var s;await u.reset(),await J(),(s=y.value)==null||s.scrollToItem(0),b()},{immediate:!0}),(s,t)=>{const O=Z,q=ee,Q=te,W=oe;return r(),p("div",{class:"container",ref_key:"stackViewEl",ref:R},[n(W,{size:"large",spinning:!e(B).isIdle},{default:o(()=>{var M,S;return[n(q,{visible:e(d),"onUpdate:visible":t[1]||(t[1]=i=>V(d)?d.value=i:null),width:"70vw","mask-closable":"",onOk:t[2]||(t[2]=i=>d.value=!1)},{cancelText:o(()=>[]),default:o(()=>[n(O,{active:"",loading:!e(A).isIdle},{default:o(()=>[c("div",{style:{width:"100%","word-break":"break-all","white-space":"pre-line","max-height":"70vh",overflow:"auto"},onDblclick:t[0]||(t[0]=i=>e(K)(e(h)))},[c("div",ge,m(s.$t("doubleClickToCopy")),1),$(" "+m(e(h)),1)],32)]),_:1},8,["loading"])]),_:1},8,["visible"]),(M=e(l))!=null&&M.length?(r(),D(e(ae),{key:0,ref_key:"scroller",ref:y,class:"file-list",items:e(l),"item-size":e(_).first,"key-field":"fullpath","item-secondary-size":e(_).second,gridItems:e(G),onScroll:e(b)},{default:o(({item:i,index:T})=>[n(re,{idx:T,file:i,"cell-width":e(L),"show-menu-idx":e(g),"onUpdate:showMenuIdx":t[3]||(t[3]=X=>V(g)?g.value=X:null),onDragstart:e(P),onDragend:e(U),onFileItemClick:e(E),"full-screen-preview-image-url":e(l)[e(a)]?e(Y)(e(l)[e(a)]):"",selected:e(x).includes(T),onContextMenuClick:e(I),onPreviewVisibleChange:e(N),"is-selected-mutil-files":e(x).length>1},null,8,["idx","file","cell-width","show-menu-idx","onDragstart","onDragend","onFileItemClick","full-screen-preview-image-url","selected","onContextMenuClick","onPreviewVisibleChange","is-selected-mutil-files"])]),_:1},8,["items","item-size","item-secondary-size","gridItems","onScroll"])):e(u).load&&s.selectedTagIds.and_tags.length===1&&!((S=s.selectedTagIds.folder_paths_str)!=null&&S.trim())?(r(),p("div",pe,[c("div",fe,[c("p",ve,m(s.$t("tagSearchNoResultsMessage")),1),n(Q,{onClick:t[4]||(t[4]=i=>e(ue)()),type:"primary"},{default:o(()=>[$(m(s.$t("rebuildImageIndex")),1)]),_:1})])])):f("",!0),e(k)?(r(),p("div",Ie,[n(e(ie),{onClick:t[5]||(t[5]=i=>e(w)("prev")),class:F({disable:!e(C)("prev")})},null,8,["class"]),n(e(le),{onClick:t[6]||(t[6]=i=>e(w)("next")),class:F({disable:!e(C)("next")})},null,8,["class"])])):f("",!0)]}),_:1},8,["spinning"]),e(k)&&e(l)&&e(l)[e(a)]?(r(),D(ne,{key:0,file:e(l)[e(a)],idx:e(a),onContextMenuClick:e(I)},null,8,["file","idx","onContextMenuClick"])):f("",!0)],512)}}});const be=se(ke,[["__scopeId","data-v-c4827cd4"]]);export{be as default};
import{d as j,l as H,ax as J,o as r,y as p,c as n,n as o,r as e,s as V,p as c,t as K,v as m,x as $,m as D,L as Y,C as f,N as F,Q as Z,R as ee,V as te,X as se}from"./index-7014c21f.js";import{L as ie,R as le,f as ne,S as oe}from"./fullScreenContextMenu-98230b57.js";import{g as ae,F as re}from"./FileItem-3525329e.js";import{g as de}from"./db-a31c442b.js";import{c as ce,u as me}from"./hook-d5d8b6fe.js";import{o as ue}from"./functionalCallableComp-c44a8b16.js";const ge={class:"hint"},pe={key:1},fe={class:"no-res-hint"},ve={class:"hint"},Ie={key:2,class:"preview-switch"},ke=j({__name:"MatchedImageGrid",props:{tabIdx:{},paneIdx:{},selectedTagIds:{},id:{}},setup(z){const v=z,u=ce(s=>de(v.selectedTagIds,s)),{queue:B,images:l,onContextMenuClickU:I,stackViewEl:R,previewIdx:a,previewing:k,onPreviewVisibleChange:N,previewImgMove:w,canPreview:C,itemSize:_,gridItems:G,showGenInfo:d,imageGenInfo:h,q:A,multiSelectedIdxs:x,onFileItemClick:E,scroller:y,showMenuIdx:g,onFileDragStart:P,onFileDragEnd:U,cellWidth:L,onScroll:b}=me(u);return H(()=>v.selectedTagIds,async()=>{var s;await u.reset(),await J(),(s=y.value)==null||s.scrollToItem(0),b()},{immediate:!0}),(s,t)=>{const O=Z,q=ee,Q=te,W=oe;return r(),p("div",{class:"container",ref_key:"stackViewEl",ref:R},[n(W,{size:"large",spinning:!e(B).isIdle},{default:o(()=>{var M,S;return[n(q,{visible:e(d),"onUpdate:visible":t[1]||(t[1]=i=>V(d)?d.value=i:null),width:"70vw","mask-closable":"",onOk:t[2]||(t[2]=i=>d.value=!1)},{cancelText:o(()=>[]),default:o(()=>[n(O,{active:"",loading:!e(A).isIdle},{default:o(()=>[c("div",{style:{width:"100%","word-break":"break-all","white-space":"pre-line","max-height":"70vh",overflow:"auto"},onDblclick:t[0]||(t[0]=i=>e(K)(e(h)))},[c("div",ge,m(s.$t("doubleClickToCopy")),1),$(" "+m(e(h)),1)],32)]),_:1},8,["loading"])]),_:1},8,["visible"]),(M=e(l))!=null&&M.length?(r(),D(e(ae),{key:0,ref_key:"scroller",ref:y,class:"file-list",items:e(l),"item-size":e(_).first,"key-field":"fullpath","item-secondary-size":e(_).second,gridItems:e(G),onScroll:e(b)},{default:o(({item:i,index:T})=>[n(re,{idx:T,file:i,"cell-width":e(L),"show-menu-idx":e(g),"onUpdate:showMenuIdx":t[3]||(t[3]=X=>V(g)?g.value=X:null),onDragstart:e(P),onDragend:e(U),onFileItemClick:e(E),"full-screen-preview-image-url":e(l)[e(a)]?e(Y)(e(l)[e(a)]):"",selected:e(x).includes(T),onContextMenuClick:e(I),onPreviewVisibleChange:e(N),"is-selected-mutil-files":e(x).length>1},null,8,["idx","file","cell-width","show-menu-idx","onDragstart","onDragend","onFileItemClick","full-screen-preview-image-url","selected","onContextMenuClick","onPreviewVisibleChange","is-selected-mutil-files"])]),_:1},8,["items","item-size","item-secondary-size","gridItems","onScroll"])):e(u).load&&s.selectedTagIds.and_tags.length===1&&!((S=s.selectedTagIds.folder_paths_str)!=null&&S.trim())?(r(),p("div",pe,[c("div",fe,[c("p",ve,m(s.$t("tagSearchNoResultsMessage")),1),n(Q,{onClick:t[4]||(t[4]=i=>e(ue)()),type:"primary"},{default:o(()=>[$(m(s.$t("rebuildImageIndex")),1)]),_:1})])])):f("",!0),e(k)?(r(),p("div",Ie,[n(e(ie),{onClick:t[5]||(t[5]=i=>e(w)("prev")),class:F({disable:!e(C)("prev")})},null,8,["class"]),n(e(le),{onClick:t[6]||(t[6]=i=>e(w)("next")),class:F({disable:!e(C)("next")})},null,8,["class"])])):f("",!0)]}),_:1},8,["spinning"]),e(k)&&e(l)&&e(l)[e(a)]?(r(),D(ne,{key:0,file:e(l)[e(a)],idx:e(a),onContextMenuClick:e(I)},null,8,["file","idx","onContextMenuClick"])):f("",!0)],512)}}});const be=se(ke,[["__scopeId","data-v-c4827cd4"]]);export{be as default};

View File

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

View File

@ -0,0 +1 @@
.regex-icon[data-v-7afef8c2]{user-select:none;padding:4px;margin:0 4px;cursor:pointer;border:1px solid var(--zp-border);border-radius:4px}.regex-icon img[data-v-7afef8c2]{height:1.5em}.regex-icon[data-v-7afef8c2]:hover{background:var(--zp-border)}.regex-icon.selected[data-v-7afef8c2]{background:var(--primary-color-1);border:1px solid var(--primary-color)}.search-bar[data-v-7afef8c2]{padding:8px 8px 0;display:flex}.search-bar.last[data-v-7afef8c2]{padding-bottom:8px}.search-bar .form-name[data-v-7afef8c2]{flex-shrink:0;padding:4px 8px}.preview-switch[data-v-7afef8c2]{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-7afef8c2]{color:#fff;margin:16px;font-size:4em;pointer-events:all;cursor:pointer}.preview-switch>*.disable[data-v-7afef8c2]{opacity:0;pointer-events:none;cursor:none}.container[data-v-7afef8c2]{background:var(--zp-secondary-background)}.container .file-list[data-v-7afef8c2]{list-style:none;padding:8px;height:100%;overflow:auto;height:var(--pane-max-height);width:100%}

View File

@ -1 +0,0 @@
.regex-icon[data-v-bbf17c78]{user-select:none;padding:4px;margin:0 4px;cursor:pointer;border:1px solid var(--zp-border);border-radius:4px}.regex-icon img[data-v-bbf17c78]{height:1.5em}.regex-icon[data-v-bbf17c78]:hover{background:var(--zp-border)}.regex-icon.selected[data-v-bbf17c78]{background:var(--primary-color-1);border:1px solid var(--primary-color)}.search-bar[data-v-bbf17c78]{padding:8px;display:flex}.preview-switch[data-v-bbf17c78]{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-bbf17c78]{color:#fff;margin:16px;font-size:4em;pointer-events:all;cursor:pointer}.preview-switch>*.disable[data-v-bbf17c78]{opacity:0;pointer-events:none;cursor:none}.container[data-v-bbf17c78]{background:var(--zp-secondary-background)}.container .file-list[data-v-bbf17c78]{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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{d as v,c3 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,c4 as z,c5 as B,V as $,X as R}from"./index-dda7184d.js";import{u as S,b as V,k as E,F as A,g as L}from"./FileItem-ba4545b5.js";import"./functionalCallableComp-9395a4a4.js";import"./db-3650b2a5.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,c3 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,c4 as z,c5 as B,V as $,X as R}from"./index-7014c21f.js";import{u as S,b as V,k as E,F as A,g as L}from"./FileItem-3525329e.js";import"./functionalCallableComp-c44a8b16.js";import"./db-a31c442b.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{c5 as t}from"./index-dda7184d.js";const c=async()=>(await t.value.get("/db/basic_info")).data,d=async()=>(await t.value.get("/db/expired_dirs")).data,p=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,folder_paths:(a.folder_paths_str??"").split(/,|\n/).map(e=>e.trim()).filter(e=>e),cursor:s})).data,u=async a=>(await t.value.post("/db/add_custom_tag",a)).data,i=async a=>(await t.value.post("/db/toggle_custom_tag_to_img",a)).data,m=async a=>{await t.value.post("/db/remove_custom_tag",a)},_=async(a,s,r)=>(await t.value.get("/db/search_by_substr",{params:{substr:a,cursor:s,regexp:r}})).data,o="/db/extra_paths",b=async a=>{await t.value.post(o,a)},l=async a=>{await t.value.delete(o,{data:a})},y=async a=>(await t.value.post("/db/get_image_tags",{paths:a})).data,v=()=>t.value.post("/db/rebuild_index"),h=a=>t.value.post("/db/batch_update_image_tag",a);export{b as a,c as b,d as c,u as d,m as e,_ as f,g,y as h,h as i,v as j,l as r,i as t,p as u};

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

@ -0,0 +1 @@
import{c5 as t}from"./index-7014c21f.js";const c=async()=>(await t.value.get("/db/basic_info")).data,d=async()=>(await t.value.get("/db/expired_dirs")).data,p=async()=>{await t.value.post("/db/update_image_data",{},{timeout:1/0})},u=async(a,s)=>(await t.value.post("/db/match_images_by_tags",{...a,folder_paths:(a.folder_paths_str??"").split(/,|\n/).map(e=>e.trim()).filter(e=>e),cursor:s})).data,g=async a=>(await t.value.post("/db/add_custom_tag",a)).data,i=async a=>(await t.value.post("/db/toggle_custom_tag_to_img",a)).data,_=async a=>{await t.value.post("/db/remove_custom_tag",a)},m=async a=>(await t.value.post("/db/search_by_substr",a)).data,r="/db/extra_paths",b=async a=>{await t.value.post(r,a)},l=async a=>{await t.value.delete(r,{data:a})},y=async a=>(await t.value.post("/db/get_image_tags",{paths:a})).data,v=()=>t.value.post("/db/rebuild_index"),h=a=>t.value.post("/db/batch_update_image_tag",a);export{b as a,c as b,d as c,g as d,_ as e,m as f,u as g,y as h,h as i,v as j,l as r,i as t,p as u};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{bn as A,$ as g,bV as q,bW as x,ar as k,ao as D,bO as z,bd as G}from"./index-dda7184d.js";import{u as N,b as O,f as Q,c as W,d as j,e as H,h as L}from"./FileItem-ba4545b5.js";let T=0;const U=()=>++T,V=(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=U();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(V(r,l=>l.files,{dataUpdateStrategy:"merge"})),K=r=>{const l=A(new Set),c=D(()=>(r.res??[]).filter(y=>!l.has(y.fullpath))),s=z(),{stackViewEl:u,multiSelectedIdxs:t,stack:a,scroller:f}=N({images:c}).toRefs(),{itemSize:v,gridItems:b,cellWidth:d,onScroll:h}=O({fetchNext:()=>r.next()}),{showMenuIdx:w}=Q(),{onFileDragStart:m,onFileDragEnd:e}=W(),{showGenInfo:n,imageGenInfo:o,q:p,onContextMenuClick:i,onFileItemClick:I}=j({openNext:G}),{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};
import{bn as A,$ as g,bV as q,bW as x,ar as k,ao as D,bO as z,bd as G}from"./index-7014c21f.js";import{u as N,b as O,f as Q,c as W,d as j,e as H,h as L}from"./FileItem-3525329e.js";let T=0;const U=()=>++T,V=(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=U();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(V(r,l=>l.files,{dataUpdateStrategy:"merge"})),K=r=>{const l=A(new Set),c=D(()=>(r.res??[]).filter(y=>!l.has(y.fullpath))),s=z(),{stackViewEl:u,multiSelectedIdxs:t,stack:a,scroller:f}=N({images:c}).toRefs(),{itemSize:v,gridItems:b,cellWidth:d,onScroll:h}=O({fetchNext:()=>r.next()}),{showMenuIdx:w}=Q(),{onFileDragStart:m,onFileDragEnd:e}=W(),{showGenInfo:n,imageGenInfo:o,q:p,onContextMenuClick:i,onFileItemClick:I}=j({openNext:G}),{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};

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/stackView-2a3ebe89.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

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

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-dda7184d.js"></script>
<script type="module" crossorigin src="/infinite_image_browsing/fe-static/assets/index-7014c21f.js"></script>
<link rel="stylesheet" href="/infinite_image_browsing/fe-static/assets/index-896679b3.css">
</head>

View File

@ -74,8 +74,17 @@ export const getImageSelectedCustomTag = async (path: string) => {
return resp.data as Tag[]
}
export const getImagesBySubstr = async (substr: string, cursor: string, regexp?: string) => {
const resp = await axiosInst.value.get('/db/search_by_substr', { params: { substr,cursor,regexp } })
export interface SearchBySubstrReq {
surstr: string;
cursor: string;
regexp: string;
folder_paths?: string[];
size?: number;
}
export const getImagesBySubstr = async (req: SearchBySubstrReq) => {
const resp = await axiosInst.value.post('/db/search_by_substr', req)
return resp.data as {
files: FileNodeInfo[],
cursor: PageCursor

View File

@ -5,7 +5,7 @@ import '@zanllp/vue-virtual-scroller/dist/vue-virtual-scroller.css'
// @ts-ignore
import { RecycleScroller } from '@zanllp/vue-virtual-scroller'
import { toRawFileUrl } from '@/util/file'
import { getDbBasicInfo, getExpiredDirs, getImagesBySubstr, updateImageData, type DataBaseBasicInfo } from '@/api/db'
import { getDbBasicInfo, getExpiredDirs, getImagesBySubstr, updateImageData, type DataBaseBasicInfo, SearchBySubstrReq } from '@/api/db'
import { copy2clipboardI18n, makeAsyncFunctionSingle, useGlobalEventListen } from '@/util'
import fullScreenContextMenu from '@/page/fileTransfer/fullScreenContextMenu.vue'
import { LeftCircleOutlined, RightCircleOutlined, regex } from '@/icon'
@ -13,9 +13,19 @@ import { message } from 'ant-design-vue'
import { t } from '@/i18n'
import { createImageSearchIter, useImageSearch } from './hook'
const props = defineProps<{ tabIdx: number; paneIdx: number, searchScope?: string }>()
const isRegex = ref(false)
const substr = ref('')
const iter = createImageSearchIter(cursor => isRegex.value ? getImagesBySubstr('', cursor, substr.value) : getImagesBySubstr(substr.value, cursor))
const folder_paths_str = ref(props.searchScope ?? '')
const iter = createImageSearchIter(cursor => {
const req: SearchBySubstrReq = {
cursor,
regexp: isRegex.value ? substr.value : '',
surstr: !isRegex.value ? substr.value : '',
folder_paths: (folder_paths_str.value ?? '').split(/,|\n/).map(v => v.trim()).filter(v => v)
}
return getImagesBySubstr(req)
})
const {
queue,
images,
@ -46,7 +56,10 @@ const info = ref<DataBaseBasicInfo>()
onMounted(async () => {
info.value = await getDbBasicInfo()
if (info.value.img_count && info.value.expired) {
onUpdateBtnClick()
await onUpdateBtnClick()
}
if (props.searchScope) {
await query()
}
})
@ -89,10 +102,14 @@ const onRegexpClick = () => {
title="Use Regular Expression"> <img :src="regex"></div>
<AButton @click="onUpdateBtnClick" :loading="!queue.isIdle" type="primary" v-if="info.expired || !info.img_count">
{{ info.img_count === 0 ? $t('generateIndexHint') : $t('UpdateIndex') }}</AButton>
<AButton v-else type="primary" @click="query" :loading="!queue.isIdle" :disabled="!substr">{{
<AButton v-else type="primary" @click="query" :loading="!queue.isIdle || iter.loading" :disabled="!substr && !folder_paths_str">{{
$t('search') }}
</AButton>
</div>
<div class="search-bar last">
<div class="form-name">{{ $t('searchScope') }}</div>
<ATextarea :auto-size="{ maxRows: 8 }" v-model:value="folder_paths_str" :placeholder="$t('specifiedSearchFolder')"/>
</div>
<ASpin size="large" :spinning="!queue.isIdle">
<AModal v-model:visible="showGenInfo" width="70vw" mask-closable @ok="showGenInfo = false">
<template #cancelText />
@ -153,8 +170,15 @@ const onRegexpClick = () => {
}
.search-bar {
padding: 8px;
padding: 8px 8px 0 8px;
&.last {
padding-bottom: 8px;
}
display: flex;
.form-name {
flex-shrink: 0;
padding: 4px 8px;
}
}
.preview-switch {

View File

@ -166,7 +166,7 @@ const conv = {
</div>
<div class="search-bar">
<div class="form-name">{{ $t('searchScope') }}</div>
<ATextarea auto-size v-model:value="matchIds.folder_paths_str" :placeholder="$t('specifiedSearchFolder')"/>
<ATextarea :auto-size="{ maxRows: 8 }" v-model:value="matchIds.folder_paths_str" :placeholder="$t('specifiedSearchFolder')"/>
</div>
</div>

View File

@ -1,4 +1,4 @@
import { useGlobalStore, type FileTransferTabPane, type Shortcut, type TagSearchTabPane } from '@/store/useGlobalStore'
import { useGlobalStore, type FileTransferTabPane, type Shortcut, type TagSearchTabPane, FuzzySearchTabPane } from '@/store/useGlobalStore'
import { useImgSliStore } from '@/store/useImgSli'
import { onLongPress, useElementSize, useMouseInElement } from '@vueuse/core'
import { ref, computed, watch, onMounted, h, reactive } from 'vue'
@ -542,13 +542,13 @@ export function useLocation () {
copy2clipboardI18n(url, t('copyLocationUrlSuccessMsg'))
}
const searchInCurrentDir = () => {
const searchInCurrentDir = (type: (TagSearchTabPane|FuzzySearchTabPane)['type'] = 'tag-search') => {
const tab = global.tabList[props.value.tabIdx]
const pane: TagSearchTabPane = {
type: 'tag-search',
const pane = {
type,
key: uniqueId(),
searchScope: currLocation.value,
name: t('imgSearch'),
name: t(type === 'tag-search' ? 'imgSearch' : 'fuzzy-search'),
}
tab.panes.push(pane)
tab.key = pane.key

View File

@ -134,7 +134,22 @@ watch(
</div>
<div class="actions">
<a class="opt" @click.prevent="refresh"> {{ $t('refresh') }} </a>
<a class="opt" @click.prevent="searchInCurrentDir"> {{ $t('search') }} </a>
<a-dropdown>
<a class="opt" @click.prevent>
{{ $t('search') }}
<down-outlined />
</a>
<template #overlay>
<a-menu>
<a-menu-item key="tag-search">
<a @click.prevent="searchInCurrentDir('tag-search')">{{ $t('imgSearch') }}</a>
</a-menu-item>
<a-menu-item key="tag-search">
<a @click.prevent="searchInCurrentDir('fuzzy-search')">{{ $t('fuzzy-search') }}</a>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
<a class="opt" @click.prevent="onWalkBtnClick" v-if="showWalkButton"> Walk </a>
<a class="opt" @click.prevent.stop="selectAll"> {{ $t('selectAll') }} </a>
<a class="opt" @click.prevent="share" v-if="!isTauri"> {{ $t('share') }} </a>

View File

@ -18,7 +18,7 @@ interface TabPaneBase {
}
interface OtherTabPane extends TabPaneBase {
type: 'empty' | 'global-setting' | 'tag-search' | 'fuzzy-search' | 'batch-download'
type: 'empty' | 'global-setting' | 'tag-search' | 'batch-download'
}
// logDetailId
@ -45,7 +45,12 @@ export interface TagSearchTabPane extends TabPaneBase {
searchScope?: string
}
export type TabPane = FileTransferTabPane | OtherTabPane | TagSearchMatchedImageGridTabPane | ImgSliTabPane | TagSearchTabPane
export interface FuzzySearchTabPane extends TabPaneBase {
type: 'fuzzy-search'
searchScope?: string
}
export type TabPane = FileTransferTabPane | OtherTabPane | TagSearchMatchedImageGridTabPane | ImgSliTabPane | TagSearchTabPane | FuzzySearchTabPane
/**
* This interface represents a tab, which contains an array of panes, an ID, and a key