diff --git a/dist/assets/index-Df7HB0Xa.js b/dist/assets/index-lohFrjJm.js similarity index 94% rename from dist/assets/index-Df7HB0Xa.js rename to dist/assets/index-lohFrjJm.js index 016b58d..28e2405 100644 --- a/dist/assets/index-Df7HB0Xa.js +++ b/dist/assets/index-lohFrjJm.js @@ -4,4 +4,4 @@ var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],en `+e.map(Yc).join(` `):` `+Yc(e[0]):`as no adapter specified`),`ERR_NOT_SUPPORT`)}return i}var Qc={getAdapter:Zc,adapters:Jc};function $c(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new _c(null,e)}function el(e){return $c(e),e.headers=Z.from(e.headers),e.data=hc.call(e,e.transformRequest),[`post`,`put`,`patch`].indexOf(e.method)!==-1&&e.headers.setContentType(`application/x-www-form-urlencoded`,!1),Qc.getAdapter(e.adapter||ic.adapter,e)(e).then(function(t){return $c(e),t.data=hc.call(e,e.transformResponse,t),t.headers=Z.from(t.headers),t},function(t){return gc(t)||($c(e),t&&t.response&&(t.response.data=hc.call(e,e.transformResponse,t.response),t.response.headers=Z.from(t.response.headers))),Promise.reject(t)})}var tl=`1.14.0`,nl={};[`object`,`boolean`,`number`,`function`,`string`,`symbol`].forEach((e,t)=>{nl[e]=function(n){return typeof n===e||`a`+(t<1?`n `:` `)+e}});var rl={};nl.transitional=function(e,t,n){function r(e,t){return`[Axios v`+tl+`] Transitional option '`+e+`'`+t+(n?`. `+n:``)}return(n,i,a)=>{if(e===!1)throw new Y(r(i,` has been removed`+(t?` in `+t:``)),Y.ERR_DEPRECATED);return t&&!rl[i]&&(rl[i]=!0,console.warn(r(i,` has been deprecated since v`+t+` and will be removed in the near future`))),e?e(n,i,a):!0}},nl.spelling=function(e){return(t,n)=>(console.warn(`${n} is likely a misspelling of ${e}`),!0)};function il(e,t,n){if(typeof e!=`object`)throw new Y(`options must be an object`,Y.ERR_BAD_OPTION_VALUE);let r=Object.keys(e),i=r.length;for(;i-- >0;){let a=r[i],o=t[a];if(o){let t=e[a],n=t===void 0||o(t,a,e);if(n!==!0)throw new Y(`option `+a+` must be `+n,Y.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new Y(`Unknown option `+a,Y.ERR_BAD_OPTION)}}var al={assertOptions:il,validators:nl},ol=al.validators,sl=class{constructor(e){this.defaults=e||{},this.interceptors={request:new Ws,response:new Ws}}async request(e,t){try{return await this._request(e,t)}catch(e){if(e instanceof Error){let t={};Error.captureStackTrace?Error.captureStackTrace(t):t=Error();let n=t.stack?t.stack.replace(/^.+\n/,``):``;try{e.stack?n&&!String(e.stack).endsWith(n.replace(/^.+\n.+\n/,``))&&(e.stack+=` `+n):e.stack=n}catch{}}throw e}}_request(e,t){typeof e==`string`?(t||={},t.url=e):t=e||{},t=jc(this.defaults,t);let{transitional:n,paramsSerializer:r,headers:i}=t;n!==void 0&&al.assertOptions(n,{silentJSONParsing:ol.transitional(ol.boolean),forcedJSONParsing:ol.transitional(ol.boolean),clarifyTimeoutError:ol.transitional(ol.boolean),legacyInterceptorReqResOrdering:ol.transitional(ol.boolean)},!1),r!=null&&(J.isFunction(r)?t.paramsSerializer={serialize:r}:al.assertOptions(r,{encode:ol.function,serialize:ol.function},!0)),t.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls===void 0?t.allowAbsoluteUrls=!0:t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls),al.assertOptions(t,{baseUrl:ol.spelling(`baseURL`),withXsrfToken:ol.spelling(`withXSRFToken`)},!0),t.method=(t.method||this.defaults.method||`get`).toLowerCase();let a=i&&J.merge(i.common,i[t.method]);i&&J.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`,`common`],e=>{delete i[e]}),t.headers=Z.concat(a,i);let o=[],s=!0;this.interceptors.request.forEach(function(e){if(typeof e.runWhen==`function`&&e.runWhen(t)===!1)return;s&&=e.synchronous;let n=t.transitional||Gs;n&&n.legacyInterceptorReqResOrdering?o.unshift(e.fulfilled,e.rejected):o.push(e.fulfilled,e.rejected)});let c=[];this.interceptors.response.forEach(function(e){c.push(e.fulfilled,e.rejected)});let l,u=0,d;if(!s){let e=[el.bind(this),void 0];for(e.unshift(...o),e.push(...c),d=e.length,l=Promise.resolve(t);u{if(!n._listeners)return;let t=n._listeners.length;for(;t-- >0;)n._listeners[t](e);n._listeners=null}),this.promise.then=e=>{let t,r=new Promise(e=>{n.subscribe(e),t=e}).then(e);return r.cancel=function(){n.unsubscribe(t)},r},e(function(e,r,i){n.reason||(n.reason=new _c(e,r,i),t(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;let t=this._listeners.indexOf(e);t!==-1&&this._listeners.splice(t,1)}toAbortSignal(){let e=new AbortController,t=t=>{e.abort(t)};return this.subscribe(t),e.signal.unsubscribe=()=>this.unsubscribe(t),e.signal}static source(){let t;return{token:new e(function(e){t=e}),cancel:t}}};function ll(e){return function(t){return e.apply(null,t)}}function ul(e){return J.isObject(e)&&e.isAxiosError===!0}var dl={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(dl).forEach(([e,t])=>{dl[t]=e});function fl(e){let t=new sl(e),n=Co(sl.prototype.request,t);return J.extend(n,sl.prototype,t,{allOwnKeys:!0}),J.extend(n,t,null,{allOwnKeys:!0}),n.create=function(t){return fl(jc(e,t))},n}var Q=fl(ic);Q.Axios=sl,Q.CanceledError=_c,Q.CancelToken=cl,Q.isCancel=gc,Q.VERSION=tl,Q.toFormData=Rs,Q.AxiosError=Y,Q.Cancel=Q.CanceledError,Q.all=function(e){return Promise.all(e)},Q.spread=ll,Q.isAxiosError=ul,Q.mergeConfig=jc,Q.AxiosHeaders=Z,Q.formToJSON=e=>nc(J.isHTMLForm(e)?new FormData(e):e),Q.getAdapter=Qc.getAdapter,Q.HttpStatusCode=dl,Q.default=Q;var $=``;async function pl(e=50){let{data:t}=await Q.get(`${$}/admin/recent`,{params:{limit:e},timeout:15e3});return t}async function ml(){let{data:e}=await Q.get(`${$}/admin/stats`,{timeout:15e3});return e}async function hl(e){let{data:t}=await Q.post(`${$}/admin/priority`,{url:e},{timeout:15e3});return t}async function gl(){let{data:e}=await Q.post(`${$}/admin/flush`,null,{timeout:6e4});return e}async function _l(){let{data:e}=await Q.get(`${$}/admin/flush/status`,{timeout:5e3});return e}async function vl(){let{data:e}=await Q.get(`${$}/admin/workers`,{timeout:1e4});return e}async function yl(e){let{data:t}=await Q.post(`${$}/admin/workers`,{workers:e},{timeout:1e4});return t}async function bl(){let{data:e}=await Q.get(`${$}/admin/backlink`,{timeout:1e4});return e}async function xl(){let{data:e}=await Q.post(`${$}/admin/backlink`,null,{timeout:1e4});return e}async function Sl(){let{data:e}=await Q.get(`${$}/admin/priority/status`,{timeout:1e4});return e}async function Cl(){let{data:e}=await Q.get(`${$}/admin/crawl/status`,{timeout:1e4});return e}async function wl(e){let{data:t}=await Q.get(`${$}/admin/url/keywords`,{params:{url:e},timeout:5e3});return t}async function Tl(){let{data:e}=await Q.get(`${$}/admin/url/keywords/stats`,{timeout:5e3});return e}async function El({page:e=1,page_size:t=50}={}){let{data:n}=await Q.get(`${$}/admin/url/keywords/list`,{params:{page:e,page_size:t},timeout:1e4});return n}var Dl={class:`p-4 md:p-8`},Ol={key:0,class:`flex items-center justify-center h-48`},kl={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},Al={class:`grid grid-cols-2 md:grid-cols-5 gap-3 md:gap-5 mb-6 md:mb-8`},jl={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},Ml={class:`text-3xl font-bold text-white`},Nl={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},Pl={class:`text-3xl font-bold text-white`},Fl={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},Il={class:`text-3xl font-bold text-white`},Ll={class:`bg-gray-900 border border-gray-800 rounded-xl p-5`},Rl={class:`bg-gray-900 border border-gray-800 rounded-xl p-5 flex flex-col justify-between`},zl=[`disabled`],Bl={key:0,class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},Vl={class:`grid grid-cols-2 md:grid-cols-4 gap-4`},Hl={class:`bg-gray-800/50 rounded-lg p-4 text-center`},Ul={class:`bg-gray-800/50 rounded-lg p-4 text-center`},Wl={class:`bg-gray-800/50 rounded-lg p-4 text-center`},Gl={class:`bg-gray-800/50 rounded-lg p-4 text-center`},Kl={class:`text-2xl font-bold text-blue-400`},ql={class:`mt-4 flex gap-2`},Jl=[`disabled`],Yl={key:1,class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},Xl={class:`flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-4`},Zl={class:`flex items-center gap-2`},Ql={class:`grid grid-cols-2 md:grid-cols-5 gap-4`},$l={class:`text-center`},eu={class:`text-3xl font-bold text-white`},tu={class:`text-lg text-gray-500`},nu={class:`text-center`},ru={class:`text-center`},iu={class:`text-3xl font-bold text-green-400`},au={class:`text-xs text-gray-600 mt-1`},ou={class:`text-center`},su={class:`text-center`},cu={class:`text-3xl font-bold text-white`},lu={key:0,class:`mt-4`},uu={class:`bg-gray-800 rounded-full h-2 overflow-hidden`},du={class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},fu={class:`flex flex-col md:flex-row md:items-center md:justify-between gap-4`},pu={class:`flex items-baseline gap-4`},mu={class:`text-3xl font-bold text-white`},hu={class:`flex items-center gap-2`},gu=[`disabled`],_u=[`disabled`],vu=[`disabled`],yu={key:2,class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5 mb-6 md:mb-8`},bu={class:`flex flex-col md:flex-row md:items-center md:justify-between gap-4`},xu={class:`flex-1`},Su={class:`grid grid-cols-1 sm:grid-cols-2 gap-3`},Cu={class:`text-lg font-bold text-white`},wu=[`title`],Tu=[`disabled`],Eu={class:`grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-5`},Du={class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5`},Ou={class:`space-y-2`},ku=[`title`],Au={class:`flex-1 bg-gray-800 rounded-full h-4 md:h-5 overflow-hidden`},ju={class:`w-12 md:w-16 text-xs text-gray-500 text-right shrink-0`},Mu={class:`bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5`},Nu={class:`space-y-2`},Pu={class:`w-8 md:w-10 text-xs text-gray-400 shrink-0 font-mono`},Fu={class:`flex-1 bg-gray-800 rounded-full h-4 md:h-5 overflow-hidden`},Iu={class:`w-12 md:w-16 text-xs text-gray-500 text-right shrink-0`},Lu={__name:`Dashboard`,setup(e){let t=F(null),n=F(!0),r=F(!1),i=F(null),a=null,o=F(0),s=F(0),c=F(0),l=F(!1),u=F(null),d=F(``),f=F(!1),p=F(null),m=F(null),h=F(!1);ar(async()=>{await Promise.all([g(),_(),y(),b(),x()]),a=setInterval(()=>{g(),_(),y(),b(),x()},5e3)}),lr(()=>{a&&clearInterval(a)});async function g(){try{t.value=await ml(),i.value=null}catch(e){i.value=`无法加载统计数据,可能人服务器未启动或端口不对`,console.error(e)}finally{n.value=!1}}async function _(){try{let e=await vl();o.value=e.configured,s.value=e.active,c.value=e.configured}catch(e){console.error(`Failed to load workers:`,e)}}async function v(){let e=parseInt(c.value,10);if(isNaN(e)||e<1||e>500){i.value=`线程数必须在 1~500 之间`;return}l.value=!0;try{await yl(e),o.value=e,i.value=null}catch(e){i.value=`修改线程数失败: `+e.message}finally{l.value=!1}}async function y(){try{m.value=await bl()}catch(e){console.error(`Failed to load backlink status:`,e)}}async function b(){try{u.value=await Sl()}catch(e){console.error(`Failed to load priority status:`,e)}}async function x(){try{p.value=await Cl()}catch(e){console.error(`Failed to load crawl status:`,e)}}async function S(){h.value=!0;try{await xl(),m.value=await bl(),i.value=null}catch(e){i.value=`触发反链计算失败: `+e.message}finally{h.value=!1}}function C(e){if(!e)return`--`;let t=new Date(e),n=t-new Date,r=Math.floor(n/6e4),i=Math.floor(r/60),a=Math.floor(i/24),o=t.toLocaleTimeString(`zh-CN`,{hour:`2-digit`,minute:`2-digit`}),s=t.toLocaleDateString(`zh-CN`,{month:`short`,day:`numeric`});return a>0?`${s} ${o} (${a}天后)`:i>0?`${s} ${o} (${i}小时后)`:r>0?`${o} (${r}分钟后)`:`${o} (即将执行)`}function w(e){return e?new Date(e).toLocaleString(`zh-CN`,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`}):`从未执行`}async function ee(){r.value=!0;try{for(await gl();await new Promise(e=>setTimeout(e,1e3)),(await _l()).flushing;);t.value=await ml()}catch(e){i.value=`刷盘失败: `+e.message}finally{r.value=!1}}function T(e){return!e&&e!==0?`0`:Number(e).toLocaleString()}function te(e){return e?Object.entries(e).sort((e,t)=>t[1]-e[1]).slice(0,10):[]}function ne(e){let t={zh:`#e53e3e`,en:`#3182ce`,ja:`#e53e3e`,ko:`#3182ce`,fr:`#38a169`,de:`#d69e2e`,es:`#38a169`,ru:`#805ad5`,other:`#718096`};return t[e]||t.other}function re(e){try{let t=new URL(e);return t.protocol===`http:`||t.protocol===`https:`}catch{return!1}}async function E(){let e=d.value.split(` -`).map(e=>e.trim()).filter(e=>e).filter(re);if(e.length===0){i.value=`未检测到有效 URL`;return}f.value=!0;let t=0,n=0;try{for(let r of e)try{await hl(r),t++}catch{n++}d.value=``,await b(),i.value=null,n>0&&(i.value=`添加完成:${t} 成功,${n} 失败`)}finally{f.value=!1}}return(e,a)=>(H(),U(`div`,Dl,[a[31]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-6 md:mb-8`},`概览`,-1),n.value?(H(),U(`div`,Ol,[...a[4]||=[W(`div`,{class:`text-gray-400 animate-pulse`},`加载中...`,-1)]])):i.value?(H(),U(`div`,kl,k(i.value),1)):t.value?(H(),U(B,{key:2},[W(`div`,Al,[W(`div`,jl,[a[5]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`已爬取 URL`,-1),W(`div`,Ml,k(T(t.value.total_urls)),1)]),W(`div`,Nl,[a[6]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`总词数`,-1),W(`div`,Pl,k(T(t.value.total_words)),1)]),W(`div`,Fl,[a[7]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`域名数量`,-1),W(`div`,Il,k(T(t.value.total_domains)),1)]),W(`div`,Ll,[a[8]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`待重爬`,-1),W(`div`,{class:O([`text-3xl font-bold`,t.value.recrawl_eligible>0?`text-orange-400`:`text-green-400`])},k(T(t.value.recrawl_eligible)),3)]),W(`div`,Rl,[W(`div`,null,[a[9]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`待刷盘`,-1),W(`div`,{class:O([`text-3xl font-bold`,t.value.pending>0?`text-yellow-400`:`text-green-400`])},k(T(t.value.pending)),3)]),W(`button`,{class:`mt-3 w-full bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium py-1.5 px-3 rounded transition-colors cursor-pointer`,disabled:r.value||!t.value.pending,onClick:ee},k(r.value?`刷盘中...`:`立即刷盘`),9,zl)])]),u.value?(H(),U(`div`,Bl,[a[14]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-4`},`优先爬取队列`,-1),W(`div`,Vl,[W(`div`,Hl,[a[10]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`一级队列`,-1),W(`div`,{class:O([`text-2xl font-bold`,(u.value.level1||0)>0?`text-yellow-400`:`text-gray-500`])},k(T(u.value.level1||0)),3)]),W(`div`,Ul,[a[11]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`二级队列`,-1),W(`div`,{class:O([`text-2xl font-bold`,(u.value.level2_queue||0)+(u.value.level2_inflight||0)>0?`text-yellow-400`:`text-gray-500`])},k(T((u.value.level2_queue||0)+(u.value.level2_inflight||0))),3)]),W(`div`,Wl,[a[12]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`线程使用`,-1),W(`div`,{class:O([`text-2xl font-bold`,u.value.active>0?`text-orange-400`:`text-gray-500`])},k(T(u.value.active)),3)]),W(`div`,Gl,[a[13]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`线程总数`,-1),W(`div`,Kl,k(u.value.max_workers),1)])]),W(`div`,ql,[jn(W(`textarea`,{"onUpdate:modelValue":a[0]||=e=>d.value=e,rows:`3`,placeholder:`批量添加 URL(每行一个,仅发送有效链接)`,class:`flex-1 bg-gray-800 border border-gray-700 text-gray-200 text-sm rounded-lg px-3 py-2 resize-none focus:outline-none focus:border-blue-500 placeholder-gray-600`},null,512),[[so,d.value]]),W(`button`,{class:`bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 text-white text-sm font-medium px-4 py-2 rounded-lg transition-colors cursor-pointer whitespace-nowrap self-end`,disabled:f.value||!d.value.trim(),onClick:E},k(f.value?`添加中...`:`批量添加`),9,Jl)])])):G(``,!0),p.value&&p.value.max_epoch>0?(H(),U(`div`,Yl,[W(`div`,Xl,[a[16]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider`},`爬取进度`,-1),W(`div`,Zl,[a[15]||=W(`span`,{class:`text-xs text-gray-500`},`状态:`,-1),W(`span`,{class:O([`px-2 py-0.5 rounded text-xs font-medium`,p.value.is_running?`bg-green-900/50 text-green-400`:`bg-gray-800 text-gray-500`])},k(p.value.is_running?`运行中`:`已停止`),3)])]),W(`div`,Ql,[W(`div`,$l,[a[17]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`当前轮次`,-1),W(`div`,eu,[Ui(k(p.value.current_epoch),1),W(`span`,tu,`/`+k(p.value.max_epoch),1)])]),W(`div`,nu,[a[18]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`本轮队列`,-1),W(`div`,{class:O([`text-3xl font-bold`,p.value.queue_length>0?`text-blue-400`:`text-gray-500`])},k(T(p.value.queue_length)),3)]),W(`div`,ru,[a[19]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`已完成`,-1),W(`div`,iu,k(T(p.value.completed_count)),1),W(`div`,au,k(p.value.queue_length>0?Math.round(p.value.completed_count/p.value.queue_length*100)+`%`:`--`),1)]),W(`div`,ou,[a[20]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`本轮新发现`,-1),W(`div`,{class:O([`text-3xl font-bold`,p.value.new_links_count>0?`text-purple-400`:`text-gray-500`])},k(T(p.value.new_links_count)),3)]),W(`div`,su,[a[21]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`已收录总计`,-1),W(`div`,cu,k(T(p.value.visited_total)),1)])]),p.value.queue_length>0?(H(),U(`div`,lu,[W(`div`,uu,[W(`div`,{class:`h-full bg-gradient-to-r from-blue-600 to-green-500 rounded-full transition-all duration-500`,style:pe({width:`${p.value.completed_count/p.value.queue_length*100}%`})},null,4)])])):G(``,!0)])):G(``,!0),W(`div`,du,[W(`div`,fu,[W(`div`,null,[a[25]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-2`},`爬虫线程`,-1),W(`div`,pu,[W(`div`,null,[a[22]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`实际运行`,-1),W(`div`,{class:O([`text-3xl font-bold`,s.value>0?`text-green-400`:`text-gray-500`])},k(s.value),3)]),a[24]||=W(`div`,{class:`text-gray-700 text-xl`},`/`,-1),W(`div`,null,[a[23]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`设定上限`,-1),W(`div`,mu,k(o.value),1)])])]),W(`div`,hu,[W(`button`,{class:`w-9 h-9 flex items-center justify-center rounded-lg bg-gray-800 hover:bg-gray-700 text-gray-300 hover:text-white text-lg font-bold transition-colors cursor-pointer`,onClick:a[1]||=e=>c.value=Math.max(1,c.value-1),disabled:l.value},`−`,8,gu),jn(W(`input`,{type:`number`,"onUpdate:modelValue":a[2]||=e=>c.value=e,min:`1`,max:`500`,class:`w-20 h-9 text-center bg-gray-800 border border-gray-700 rounded-lg text-white text-sm focus:outline-none focus:border-blue-500 transition-colors [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none`,onKeyup:go(v,[`enter`])},null,544),[[so,c.value,void 0,{number:!0}]]),W(`button`,{class:`w-9 h-9 flex items-center justify-center rounded-lg bg-gray-800 hover:bg-gray-700 text-gray-300 hover:text-white text-lg font-bold transition-colors cursor-pointer`,onClick:a[3]||=e=>c.value=Math.min(500,c.value+1),disabled:l.value},`+`,8,_u),W(`button`,{class:`h-9 px-4 bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium rounded-lg transition-colors cursor-pointer`,disabled:l.value||c.value===o.value,onClick:v},k(l.value?`保存中...`:`应用`),9,vu)])])]),m.value?(H(),U(`div`,yu,[W(`div`,bu,[W(`div`,xu,[a[28]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-3`},`反链计算(PageRank)`,-1),W(`div`,Su,[W(`div`,null,[a[26]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`下次自动执行`,-1),W(`div`,{class:O([`text-lg font-bold`,m.value.running?`text-yellow-400`:`text-white`])},k(m.value.running?`计算中...`:C(m.value.next_run)),3)]),W(`div`,null,[a[27]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`上次完成`,-1),W(`div`,Cu,k(w(m.value.last_run)),1)])]),m.value.last_error?(H(),U(`div`,{key:0,class:`mt-2 text-xs text-red-400 truncate`,title:m.value.last_error},` 上次错误:`+k(m.value.last_error),9,wu)):G(``,!0)]),W(`button`,{class:`h-9 px-4 bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium rounded-lg transition-colors cursor-pointer whitespace-nowrap`,disabled:h.value||m.value.running,onClick:S},k(h.value?`已触发...`:m.value.running?`计算中...`:`立即执行`),9,Tu)])])):G(``,!0),W(`div`,Eu,[W(`div`,Du,[a[29]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 mb-3 md:mb-4 uppercase tracking-wider`},`域名分布 Top 10`,-1),W(`div`,Ou,[(H(!0),U(B,null,L(te(t.value.domains),([e,n])=>(H(),U(`div`,{key:e,class:`flex items-center gap-2 md:gap-3`},[W(`div`,{class:`w-24 md:w-36 text-xs text-gray-400 truncate shrink-0`,title:e},k(e),9,ku),W(`div`,Au,[W(`div`,{class:`h-full bg-blue-600 rounded-full transition-all duration-500`,style:pe({width:`${n/t.value.domains[Object.keys(t.value.domains)[0]]*100}%`})},null,4)]),W(`div`,ju,k(T(n)),1)]))),128))])]),W(`div`,Mu,[a[30]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 mb-3 md:mb-4 uppercase tracking-wider`},`语种分布`,-1),W(`div`,Nu,[(H(!0),U(B,null,L(Object.entries(t.value.languages||{}).sort((e,t)=>t[1]-e[1]),([e,n])=>(H(),U(`div`,{key:e,class:`flex items-center gap-2 md:gap-3`},[W(`div`,Pu,k(e),1),W(`div`,Fu,[W(`div`,{class:`h-full rounded-full transition-all duration-500`,style:pe({width:`${n/t.value.total_urls*100}%`,backgroundColor:ne(e)})},null,4)]),W(`div`,Iu,k(T(n)),1)]))),128))])])])],64)):G(``,!0)]))}},Ru={class:`p-4 md:p-8`},zu={class:`flex flex-col md:flex-row md:items-center justify-between mb-4 md:mb-6 gap-3`},Bu={class:`text-sm text-gray-500`},Vu={class:`flex items-center gap-2 md:gap-3`},Hu=[`value`],Uu={class:`flex flex-col sm:flex-row items-stretch sm:items-center gap-3 mb-4 md:mb-5`},Wu={class:`relative flex-1 max-w-full sm:max-w-sm`},Gu={key:0,class:`flex items-center justify-center h-48`},Ku={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},qu={key:2,class:`bg-gray-900 border border-gray-800 rounded-xl overflow-hidden`},Ju={class:`hidden md:table w-full text-sm`},Yu={class:`px-5 py-3.5`},Xu=[`href`],Zu={class:`font-medium text-gray-200 group-hover:text-white line-clamp-2`},Qu={class:`text-xs text-gray-600 mt-0.5 break-all line-clamp-1`},$u={key:0,class:`text-xs text-gray-500 mt-1 line-clamp-1`},ed=[`onClick`],td={key:0,class:`text-gray-500`},nd={key:1,class:`mt-2`},rd={class:`flex flex-wrap gap-1.5`},id=[`title`],ad={class:`text-gray-500 text-[10px] ml-0.5`},od={class:`text-xs text-gray-600 mt-1`},sd={key:1,class:`text-xs text-gray-600`},cd={class:`px-5 py-3.5`},ld={class:`text-gray-400 text-xs font-mono`},ud={class:`px-5 py-3.5`},dd={key:1,class:`text-xs text-gray-600`},fd={class:`px-5 py-3.5 text-gray-500 text-xs tabular-nums`},pd={class:`px-5 py-3.5 text-gray-500 text-xs`},md={key:0},hd={class:`md:hidden divide-y divide-gray-800`},gd=[`href`],_d={class:`font-medium text-gray-200 text-sm line-clamp-2 mb-1`},vd={class:`text-xs text-gray-500 break-all line-clamp-1 mb-2`},yd={class:`flex items-center gap-2 text-xs mb-2`},bd={class:`text-gray-400 font-mono`},xd={class:`text-gray-600 ml-auto`},Sd=[`onClick`],Cd={key:0,class:`text-gray-500`},wd={key:0,class:`mb-2`},Td={class:`flex flex-wrap gap-1`},Ed=[`title`],Dd={class:`text-gray-500 text-[9px] ml-0.5`},Od={class:`text-[10px] text-gray-600 mt-1`},kd={key:1,class:`text-xs text-gray-600`},Ad={key:0,class:`p-8 text-center text-gray-600`},jd={key:3,class:`mt-3 text-xs text-gray-600 text-right pb-4 md:pb-0`},Md={__name:`RecentCrawls`,setup(e){let t=F([]),n=F(0),r=F(!0),i=F(null),a=F(``),o=F(``),s=[20,50,100,200],c=F(50),l=F(new Set),u=F({}),d=F(new Set);ar(async()=>{await f()});async function f(){r.value=!0,i.value=null;try{let e=await pl(c.value);t.value=e.items||[],n.value=e.total||0}catch(e){i.value=`无法加载数据,可能人服务器未启动`,console.error(e)}finally{r.value=!1}}async function p(e){c.value=e,await f()}let m=ga(()=>{let e=t.value;if(a.value){let t=a.value.toLowerCase();e=e.filter(e=>e.title?.toLowerCase().includes(t)||e.url?.toLowerCase().includes(t)||e.domain?.toLowerCase().includes(t))}return o.value&&(e=e.filter(e=>Object.keys(e.language||{}).includes(o.value))),e});function h(e){return e?new Date(e*1e3).toLocaleString(`zh-CN`,{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1}):`-`}function g(e){return{zh:{label:`中文`,cls:`bg-red-900/60 text-red-300`},en:{label:`EN`,cls:`bg-blue-900/60 text-blue-300`},ja:{label:`日`,cls:`bg-pink-900/60 text-pink-300`},ko:{label:`한`,cls:`bg-blue-900/60 text-blue-300`},fr:{label:`FR`,cls:`bg-green-900/60 text-green-300`},de:{label:`DE`,cls:`bg-yellow-900/60 text-yellow-300`},es:{label:`ES`,cls:`bg-green-900/60 text-green-300`},ru:{label:`RU`,cls:`bg-purple-900/60 text-purple-300`}}[e]||{label:e,cls:`bg-gray-800 text-gray-400`}}function _(e){return e?Object.entries(e).sort((e,t)=>t[1]-e[1])[0]:null}async function v(e){if(l.value.has(e)){l.value.delete(e);return}if(l.value.add(e),!u.value[e]){d.value.add(e);try{let t=await wl(e);u.value[e]=t.keywords||[]}catch(t){console.error(`Failed to load keywords:`,t),u.value[e]=[]}finally{d.value.delete(e)}}}return(e,t)=>(H(),U(`div`,Ru,[W(`div`,zu,[W(`div`,null,[t[4]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-1`},`最近爬取`,-1),W(`p`,Bu,`共 `+k(n.value.toLocaleString())+` 条记录`,1)]),W(`div`,Vu,[jn(W(`select`,{"onUpdate:modelValue":t[0]||=e=>c.value=e,onChange:t[1]||=e=>p(c.value),class:`bg-gray-900 border border-gray-700 text-gray-300 text-sm rounded-lg px-3 py-2 focus:border-blue-500 focus:outline-none`},[(H(),U(B,null,L(s,e=>W(`option`,{key:e,value:e},`显示 `+k(e)+` 条`,9,Hu)),64))],544),[[co,c.value]]),W(`button`,{onClick:f,class:`bg-blue-600 hover:bg-blue-700 text-white text-sm px-4 py-2 rounded-lg transition-colors`},` 刷新 `)])]),W(`div`,Uu,[W(`div`,Wu,[jn(W(`input`,{"onUpdate:modelValue":t[2]||=e=>a.value=e,type:`text`,placeholder:`搜索标题、URL、域名...`,class:`w-full bg-gray-900 border border-gray-700 text-gray-200 text-sm rounded-lg pl-10 pr-4 py-2 focus:border-blue-500 focus:outline-none placeholder-gray-600`},null,512),[[so,a.value]]),t[5]||=W(`span`,{class:`absolute left-3 top-1/2 -translate-y-1/2 text-gray-500`},`🔍`,-1)]),jn(W(`select`,{"onUpdate:modelValue":t[3]||=e=>o.value=e,class:`bg-gray-900 border border-gray-700 text-gray-300 text-sm rounded-lg px-3 py-2 focus:border-blue-500 focus:outline-none`},[...t[6]||=[Wi(``,9)]],512),[[co,o.value]])]),r.value?(H(),U(`div`,Gu,[...t[7]||=[W(`div`,{class:`text-gray-400 animate-pulse`},`加载中...`,-1)]])):i.value?(H(),U(`div`,Ku,k(i.value),1)):(H(),U(`div`,qu,[W(`table`,Ju,[t[9]||=W(`thead`,null,[W(`tr`,{class:`border-b border-gray-800`},[W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider`},`标题`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-28`},`域名`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-16`},`语种`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-20`},`字数`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-48`},`爬取时间`)])],-1),W(`tbody`,null,[(H(!0),U(B,null,L(m.value,e=>(H(),U(`tr`,{key:e.url,class:`border-b border-gray-800/50 hover:bg-gray-800/40 transition-colors group`},[W(`td`,Yu,[W(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,class:`block hover:opacity-80 transition-opacity`},[W(`div`,Zu,k(e.title||`(无标题)`),1),W(`div`,Qu,k(e.url),1)],8,Xu),e.description?(H(),U(`div`,$u,k(e.description),1)):G(``,!0),W(`button`,{onClick:mo(t=>v(e.url),[`prevent`]),class:`mt-2 text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1`},[W(`span`,null,k(l.value.has(e.url)?`▼`:`▶`)+` 关键词`,1),d.value.has(e.url)?(H(),U(`span`,td,`加载中...`)):G(``,!0)],8,ed),l.value.has(e.url)?(H(),U(`div`,nd,[u.value[e.url]?.length?(H(),U(B,{key:0},[W(`div`,rd,[(H(!0),U(B,null,L(u.value[e.url],e=>(H(),U(`span`,{key:e.word,class:`text-xs px-2 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700`,title:`权重: ${e.weight.toFixed(4)}`},[Ui(k(e.word)+` `,1),W(`span`,ad,k(e.weight.toFixed(2)),1)],8,id))),128))]),W(`div`,od,` 共 `+k(u.value[e.url].length)+` 个关键词 `,1)],64)):d.value.has(e.url)?G(``,!0):(H(),U(`span`,sd,` 暂无关键词(服务重启后缓存已清空) `))])):G(``,!0)]),W(`td`,cd,[W(`span`,ld,k(e.domain),1)]),W(`td`,ud,[_(e.language)?(H(),U(`span`,{key:0,class:O([`text-xs px-1.5 py-0.5 rounded font-medium`,g(_(e.language)[0]).cls])},k(g(_(e.language)[0]).label)+` `+k((_(e.language)[1]*100).toFixed(0))+`% `,3)):(H(),U(`span`,dd,`-`))]),W(`td`,fd,k(e.word_count.toLocaleString()),1),W(`td`,pd,k(h(e.crawled_at)),1)]))),128)),m.value.length?G(``,!0):(H(),U(`tr`,md,[...t[8]||=[W(`td`,{colspan:`5`,class:`px-5 py-12 text-center text-gray-600`},` 没有找到匹配的记录 `,-1)]]))])]),W(`div`,hd,[(H(!0),U(B,null,L(m.value,e=>(H(),U(`div`,{key:e.url,class:`block p-4 hover:bg-gray-800/40 transition-colors`},[W(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,class:`block`},[W(`div`,_d,k(e.title||`(无标题)`),1),W(`div`,vd,k(e.url),1)],8,gd),W(`div`,yd,[W(`span`,bd,k(e.domain),1),_(e.language)?(H(),U(`span`,{key:0,class:O([`px-1.5 py-0.5 rounded font-medium`,g(_(e.language)[0]).cls])},k(g(_(e.language)[0]).label),3)):G(``,!0),W(`span`,xd,k(h(e.crawled_at)),1)]),W(`button`,{onClick:mo(t=>v(e.url),[`prevent`]),class:`text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1 mb-2`},[W(`span`,null,k(l.value.has(e.url)?`▼`:`▶`)+` 关键词`,1),d.value.has(e.url)?(H(),U(`span`,Cd,`加载中...`)):G(``,!0)],8,Sd),l.value.has(e.url)?(H(),U(`div`,wd,[u.value[e.url]?.length?(H(),U(B,{key:0},[W(`div`,Td,[(H(!0),U(B,null,L(u.value[e.url],e=>(H(),U(`span`,{key:e.word,class:`text-[10px] px-1.5 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700`,title:`权重: ${e.weight.toFixed(4)}`},[Ui(k(e.word)+` `,1),W(`span`,Dd,k(e.weight.toFixed(2)),1)],8,Ed))),128))]),W(`div`,Od,` 共 `+k(u.value[e.url].length)+` 个 `,1)],64)):d.value.has(e.url)?G(``,!0):(H(),U(`span`,kd,` 暂无关键词 `))])):G(``,!0)]))),128)),m.value.length?G(``,!0):(H(),U(`div`,Ad,` 没有找到匹配的记录 `))])])),!r.value&&!i.value&&m.value.length?(H(),U(`div`,jd,` 筛选后 `+k(m.value.length)+` 条 / 共 `+k(n.value.toLocaleString())+` 条 `,1)):G(``,!0)]))}},Nd={class:`flex flex-col h-full`},Pd={class:`bg-gray-950 border-b border-gray-800 px-4 md:px-8 py-4 md:py-6`},Fd={class:`max-w-3xl mx-auto`},Id={class:`relative`},Ld={class:`flex-1 overflow-y-auto px-8 py-6`},Rd={class:`max-w-3xl mx-auto`},zd={key:0,class:`flex items-center gap-3 text-gray-400 py-8`},Bd={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},Vd={key:2,class:`py-16 text-center text-gray-600`},Hd={key:3,class:`py-16 text-center text-gray-600`},Ud={key:4,class:`flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4 mb-4 md:mb-5 text-sm text-gray-500`},Wd={class:`flex items-center gap-2`},Gd={class:`text-gray-300`},Kd={class:`flex flex-wrap gap-2 sm:ml-auto`},qd={class:`text-gray-300`},Jd={key:5,class:`space-y-2 md:space-y-1`},Yd=[`onClick`],Xd={class:`flex items-start gap-2 md:gap-3 mb-2`},Zd={class:`flex-1 min-w-0`},Qd={class:`text-blue-400 group-hover:text-blue-300 text-base md:text-lg leading-snug`},$d={class:`text-xs text-gray-600 mt-0.5 truncate`},ef={class:`flex flex-col items-end gap-1 shrink-0`},tf={class:`text-xs text-gray-600`},nf={class:`w-12 md:w-14 bg-gray-800 rounded-full h-1.5 overflow-hidden`},rf={class:`text-gray-400 text-sm leading-relaxed mb-2 md:mb-3`},af={class:`flex flex-wrap items-center gap-2 md:gap-3 text-xs`},of={class:`flex flex-wrap gap-1.5`},sf=[`title`],cf={class:`text-blue-400`},lf={class:`text-gray-600 ml-1`},uf={key:0,class:`text-gray-600`},df={class:`text-gray-700 ml-0 md:ml-auto`},ff={class:`text-xs text-gray-700 mt-1 truncate`},pf={key:6,class:`mt-6 text-center`},mf=``,hf=10,gf={__name:`SearchView`,setup(e){let t=F(``),n=F([]),r=F(0),i=F({}),a=F(!1),o=F(null),s=F(0),c=null;async function l(e,t=!1){if(!e.trim()){n.value=[],r.value=0,i.value={};return}t||(s.value=0),a.value=!0,o.value=null;try{let a=encodeURIComponent(e),o=s.value*hf,c=await fetch(`${mf}/search?qh=${a}&slice=${o}:${o+hf}`);if(!c.ok)throw Error(`HTTP ${c.status}`);let l=await c.json();t?n.value=[...n.value,...l.results||[]]:n.value=l.results||[],r.value=l.total||0,i.value=l.counts||{}}catch(e){o.value=`搜索失败,可能是人服务器未启动`,console.error(e)}finally{a.value=!1}}function u(){clearTimeout(c),c=setTimeout(()=>l(t.value),400)}function d(e){e.key===`Enter`&&(clearTimeout(c),l(t.value))}function f(e,t=120){return e?e.length>t?e.slice(0,t)+`…`:e:``}function p(e){return e?e.toLocaleString():`0`}function m(e){return Math.min(100,Math.round(e*100))}function h(e){return{zh:`中文`,en:`EN`,ja:`JA`,ko:`KO`,fr:`FR`,de:`DE`,es:`ES`,ru:`RU`}[e]||e?.toUpperCase()}function g(e){try{return new URL(e).hostname}catch{return e}}function _(e){window.open(e,`_blank`)}return(e,c)=>(H(),U(`div`,Nd,[W(`div`,Pd,[W(`div`,Fd,[W(`div`,Id,[jn(W(`input`,{"onUpdate:modelValue":c[0]||=e=>t.value=e,onInput:u,onKeydown:d,type:`text`,placeholder:`输入关键词搜索,或用 site:example.com 限定域名`,class:`w-full bg-gray-900 border border-gray-700 rounded-xl md:rounded-2xl px-4 md:px-6 py-3 md:py-4 pr-20 md:pr-14 text-white text-base md:text-lg placeholder-gray-600 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition`},null,544),[[so,t.value]]),W(`button`,{onClick:c[1]||=e=>l(t.value),class:`absolute right-2 md:right-3 top-1/2 -translate-y-1/2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg md:rounded-xl px-3 md:px-4 py-1.5 md:py-2 text-sm font-medium transition`},` 搜索 `)])])]),W(`div`,Ld,[W(`div`,Rd,[a.value?(H(),U(`div`,zd,[...c[3]||=[W(`div`,{class:`w-5 h-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin`},null,-1),W(`span`,null,`搜索中...`,-1)]])):o.value?(H(),U(`div`,Bd,k(o.value),1)):t.value.trim()?n.value.length===0&&!a.value?(H(),U(`div`,Hd,` 未找到相关结果 `)):n.value.length>0?(H(),U(`div`,Ud,[W(`div`,Wd,[W(`span`,null,[c[4]||=Ui(`找到约 `,-1),W(`strong`,Gd,k(p(r.value)),1),c[5]||=Ui(` 条结果`,-1)]),c[6]||=W(`span`,{class:`text-gray-700`},`|`,-1),W(`span`,null,k(n.value.length)+` 条已加载`,1)]),W(`div`,Kd,[(H(!0),U(B,null,L(i.value,(e,t)=>(H(),U(`span`,{key:t,class:`inline-flex items-center gap-1 bg-gray-800 rounded px-2 py-0.5 text-xs text-gray-400`},[W(`span`,qd,k(t),1),W(`span`,null,k(p(e)),1)]))),128))])])):G(``,!0):(H(),U(`div`,Vd,` 输入关键词开始搜索 `)),n.value.length>0?(H(),U(`div`,Jd,[(H(!0),U(B,null,L(n.value,(e,t)=>(H(),U(`div`,{key:t,onClick:t=>_(e.url),class:`group block bg-gray-900/50 hover:bg-gray-900 border border-gray-800 hover:border-gray-700 rounded-xl p-4 md:p-5 cursor-pointer transition`},[W(`div`,Xd,[W(`div`,Zd,[W(`div`,Qd,k(e.snippet?.title||g(e.url)),1),W(`div`,$d,k(g(e.url)),1)]),W(`div`,ef,[W(`div`,tf,k(m(e.score))+`%`,1),W(`div`,nf,[W(`div`,{class:`h-full bg-blue-500 rounded-full`,style:pe({width:m(e.score)+`%`})},null,4)])])]),W(`p`,rf,k(f(e.snippet?.description||e.snippet?.text)),1),W(`div`,af,[W(`div`,of,[(H(!0),U(B,null,L(e.relevance,(e,t)=>(H(),U(`span`,{key:t,class:`inline-flex items-center bg-gray-800 rounded px-1.5 py-0.5`,title:`${t}: ${(e*100).toFixed(1)}%`},[W(`span`,cf,k(t),1),W(`span`,lf,k((e*100).toFixed(0))+`%`,1)],8,sf))),128))]),e.snippet?.language?(H(),U(`span`,uf,k(h(e.snippet.language)),1)):G(``,!0),W(`span`,df,k(p(e.domain_count))+` 个结果`,1)]),W(`div`,ff,k(e.url),1)],8,Yd))),128))])):G(``,!0),n.value.length>0&&n.value.length{s.value++,l(t.value,!0)},class:`bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-xl px-6 py-2.5 text-sm transition`},` 加载更多 (`+k(n.value.length)+`/`+k(p(r.value))+`) `,1)])):G(``,!0)])])]))}},_f={class:`p-4 md:p-8`},vf={class:`flex flex-col md:flex-row md:items-center justify-between mb-4 md:mb-6 gap-3`},yf={class:`text-sm text-gray-500`},bf={class:`flex items-center gap-3`},xf={class:`bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 flex items-center gap-3`},Sf={class:`flex flex-col`},Cf={class:`text-sm font-medium text-gray-300`},wf={class:`w-12 h-12 relative`},Tf={viewBox:`0 0 36 36`,class:`w-full h-full transform -rotate-90`},Ef=[`stroke-dasharray`],Df={class:`absolute inset-0 flex items-center justify-center text-[8px] font-medium text-gray-400`},Of={class:`mb-4 md:mb-5`},kf={class:`relative max-w-sm`},Af={key:0,class:`flex items-center justify-center h-48`},jf={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},Mf={key:2,class:`bg-gray-900 border border-gray-800 rounded-xl p-12 text-center`},Nf={key:3,class:`bg-gray-900 border border-gray-800 rounded-xl overflow-hidden`},Pf={class:`divide-y divide-gray-800`},Ff=[`onClick`],If={class:`flex items-start gap-2 mb-1`},Lf={class:`shrink-0 text-gray-600 mt-0.5 text-xs select-none`},Rf=[`href`,`title`],zf={key:0,class:`text-sm text-gray-300 font-medium mb-1`},Bf={key:1,class:`text-xs text-gray-500 leading-relaxed mb-2`},Vf={key:2,class:`mt-2`},Hf={class:`flex flex-wrap gap-1.5`},Uf=[`title`],Wf={class:`text-gray-500 text-[10px] ml-0.5`},Gf={class:`text-xs text-gray-600 mt-2`},Kf={key:1,class:`text-xs text-gray-600`},qf={key:2,class:`text-xs text-gray-500`},Jf={class:`px-4 py-3 border-t border-gray-800 flex items-center justify-between`},Yf={class:`text-xs text-gray-500`},Xf={class:`flex items-center gap-1`},Zf=[`disabled`],Qf=[`onClick`],$f={key:0,class:`px-1 py-1 text-xs text-gray-600`},ep=[`disabled`],tp={__name:`KeywordsCache`,setup(e){let t=F(!0),n=F(null),r=F({size:0,max_size:1e4}),i=F([]),a=F(new Set),o=F({}),s=F(new Set),c=F(``),l=F(0),u=F(1),d=F(50),f=null;ar(async()=>{await m(),f=setInterval(p,5e3)}),lr(()=>{f&&clearInterval(f)});async function p(){try{let e=await Tl();r.value.size=e.size||0,r.value.max_size=e.max_size||1e4}catch{}}async function m(){t.value=!0,n.value=null;try{let e=await El({page:u.value,page_size:d.value});r.value.size=e.size||0,r.value.max_size=e.max_size||1e4,l.value=e.total||0,i.value=e.items||[],i.value.forEach(e=>a.value.add(e.url)),i.value.forEach(e=>{e.keywords?.length&&(o.value[e.url]=e.keywords)})}catch(e){n.value=`无法加载缓存数据`,console.error(e)}finally{t.value=!1}}let h=ga(()=>r.value.max_size?r.value.size/r.value.max_size:0),g=ga(()=>Math.max(1,Math.ceil(l.value/d.value)));function _(){u.value>1&&(u.value--,m())}function v(){u.value=1&&e<=g.value&&e!==u.value&&(u.value=e,m())}function b(){u.value=1,m()}async function x(e){if(a.value.has(e)){a.value.delete(e);return}if(a.value.add(e),!o.value[e]){s.value.add(e);try{let t=await wl(e);o.value[e]=t.keywords||[]}catch{o.value[e]=[]}finally{s.value.delete(e)}}}function S(e,t=80){return e.length<=t?e:e.slice(0,t)+`...`}function C(e,t=200){return e?e.length<=t?e:e.slice(0,t)+`...`:``}return(e,f)=>(H(),U(`div`,_f,[W(`div`,vf,[W(`div`,null,[f[3]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-1`},`关键词缓存`,-1),W(`p`,yf,`LRU 缓存,共 `+k(r.value.size.toLocaleString())+` 条记录`,1)]),W(`div`,bf,[W(`div`,xf,[W(`div`,Sf,[f[4]||=W(`span`,{class:`text-[10px] text-gray-500 uppercase`},`容量`,-1),W(`span`,Cf,k(r.value.size.toLocaleString())+` / `+k(r.value.max_size.toLocaleString()),1)]),W(`div`,wf,[(H(),U(`svg`,Tf,[f[5]||=W(`path`,{class:`text-gray-800`,d:`M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831`,fill:`none`,stroke:`currentColor`,"stroke-width":`4`},null,-1),W(`path`,{class:O(h.value>.9?`text-red-500`:h.value>.7?`text-yellow-500`:`text-green-500`),d:`M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831`,fill:`none`,stroke:`currentColor`,"stroke-width":`4`,"stroke-dasharray":`${(h.value*100).toFixed(0)}, 100`},null,10,Ef)])),W(`span`,Df,k((h.value*100).toFixed(0))+`% `,1)])]),W(`button`,{onClick:m,class:`bg-blue-600 hover:bg-blue-700 text-white text-sm px-4 py-2 rounded-lg transition-colors`},` 刷新 `)])]),W(`div`,Of,[W(`div`,kf,[jn(W(`input`,{"onUpdate:modelValue":f[0]||=e=>c.value=e,onInput:b,type:`text`,placeholder:`搜索 URL、标题或摘要...`,class:`w-full bg-gray-900 border border-gray-700 text-gray-200 text-sm rounded-lg pl-10 pr-4 py-2 focus:border-blue-500 focus:outline-none placeholder-gray-600`},null,544),[[so,c.value]]),f[6]||=W(`span`,{class:`absolute left-3 top-1/2 -translate-y-1/2 text-gray-500`},`🔍`,-1)])]),t.value?(H(),U(`div`,Af,[...f[7]||=[W(`div`,{class:`text-gray-400 animate-pulse`},`加载中...`,-1)]])):n.value?(H(),U(`div`,jf,k(n.value),1)):i.value.length?(H(),U(`div`,Nf,[W(`div`,Pf,[(H(!0),U(B,null,L(i.value,e=>(H(),U(`div`,{key:e.url,class:`p-4 hover:bg-gray-800/40 transition-colors cursor-pointer`,onClick:t=>x(e.url)},[W(`div`,If,[W(`span`,Lf,k(a.value.has(e.url)?`▼`:`▶`),1),W(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,class:`text-sm text-blue-400 hover:text-blue-300 break-all line-clamp-2`,title:e.url,onClick:f[1]||=mo(()=>{},[`stop`])},k(S(e.url)),9,Rf)]),e.title?(H(),U(`div`,zf,k(e.title),1)):G(``,!0),e.snippet?(H(),U(`div`,Bf,k(C(e.snippet)),1)):G(``,!0),a.value.has(e.url)?(H(),U(`div`,Vf,[o.value[e.url]?.length?(H(),U(B,{key:0},[W(`div`,Hf,[(H(!0),U(B,null,L(o.value[e.url],e=>(H(),U(`span`,{key:e.word,class:`text-xs px-2 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700`,title:`权重: ${e.weight.toFixed(4)}`},[Ui(k(e.word)+` `,1),W(`span`,Wf,k(e.weight.toFixed(2)),1)],8,Uf))),128))]),W(`div`,Gf,` 共 `+k(o.value[e.url].length)+` 个关键词 `,1)],64)):s.value.has(e.url)?(H(),U(`span`,qf,` 加载中... `)):(H(),U(`span`,Kf,` 暂无关键词 `))])):G(``,!0)],8,Ff))),128))]),W(`div`,Jf,[W(`div`,Yf,` 第 `+k((u.value-1)*d.value+1)+`-`+k(Math.min(u.value*d.value,l.value))+` 条 / 共 `+k(l.value.toLocaleString())+` 条 `,1),W(`div`,Xf,[W(`button`,{onClick:_,disabled:u.value===1,class:`px-2 py-1 text-xs rounded bg-gray-800 text-gray-400 hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed`},` ‹ `,8,Zf),(H(!0),U(B,null,L(g.value<=5?g.value:(()=>{let e=[],t=Math.max(1,u.value-2),n=Math.min(g.value,t+4);for(let r=t;r<=n;r++)e.push(r);return e})(),e=>(H(),U(`button`,{key:e,onClick:t=>y(e),class:O([`px-2 py-1 text-xs rounded`,e===u.value?`bg-blue-600 text-white`:`bg-gray-800 text-gray-400 hover:bg-gray-700`])},k(e),11,Qf))),128)),g.value>5&&u.value5&&u.valuey(g.value),class:`px-2 py-1 text-xs rounded bg-gray-800 text-gray-400 hover:bg-gray-700`},k(g.value),1)):G(``,!0),W(`button`,{onClick:v,disabled:u.value===g.value,class:`px-2 py-1 text-xs rounded bg-gray-800 text-gray-400 hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed`},` › `,8,ep)])])])):(H(),U(`div`,Mf,[...f[8]||=[W(`div`,{class:`text-4xl mb-3`},`📭`,-1),W(`div`,{class:`text-gray-400`},`缓存为空`,-1),W(`div`,{class:`text-xs text-gray-600 mt-1`},`爬取页面时会自动填充此缓存`,-1)]]))]))}},np={class:`flex h-screen bg-gray-950 text-gray-100 font-sans`},rp={class:`hidden md:flex w-56 bg-gray-900 border-r border-gray-800 flex-col shrink-0`},ip={class:`flex-1 py-4 px-3 space-y-1`},ap=[`onClick`],op={class:`flex-1 overflow-y-auto pb-16 md:pb-0`},sp={class:`md:hidden fixed bottom-0 left-0 right-0 bg-gray-900 border-t border-gray-800 flex justify-around items-center h-16 z-50`},cp=[`onClick`],lp={class:`text-lg`},up={class:`text-[10px]`};bo({__name:`App`,setup(e){let t=F(`dashboard`),n=[{id:`dashboard`,label:`概览`,icon:`📊`},{id:`recent`,label:`最近`,icon:`🕷️`},{id:`keywords`,label:`缓存`,icon:`💾`},{id:`search`,label:`搜索`,icon:`🔍`}];return(e,r)=>(H(),U(`div`,np,[W(`aside`,rp,[r[0]||=W(`div`,{class:`px-5 py-5 border-b border-gray-800`},[W(`div`,{class:`text-lg font-semibold text-white tracking-tight`},`SESE Admin`),W(`div`,{class:`text-xs text-gray-500 mt-0.5`},`爬取内容监控`)],-1),W(`nav`,ip,[(H(),U(B,null,L(n,e=>W(`button`,{key:e.id,onClick:n=>t.value=e.id,class:O([`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors`,t.value===e.id?`bg-blue-600 text-white`:`text-gray-400 hover:text-white hover:bg-gray-800`])},[W(`span`,null,k(e.icon),1),Ui(` `+k(e.label),1)],10,ap)),64))]),r[1]||=W(`div`,{class:`px-5 py-4 border-t border-gray-800`},[W(`div`,{class:`text-xs text-gray-600`},`sese-engine v1.0`)],-1)]),W(`main`,op,[t.value===`dashboard`?(H(),Pi(Lu,{key:0})):t.value===`recent`?(H(),Pi(Md,{key:1})):t.value===`keywords`?(H(),Pi(tp,{key:2})):t.value===`search`?(H(),Pi(gf,{key:3})):G(``,!0)]),W(`nav`,sp,[(H(),U(B,null,L(n,e=>W(`button`,{key:e.id,onClick:n=>t.value=e.id,class:O([`flex flex-col items-center justify-center gap-0.5 flex-1 h-full transition-colors`,t.value===e.id?`text-blue-400`:`text-gray-500`])},[W(`span`,lp,k(e.icon),1),W(`span`,up,k(e.label),1)],10,cp)),64))])]))}}).mount(`#app`); \ No newline at end of file +`).map(e=>e.trim()).filter(e=>e).filter(re);if(e.length===0){i.value=`未检测到有效 URL`;return}f.value=!0;let t=0,n=0;try{for(let r of e)try{await hl(r),t++}catch{n++}d.value=``,await b(),i.value=null,n>0&&(i.value=`添加完成:${t} 成功,${n} 失败`)}finally{f.value=!1}}return(e,a)=>(H(),U(`div`,Dl,[a[31]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-6 md:mb-8`},`概览`,-1),n.value?(H(),U(`div`,Ol,[...a[4]||=[W(`div`,{class:`text-gray-400 animate-pulse`},`加载中...`,-1)]])):i.value?(H(),U(`div`,kl,k(i.value),1)):t.value?(H(),U(B,{key:2},[W(`div`,Al,[W(`div`,jl,[a[5]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`已爬取 URL`,-1),W(`div`,Ml,k(T(t.value.total_urls)),1)]),W(`div`,Nl,[a[6]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`总词数`,-1),W(`div`,Pl,k(T(t.value.total_words)),1)]),W(`div`,Fl,[a[7]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`域名数量`,-1),W(`div`,Il,k(T(t.value.total_domains)),1)]),W(`div`,Ll,[a[8]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`待重爬`,-1),W(`div`,{class:O([`text-3xl font-bold`,t.value.recrawl_eligible>0?`text-orange-400`:`text-green-400`])},k(T(t.value.recrawl_eligible)),3)]),W(`div`,Rl,[W(`div`,null,[a[9]||=W(`div`,{class:`text-sm text-gray-500 mb-2`},`待刷盘`,-1),W(`div`,{class:O([`text-3xl font-bold`,t.value.pending>0?`text-yellow-400`:`text-green-400`])},k(T(t.value.pending)),3)]),W(`button`,{class:`mt-3 w-full bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium py-1.5 px-3 rounded transition-colors cursor-pointer`,disabled:r.value||!t.value.pending,onClick:ee},k(r.value?`刷盘中...`:`立即刷盘`),9,zl)])]),u.value?(H(),U(`div`,Bl,[a[14]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-4`},`优先爬取队列`,-1),W(`div`,Vl,[W(`div`,Hl,[a[10]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`一级队列`,-1),W(`div`,{class:O([`text-2xl font-bold`,(u.value.level1||0)>0?`text-yellow-400`:`text-gray-500`])},k(T(u.value.level1||0)),3)]),W(`div`,Ul,[a[11]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`二级队列`,-1),W(`div`,{class:O([`text-2xl font-bold`,(u.value.level2_queue||0)+(u.value.level2_inflight||0)>0?`text-yellow-400`:`text-gray-500`])},k(T((u.value.level2_queue||0)+(u.value.level2_inflight||0))),3)]),W(`div`,Wl,[a[12]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`线程使用`,-1),W(`div`,{class:O([`text-2xl font-bold`,u.value.active>0?`text-orange-400`:`text-gray-500`])},k(T(u.value.active)),3)]),W(`div`,Gl,[a[13]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`线程总数`,-1),W(`div`,Kl,k(u.value.max_workers),1)])]),W(`div`,ql,[jn(W(`textarea`,{"onUpdate:modelValue":a[0]||=e=>d.value=e,rows:`3`,placeholder:`批量添加 URL(每行一个,仅发送有效链接)`,class:`flex-1 bg-gray-800 border border-gray-700 text-gray-200 text-sm rounded-lg px-3 py-2 resize-none focus:outline-none focus:border-blue-500 placeholder-gray-600`},null,512),[[so,d.value]]),W(`button`,{class:`bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 text-white text-sm font-medium px-4 py-2 rounded-lg transition-colors cursor-pointer whitespace-nowrap self-end`,disabled:f.value||!d.value.trim(),onClick:E},k(f.value?`添加中...`:`批量添加`),9,Jl)])])):G(``,!0),p.value&&p.value.max_epoch>0?(H(),U(`div`,Yl,[W(`div`,Xl,[a[16]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider`},`爬取进度`,-1),W(`div`,Zl,[a[15]||=W(`span`,{class:`text-xs text-gray-500`},`状态:`,-1),W(`span`,{class:O([`px-2 py-0.5 rounded text-xs font-medium`,p.value.is_running?`bg-green-900/50 text-green-400`:`bg-gray-800 text-gray-500`])},k(p.value.is_running?`运行中`:`已停止`),3)])]),W(`div`,Ql,[W(`div`,$l,[a[17]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`当前轮次`,-1),W(`div`,eu,[Ui(k(p.value.current_epoch),1),W(`span`,tu,`/`+k(p.value.max_epoch),1)])]),W(`div`,nu,[a[18]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`本轮队列`,-1),W(`div`,{class:O([`text-3xl font-bold`,p.value.queue_length>0?`text-blue-400`:`text-gray-500`])},k(T(p.value.queue_length)),3)]),W(`div`,ru,[a[19]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`已完成`,-1),W(`div`,iu,k(T(p.value.completed_count)),1),W(`div`,au,k(p.value.queue_length>0?Math.round(p.value.completed_count/p.value.queue_length*100)+`%`:`--`),1)]),W(`div`,ou,[a[20]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`本轮新发现`,-1),W(`div`,{class:O([`text-3xl font-bold`,p.value.new_links_count>0?`text-purple-400`:`text-gray-500`])},k(T(p.value.new_links_count)),3)]),W(`div`,su,[a[21]||=W(`div`,{class:`text-xs text-gray-500 mb-1`},`已收录总计`,-1),W(`div`,cu,k(T(p.value.visited_total)),1)])]),p.value.queue_length>0?(H(),U(`div`,lu,[W(`div`,uu,[W(`div`,{class:`h-full bg-gradient-to-r from-blue-600 to-green-500 rounded-full transition-all duration-500`,style:pe({width:`${p.value.completed_count/p.value.queue_length*100}%`})},null,4)])])):G(``,!0)])):G(``,!0),W(`div`,du,[W(`div`,fu,[W(`div`,null,[a[25]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-2`},`爬虫线程`,-1),W(`div`,pu,[W(`div`,null,[a[22]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`实际运行`,-1),W(`div`,{class:O([`text-3xl font-bold`,s.value>0?`text-green-400`:`text-gray-500`])},k(s.value),3)]),a[24]||=W(`div`,{class:`text-gray-700 text-xl`},`/`,-1),W(`div`,null,[a[23]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`设定上限`,-1),W(`div`,mu,k(o.value),1)])])]),W(`div`,hu,[W(`button`,{class:`w-9 h-9 flex items-center justify-center rounded-lg bg-gray-800 hover:bg-gray-700 text-gray-300 hover:text-white text-lg font-bold transition-colors cursor-pointer`,onClick:a[1]||=e=>c.value=Math.max(1,c.value-1),disabled:l.value},`−`,8,gu),jn(W(`input`,{type:`number`,"onUpdate:modelValue":a[2]||=e=>c.value=e,min:`1`,max:`500`,class:`w-20 h-9 text-center bg-gray-800 border border-gray-700 rounded-lg text-white text-sm focus:outline-none focus:border-blue-500 transition-colors [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none`,onKeyup:go(v,[`enter`])},null,544),[[so,c.value,void 0,{number:!0}]]),W(`button`,{class:`w-9 h-9 flex items-center justify-center rounded-lg bg-gray-800 hover:bg-gray-700 text-gray-300 hover:text-white text-lg font-bold transition-colors cursor-pointer`,onClick:a[3]||=e=>c.value=Math.min(500,c.value+1),disabled:l.value},`+`,8,_u),W(`button`,{class:`h-9 px-4 bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium rounded-lg transition-colors cursor-pointer`,disabled:l.value||c.value===o.value,onClick:v},k(l.value?`保存中...`:`应用`),9,vu)])])]),m.value?(H(),U(`div`,yu,[W(`div`,bu,[W(`div`,xu,[a[28]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 uppercase tracking-wider mb-3`},`反链计算(PageRank)`,-1),W(`div`,Su,[W(`div`,null,[a[26]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`下次自动执行`,-1),W(`div`,{class:O([`text-lg font-bold`,m.value.running?`text-yellow-400`:`text-white`])},k(m.value.running?`计算中...`:C(m.value.next_run)),3)]),W(`div`,null,[a[27]||=W(`div`,{class:`text-xs text-gray-500 mb-0.5`},`上次完成`,-1),W(`div`,Cu,k(w(m.value.last_run)),1)])]),m.value.last_error?(H(),U(`div`,{key:0,class:`mt-2 text-xs text-red-400 truncate`,title:m.value.last_error},` 上次错误:`+k(m.value.last_error),9,wu)):G(``,!0)]),W(`button`,{class:`h-9 px-4 bg-blue-700 hover:bg-blue-600 disabled:bg-gray-700 disabled:text-gray-500 text-white text-sm font-medium rounded-lg transition-colors cursor-pointer whitespace-nowrap`,disabled:h.value||m.value.running,onClick:S},k(h.value?`已触发...`:m.value.running?`计算中...`:`立即执行`),9,Tu)])])):G(``,!0),W(`div`,Eu,[W(`div`,Du,[a[29]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 mb-3 md:mb-4 uppercase tracking-wider`},`域名分布 Top 10`,-1),W(`div`,Ou,[(H(!0),U(B,null,L(te(t.value.domains),([e,n])=>(H(),U(`div`,{key:e,class:`flex items-center gap-2 md:gap-3`},[W(`div`,{class:`w-24 md:w-36 text-xs text-gray-400 truncate shrink-0`,title:e},k(e),9,ku),W(`div`,Au,[W(`div`,{class:`h-full bg-blue-600 rounded-full transition-all duration-500`,style:pe({width:`${n/t.value.domains[Object.keys(t.value.domains)[0]]*100}%`})},null,4)]),W(`div`,ju,k(T(n)),1)]))),128))])]),W(`div`,Mu,[a[30]||=W(`h2`,{class:`text-sm font-semibold text-gray-300 mb-3 md:mb-4 uppercase tracking-wider`},`语种分布`,-1),W(`div`,Nu,[(H(!0),U(B,null,L(Object.entries(t.value.languages||{}).sort((e,t)=>t[1]-e[1]),([e,n])=>(H(),U(`div`,{key:e,class:`flex items-center gap-2 md:gap-3`},[W(`div`,Pu,k(e),1),W(`div`,Fu,[W(`div`,{class:`h-full rounded-full transition-all duration-500`,style:pe({width:`${n/t.value.total_urls*100}%`,backgroundColor:ne(e)})},null,4)]),W(`div`,Iu,k(T(n)),1)]))),128))])])])],64)):G(``,!0)]))}},Ru={class:`p-4 md:p-8`},zu={class:`flex flex-col md:flex-row md:items-center justify-between mb-4 md:mb-6 gap-3`},Bu={class:`text-sm text-gray-500`},Vu={class:`flex items-center gap-2 md:gap-3`},Hu=[`value`],Uu={class:`flex flex-col sm:flex-row items-stretch sm:items-center gap-3 mb-4 md:mb-5`},Wu={class:`relative flex-1 max-w-full sm:max-w-sm`},Gu={key:0,class:`flex items-center justify-center h-48`},Ku={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},qu={key:2,class:`bg-gray-900 border border-gray-800 rounded-xl overflow-hidden`},Ju={class:`hidden md:table w-full text-sm`},Yu={class:`px-5 py-3.5`},Xu=[`href`],Zu={class:`font-medium text-gray-200 group-hover:text-white line-clamp-2`},Qu={class:`text-xs text-gray-600 mt-0.5 break-all line-clamp-1`},$u={key:0,class:`text-xs text-gray-500 mt-1 line-clamp-1`},ed=[`onClick`],td={key:0,class:`text-gray-500`},nd={key:1,class:`mt-2`},rd={class:`flex flex-wrap gap-1.5`},id=[`title`],ad={class:`text-gray-500 text-[10px] ml-0.5`},od={class:`text-xs text-gray-600 mt-1`},sd={key:1,class:`text-xs text-gray-600`},cd={class:`px-5 py-3.5`},ld={class:`text-gray-400 text-xs font-mono`},ud={class:`px-5 py-3.5`},dd={key:1,class:`text-xs text-gray-600`},fd={class:`px-5 py-3.5 text-gray-500 text-xs tabular-nums`},pd={class:`px-5 py-3.5 text-gray-500 text-xs`},md={key:0},hd={class:`md:hidden divide-y divide-gray-800`},gd=[`href`],_d={class:`font-medium text-gray-200 text-sm line-clamp-2 mb-1`},vd={class:`text-xs text-gray-500 break-all line-clamp-1 mb-2`},yd={class:`flex items-center gap-2 text-xs mb-2`},bd={class:`text-gray-400 font-mono`},xd={class:`text-gray-600 ml-auto`},Sd=[`onClick`],Cd={key:0,class:`text-gray-500`},wd={key:0,class:`mb-2`},Td={class:`flex flex-wrap gap-1`},Ed=[`title`],Dd={class:`text-gray-500 text-[9px] ml-0.5`},Od={class:`text-[10px] text-gray-600 mt-1`},kd={key:1,class:`text-xs text-gray-600`},Ad={key:0,class:`p-8 text-center text-gray-600`},jd={key:3,class:`mt-3 text-xs text-gray-600 text-right pb-4 md:pb-0`},Md={__name:`RecentCrawls`,setup(e){let t=F([]),n=F(0),r=F(!0),i=F(null),a=F(``),o=F(``),s=[20,50,100,200],c=F(50),l=F(new Set),u=F({}),d=F(new Set);ar(async()=>{await f()});async function f(){r.value=!0,i.value=null;try{let e=await pl(c.value);t.value=e.items||[],n.value=e.total||0}catch(e){i.value=`无法加载数据,可能人服务器未启动`,console.error(e)}finally{r.value=!1}}async function p(e){c.value=e,await f()}let m=ga(()=>{let e=t.value;if(a.value){let t=a.value.toLowerCase();e=e.filter(e=>e.title?.toLowerCase().includes(t)||e.url?.toLowerCase().includes(t)||e.domain?.toLowerCase().includes(t))}return o.value&&(e=e.filter(e=>Object.keys(e.language||{}).includes(o.value))),e});function h(e){return e?new Date(e*1e3).toLocaleString(`zh-CN`,{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1}):`-`}function g(e){return{zh:{label:`中文`,cls:`bg-red-900/60 text-red-300`},en:{label:`EN`,cls:`bg-blue-900/60 text-blue-300`},ja:{label:`日`,cls:`bg-pink-900/60 text-pink-300`},ko:{label:`한`,cls:`bg-blue-900/60 text-blue-300`},fr:{label:`FR`,cls:`bg-green-900/60 text-green-300`},de:{label:`DE`,cls:`bg-yellow-900/60 text-yellow-300`},es:{label:`ES`,cls:`bg-green-900/60 text-green-300`},ru:{label:`RU`,cls:`bg-purple-900/60 text-purple-300`}}[e]||{label:e,cls:`bg-gray-800 text-gray-400`}}function _(e){return e?Object.entries(e).sort((e,t)=>t[1]-e[1])[0]:null}async function v(e){if(l.value.has(e)){l.value.delete(e);return}if(l.value.add(e),!u.value[e]){d.value.add(e);try{let t=await wl(e);u.value[e]=t.keywords||[]}catch(t){console.error(`Failed to load keywords:`,t),u.value[e]=[]}finally{d.value.delete(e)}}}return(e,t)=>(H(),U(`div`,Ru,[W(`div`,zu,[W(`div`,null,[t[4]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-1`},`最近爬取`,-1),W(`p`,Bu,`共 `+k(n.value.toLocaleString())+` 条记录`,1)]),W(`div`,Vu,[jn(W(`select`,{"onUpdate:modelValue":t[0]||=e=>c.value=e,onChange:t[1]||=e=>p(c.value),class:`bg-gray-900 border border-gray-700 text-gray-300 text-sm rounded-lg px-3 py-2 focus:border-blue-500 focus:outline-none`},[(H(),U(B,null,L(s,e=>W(`option`,{key:e,value:e},`显示 `+k(e)+` 条`,9,Hu)),64))],544),[[co,c.value]]),W(`button`,{onClick:f,class:`bg-blue-600 hover:bg-blue-700 text-white text-sm px-4 py-2 rounded-lg transition-colors`},` 刷新 `)])]),W(`div`,Uu,[W(`div`,Wu,[jn(W(`input`,{"onUpdate:modelValue":t[2]||=e=>a.value=e,type:`text`,placeholder:`搜索标题、URL、域名...`,class:`w-full bg-gray-900 border border-gray-700 text-gray-200 text-sm rounded-lg pl-10 pr-4 py-2 focus:border-blue-500 focus:outline-none placeholder-gray-600`},null,512),[[so,a.value]]),t[5]||=W(`span`,{class:`absolute left-3 top-1/2 -translate-y-1/2 text-gray-500`},`🔍`,-1)]),jn(W(`select`,{"onUpdate:modelValue":t[3]||=e=>o.value=e,class:`bg-gray-900 border border-gray-700 text-gray-300 text-sm rounded-lg px-3 py-2 focus:border-blue-500 focus:outline-none`},[...t[6]||=[Wi(``,9)]],512),[[co,o.value]])]),r.value?(H(),U(`div`,Gu,[...t[7]||=[W(`div`,{class:`text-gray-400 animate-pulse`},`加载中...`,-1)]])):i.value?(H(),U(`div`,Ku,k(i.value),1)):(H(),U(`div`,qu,[W(`table`,Ju,[t[9]||=W(`thead`,null,[W(`tr`,{class:`border-b border-gray-800`},[W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider`},`标题`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-28`},`域名`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-16`},`语种`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-20`},`字数`),W(`th`,{class:`text-left px-5 py-3 text-gray-500 font-medium text-xs uppercase tracking-wider w-48`},`爬取时间`)])],-1),W(`tbody`,null,[(H(!0),U(B,null,L(m.value,e=>(H(),U(`tr`,{key:e.url,class:`border-b border-gray-800/50 hover:bg-gray-800/40 transition-colors group`},[W(`td`,Yu,[W(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,class:`block hover:opacity-80 transition-opacity`},[W(`div`,Zu,k(e.title||`(无标题)`),1),W(`div`,Qu,k(e.url),1)],8,Xu),e.description?(H(),U(`div`,$u,k(e.description),1)):G(``,!0),W(`button`,{onClick:mo(t=>v(e.url),[`prevent`]),class:`mt-2 text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1`},[W(`span`,null,k(l.value.has(e.url)?`▼`:`▶`)+` 关键词`,1),d.value.has(e.url)?(H(),U(`span`,td,`加载中...`)):G(``,!0)],8,ed),l.value.has(e.url)?(H(),U(`div`,nd,[u.value[e.url]?.length?(H(),U(B,{key:0},[W(`div`,rd,[(H(!0),U(B,null,L(u.value[e.url],e=>(H(),U(`span`,{key:e.word,class:`text-xs px-2 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700`,title:`权重: ${e.weight.toFixed(4)}`},[Ui(k(e.word)+` `,1),W(`span`,ad,k(e.weight.toFixed(2)),1)],8,id))),128))]),W(`div`,od,` 共 `+k(u.value[e.url].length)+` 个关键词 `,1)],64)):d.value.has(e.url)?G(``,!0):(H(),U(`span`,sd,` 暂无关键词(服务重启后缓存已清空) `))])):G(``,!0)]),W(`td`,cd,[W(`span`,ld,k(e.domain),1)]),W(`td`,ud,[_(e.language)?(H(),U(`span`,{key:0,class:O([`text-xs px-1.5 py-0.5 rounded font-medium`,g(_(e.language)[0]).cls])},k(g(_(e.language)[0]).label)+` `+k((_(e.language)[1]*100).toFixed(0))+`% `,3)):(H(),U(`span`,dd,`-`))]),W(`td`,fd,k(e.word_count.toLocaleString()),1),W(`td`,pd,k(h(e.crawled_at)),1)]))),128)),m.value.length?G(``,!0):(H(),U(`tr`,md,[...t[8]||=[W(`td`,{colspan:`5`,class:`px-5 py-12 text-center text-gray-600`},` 没有找到匹配的记录 `,-1)]]))])]),W(`div`,hd,[(H(!0),U(B,null,L(m.value,e=>(H(),U(`div`,{key:e.url,class:`block p-4 hover:bg-gray-800/40 transition-colors`},[W(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,class:`block`},[W(`div`,_d,k(e.title||`(无标题)`),1),W(`div`,vd,k(e.url),1)],8,gd),W(`div`,yd,[W(`span`,bd,k(e.domain),1),_(e.language)?(H(),U(`span`,{key:0,class:O([`px-1.5 py-0.5 rounded font-medium`,g(_(e.language)[0]).cls])},k(g(_(e.language)[0]).label),3)):G(``,!0),W(`span`,xd,k(h(e.crawled_at)),1)]),W(`button`,{onClick:mo(t=>v(e.url),[`prevent`]),class:`text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1 mb-2`},[W(`span`,null,k(l.value.has(e.url)?`▼`:`▶`)+` 关键词`,1),d.value.has(e.url)?(H(),U(`span`,Cd,`加载中...`)):G(``,!0)],8,Sd),l.value.has(e.url)?(H(),U(`div`,wd,[u.value[e.url]?.length?(H(),U(B,{key:0},[W(`div`,Td,[(H(!0),U(B,null,L(u.value[e.url],e=>(H(),U(`span`,{key:e.word,class:`text-[10px] px-1.5 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700`,title:`权重: ${e.weight.toFixed(4)}`},[Ui(k(e.word)+` `,1),W(`span`,Dd,k(e.weight.toFixed(2)),1)],8,Ed))),128))]),W(`div`,Od,` 共 `+k(u.value[e.url].length)+` 个 `,1)],64)):d.value.has(e.url)?G(``,!0):(H(),U(`span`,kd,` 暂无关键词 `))])):G(``,!0)]))),128)),m.value.length?G(``,!0):(H(),U(`div`,Ad,` 没有找到匹配的记录 `))])])),!r.value&&!i.value&&m.value.length?(H(),U(`div`,jd,` 筛选后 `+k(m.value.length)+` 条 / 共 `+k(n.value.toLocaleString())+` 条 `,1)):G(``,!0)]))}},Nd={class:`flex flex-col h-full`},Pd={class:`bg-gray-950 border-b border-gray-800 px-4 md:px-8 py-4 md:py-6`},Fd={class:`max-w-3xl mx-auto`},Id={class:`relative`},Ld={class:`flex-1 overflow-y-auto px-8 py-6`},Rd={class:`max-w-3xl mx-auto`},zd={key:0,class:`flex items-center gap-3 text-gray-400 py-8`},Bd={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},Vd={key:2,class:`py-16 text-center text-gray-600`},Hd={key:3,class:`py-16 text-center text-gray-600`},Ud={key:4,class:`flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4 mb-4 md:mb-5 text-sm text-gray-500`},Wd={class:`flex items-center gap-2`},Gd={class:`text-gray-300`},Kd={class:`flex flex-wrap gap-2 sm:ml-auto`},qd={class:`text-gray-300`},Jd={key:5,class:`space-y-2 md:space-y-1`},Yd=[`onClick`],Xd={class:`flex items-start gap-2 md:gap-3 mb-2`},Zd={class:`flex-1 min-w-0`},Qd={class:`text-blue-400 group-hover:text-blue-300 text-base md:text-lg leading-snug`},$d={class:`text-xs text-gray-600 mt-0.5 truncate`},ef={class:`flex flex-col items-end gap-1 shrink-0`},tf={class:`text-xs text-gray-600`},nf={class:`w-12 md:w-14 bg-gray-800 rounded-full h-1.5 overflow-hidden`},rf={class:`text-gray-400 text-sm leading-relaxed mb-2 md:mb-3`},af={class:`flex flex-wrap items-center gap-2 md:gap-3 text-xs`},of={class:`flex flex-wrap gap-1.5`},sf=[`title`],cf={class:`text-blue-400`},lf={class:`text-gray-600 ml-1`},uf={key:0,class:`text-gray-600`},df={class:`text-gray-700 ml-0 md:ml-auto`},ff={class:`text-xs text-gray-700 mt-1 truncate`},pf={key:6,class:`mt-6 text-center`},mf=``,hf=10,gf={__name:`SearchView`,setup(e){let t=F(``),n=F([]),r=F(0),i=F({}),a=F(!1),o=F(null),s=F(0),c=null;async function l(e,t=!1){if(!e.trim()){n.value=[],r.value=0,i.value={};return}t||(s.value=0),a.value=!0,o.value=null;try{let a=encodeURIComponent(e),o=s.value*hf,c=await fetch(`${mf}/search?qh=${a}&slice=${o}:${o+hf}`);if(!c.ok)throw Error(`HTTP ${c.status}`);let l=await c.json();t?n.value=[...n.value,...l.results||[]]:n.value=l.results||[],r.value=l.total||0,i.value=l.counts||{}}catch(e){o.value=`搜索失败,可能是人服务器未启动`,console.error(e)}finally{a.value=!1}}function u(){clearTimeout(c),c=setTimeout(()=>l(t.value),400)}function d(e){e.key===`Enter`&&(clearTimeout(c),l(t.value))}function f(e,t=120){return e?e.length>t?e.slice(0,t)+`…`:e:``}function p(e){return e?e.toLocaleString():`0`}function m(e){return Math.min(100,Math.round(e*100))}function h(e){return{zh:`中文`,en:`EN`,ja:`JA`,ko:`KO`,fr:`FR`,de:`DE`,es:`ES`,ru:`RU`}[e]||e?.toUpperCase()}function g(e){try{return new URL(e).hostname}catch{return e}}function _(e){window.open(e,`_blank`)}return(e,c)=>(H(),U(`div`,Nd,[W(`div`,Pd,[W(`div`,Fd,[W(`div`,Id,[jn(W(`input`,{"onUpdate:modelValue":c[0]||=e=>t.value=e,onInput:u,onKeydown:d,type:`text`,placeholder:`输入关键词搜索,或用 site:example.com 限定域名`,class:`w-full bg-gray-900 border border-gray-700 rounded-xl md:rounded-2xl px-4 md:px-6 py-3 md:py-4 pr-20 md:pr-14 text-white text-base md:text-lg placeholder-gray-600 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition`},null,544),[[so,t.value]]),W(`button`,{onClick:c[1]||=e=>l(t.value),class:`absolute right-2 md:right-3 top-1/2 -translate-y-1/2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg md:rounded-xl px-3 md:px-4 py-1.5 md:py-2 text-sm font-medium transition`},` 搜索 `)])])]),W(`div`,Ld,[W(`div`,Rd,[a.value?(H(),U(`div`,zd,[...c[3]||=[W(`div`,{class:`w-5 h-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin`},null,-1),W(`span`,null,`搜索中...`,-1)]])):o.value?(H(),U(`div`,Bd,k(o.value),1)):t.value.trim()?n.value.length===0&&!a.value?(H(),U(`div`,Hd,` 未找到相关结果 `)):n.value.length>0?(H(),U(`div`,Ud,[W(`div`,Wd,[W(`span`,null,[c[4]||=Ui(`找到约 `,-1),W(`strong`,Gd,k(p(r.value)),1),c[5]||=Ui(` 条结果`,-1)]),c[6]||=W(`span`,{class:`text-gray-700`},`|`,-1),W(`span`,null,k(n.value.length)+` 条已加载`,1)]),W(`div`,Kd,[(H(!0),U(B,null,L(i.value,(e,t)=>(H(),U(`span`,{key:t,class:`inline-flex items-center gap-1 bg-gray-800 rounded px-2 py-0.5 text-xs text-gray-400`},[W(`span`,qd,k(t),1),W(`span`,null,k(p(e)),1)]))),128))])])):G(``,!0):(H(),U(`div`,Vd,` 输入关键词开始搜索 `)),n.value.length>0?(H(),U(`div`,Jd,[(H(!0),U(B,null,L(n.value,(e,t)=>(H(),U(`div`,{key:t,onClick:t=>_(e.url),class:`group block bg-gray-900/50 hover:bg-gray-900 border border-gray-800 hover:border-gray-700 rounded-xl p-4 md:p-5 cursor-pointer transition`},[W(`div`,Xd,[W(`div`,Zd,[W(`div`,Qd,k(e.snippet?.title||g(e.url)),1),W(`div`,$d,k(g(e.url)),1)]),W(`div`,ef,[W(`div`,tf,k(m(e.score))+`%`,1),W(`div`,nf,[W(`div`,{class:`h-full bg-blue-500 rounded-full`,style:pe({width:m(e.score)+`%`})},null,4)])])]),W(`p`,rf,k(f(e.snippet?.description||e.snippet?.text)),1),W(`div`,af,[W(`div`,of,[(H(!0),U(B,null,L(e.relevance,(e,t)=>(H(),U(`span`,{key:t,class:`inline-flex items-center bg-gray-800 rounded px-1.5 py-0.5`,title:`${t}: ${(e*100).toFixed(1)}%`},[W(`span`,cf,k(t),1),W(`span`,lf,k((e*100).toFixed(0))+`%`,1)],8,sf))),128))]),e.snippet?.language?(H(),U(`span`,uf,k(h(e.snippet.language)),1)):G(``,!0),W(`span`,df,k(p(e.domain_count))+` 个结果`,1)]),W(`div`,ff,k(e.url),1)],8,Yd))),128))])):G(``,!0),n.value.length>0&&n.value.length{s.value++,l(t.value,!0)},class:`bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-xl px-6 py-2.5 text-sm transition`},` 加载更多 (`+k(n.value.length)+`/`+k(p(r.value))+`) `,1)])):G(``,!0)])])]))}},_f={class:`p-4 md:p-8`},vf={class:`flex flex-col md:flex-row md:items-center justify-between mb-4 md:mb-6 gap-3`},yf={class:`text-sm text-gray-500`},bf={class:`flex items-center gap-3`},xf={class:`bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 flex items-center gap-3`},Sf={class:`flex flex-col`},Cf={class:`text-sm font-medium text-gray-300`},wf={class:`w-12 h-12 relative`},Tf={viewBox:`0 0 36 36`,class:`w-full h-full transform -rotate-90`},Ef=[`stroke-dasharray`],Df={class:`absolute inset-0 flex items-center justify-center text-[8px] font-medium text-gray-400`},Of={class:`mb-4 md:mb-5`},kf={class:`relative max-w-sm`},Af={key:0,class:`flex items-center justify-center h-48`},jf={key:1,class:`bg-red-900/30 border border-red-800 rounded-lg p-4 text-red-300`},Mf={key:2,class:`bg-gray-900 border border-gray-800 rounded-xl p-12 text-center`},Nf={key:3,class:`bg-gray-900 border border-gray-800 rounded-xl overflow-hidden`},Pf={class:`divide-y divide-gray-800`},Ff=[`onClick`],If={class:`flex items-start gap-2 mb-1`},Lf={class:`shrink-0 text-gray-600 mt-0.5 text-xs select-none`},Rf=[`href`,`title`],zf={key:0,class:`text-sm text-gray-300 font-medium mb-1`},Bf={key:1,class:`text-xs text-gray-500 leading-relaxed mb-2`},Vf={key:2,class:`mt-2`},Hf={class:`flex flex-wrap gap-1.5`},Uf=[`title`],Wf={class:`text-gray-500 text-[10px] ml-0.5`},Gf={class:`text-xs text-gray-600 mt-2`},Kf={key:1,class:`text-xs text-gray-600`},qf={key:2,class:`text-xs text-gray-500`},Jf={class:`px-4 py-3 border-t border-gray-800 flex items-center justify-between`},Yf={class:`text-xs text-gray-500`},Xf={class:`flex items-center gap-1`},Zf=[`disabled`],Qf=[`onClick`],$f={key:0,class:`px-1 py-1 text-xs text-gray-600`},ep=[`disabled`],tp={__name:`KeywordsCache`,setup(e){let t=F(!0),n=F(null),r=F({size:0,max_size:1e4}),i=F([]),a=F({}),o=F({}),s=F(new Set),c=F(``),l=F(0),u=F(1),d=F(50),f=null;ar(async()=>{await m(),f=setInterval(p,5e3)}),lr(()=>{f&&clearInterval(f)});async function p(){try{let e=await Tl();r.value.size=e.size||0,r.value.max_size=e.max_size||1e4}catch{}}async function m(){t.value=!0,n.value=null;try{let e=await El({page:u.value,page_size:d.value});r.value.size=e.size||0,r.value.max_size=e.max_size||1e4,l.value=e.total||0,i.value=e.items||[];let t={};i.value.forEach(e=>t[e.url]=!0),a.value=t,i.value.forEach(e=>{e.keywords?.length&&(o.value[e.url]=e.keywords)})}catch(e){n.value=`无法加载缓存数据`,console.error(e)}finally{t.value=!1}}let h=ga(()=>r.value.max_size?r.value.size/r.value.max_size:0),g=ga(()=>Math.max(1,Math.ceil(l.value/d.value)));function _(){u.value>1&&(u.value--,m())}function v(){u.value=1&&e<=g.value&&e!==u.value&&(u.value=e,m())}function b(){u.value=1,m()}async function x(e){if(a.value[e]){let t={...a.value};delete t[e],a.value=t;return}if(a.value={...a.value,[e]:!0},!o.value[e]){s.value.add(e);try{let t=await wl(e);o.value[e]=t.keywords||[]}catch{o.value[e]=[]}finally{s.value.delete(e)}}}function S(e,t=80){return e.length<=t?e:e.slice(0,t)+`...`}function C(e,t=200){return e?e.length<=t?e:e.slice(0,t)+`...`:``}return(e,f)=>(H(),U(`div`,_f,[W(`div`,vf,[W(`div`,null,[f[3]||=W(`h1`,{class:`text-xl md:text-2xl font-semibold text-white mb-1`},`关键词缓存`,-1),W(`p`,yf,`LRU 缓存,共 `+k(r.value.size.toLocaleString())+` 条记录`,1)]),W(`div`,bf,[W(`div`,xf,[W(`div`,Sf,[f[4]||=W(`span`,{class:`text-[10px] text-gray-500 uppercase`},`容量`,-1),W(`span`,Cf,k(r.value.size.toLocaleString())+` / `+k(r.value.max_size.toLocaleString()),1)]),W(`div`,wf,[(H(),U(`svg`,Tf,[f[5]||=W(`path`,{class:`text-gray-800`,d:`M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831`,fill:`none`,stroke:`currentColor`,"stroke-width":`4`},null,-1),W(`path`,{class:O(h.value>.9?`text-red-500`:h.value>.7?`text-yellow-500`:`text-green-500`),d:`M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831`,fill:`none`,stroke:`currentColor`,"stroke-width":`4`,"stroke-dasharray":`${(h.value*100).toFixed(0)}, 100`},null,10,Ef)])),W(`span`,Df,k((h.value*100).toFixed(0))+`% `,1)])]),W(`button`,{onClick:m,class:`bg-blue-600 hover:bg-blue-700 text-white text-sm px-4 py-2 rounded-lg transition-colors`},` 刷新 `)])]),W(`div`,Of,[W(`div`,kf,[jn(W(`input`,{"onUpdate:modelValue":f[0]||=e=>c.value=e,onInput:b,type:`text`,placeholder:`搜索 URL、标题或摘要...`,class:`w-full bg-gray-900 border border-gray-700 text-gray-200 text-sm rounded-lg pl-10 pr-4 py-2 focus:border-blue-500 focus:outline-none placeholder-gray-600`},null,544),[[so,c.value]]),f[6]||=W(`span`,{class:`absolute left-3 top-1/2 -translate-y-1/2 text-gray-500`},`🔍`,-1)])]),t.value?(H(),U(`div`,Af,[...f[7]||=[W(`div`,{class:`text-gray-400 animate-pulse`},`加载中...`,-1)]])):n.value?(H(),U(`div`,jf,k(n.value),1)):i.value.length?(H(),U(`div`,Nf,[W(`div`,Pf,[(H(!0),U(B,null,L(i.value,e=>(H(),U(`div`,{key:e.url,class:`p-4 hover:bg-gray-800/40 transition-colors cursor-pointer`,onClick:t=>x(e.url)},[W(`div`,If,[W(`span`,Lf,k(a.value[e.url]?`▼`:`▶`),1),W(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,class:`text-sm text-blue-400 hover:text-blue-300 break-all line-clamp-2`,title:e.url,onClick:f[1]||=mo(()=>{},[`stop`])},k(S(e.url)),9,Rf)]),e.title?(H(),U(`div`,zf,k(e.title),1)):G(``,!0),e.snippet?(H(),U(`div`,Bf,k(C(e.snippet)),1)):G(``,!0),a.value[e.url]?(H(),U(`div`,Vf,[o.value[e.url]?.length?(H(),U(B,{key:0},[W(`div`,Hf,[(H(!0),U(B,null,L(o.value[e.url],e=>(H(),U(`span`,{key:e.word,class:`text-xs px-2 py-0.5 rounded bg-gray-800 text-gray-300 border border-gray-700`,title:`权重: ${e.weight.toFixed(4)}`},[Ui(k(e.word)+` `,1),W(`span`,Wf,k(e.weight.toFixed(2)),1)],8,Uf))),128))]),W(`div`,Gf,` 共 `+k(o.value[e.url].length)+` 个关键词 `,1)],64)):s.value.has(e.url)?(H(),U(`span`,qf,` 加载中... `)):(H(),U(`span`,Kf,` 暂无关键词 `))])):G(``,!0)],8,Ff))),128))]),W(`div`,Jf,[W(`div`,Yf,` 第 `+k((u.value-1)*d.value+1)+`-`+k(Math.min(u.value*d.value,l.value))+` 条 / 共 `+k(l.value.toLocaleString())+` 条 `,1),W(`div`,Xf,[W(`button`,{onClick:_,disabled:u.value===1,class:`px-2 py-1 text-xs rounded bg-gray-800 text-gray-400 hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed`},` ‹ `,8,Zf),(H(!0),U(B,null,L(g.value<=5?g.value:(()=>{let e=[],t=Math.max(1,u.value-2),n=Math.min(g.value,t+4);for(let r=t;r<=n;r++)e.push(r);return e})(),e=>(H(),U(`button`,{key:e,onClick:t=>y(e),class:O([`px-2 py-1 text-xs rounded`,e===u.value?`bg-blue-600 text-white`:`bg-gray-800 text-gray-400 hover:bg-gray-700`])},k(e),11,Qf))),128)),g.value>5&&u.value5&&u.valuey(g.value),class:`px-2 py-1 text-xs rounded bg-gray-800 text-gray-400 hover:bg-gray-700`},k(g.value),1)):G(``,!0),W(`button`,{onClick:v,disabled:u.value===g.value,class:`px-2 py-1 text-xs rounded bg-gray-800 text-gray-400 hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed`},` › `,8,ep)])])])):(H(),U(`div`,Mf,[...f[8]||=[W(`div`,{class:`text-4xl mb-3`},`📭`,-1),W(`div`,{class:`text-gray-400`},`缓存为空`,-1),W(`div`,{class:`text-xs text-gray-600 mt-1`},`爬取页面时会自动填充此缓存`,-1)]]))]))}},np={class:`flex h-screen bg-gray-950 text-gray-100 font-sans`},rp={class:`hidden md:flex w-56 bg-gray-900 border-r border-gray-800 flex-col shrink-0`},ip={class:`flex-1 py-4 px-3 space-y-1`},ap=[`onClick`],op={class:`flex-1 overflow-y-auto pb-16 md:pb-0`},sp={class:`md:hidden fixed bottom-0 left-0 right-0 bg-gray-900 border-t border-gray-800 flex justify-around items-center h-16 z-50`},cp=[`onClick`],lp={class:`text-lg`},up={class:`text-[10px]`};bo({__name:`App`,setup(e){let t=F(`dashboard`),n=[{id:`dashboard`,label:`概览`,icon:`📊`},{id:`recent`,label:`最近`,icon:`🕷️`},{id:`keywords`,label:`缓存`,icon:`💾`},{id:`search`,label:`搜索`,icon:`🔍`}];return(e,r)=>(H(),U(`div`,np,[W(`aside`,rp,[r[0]||=W(`div`,{class:`px-5 py-5 border-b border-gray-800`},[W(`div`,{class:`text-lg font-semibold text-white tracking-tight`},`SESE Admin`),W(`div`,{class:`text-xs text-gray-500 mt-0.5`},`爬取内容监控`)],-1),W(`nav`,ip,[(H(),U(B,null,L(n,e=>W(`button`,{key:e.id,onClick:n=>t.value=e.id,class:O([`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors`,t.value===e.id?`bg-blue-600 text-white`:`text-gray-400 hover:text-white hover:bg-gray-800`])},[W(`span`,null,k(e.icon),1),Ui(` `+k(e.label),1)],10,ap)),64))]),r[1]||=W(`div`,{class:`px-5 py-4 border-t border-gray-800`},[W(`div`,{class:`text-xs text-gray-600`},`sese-engine v1.0`)],-1)]),W(`main`,op,[t.value===`dashboard`?(H(),Pi(Lu,{key:0})):t.value===`recent`?(H(),Pi(Md,{key:1})):t.value===`keywords`?(H(),Pi(tp,{key:2})):t.value===`search`?(H(),Pi(gf,{key:3})):G(``,!0)]),W(`nav`,sp,[(H(),U(B,null,L(n,e=>W(`button`,{key:e.id,onClick:n=>t.value=e.id,class:O([`flex flex-col items-center justify-center gap-0.5 flex-1 h-full transition-colors`,t.value===e.id?`text-blue-400`:`text-gray-500`])},[W(`span`,lp,k(e.icon),1),W(`span`,up,k(e.label),1)],10,cp)),64))])]))}}).mount(`#app`); \ No newline at end of file diff --git a/dist/index.html b/dist/index.html index 96589f3..ca62178 100644 --- a/dist/index.html +++ b/dist/index.html @@ -5,7 +5,7 @@ SESE 爬取管理 - + diff --git a/install.sh b/install.sh index f8235b6..1f9bb41 100644 --- a/install.sh +++ b/install.sh @@ -1,72 +1,125 @@ #!/bin/bash +set -e + +# ============================================= +# SESE 安装脚本 +# ============================================= APP_NAME="SESE" APP_PATH="/opt/$APP_NAME" SERVICE_FILE="/etc/systemd/system/$APP_NAME.service" LOG_PATH="/var/log/$APP_NAME" -echo "正在安装 $APP_NAME..." +# 检测是否在工程目录内 +if [ -d ".git" ] && [ -f "main.go" ]; then + echo "==> 检测到工程目录,直接使用当前代码" + PROJECT_DIR="$(pwd)" +else + echo "==> 拉取工程代码..." + cd /opt + if [ -d "$APP_NAME" ]; then + echo "工程已存在,更新代码..." + cd "$APP_NAME" + PROJECT_DIR="$(pwd)" + git pull origin master + git submodule update --init --recursive + else + git clone https://git.lmve.net/kevin/sese-engine-go.git "$APP_NAME" + cd "$APP_NAME" + PROJECT_DIR="$(pwd)" + git submodule update --init --recursive + fi +fi -# 编译前端 -echo "编译前端..." -cd sese-engine-ui +echo "==> 检查并停止旧版本..." +if systemctl is-active --quiet "$APP_NAME" 2>/dev/null; then + echo "停止旧服务..." + systemctl stop "$APP_NAME" + systemctl disable "$APP_NAME" 2>/dev/null || true +fi + +# 清理旧进程 +pkill -f "$APP_PATH/$APP_NAME" 2>/dev/null || true + +echo "==> 安装依赖..." + +# 安装必要工具 +if command -v apt-get &> /dev/null; then + apt-get update + apt-get install -y git nodejs npm golang-go make +elif command -v yum &> /dev/null; then + yum install -y git nodejs npm golang make +elif command -v pacman &> /dev/null; then + pacman -Sy --noconfirm git nodejs npm go make +fi + +echo "==> 创建目录..." +mkdir -p "$APP_PATH" +mkdir -p "$(dirname "$LOG_PATH")" + +echo "==> 编译前端..." +cd "$PROJECT_DIR/sese-engine-ui" +npm install npm run build -cd .. -cp -r sese-engine-ui/dist ./dist -# 编译应用 -echo "编译应用..." -CGO_ENABLED=0 GOOS=linux go build -o $APP_NAME -ldflags="-s -w" main.go +echo "==> 编译后端..." +cd "$PROJECT_DIR" +go mod download +CGO_ENABLED=0 go build -o "$APP_NAME" . -# 先停止服务 -sudo systemctl stop $APP_NAME -sudo systemctl disable $APP_NAME +echo "==> 部署到 $APP_PATH..." +mkdir -p "$APP_PATH" +cp "$PROJECT_DIR/$APP_NAME" "$APP_PATH/" +cp -r "$PROJECT_DIR/dist" "$APP_PATH/" -# 创建目录 -echo "创建目录..." -sudo mkdir -p $APP_PATH -sudo mkdir -p $LOG_PATH +echo "==> 配置用户..." +if id -u www &> /dev/null; then + echo "用户 www 已存在,跳过创建" +else + echo "创建用户 www..." + useradd -r -s /usr/sbin/nologin -d /var/www -M www +fi -# 复制文件 -echo "复制文件..." -sudo cp $APP_NAME $APP_PATH/ -sudo cp -r dist $APP_PATH/ +echo "==> 配置权限..." +chown -R www:www "$APP_PATH" +chown -R www:www "$LOG_PATH" +chmod +x "$APP_PATH/$APP_NAME" -# 创建服务文件 -echo "创建服务文件..." -sudo tee $SERVICE_FILE > /dev/null < 创建 systemd 服务..." +cat > "$SERVICE_FILE" < 重新加载 systemd 并启动服务..." +systemctl daemon-reload +systemctl enable "$APP_NAME" +systemctl restart "$APP_NAME" -# 重载并启动 -echo "启动服务..." -sudo systemctl daemon-reload -sudo systemctl enable $APP_NAME -sudo systemctl start $APP_NAME - -echo "安装完成!" -echo "使用以下命令管理服务:" -echo " sudo systemctl status $APP_NAME" -echo " sudo journalctl -u $APP_NAME -f" \ No newline at end of file +echo "" +echo "==> 安装完成!" +echo " 服务状态: systemctl status $APP_NAME" +echo " 日志查看: journalctl -u $APP_NAME -f" +echo " 应用日志: tail -f $LOG_PATH/stdout.log" diff --git a/sese-engine-ui b/sese-engine-ui index 5777561..52c1b9d 160000 --- a/sese-engine-ui +++ b/sese-engine-ui @@ -1 +1 @@ -Subproject commit 5777561d9f07d4963bf43822bba2627f5c5f55dc +Subproject commit 52c1b9de996d12e63bc2774f502dad3cec08bc6c