| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049 |
- <template>
- <div class="serve-page">
- <section class="registry-head">
- <div class="wrap">
- <div class="registry-inner">
- <div class="registry-text">
- <h1>MCP<span class="brand-blue">服务中心</span></h1>
- <div class="sub-title">企业级 MCP 智能服务中枢,打通大模型与环评专业能力</div>
- <p class="lead">
- 汇聚环评云助手全量 MCP 智能服务,遵循标准 MCP 服务目录规范。统一 Token 即可接入主流大模型,快速将法规检索、政策问答、排放限值等环评能力注入 AI 应用与 Agent,服务持续迭代更新
- </p>
- <div class="cta-row">
- <button type="button" class="btn-cta" :disabled="giftClaiming" @click="claimGift()">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 12 20 22 4 22 4 12"></polyline><rect x="2" y="7" width="20" height="5"></rect><line x1="12" y1="22" x2="12" y2="7"></line><path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z"></path><path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z"></path></svg>
- 领取MCP体验礼包
- </button>
- <button type="button" class="btn-ghost" @click="scrollToMatrix">
- 浏览全部服务
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14"></path><path d="m19 12-7 7-7-7"></path></svg>
- </button>
- </div>
- <div class="hero-meta">
- <span class="hm-item">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" fill="#EAF2FF"></circle><path d="m8.5 12.2 2.4 2.4 4.8-5" stroke="#1678FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>
- 统一接入 <b>生态环境领域最全数据</b>
- </span>
- <span class="hm-item">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" fill="#EAF2FF"></circle><path d="m8.5 12.2 2.4 2.4 4.8-5" stroke="#1678FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>
- 标准 MCP 协议接入
- </span>
- <span class="hm-item">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" fill="#EAF2FF"></circle><path d="m8.5 12.2 2.4 2.4 4.8-5" stroke="#1678FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>
- 统一 Token 授权
- </span>
- </div>
- </div>
- <div class="hero-visual" aria-hidden="true">
- <div class="hv-card">
- <div class="hv-head">
- <div class="hv-title">
- <span class="hv-sm">
- <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="6" r="3"></circle><circle cx="18" cy="18" r="3"></circle><path d="M8.7 10.7 15.3 7.3"></path><path d="m8.7 13.3 6.6 3.4"></path></svg>
- </span>
- MCP 服务中枢
- </div>
- <span class="hv-live"><span class="hv-ping"></span>实时连接</span>
- </div>
- <div class="hv-flow">
- <div class="hv-node">
- <span class="hv-ico app">
- <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="6" width="12" height="12" rx="2.5"></rect><rect x="10" y="10" width="4" height="4" rx="1"></rect><path d="M9 2.5v3M15 2.5v3M9 18.5v3M15 18.5v3M2.5 9h3M2.5 15h3M18.5 9h3M18.5 15h3"></path></svg>
- </span>
- <span class="hv-nt"><span class="hv-n1">AI 应用 / Agent</span><span class="hv-n2">智能体即插即用</span></span>
- </div>
- <div class="hv-line"></div>
- <div class="hv-node">
- <span class="hv-ico hub">
- <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m12 2 10 5.5L12 13 2 7.5z"></path><path d="m2 12.5 10 5.5 10-5.5"></path><path d="m2 17.5 10 5.5 10-5.5"></path></svg>
- </span>
- <span class="hv-nt"><span class="hv-n1">环评云助手 MCP</span><span class="hv-n2">统一接入服务目录</span></span>
- </div>
- </div>
- <div class="hv-grid">
- <div class="hv-tile" style="color:#059669;">
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path></svg>
- <span class="tx">法规检索</span>
- </div>
- <div class="hv-tile" style="color:#3B82F6;">
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"></circle><path d="m21 21-4.3-4.3"></path><path d="M11 8v6"></path><path d="M8 11h6"></path></svg>
- <span class="tx">语义检索</span>
- </div>
- <div class="hv-tile" style="color:#7C3AED;">
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8z"></path></svg>
- <span class="tx">政策问答</span>
- </div>
- <div class="hv-tile" style="color:#E8890C;">
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z"></path></svg>
- <span class="tx">排放限值</span>
- </div>
- <div class="hv-tile" style="color:#EF4444;">
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path><path d="M16 13H8"></path><path d="M16 17H8"></path><path d="M10 9H8"></path></svg>
- <span class="tx">报告检索</span>
- </div>
- <div class="hv-tile" style="color:#6366F1;">
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="m12 2 10 5.5L12 13 2 7.5z"></path><path d="m2 12.5 10 5.5 10-5.5"></path><path d="m2 17.5 10 5.5 10-5.5"></path></svg>
- <span class="tx">标准汇编</span>
- </div>
- </div>
- </div>
- <div class="hv-chip">生态环境领域最全数据</div>
- </div>
- </div>
- </div>
- </section>
- <section class="section" id="matrix">
- <div class="wrap">
- <div class="notice-bar">
- <span class="notice-horn">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 11l18-5v12L3 13v-2z"></path><path d="M11.6 16.8a3 3 0 1 1-5.8-1.6"></path></svg>
- </span>
- <div class="notice-roll">
- <span class="notice-text">环评云助手MCP工具限时免费调用,欢迎咨询和反馈。</span>
- </div>
- </div>
- <div class="matrix-bar">
- <div class="matrix-bar-left">
- <span class="matrix-count">{{ matrixCountText }}</span>
- </div>
- <div class="matrix-bar-right">
- <div class="matrix-search">
- <svg class="matrix-search-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"></circle><path d="m20 20-3.5-3.5"></path></svg>
- <input
- v-model.trim="searchKeyword"
- type="text"
- placeholder="搜索服务或工具关键字"
- autocomplete="off"
- />
- <button
- v-show="searchKeyword"
- type="button"
- class="matrix-search-clear show"
- aria-label="清空"
- @click="searchKeyword = ''"
- >
- <svg width="8" height="8" viewBox="0 0 8 8" aria-hidden="true">
- <path
- d="M1.5 1.5l5 5M6.5 1.5l-5 5"
- fill="none"
- stroke="currentColor"
- stroke-width="1.4"
- stroke-linecap="round"
- />
- </svg>
- </button>
- </div>
- </div>
- </div>
- <div ref="serviceGridRef" class="service-grid masonry">
- <div
- v-for="(column, colIndex) in masonryColumns"
- :key="colIndex"
- class="masonry-col"
- >
- <article
- v-for="item in column"
- :key="item.id"
- class="svc-card"
- >
- <div class="svc-top">
- <div class="svc-icon" :style="item.iconStyle">
- <img
- v-if="item.iconKind === 'img'"
- :src="item.iconSrc"
- :alt="item.name"
- class="svc-icon-img"
- decoding="async"
- referrerpolicy="no-referrer"
- @error="onIconError(item)"
- />
- <span
- v-else-if="item.iconKind === 'inline'"
- class="svc-icon-inline"
- v-html="item.iconSrc"
- ></span>
- <svg v-else class="svc-icon-fallback" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="m12 2 10 5.5L12 13 2 7.5z"></path><path d="m2 12.5 10 5.5 10-5.5"></path><path d="m2 17.5 10 5.5 10-5.5"></path></svg>
- </div>
- <h3>{{ item.name }}</h3>
- </div>
- <p class="svc-desc">{{ item.desc }}</p>
- <div class="svc-tools">
- <b>包含工具</b>
- <em>{{ item.tools?.length || 0 }} 个</em>
- </div>
- <div class="svc-tool-list">
- <div
- v-for="(tool, index) in item.tools"
- :key="tool.id || tool.code"
- class="tool-item"
- :class="{ active: selectedTool[item.id] === index }"
- @click="selectedTool[item.id] = index"
- >
- <span class="tool-name">{{ tool.label }}</span>
- <span class="tool-fee">
- <template v-if="tool.fee === 'limit'">
- <span class="fee-tag rate">{{ tool.rate }}x tokens</span>
- <span class="fee-tag limitfree">限时免费</span>
- </template>
- <span v-else class="fee-tag free">免费工具</span>
- </span>
- </div>
- </div>
- <div class="svc-actions">
- <button type="button" class="btn-sm ghost" @click="openServiceDetail(item)">
- <svg class="btn-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7-10-7-10-7z"></path><circle cx="12" cy="12" r="3"></circle></svg>
- 查看详情
- </button>
- <button type="button" class="btn-sm primary" @click="openServiceSimulate(item)">
- <svg class="btn-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2 4.5 13.5H11l-1 8.5 8.5-11.5H12z"></path></svg>
- 模拟调用
- </button>
- </div>
- </article>
- </div>
- </div>
- <div v-show="!filteredServiceList.length" class="matrix-empty">
- 当前筛选条件下没有匹配的服务。
- </div>
- <div v-show="filteredServiceList.length" class="matrix-end show">
- 已经到底啦,共 <b>{{ filteredServiceList.length }}</b> 个服务全部加载完成
- </div>
- </div>
- </section>
- <footer class="site-footer">
- <div class="wrap">
- <div class="footer-main">
- <div class="footer-brand">
- <div class="fb-title">
- <span class="fb-logo">云</span>
- 环评云助手 · MCP服务中心
- </div>
- <p class="fb-desc">统一 Token 接入主流大模型,一站式调用法规检索、政策问答、排放限值等环评 MCP 能力。</p>
- </div>
- <div class="footer-col">
- <h4>本页快捷跳转</h4>
- <ul>
- <li><button type="button" :disabled="giftClaiming" @click="claimGift()"><span class="footer-link-ico">◆</span>领取MCP体验礼包</button></li>
- <li><button type="button" @click="scrollToTop"><span class="footer-link-ico">↑</span>回到顶部</button></li>
- </ul>
- </div>
- <div class="footer-col">
- <h4>快捷产品入口</h4>
- <ul>
- <li><button type="button" @click="openRouteInNewTab('mcp')"><span class="footer-link-ico">➤</span>MCP 服务</button></li>
- <li><a href="https://www.eiacloud.com" target="_blank" rel="noopener noreferrer"><span class="footer-link-ico">➤</span>环评云助手官网</a></li>
- <li><a href="https://www.eiacloud.com/bbs" target="_blank" rel="noopener noreferrer"><span class="footer-link-ico">➤</span>环评论坛</a></li>
- </ul>
- </div>
- <div class="footer-col">
- <h4>服务信息</h4>
- <ul>
- <li><a href="https://www.eiacloud.com/hpy/lawstandardApp/getDetailYS?type=agreement" target="_blank" rel="noopener noreferrer"><span class="footer-link-ico">◎</span>用户协议</a></li>
- <li><a href="https://www.eiacloud.com/hpy/lawstandardApp/getDetailYS" target="_blank" rel="noopener noreferrer"><span class="footer-link-ico">◎</span>隐私政策</a></li>
- <li class="footer-cs">
- <button type="button">
- <span class="footer-link-ico">◎</span>联系客服获取接入支持
- </button>
- <div class="home-footer-popover">
- <img src="@/assets/images/service_WeChat.png" alt="云助手客服微信" class="home-footer-popover-img" />
- <div class="home-footer-popover-sub">云助手客服微信</div>
- </div>
- </li>
- </ul>
- </div>
- </div>
- </div>
- </footer>
- <!-- 未登录:需先登录弹窗 -->
- <Teleport to="body">
- <div
- v-if="needLoginVisible"
- class="mcp-serve-gift wb-overlay show"
- @click.self="needLoginVisible = false"
- >
- <div class="wb-modal wb-gift-modal" role="dialog" aria-modal="true" aria-labelledby="needLoginTitle">
- <button type="button" class="wb-close" aria-label="关闭" @click="needLoginVisible = false">
- <svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true">
- <path
- d="M2.5 2.5l7 7M9.5 2.5l-7 7"
- fill="none"
- stroke="currentColor"
- stroke-width="1.6"
- stroke-linecap="round"
- />
- </svg>
- </button>
- <div class="wb-gift-hero">
- <div class="wb-gift-kicker">MCP 体验礼包</div>
- <div class="wb-gift-visual" aria-hidden="true">
- <svg class="wb-gift-icon" viewBox="0 0 48 48" fill="none" stroke="#E8890C" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
- <rect x="6" y="16" width="36" height="26" rx="3.5"></rect>
- <rect x="3" y="10" width="42" height="8" rx="2.6"></rect>
- <path d="M24 10v32"></path>
- <path d="M24 10c-3.6 0-8.4-.7-8.4-4.4 0-2.4 1.9-3.6 3.8-3.6 3.2 0 4.6 4 4.6 8z"></path>
- <path d="M24 10c3.6 0 8.4-.7 8.4-4.4 0-2.4-1.9-3.6-3.8-3.6-3.2 0-4.6 4-4.6 8z"></path>
- </svg>
- </div>
- <h3 class="wb-gift-title" id="needLoginTitle">登录即可领取 {{ giftPointsText }} 云点体验礼包</h3>
- <p class="wb-gift-sub">用于 MCP 工具调用体验,约可完成 200 次提问</p>
- </div>
- <div class="wb-modal-foot wb-gift-foot">
- <button type="button" class="wb-btn ghost" @click="needLoginVisible = false">暂不领取</button>
- <button type="button" class="wb-btn primary" @click="goLoginToClaim">去登录领取</button>
- </div>
- </div>
- </div>
- <!-- 领取成功弹窗 -->
- <div
- v-if="claimSuccessVisible"
- class="mcp-serve-gift wb-overlay show"
- @click.self="claimSuccessVisible = false"
- >
- <div class="wb-modal wb-gift-modal" role="dialog" aria-modal="true" aria-labelledby="claimSuccessName">
- <button type="button" class="wb-close" aria-label="关闭" @click="claimSuccessVisible = false">
- <svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true">
- <path
- d="M2.5 2.5l7 7M9.5 2.5l-7 7"
- fill="none"
- stroke="currentColor"
- stroke-width="1.6"
- stroke-linecap="round"
- />
- </svg>
- </button>
- <div class="wb-gift-hero">
- <div class="wb-gift-kicker is-ok">领取成功</div>
- <div
- class="wb-gift-visual is-ok"
- :class="{ 'wb-gift-open': giftOpenAnim }"
- aria-hidden="true"
- >
- <svg class="wb-gift-icon" viewBox="0 0 48 48" fill="none" stroke="#148A45" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
- <rect x="6" y="16" width="36" height="26" rx="3.5"></rect>
- <rect class="gift-lid" x="3" y="10" width="42" height="8" rx="2.6"></rect>
- <path d="M24 10v32"></path>
- <path d="M24 10c-3.6 0-8.4-.7-8.4-4.4 0-2.4 1.9-3.6 3.8-3.6 3.2 0 4.6 4 4.6 8z"></path>
- <path d="M24 10c3.6 0 8.4-.7 8.4-4.4 0-2.4-1.9-3.6-3.8-3.6-3.2 0-4.6 4-4.6 8z"></path>
- </svg>
- </div>
- <h3 class="wb-gift-title" id="claimSuccessName">MCP 体验礼包领取成功</h3>
- <p class="wb-gift-sub" v-html="claimSuccessMsg"></p>
- </div>
- <div class="wb-modal-foot wb-gift-foot">
- <button type="button" class="wb-btn primary" @click="claimSuccessVisible = false">开始体验</button>
- </div>
- </div>
- </div>
- </Teleport>
- </div>
- </template>
- <script setup>
- import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref } from "vue";
- import { useRouter } from "vue-router";
- import { ElMessage } from "element-plus";
- import { getMcpCenterList, getMcpReceiveYdStatus, receiveMcpYd } from "@/api/mcp";
- import { isUserLoggedIn } from "@/utils/auth";
- import globalStore from "@/store/modules/global";
- const router = useRouter();
- const global = globalStore();
- const serviceList = ref([]);
- const selectedTool = reactive({});
- const searchKeyword = ref("");
- const serviceGridRef = ref(null);
- const columnCount = ref(3);
- const giftReceived = ref(false);
- const giftClaiming = ref(false);
- /** 体验礼包赠送云点数 */
- const receivedYdCount = ref(0);
- /** receiveYd 原始返回(具体字段稍后对齐) */
- const receiveYdRaw = ref(null);
- const needLoginVisible = ref(false);
- const claimSuccessVisible = ref(false);
- const giftOpenAnim = ref(false);
- const claimSuccessMsg = ref("");
- let resizeTimer = null;
- const giftPointsText = computed(() => {
- return receivedYdCount.value > 0 ? receivedYdCount.value : 10;
- });
- const userDisplayName = computed(() => {
- const info = global.userInfo || {};
- return info.showName || info.username || "环评用户";
- });
- const userYundianBalance = computed(() => {
- if (global.yundian != null && global.yundian !== "") return Number(global.yundian) || 0;
- return Number(global.userInfo?.ydCount) || 0;
- });
- const syncSelectedTool = (list) => {
- list.forEach((item) => {
- if (selectedTool[item.id] === undefined) selectedTool[item.id] = 0;
- });
- };
- const parseCssStyle = (cssText) => {
- if (!cssText) return {};
- const style = {};
- String(cssText)
- .split(";")
- .forEach((rule) => {
- const colonIndex = rule.indexOf(":");
- if (colonIndex === -1) return;
- const key = rule.slice(0, colonIndex).trim();
- const value = rule.slice(colonIndex + 1).trim();
- if (!key || !value) return;
- const prop = key.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
- style[prop] = value;
- });
- return style;
- };
- const formatPayCoefficient = (value) => {
- if (value == null || value === "") return "";
- const num = Number(value);
- if (Number.isNaN(num)) return String(value);
- return String(num);
- };
- /** 统一解析 png / svg 地址,以及内联 svg 字符串 */
- const resolveIconSource = (iconUrl) => {
- const raw = String(iconUrl || "").trim();
- if (!raw) return { iconKind: "none", iconSrc: "" };
- if (/^<svg[\s>]/i.test(raw) || raw.startsWith("<?xml")) {
- return { iconKind: "inline", iconSrc: raw };
- }
- return { iconKind: "img", iconSrc: raw };
- };
- const onIconError = (item) => {
- if (!item) return;
- item.iconKind = "none";
- item.iconSrc = "";
- };
- const openRouteInNewTab = (name, query = {}) => {
- const { href } = router.resolve({ name, query });
- window.open(href, "_blank", "noopener,noreferrer");
- };
- /** 查看详情:跳服务详情,并带上当前选中工具 */
- const openServiceDetail = (item) => {
- if (!item?.id) {
- ElMessage.info("该服务详情页待平台公布");
- return;
- }
- const toolIndex = selectedTool[item.id] ?? 0;
- const tool = item.tools?.[toolIndex];
- const query = { id: item.id };
- if (tool?.id) query.toolId = tool.id;
- openRouteInNewTab("mcpServer", query);
- };
- /** 模拟调用:跳在线测试,定位服务并带上当前选中工具 */
- const openServiceSimulate = (item) => {
- const toolIndex = selectedTool[item.id] ?? 0;
- const tool = item.tools?.[toolIndex];
- const query = {};
- if (item?.id) query.id = item.id;
- if (tool?.id) query.toolId = tool.id;
- if (tool?.code) query.toolCode = tool.code;
- openRouteInNewTab("mcpOnlineTest", query);
- };
- /** payType: 1 免费工具;2 按系数计费(展示限时免费) */
- const mapToolItem = (tool = {}) => {
- const payType = Number(tool.payType);
- const isLimit = payType === 2;
- return {
- id: tool.id || "",
- label: tool.name || "",
- code: tool.code || "",
- desc: tool.describe || "",
- fee: isLimit ? "limit" : "free",
- rate: isLimit ? formatPayCoefficient(tool.payCoefficient) : "",
- sort: Number(tool.sort) || 0,
- };
- };
- const mapCenterItem = (item = {}) => {
- const tools = (Array.isArray(item.toolList) ? item.toolList : [])
- .map(mapToolItem)
- .sort((a, b) => a.sort - b.sort);
- const tags = String(item.tagSet || "")
- .split(",")
- .map((tag) => tag.trim())
- .filter(Boolean);
- const icon = resolveIconSource(item.iconUrl);
- return {
- id: item.id || "",
- name: item.name || "",
- code: item.code || "",
- desc: item.describe || "",
- tags,
- iconUrl: item.iconUrl || "",
- iconKind: icon.iconKind,
- iconSrc: icon.iconSrc,
- iconStyle: parseCssStyle(item.iconStyle),
- cardBackground: String(item.cardBackground || "").replace(/;+\s*$/, ""),
- sort: Number(item.sort) || 0,
- tools,
- };
- };
- const fetchCenterList = async () => {
- try {
- const res = await getMcpCenterList();
- const list = Array.isArray(res?.data) ? res.data : [];
- serviceList.value = list
- .map(mapCenterItem)
- .sort((a, b) => a.sort - b.sort);
- syncSelectedTool(serviceList.value);
- nextTick(updateColumnCount);
- } catch (error) {
- console.error(error);
- }
- };
- const fetchReceiveYdStatus = async () => {
- try {
- const res = await getMcpReceiveYdStatus();
- // { msg, code, receivedYdCount, claimed }
- giftReceived.value = res?.claimed === true || res?.claimed === 1 || res?.claimed === "1";
- const count = Number(res?.receivedYdCount);
- receivedYdCount.value = Number.isFinite(count) ? count : 0;
- } catch (error) {
- console.error(error);
- }
- };
- const filteredServiceList = computed(() => {
- const kw = searchKeyword.value.trim().toLowerCase();
- if (!kw) return serviceList.value;
- return serviceList.value.filter((item) => {
- const hay = [
- item.name,
- item.code,
- item.desc,
- ...(item.tags || []),
- ...(item.tools || []).flatMap((tool) => [tool.label, tool.code]),
- ]
- .join(" ")
- .toLowerCase();
- return hay.includes(kw);
- });
- });
- const masonryColumns = computed(() => {
- const list = filteredServiceList.value;
- const n = Math.max(1, columnCount.value);
- const cols = Array.from({ length: n }, () => []);
- const heights = Array.from({ length: n }, () => 0);
- list.forEach((item) => {
- let best = 0;
- for (let i = 1; i < n; i += 1) {
- if (heights[i] < heights[best]) best = i;
- }
- cols[best].push(item);
- heights[best] += 220 + (item.tools?.length || 0) * 52;
- });
- return cols;
- });
- const updateColumnCount = () => {
- const width = serviceGridRef.value?.clientWidth || 0;
- if (!width) return;
- columnCount.value = Math.max(1, Math.floor((width + 18) / (330 + 18)));
- };
- const matrixCountText = computed(() => {
- const total = filteredServiceList.value.length;
- if (searchKeyword.value.trim()) {
- return `筛选结果 ${total} 个服务 · 已加载 ${total} 个`;
- }
- return `共 ${total} 个服务 · 已加载 ${total} 个`;
- });
- const onWindowResize = () => {
- clearTimeout(resizeTimer);
- resizeTimer = setTimeout(updateColumnCount, 160);
- };
- onMounted(async () => {
- nextTick(updateColumnCount);
- window.addEventListener("resize", onWindowResize);
- window.addEventListener("keydown", onGiftModalKeydown);
- fetchCenterList();
- await fetchReceiveYdStatus();
- await resumeClaimAfterLogin();
- });
- onBeforeUnmount(() => {
- clearTimeout(resizeTimer);
- window.removeEventListener("resize", onWindowResize);
- window.removeEventListener("keydown", onGiftModalKeydown);
- });
- const onGiftModalKeydown = (e) => {
- if (e.key !== "Escape") return;
- needLoginVisible.value = false;
- claimSuccessVisible.value = false;
- };
- const scrollToMatrix = () => {
- document.getElementById("matrix")?.scrollIntoView({ behavior: "smooth", block: "start" });
- };
- const scrollToTop = () => {
- const main = document.querySelector(".page-main");
- if (main) {
- main.scrollTo({ top: 0, behavior: "smooth" });
- return;
- }
- const shell = document.querySelector(".page-shell");
- if (shell) {
- shell.scrollTo({ top: 0, behavior: "smooth" });
- return;
- }
- window.scrollTo({ top: 0, behavior: "smooth" });
- };
- const playGiftOpen = async () => {
- giftOpenAnim.value = false;
- await nextTick();
- giftOpenAnim.value = true;
- };
- const openClaimSuccessModal = async () => {
- const points = receivedYdCount.value > 0 ? receivedYdCount.value : 10;
- claimSuccessMsg.value =
- `尊敬的用户 <b>${userDisplayName.value}</b>,体验礼包已到账 <span class="wb-points-em">${points} 云点</span><br>当前余额 <b>${userYundianBalance.value}</b> 云点,可直接用于 MCP 工具调用`;
- claimSuccessVisible.value = true;
- await playGiftOpen();
- };
- const goLoginToClaim = () => {
- needLoginVisible.value = false;
- try {
- sessionStorage.setItem("mcp_claim_gift_pending", "1");
- } catch (_) {
- /* ignore */
- }
- router.push({
- name: "login",
- query: { redirect: router.currentRoute.value.fullPath || "/serve" },
- });
- };
- const resumeClaimAfterLogin = async () => {
- let pending = false;
- try {
- pending = sessionStorage.getItem("mcp_claim_gift_pending") === "1";
- if (pending) sessionStorage.removeItem("mcp_claim_gift_pending");
- } catch (_) {
- /* ignore */
- }
- if (!pending || !isUserLoggedIn()) return;
- await claimGift();
- };
- const claimGift = async () => {
- if (!isUserLoggedIn()) {
- needLoginVisible.value = true;
- return;
- }
- if (giftReceived.value) {
- openClaimSuccessModal();
- return;
- }
- if (giftClaiming.value) return;
- giftClaiming.value = true;
- try {
- const res = await receiveMcpYd();
- receiveYdRaw.value = res ?? null;
- giftReceived.value = true;
- await fetchReceiveYdStatus();
- const points = receivedYdCount.value > 0 ? receivedYdCount.value : 10;
- const nextBalance = userYundianBalance.value + points;
- global.setYundian(nextBalance);
- await openClaimSuccessModal();
- } catch (error) {
- console.error(error);
- } finally {
- giftClaiming.value = false;
- }
- };
- </script>
- <style scoped lang="less">
- .serve-page {
- --bg: #F6F9FD;
- --surface: #FFFFFF;
- --surface-2: #F7FAFE;
- --ink: #1B2533;
- --ink-2: #3E5063;
- --muted: #72859A;
- --line: #E4ECF6;
- --line-strong: #C9D8EA;
- --brand: #1678ff;
- --brand-ink: #0b63e0;
- --brand-soft: #EAF2FF;
- --brand-soft-2: #F0F6FF;
- --radius: 16px;
- --shadow-card: 0 1px 2px rgba(22, 51, 84, 0.05), 0 10px 28px -18px rgba(22, 51, 84, 0.18);
- --shadow-lift: 0 2px 6px rgba(22, 51, 84, 0.06), 0 20px 44px -22px rgba(22, 51, 84, 0.26);
- --font-sans: "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
- --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
- box-sizing: border-box;
- min-height: calc(100vh - 64px);
- background: var(--bg);
- color: var(--ink);
- font-family: var(--font-sans);
- line-height: 1.6;
- font-size: 15px;
- }
- .serve-page *,
- .serve-page *::before,
- .serve-page *::after {
- box-sizing: border-box;
- }
- .serve-page button {
- appearance: none;
- font-family: inherit;
- outline: none;
- }
- .serve-page button:focus,
- .serve-page button:focus-visible {
- outline: none;
- }
- .wrap {
- max-width: 1180px;
- margin: 0 auto;
- padding: 0 24px;
- }
- .registry-head {
- position: relative;
- overflow: hidden;
- display: flex;
- align-items: center;
- height: 520px;
- box-sizing: border-box;
- padding: 0;
- background:
- radial-gradient(720px 400px at 84% -18%, rgba(22, 120, 255, 0.16), transparent 62%),
- radial-gradient(560px 380px at -6% 116%, rgba(77, 154, 255, 0.13), transparent 60%),
- linear-gradient(118deg, #EDF5FF 0%, #F8FBFF 46%, #E9F2FF 100%);
- border-bottom: 1px solid var(--line);
- }
- .registry-head::before {
- content: "";
- position: absolute;
- inset: 0;
- pointer-events: none;
- background-image:
- linear-gradient(rgba(22, 120, 255, 0.05) 1px, transparent 1px),
- linear-gradient(90deg, rgba(22, 120, 255, 0.05) 1px, transparent 1px);
- background-size: 44px 44px;
- -webkit-mask-image: radial-gradient(900px 480px at 66% 42%, #000 0%, transparent 78%);
- mask-image: radial-gradient(900px 480px at 66% 42%, #000 0%, transparent 78%);
- }
- .registry-head::after {
- content: "";
- position: absolute;
- right: -200px;
- top: -230px;
- width: 580px;
- height: 580px;
- border-radius: 50%;
- pointer-events: none;
- border: 1px dashed rgba(22, 120, 255, 0.20);
- box-shadow: 0 0 0 44px rgba(22, 120, 255, 0.035), 0 0 0 106px rgba(22, 120, 255, 0.02);
- }
- .registry-head .wrap {
- position: relative;
- z-index: 1;
- width: 100%;
- }
- .registry-inner {
- display: flex;
- align-items: center;
- gap: 56px;
- }
- .registry-text {
- flex: 1;
- min-width: 0;
- max-width: 656px;
- }
- .registry-head h1 {
- margin: 0;
- font-weight: 800;
- font-size: clamp(40px, 5.2vw, 58px);
- line-height: 1.12;
- letter-spacing: -1.5px;
- color: #0B1F3F;
- }
- .brand-blue {
- background: linear-gradient(100deg, #3B96FF, #1678FF 55%, #0A5FE0);
- -webkit-background-clip: text;
- background-clip: text;
- color: transparent;
- }
- .sub-title {
- margin-top: 16px;
- font-weight: 600;
- font-size: clamp(16px, 1.9vw, 19px);
- color: #2E4A6B;
- }
- .lead {
- margin: 22px 0 0;
- max-width: 600px;
- color: #5F7490;
- font-size: 14.5px;
- line-height: 2;
- }
- .cta-row {
- display: flex;
- align-items: center;
- gap: 14px;
- flex-wrap: wrap;
- margin-top: 38px;
- }
- .btn-cta,
- .btn-ghost {
- display: inline-flex;
- align-items: center;
- gap: 8px;
- border-radius: 999px;
- cursor: pointer;
- font-family: inherit;
- }
- .btn-cta {
- padding: 13px 28px;
- font-size: 14.5px;
- font-weight: 700;
- background: linear-gradient(135deg, #2E8CFF, #1678FF 60%, #0B63E0);
- color: #fff;
- border: none;
- box-shadow: 0 12px 26px -10px rgba(22, 120, 255, 0.65);
- }
- .btn-cta:hover {
- transform: translateY(-2px);
- filter: brightness(1.07);
- border-color: transparent;
- box-shadow: 0 16px 34px -12px rgba(22, 120, 255, 0.78);
- }
- .btn-cta:disabled {
- opacity: 0.6;
- cursor: not-allowed;
- transform: none;
- filter: none;
- box-shadow: 0 8px 18px -10px rgba(22, 120, 255, 0.4);
- }
- .btn-ghost {
- padding: 13px 26px;
- font-size: 14px;
- font-weight: 600;
- background: rgba(255, 255, 255, 0.8);
- color: #33506F;
- border: 1px solid rgba(154, 182, 214, 0.9);
- }
- .btn-ghost:hover {
- border-color: #1678FF;
- color: #1678FF;
- background: #fff;
- transform: translateY(-1px);
- }
- .hero-meta {
- display: flex;
- align-items: center;
- gap: 24px;
- flex-wrap: wrap;
- margin-top: 26px;
- }
- .hm-item {
- display: inline-flex;
- align-items: center;
- gap: 7px;
- font-size: 13px;
- color: #57708C;
- font-weight: 500;
- }
- .hm-item b {
- color: #1678FF;
- font-weight: 700;
- }
- .hero-visual {
- flex: none;
- width: 420px;
- position: relative;
- }
- .hv-card {
- position: relative;
- background: rgba(255, 255, 255, 0.82);
- border: 1px solid rgba(22, 120, 255, 0.16);
- border-radius: 22px;
- padding: 18px;
- box-shadow: 0 30px 64px -26px rgba(22, 80, 160, 0.38);
- animation: hvFloat 7s ease-in-out infinite;
- }
- @keyframes hvFloat {
- 0%, 100% { transform: translateY(0); }
- 50% { transform: translateY(-7px); }
- }
- .hv-head {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 10px;
- padding: 0 2px 13px;
- border-bottom: 1px dashed rgba(22, 120, 255, 0.2);
- }
- .hv-title {
- display: inline-flex;
- align-items: center;
- gap: 8px;
- font-size: 12.5px;
- font-weight: 700;
- color: #22395C;
- }
- .hv-sm {
- width: 22px;
- height: 22px;
- flex: none;
- border-radius: 7px;
- background: linear-gradient(135deg, #EAF2FF, #DCE9FF);
- display: inline-flex;
- align-items: center;
- justify-content: center;
- color: #1678FF;
- }
- .hv-live {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- font-size: 11px;
- font-weight: 600;
- color: #17a14b;
- }
- .hv-ping {
- width: 6px;
- height: 6px;
- border-radius: 50%;
- background: #17a14b;
- animation: hvPing 2s ease-out infinite;
- }
- @keyframes hvPing {
- 0% { box-shadow: 0 0 0 0 rgba(23, 161, 75, 0.4); }
- 70% { box-shadow: 0 0 0 8px rgba(23, 161, 75, 0); }
- 100% { box-shadow: 0 0 0 0 rgba(23, 161, 75, 0); }
- }
- .hv-flow {
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 16px 2px 14px;
- }
- .hv-node {
- flex: 1;
- min-width: 0;
- display: flex;
- align-items: center;
- gap: 9px;
- padding: 10px 11px;
- border-radius: 14px;
- background: #fff;
- border: 1px solid #E3ECF7;
- }
- .hv-ico {
- width: 32px;
- height: 32px;
- flex: none;
- border-radius: 9px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .hv-ico.app {
- background: linear-gradient(135deg, #EDF4FF, #DCE9FF);
- color: #1678FF;
- }
- .hv-ico.hub {
- background: linear-gradient(135deg, #1678FF, #0A5FE0);
- color: #fff;
- }
- .hv-n1,
- .hv-n2 {
- display: block;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .hv-n1 {
- font-size: 12px;
- font-weight: 700;
- color: #22354F;
- }
- .hv-n2 {
- font-size: 10.5px;
- color: #8CA0B8;
- }
- .hv-line {
- flex: none;
- width: 36px;
- height: 2px;
- border-radius: 2px;
- background: repeating-linear-gradient(90deg, rgba(22, 120, 255, 0.8) 0 5px, transparent 5px 10px);
- animation: hvDash 1.1s linear infinite;
- }
- @keyframes hvDash {
- to { background-position: 10px 0; }
- }
- .hv-grid {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 8px;
- }
- .hv-tile {
- display: flex;
- align-items: center;
- gap: 7px;
- min-width: 0;
- padding: 10px 11px;
- border-radius: 12px;
- background: #fff;
- border: 1px solid #E6EEF8;
- }
- .hv-tile svg {
- flex: none;
- width: 18px;
- height: 18px;
- }
- .hv-tile .tx {
- font-size: 11px;
- font-weight: 600;
- color: #3A5575;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .hv-chip {
- position: absolute;
- right: -10px;
- bottom: -13px;
- padding: 7px 12px;
- border-radius: 10px;
- background: linear-gradient(135deg, #0B63E0, #1678FF);
- color: #fff;
- font-size: 10.5px;
- font-weight: 600;
- box-shadow: 0 12px 24px -10px rgba(22, 120, 255, 0.6);
- }
- .section {
- padding: 30px 0 72px;
- }
- #matrix {
- scroll-margin-top: 12px;
- }
- .notice-bar {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 11px 16px;
- margin-bottom: 16px;
- background: linear-gradient(90deg, #EAF2FF 0%, #F0F7FF 60%, rgba(240, 247, 255, 0) 100%);
- border: 1px solid #D6E6FF;
- border-radius: 10px;
- overflow: hidden;
- white-space: nowrap;
- }
- .notice-horn {
- flex: none;
- display: inline-flex;
- color: #E8890C;
- }
- .notice-roll {
- position: relative;
- flex: 1;
- min-width: 0;
- height: 20px;
- overflow: hidden;
- }
- .notice-text {
- display: block;
- height: 20px;
- line-height: 20px;
- font-size: 13.5px;
- font-weight: 600;
- color: #1D4ED8;
- letter-spacing: 0.02em;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .matrix-bar {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 16px;
- flex-wrap: wrap;
- margin-bottom: 18px;
- }
- .matrix-bar-left,
- .matrix-bar-right {
- display: flex;
- align-items: center;
- gap: 10px;
- flex-wrap: wrap;
- }
- .matrix-count {
- font-size: 13px;
- color: var(--muted);
- font-variant-numeric: tabular-nums;
- }
- .matrix-search {
- position: relative;
- display: flex;
- align-items: center;
- gap: 7px;
- width: 250px;
- padding: 7px 12px;
- background: var(--surface);
- border: 1px solid var(--line-strong);
- border-radius: 9px;
- }
- .matrix-search:focus-within {
- border-color: var(--brand);
- box-shadow: 0 0 0 3px rgba(22, 120, 255, 0.12);
- }
- .matrix-search-ico {
- width: 15px;
- height: 15px;
- flex: none;
- color: var(--muted);
- }
- .matrix-search input {
- flex: 1;
- min-width: 0;
- border: none;
- outline: none;
- background: transparent;
- font-size: 13px;
- color: var(--ink);
- font-family: var(--font-sans);
- }
- .matrix-search input::placeholder {
- color: var(--muted);
- }
- .matrix-search-clear {
- display: none;
- flex: none;
- box-sizing: border-box;
- width: 16px;
- height: 16px;
- min-width: 16px;
- min-height: 16px;
- margin: 0;
- padding: 0;
- border: none;
- border-radius: 50%;
- background: var(--line-strong);
- color: #fff;
- font-size: 0;
- line-height: 0;
- cursor: pointer;
- appearance: none;
- align-items: center;
- justify-content: center;
- align-self: center;
- }
- .matrix-search-clear.show {
- display: inline-flex;
- }
- .matrix-search-clear svg {
- display: block;
- flex: none;
- }
- .service-grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
- gap: 18px;
- align-items: start;
- }
- .service-grid.masonry {
- display: flex;
- align-items: flex-start;
- gap: 18px;
- }
- .masonry-col {
- flex: 1;
- min-width: 0;
- display: flex;
- flex-direction: column;
- gap: 18px;
- }
- .svc-card {
- position: relative;
- display: flex;
- flex-direction: column;
- background:
- linear-gradient(var(--brand), var(--brand)) top / 100% 3px no-repeat,
- var(--surface);
- border: 1px solid var(--line);
- border-radius: var(--radius);
- padding: 22px;
- box-shadow: var(--shadow-card);
- overflow: visible;
- transition: transform 0.22s, box-shadow 0.22s, border-color 0.22s;
- }
- .svc-card:hover {
- transform: translateY(-3px);
- box-shadow: var(--shadow-lift);
- border-color: var(--line-strong);
- z-index: 2;
- }
- .svc-top {
- display: flex;
- align-items: center;
- gap: 12px;
- min-height: 60.8px;
- }
- .svc-icon {
- width: 46px;
- height: 46px;
- border-radius: 13px;
- flex: none;
- color: #fff;
- background: var(--brand);
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .svc-icon-img,
- .svc-icon-fallback {
- width: 24px;
- height: 24px;
- display: block;
- flex: none;
- }
- .svc-icon-img {
- object-fit: contain;
- }
- .svc-icon-inline {
- width: 24px;
- height: 24px;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- flex: none;
- line-height: 0;
- }
- .svc-icon-inline :deep(svg) {
- width: 24px;
- height: 24px;
- display: block;
- }
- .svc-card h3 {
- margin: 0;
- font-family: "PingFang SC", "PingFangSC-Regular", "Helvetica Neue", sans-serif;
- font-weight: 700;
- font-size: 19px;
- letter-spacing: 0.01em;
- line-height: 1.6;
- flex: 1;
- min-width: 0;
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
- }
- .svc-desc {
- order: 1;
- margin: 12px 0 0;
- font-size: 13.5px;
- color: var(--ink-2);
- line-height: 1.7;
- font-family: "PingFang SC", "PingFangSC-Regular", "Helvetica Neue", sans-serif;
- font-weight: 400;
- display: -webkit-box;
- -webkit-line-clamp: 3;
- -webkit-box-orient: vertical;
- overflow: hidden;
- }
- .svc-tools {
- order: 2;
- display: flex;
- align-items: baseline;
- gap: 8px;
- flex-wrap: wrap;
- margin-top: 16px;
- padding-top: 16px;
- border-top: 1px dashed var(--line);
- }
- .svc-tools b {
- font-size: 12.5px;
- color: var(--ink-2);
- font-weight: 600;
- padding-top: 2px;
- flex: none;
- }
- .svc-tools em {
- font-style: normal;
- font-size: 11.5px;
- color: var(--muted);
- }
- .svc-tool-list {
- order: 3;
- margin-top: 12px;
- display: flex;
- flex-direction: column;
- gap: 8px;
- }
- .tool-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12px;
- padding: 9px 12px;
- border: 1px solid var(--line);
- border-radius: 10px;
- background: var(--surface-2);
- cursor: pointer;
- transition: 0.16s;
- }
- .tool-item:hover {
- border-color: var(--brand);
- background: var(--brand-soft-2);
- }
- .tool-item.active {
- border-color: var(--brand);
- background: var(--brand-soft);
- box-shadow: 0 6px 12px -8px rgba(22, 120, 255, 0.45);
- }
- .tool-item .tool-name {
- flex: 1;
- min-width: 0;
- font-size: 13px;
- font-weight: 600;
- color: var(--ink);
- line-height: 1.5;
- }
- .tool-item.active .tool-name {
- color: var(--brand-ink);
- }
- .tool-fee {
- display: flex;
- align-items: center;
- gap: 6px;
- flex: none;
- }
- .fee-tag {
- flex: none;
- display: inline-flex;
- align-items: center;
- gap: 5px;
- font-size: 11.5px;
- font-weight: 600;
- padding: 3px 10px;
- border-radius: 999px;
- white-space: nowrap;
- letter-spacing: 0.01em;
- }
- .fee-tag.free {
- color: #0F766E;
- background: #E6F7F1;
- border: 1px solid #BFE9DD;
- }
- .fee-tag.rate {
- color: #A05A00;
- background: #FFF4E3;
- border: 1px solid #F5E3C2;
- font-family: var(--font-mono);
- font-size: 10.5px;
- font-variant-numeric: tabular-nums;
- }
- .fee-tag.limitfree {
- color: #C62828;
- background: #FFEBEC;
- border: 1px solid #FBD5D8;
- }
- .svc-actions {
- order: 4;
- margin-top: auto;
- display: flex;
- gap: 10px;
- flex-wrap: wrap;
- justify-content: flex-end;
- padding-top: 13px;
- }
- .btn-ico {
- width: 14px;
- height: 14px;
- flex: none;
- }
- .btn-sm {
- display: inline-flex;
- align-items: center;
- gap: 7px;
- padding: 8px 16px;
- border-radius: 999px;
- font-size: 13px;
- font-weight: 600;
- cursor: pointer;
- font-family: inherit;
- border: 1px solid transparent;
- }
- .btn-sm.primary {
- background: var(--brand);
- color: #fff;
- box-shadow: 0 8px 16px -8px rgba(22, 120, 255, 0.6);
- }
- .btn-sm.primary:hover {
- filter: brightness(1.06);
- border-color: transparent;
- }
- .btn-sm.ghost {
- background: var(--surface);
- color: var(--brand-ink);
- border-color: var(--line-strong);
- }
- .btn-sm.ghost:hover {
- border-color: var(--brand);
- }
- .matrix-empty {
- text-align: center;
- padding: 60px 20px;
- color: var(--muted);
- background: var(--surface);
- border: 1px dashed var(--line-strong);
- border-radius: var(--radius);
- }
- .matrix-end {
- display: none;
- text-align: center;
- padding: 30px 20px 6px;
- color: var(--muted);
- font-size: 12.5px;
- }
- .matrix-end.show {
- display: block;
- }
- .matrix-end::before {
- content: "";
- display: block;
- width: 56px;
- height: 1px;
- background: var(--line-strong);
- margin: 0 auto 16px;
- }
- .site-footer {
- padding: 44px 0 34px;
- background: #fff;
- border-top: 1px solid var(--line);
- }
- .footer-main {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- gap: 40px;
- flex-wrap: wrap;
- }
- .footer-brand {
- flex: 1.2;
- min-width: 260px;
- max-width: 380px;
- }
- .fb-title {
- display: flex;
- align-items: center;
- gap: 9px;
- font-size: 20px;
- font-weight: 700;
- color: var(--brand-ink);
- }
- .fb-logo {
- width: 26px;
- height: 26px;
- border-radius: 7px;
- background: linear-gradient(135deg, #2E8CFF, #1678FF);
- color: #fff;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 14px;
- font-weight: 700;
- }
- .fb-desc {
- margin-top: 12px;
- font-size: 13px;
- line-height: 1.9;
- color: var(--muted);
- }
- .footer-col {
- min-width: 150px;
- }
- .footer-col h4 {
- font-size: 14px;
- font-weight: 600;
- color: var(--ink);
- margin: 0 0 14px;
- position: relative;
- padding-left: 11px;
- }
- .footer-col h4::before {
- content: "";
- position: absolute;
- left: 0;
- top: 3px;
- width: 3px;
- height: 13px;
- border-radius: 2px;
- background: var(--brand);
- }
- .footer-col ul {
- list-style: none;
- margin: 0;
- padding: 0;
- }
- .footer-col li {
- margin-bottom: 10px;
- }
- .footer-col a,
- .footer-col button {
- background: none;
- border: none;
- border-radius: 0;
- padding: 0;
- cursor: pointer;
- font-size: 13px;
- font-weight: 400;
- color: #43556b;
- font-family: inherit;
- text-align: left;
- text-decoration: none;
- }
- .footer-col a:hover,
- .footer-col button:hover {
- color: var(--brand);
- border-color: transparent;
- }
- .footer-link-ico {
- margin-right: 6px;
- color: #93A5BB;
- }
- .footer-cs {
- position: relative;
- }
- .footer-cs:hover .home-footer-popover {
- opacity: 1;
- visibility: visible;
- transform: translateY(0);
- }
- .home-footer-popover {
- position: absolute;
- left: 0;
- bottom: calc(100% + 10px);
- z-index: 20;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 4px;
- width: 140px;
- padding: 10px;
- background: #fff;
- box-shadow: 0 0 6px rgba(0, 0, 0, 0.1);
- border-radius: 8px;
- opacity: 0;
- visibility: hidden;
- transform: translateY(4px);
- transition: 0.18s;
- }
- .home-footer-popover-img {
- display: block;
- width: 100%;
- height: auto;
- }
- .home-footer-popover-sub {
- font-size: 12px;
- color: #666;
- line-height: 16px;
- }
- @media (max-width: 1160px) {
- .hero-visual {
- display: none;
- }
- .registry-text {
- max-width: none;
- }
- .registry-head {
- height: auto;
- padding: 68px 0 78px;
- }
- }
- @media (max-width: 760px) {
- .footer-main {
- flex-direction: column;
- gap: 26px;
- }
- }
- @media (max-width: 520px) {
- .service-grid.masonry {
- flex-direction: column;
- }
- .matrix-search {
- width: 100%;
- }
- }
- </style>
- <style lang="less">
- /* 礼包弹窗:Teleport 到 body,使用非 scoped 保证样式完整生效 */
- .mcp-serve-gift.wb-overlay {
- position: fixed;
- inset: 0;
- z-index: 2000;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 24px;
- box-sizing: border-box;
- background: rgba(16, 34, 64, 0.42);
- backdrop-filter: blur(2px);
- font-family: "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
- }
- .mcp-serve-gift .wb-modal {
- width: min(94vw, 460px);
- background: #fff;
- border-radius: 24px;
- box-shadow:
- 0 40px 80px -24px rgba(8, 32, 72, 0.48),
- 0 0 0 1px rgba(22, 120, 255, 0.06);
- overflow: hidden;
- transform: translateY(0) scale(1);
- box-sizing: border-box;
- }
- .mcp-serve-gift .wb-close {
- position: absolute;
- top: 16px;
- right: 16px;
- z-index: 3;
- box-sizing: border-box;
- width: 32px;
- height: 32px;
- min-width: 32px;
- min-height: 32px;
- margin: 0;
- padding: 0;
- border: none;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.78);
- box-shadow: 0 4px 12px rgba(22, 51, 84, 0.08);
- color: #8ca0b8;
- font-size: 0;
- line-height: 0;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- appearance: none;
- }
- .mcp-serve-gift .wb-close svg {
- display: block;
- flex: none;
- }
- .mcp-serve-gift .wb-close:hover {
- background: #fff;
- color: #1678ff;
- }
- .mcp-serve-gift .wb-gift-modal {
- position: relative;
- }
- .mcp-serve-gift .wb-gift-hero {
- position: relative;
- overflow: hidden;
- padding: 36px 36px 8px;
- text-align: center;
- background:
- radial-gradient(ellipse 90% 70% at 50% -20%, rgba(255, 196, 80, 0.22), transparent 58%),
- radial-gradient(ellipse 70% 55% at 100% 0%, rgba(46, 140, 255, 0.18), transparent 55%),
- radial-gradient(ellipse 70% 55% at 0% 10%, rgba(22, 120, 255, 0.12), transparent 55%),
- linear-gradient(180deg, #eef5ff 0%, #f8fbff 42%, #ffffff 100%);
- }
- .mcp-serve-gift .wb-gift-hero::before {
- content: "";
- position: absolute;
- inset: 0;
- pointer-events: none;
- background-image:
- linear-gradient(rgba(22, 120, 255, 0.035) 1px, transparent 1px),
- linear-gradient(90deg, rgba(22, 120, 255, 0.035) 1px, transparent 1px);
- background-size: 28px 28px;
- -webkit-mask-image: radial-gradient(420px 220px at 50% 20%, #000 0%, transparent 75%);
- mask-image: radial-gradient(420px 220px at 50% 20%, #000 0%, transparent 75%);
- opacity: 0.9;
- }
- .mcp-serve-gift .wb-gift-hero > * {
- position: relative;
- z-index: 1;
- }
- .mcp-serve-gift .wb-gift-kicker {
- display: inline-flex;
- align-items: center;
- gap: 7px;
- margin: 0 auto 16px;
- padding: 6px 14px;
- border-radius: 999px;
- font-size: 12.5px;
- font-weight: 600;
- color: #1678ff;
- background: rgba(255, 255, 255, 0.78);
- border: 1px solid rgba(22, 120, 255, 0.18);
- box-shadow: 0 8px 20px -12px rgba(22, 120, 255, 0.45);
- backdrop-filter: blur(6px);
- }
- .mcp-serve-gift .wb-gift-kicker.is-ok {
- color: #148a45;
- background: rgba(255, 255, 255, 0.82);
- border-color: rgba(23, 161, 75, 0.22);
- box-shadow: 0 8px 20px -12px rgba(23, 161, 75, 0.35);
- }
- .mcp-serve-gift .wb-gift-visual {
- width: 64px;
- height: 64px;
- margin: 0 auto 14px;
- position: relative;
- border-radius: 20px;
- background: radial-gradient(circle at 50% 35%, #ffffff 0%, #fff8e8 60%, #ffe9b8 100%);
- box-shadow:
- 0 12px 24px -12px rgba(176, 110, 0, 0.4),
- 0 0 0 1px rgba(255, 200, 100, 0.25),
- inset 0 1px 0 rgba(255, 255, 255, 0.9);
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .mcp-serve-gift .wb-gift-visual .wb-gift-icon {
- width: 44px;
- height: 44px;
- display: block;
- overflow: visible;
- }
- .mcp-serve-gift .wb-gift-open {
- animation: mcpServeGiftPop 0.78s cubic-bezier(0.18, 1.5, 0.42, 1) both;
- }
- .mcp-serve-gift .wb-gift-open::before {
- content: "";
- position: absolute;
- left: 50%;
- top: 50%;
- width: 64px;
- height: 64px;
- margin: -32px 0 0 -32px;
- border-radius: 50%;
- border: 2px solid rgba(23, 161, 75, 0.5);
- pointer-events: none;
- animation: mcpServeGiftRing 1.1s ease-out 0.18s both;
- }
- .mcp-serve-gift .wb-gift-open .gift-lid {
- transform-origin: 24px 14px;
- animation: mcpServeGiftLid 0.82s cubic-bezier(0.3, 1.6, 0.4, 1) 0.12s both;
- }
- @keyframes mcpServeGiftPop {
- 0% {
- transform: scale(0.45) rotate(-12deg);
- opacity: 0;
- }
- 55% {
- transform: scale(1.14) rotate(4deg);
- opacity: 1;
- }
- 78% {
- transform: scale(0.96) rotate(-1.5deg);
- }
- 100% {
- transform: scale(1) rotate(0);
- opacity: 1;
- }
- }
- @keyframes mcpServeGiftLid {
- 0% {
- transform: translateY(0) rotate(0);
- }
- 45% {
- transform: translateY(-13px) rotate(-14deg);
- }
- 100% {
- transform: translateY(-8px) rotate(-9deg);
- }
- }
- @keyframes mcpServeGiftRing {
- 0% {
- transform: scale(0.55);
- opacity: 0.85;
- }
- 100% {
- transform: scale(2.05);
- opacity: 0;
- }
- }
- .mcp-serve-gift .wb-gift-visual.is-ok {
- background: radial-gradient(circle at 50% 35%, #ffffff 0%, #e9f9ef 55%, #b7e8c8 100%);
- box-shadow:
- 0 22px 44px -18px rgba(23, 161, 75, 0.38),
- 0 0 0 1px rgba(23, 161, 75, 0.18),
- inset 0 1px 0 rgba(255, 255, 255, 0.9);
- }
- .mcp-serve-gift .wb-gift-visual.is-ok svg {
- width: 46px;
- height: 46px;
- stroke: #148a45;
- fill: none;
- stroke-width: 2.8;
- stroke-linecap: round;
- stroke-linejoin: round;
- }
- .mcp-serve-gift .wb-gift-title {
- font-size: 22px;
- font-weight: 700;
- color: #0b1f3f;
- letter-spacing: 0.01em;
- margin: 0;
- line-height: 1.4;
- }
- .mcp-serve-gift .wb-gift-sub {
- margin: 12px auto 0;
- max-width: 100%;
- font-size: 14px;
- line-height: 1.75;
- color: #5f7490;
- }
- .mcp-serve-gift .wb-gift-sub b {
- color: #0b1f3f;
- font-weight: 600;
- }
- .mcp-serve-gift .wb-gift-sub .wb-points-em {
- color: #e03131;
- font-weight: 700;
- }
- .mcp-serve-gift .wb-modal-foot.wb-gift-foot {
- padding: 22px 36px 30px;
- display: flex;
- gap: 12px;
- justify-content: stretch;
- text-align: center;
- background: linear-gradient(180deg, rgba(255, 255, 255, 0), #fff 28%);
- box-sizing: border-box;
- }
- .mcp-serve-gift .wb-btn {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- flex: 1;
- min-width: 0;
- min-height: 46px;
- margin: 0;
- padding: 10px 20px;
- border: 1px solid transparent;
- border-radius: 14px;
- font-size: 15px;
- font-weight: 600;
- font-family: inherit;
- line-height: 1.2;
- cursor: pointer;
- appearance: none;
- outline: none;
- box-sizing: border-box;
- transition: 0.18s;
- }
- .mcp-serve-gift .wb-btn.primary {
- background: linear-gradient(135deg, #2e8cff, #1678ff 60%, #0b63e0);
- color: #fff;
- border-color: transparent;
- box-shadow: 0 12px 28px -10px rgba(22, 120, 255, 0.65);
- }
- .mcp-serve-gift .wb-btn.primary:hover {
- filter: brightness(1.07);
- transform: translateY(-1px);
- }
- .mcp-serve-gift .wb-btn.ghost {
- background: #f5f8fc;
- color: #4a6078;
- border-color: #e2eaf3;
- box-shadow: none;
- }
- .mcp-serve-gift .wb-btn.ghost:hover {
- background: #fff;
- border-color: #1678ff;
- color: #1678ff;
- }
- </style>
|