新闻详情

新闻详情

首页 / 资讯中心 / 详情

uniapp微信小程序手机号获取:getPhoneNumber与code换取

发布时间:2026/9/29 9:36:18来源:尧图网络
uniapp微信小程序手机号获取:getPhoneNumber与code换取
上周帮一个做社区团购的朋友收尾项目登录页卡在一键获取手机号上整整两天。他给我的截图里前端代码写得没错按钮、事件、回调全都照着网上那篇 2021 年的教程抄的encryptedData和iv也确实打印出来了可后端解密就是报41001。问题不在他写的代码上而在于微信这套手机号获取机制在过去两年里换了底层链路网上能搜到的大部分教程还在讲session_key解密那条老路抄下来自然处处漏风。这篇就把微信小程序里获取用户手机号这件事按我们团队真实项目里跑通的顺序完整捋一遍老方案为什么必须换、前置资质和隐私协议这两个最容易翻车的点怎么处理、uniapp 端按钮到底怎么写、服务端用 code 换取手机号的完整链路以及最实际的问题——这东西现在是按次收费的怎么在业务里把它用得不心疼。适合正在做微信小程序登录模块的前端和后端也适合用 uniapp 一份代码多端发布、需要给 App 和 H5 留兜底方案的同学。1. 老方案为什么必须换掉encryptedData 解密这条路的现实困境1.1 两条链路的核心差异到底在哪先把这个事情的来龙去脉说清楚。早期的手机号获取本质上是一个前端采集密文、后端拿钥匙解密的模式用户点击按钮后微信在客户端把手机号用session_key加密返回encryptedData和iv两个 Base64 字符串给前端前端再原封不动传给自己的服务器服务器得先通过wx.login拿到临时 code换回openid和session_key最后用session_key和iv对密文做 AES-128-CBC 解密。这条链路最大的问题不是技术难度而是它对会话状态的强依赖。session_key是一把钥匙但它不由你的服务器掌控——用户换个设备登录、长时间不活跃、微信端刷新登录态这把钥匙随时可能失效。一旦失效解密就会抛出-41001或者-41003而这个时候用户已经点过按钮了你既没法复用那个encryptedData也不能让用户再点一次体验和排查成本都很高。新的方案把这条链路彻底拆开了。同样是点击按钮微信直接返回一个一次性的动态令牌code你的服务器拿着这个code和自己的access_token去调微信的服务端接口直接换回手机号明文。整个过程中session_key这个中间态被彻底拿掉了前端只需要负责把code递出去后端只依赖自己的access_token——这是一个服务器完全可控的东西。对比维度老方案encryptedData 解密新方案code 换取前端需要传的字段encryptedData iv 登录 code仅一个 code后端依赖session_key易失效access_token服务器自管解密位置自己的服务器微信服务端密钥泄露风险session_key 一旦外泄风险极高无 session_key 概念有效期视会话而定不确定code 5 分钟内有效且只能用一次排查难度解密失败原因笼统错误码明确定位快从表里能看出来新方案不只是更安全更关键的是可排查性变强了。老方案失败时你只能看到一句decrypt data fail新方案失败时微信会明确告诉你40029 invalid code还是40001 invalid credential这在实际运维里差别巨大。1.2 什么情况下你还会碰到 encryptedData这里要提醒一个容易被误解的点新方案上线后老方案并不是立刻下线而是并行了一段时间。微信的过渡策略是按客户端基础库版本区分——基础库 2.21.2 及以上走code更低版本仍然只返回encryptedData。所以会出现一种情况你在真机上调试e.detail里code和encryptedData同时存在。这不是 bug是微信为了兼容做的一段时间的双发。官方明确建议忽略encryptedData只处理code。但如果你面对的是存量用户里还有一批老版本客户端比如某些定制机、长期未更新的旧设备稳妥的做法是在后端保留一条解密分支// 后端兼容兜底逻辑仅在 code 不存在时走解密 async function resolvePhone(detail, sessionKey) { if (detail.code) { // 新链路优先 return await exchangePhoneByCode(detail.code); } // 老链路兜底 if (detail.encryptedData detail.iv sessionKey) { return await decryptPhone(detail.encryptedData, detail.iv, sessionKey); } throw new Error(无法识别手机号获取回调请检查基础库版本); }真正麻烦的不是写这段兜底而是很多现成的登录 SDK尤其是 uni-app 生态里一些封装好的uni-id版本内部还在跑老逻辑你直接在项目里引入会遇到明明后台已开通手机号服务却总是解密失败的诡异现象。判断方法很简单翻一下 SDK 源码里有没有sessionKey相关的字段有就是老实现需要升级版本或者自己重写这一段。还有一个特别隐蔽的坑我第一次踩的时候排查了小半天wx.login返回的code和getPhoneNumber返回的code是两个完全不同的东西。前者是用来换openid/session_key的登录凭证后者是手机号的动态令牌两者名字一样有效期都是 5 分钟都可以用一次但接口完全不通用。把登录 code 传给getuserphonenumber会直接返回40029把手机号 code 传给jscode2session同样报40029。命令不认账但错误码一样很容易误判成code 过期了。2. 前置条件没对齐代码写对了也白搭2.1 个人主体小程序在资质这一关就被拦住了这个必须放在最前面说因为它是硬性门槛手机号快速验证组件只对非个人主体的小程序开放并且需要完成微信认证。个人主体的小程序在后台根本找不到手机号验证这个功能入口写再多代码也是白搭。我在外包项目里见过好几次这样的沟通场景客户拿着一个还没做认证的个人号让开发先做登录功能开发吭哧吭哧写完一测试直接失败最后卡在商务环节。所以接到需求的第一件事不是打开 IDE而是确认三件事小程序主体是不是企业/个体工商户、微信认证有没有过、后台功能菜单下面能不能看到手机号验证。这三条任何一条不满足方案就要提前改道别等到代码写完再返工。2.2 手机号验证服务的开通与计费手机号验证现在是按次计费的增值服务需要在小程序后台 → 功能 → 手机号验证里主动开通并且账号里要有余额。这一点和早期免费随便调的时代完全不同。我不在这里写死单价因为官方调整过几轮而且快速验证和实时验证的报价不一定一样。你在后台这个页面里能看到当前的实时单价、剩余额度以及扣费明细比任何博客里的数字都准。新开通的账号通常会给一定量的免费体验额度用来跑通链路足够了。有个细节值得留意余额耗尽时接口不会返回一个特别友好的提示。你可能会看到errcode从 0 变成别的值或者干脆超时。所以上线前一定要在监控里给这个接口加告警尤其是余额低于某个阈值时提前通知否则线上用户点一键登录点不动你的日志里只会留下一堆看不出原因的失败记录。2.3 用户隐私保护指引最容易被忽略、也最容易致命的一环这是我见过翻车率最高的一步。微信要求小程序在调用涉及用户信息的接口前必须在小程序后台 → 设置 → 服务内容声明 → 用户隐私保护指引里明确声明收集手机号这项信息。没声明的后果不是警告而是接口直接调用失败。失败时的errMsg大致长这样getPhoneNumber:fail api scope is not declared in the privacy agreement或者另一个变体privacy permission is not authorized。新手看到这句往往会以为是代码问题去翻文档翻半天实际上后台补一条声明、等审核通过通常几分钟到几小时问题就没了。除了后台声明客户端侧还有一层隐私弹窗的交互要求。从 2023 年 9 月之后微信引入了统一的隐私授权弹窗机制你需要在合适的时机引导用户同意隐私协议。uniapp 里的处理方式大致是这样// uniapp 中处理隐私授权放在小程序启动或首次需要用户信息时 // #ifdef MP-WEIXIN function ensurePrivacyAuthorized() { return new Promise((resolve, reject) { if (typeof wx.getPrivacySetting ! function) { // 基础库过低不需要处理隐私弹窗 return resolve(true); } wx.getPrivacySetting({ success: (res) { if (res.needAuthorization) { // 需要弹出隐私协议引导用户点击同意 // 实际项目中通常用一个自定义弹窗组件承载这段交互 reject({ needAuthorization: true, privacyContractName: res.privacyContractName }); } else { resolve(true); } }, fail: (err) reject(err) }); }); } // #endif这里的经验是不要把隐私弹窗和获取手机号按钮做成同一个动作。用户点击一键登录时理想流程是隐私弹窗先出现用户点同意后按钮的授权回调才继续走。如果两个弹窗叠在一起用户很容易在慌乱中点到拒绝而拒绝之后的手机号授权在同一个页面上是不太好重新触发的只能引导用户去右上角设置里手动打开转化率掉得很难看。3. uniapp 端 getPhoneNumber 按钮的写法与事件细节3.1 模板层open-type 和事件名的书写规范手机号授权没有对应的 JS API只能靠button组件的open-type属性触发。这一点官方文档写得很清楚但 uniapp 里的写法有几个容易写错的细节。先看基础写法Vue2 和 Vue3 语法在模板层基本一致template view classlogin-page !-- 注意必须是 button 元素事件名全小写 -- button classphone-btn open-typegetPhoneNumber :loadingloading :disabledloading getphonenumberhandleGetPhoneNumber {{ loading ? 正在登录... : 微信手机号一键登录 }} /button /view /template三个必须记住的点第一open-type的值是getPhoneNumber驼峰写法这是属性值不是事件名不能写成全小写。第二事件绑定统一用全小写的getphonenumber。uniapp 在编译到小程序端时会把它转成bindgetphonenumber如果你写成getPhoneNumber这种驼峰形式部分版本的编译器不会做转换结果就是按钮点了没反应而且控制台一片安静连报错都没有。我见过至少三个项目栽在这上面。第三不能图省事用view加个click来模拟。微信对这类敏感授权接口做的是真实用户手势校验view上的点击事件拿不到授权回调只会静默失败。3.2 e.detail 里的字段结构成功和失败都要看清回调参数里e.detail的结构决定了你后端怎么写值得单独拆开看// 授权成功的 e.detail { errMsg: getPhoneNumber:ok, code: e1a2b3c4d5e6f7..., // 手机号动态令牌5 分钟有效且只能用一次 encryptedData: ..., // 老字段官方建议忽略 iv: ... // 老字段官方建议忽略 } // 用户点击了拒绝的 e.detail { errMsg: getPhoneNumber:fail user deny } // 后台未声明隐私协议时 { errMsg: getPhoneNumber:fail api scope is not declared in the privacy agreement }从这一组结构能直接读出处理策略errMsg getPhoneNumber:ok且code存在才继续往后走user deny就弹一个温和的引导提示告诉用户可以去右上角菜单里重新允许不要用 alert 硬怼剩下那些fail开头的基本都是配置问题直接打日志上报别在前端猜。const handleGetPhoneNumber (e) { const { errMsg, code } e.detail || {}; if (errMsg ! getPhoneNumber:ok || !code) { if (errMsg errMsg.indexOf(user deny) -1) { uni.showToast({ title: 你取消了手机号授权, icon: none }); } else { uni.showToast({ title: 授权失败请稍后重试, icon: none }); // 上报异常便于发现后台配置问题 console.error([phone-auth] 授权失败, errMsg); } return; } // 只把 code 交给后端前端不接触任何敏感数据 submitPhoneCode(code); };这里有个很小的工程习惯但在多端项目里很值把submitPhoneCode单独抽成一个函数不要在回调里直接写业务逻辑。因为 App 端和 H5 端拿不到这个 code后面 6.2 会细说你需要在这层做个平台分发回调里保持干净会省很多事。3.3 按钮样式与用户手势上的三个隐形坑第一个坑是按钮的可用状态。:disabledloading这个写法看起来是好的防抖但如果你的 loading 状态因为网络请求卡住而一直没复位用户就会陷入按钮点不动、页面也不报错的死局。我的做法是给请求加超时兜底无论成功失败都在finally里把 loading 置回 false同时给按钮加一个 3 秒的自动解锁。第二个坑是样式遮挡。很多项目为了好看会把 button 用 CSS 改造成圆角卡片常见做法是background: none; border: none;然后自己画。这本身没问题但如果你用了pointer-events: none或者把一个透明遮罩层盖在按钮上面事件就传不到 button 上了。判断方法很土但很有效临时给 button 加个红色背景看看点击区域到底在哪。第三个坑是弹窗动画。有项目把手机号按钮放在一个自绘的授权弹窗里弹窗有个 300ms 的缩放动画。用户在动画还没结束时快速点击事件有时会丢失。这个属于交互细节解决办法是在动画完成后再允许按钮可点击或者干脆别把授权按钮放进需要动画的容器里。4. 服务端用 code 换手机号一条完整的链路4.1 access_token 的集中管理与刷新策略新链路的服务端只依赖一样东西access_token。它是调用几乎所有微信服务端接口的通行证默认有效期 7200 秒。这里有两个必须处理好的问题。第一个问题是不要每次请求都去换 token。access_token的获取接口有每日调用次数限制如果你每个用户登录都去拉一次高峰期很容易把额度打满然后全站接口一起报45009 reach max api daily quota limit。正确做法是把 token 缓存在 Redis 里设置 7200 秒过期并且提前 300 秒刷新避免临界点上拿到一个马上失效的 token。第二个问题是多服务实例的 token 互相顶掉。如果你的后端有多个进程或者多个服务都需要调微信接口各自维护一份 token就会出现A 服务刚换的新 tokenB 服务拿旧的去调把 A 的顶掉了这种经典问题。官方为此提供了稳定版接口不占用普通 token 的调用配额也不容易被互相覆盖// Node.js使用稳定版接口获取 access_token const axios require(axios); async function fetchStableToken(appid, secret, forceRefresh false) { const url https://api.weixin.qq.com/cgi-bin/stable_token; const { data } await axios.post(url, { grant_type: client_credential, appid, secret, force_refresh: forceRefresh }); if (data.errcode) { throw new Error(获取 access_token 失败: ${data.errcode} ${data.errmsg}); } return data.access_token; } // 带缓存的取用封装 async function getAccessToken() { const cacheKey wx:token:${process.env.WX_APPID}; const cached await redis.get(cacheKey); if (cached) return cached; const token await fetchStableToken(process.env.WX_APPID, process.env.WX_SECRET); // 提前 300 秒过期留出刷新余量 await redis.set(cacheKey, token, EX, 7200 - 300); return token; }还有一个安全习惯必须强调AppSecret只能待在服务端环境变量里绝对不能出现在小程序代码包或者前端请求参数中。access_token同理它相当于你小程序的万能钥匙泄露后别人可以以你的名义调各种接口。4.2 调用 getuserphonenumber 换取手机号拿到 token 之后换取手机号就是一次很直接的 POST 请求// Node.js用 code 换取手机号 async function exchangePhoneByCode(code) { const accessToken await getAccessToken(); const url https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token${accessToken}; const { data } await axios.post(url, { code }); if (data.errcode ! 0) { // 把错误码抛出去交给上层做具体处理 const err new Error(换取手机号失败: ${data.errcode} ${data.errmsg}); err.wxErrCode data.errcode; throw err; } const info data.phone_info; return { phoneNumber: info.phoneNumber, // 带区号的完整号码 purePhoneNumber: info.purePhoneNumber, // 纯号码不带区号 countryCode: info.countryCode, // 国家码如 86 watermarkAppId: info.watermark info.watermark.appid }; }返回值里有个watermark.appid强烈建议做一次校验确认它等于你自己的appid。这是防止令牌被跨应用滥用的最后一道防线代码只有一行但能在出现异常调用时第一时间发现。实际返回的成功响应长这样方便你对照调试{ errcode: 0, errmsg: ok, phone_info: { phoneNumber: 13800000000, purePhoneNumber: 13800000000, countryCode: 86, watermark: { timestamp: 1700000000, appid: wx1234567890abcdef } } }4.3 错误码对照表与推荐的排查顺序这个接口的错误码不多但每一个都对应一个非常具体的配置或代码问题。我把它整理成一张表建议贴在项目文档里errcode含义最常见的原因与处理0成功正常返回 phone_info40001access_token 无效token 过期或被其他服务顶掉检查缓存刷新逻辑40029code 无效code 已过期、已被用过或前端误传了登录 code40013appid 无效环境变量里的 appid 和服务端 token 对应的不是同一个45011频率限制短时间内调用过于频繁需要做接口级限流47003参数格式错误请求体不是合法的 JSON或 code 字段名写错-1系统繁忙微信侧偶发建议做一次指数退避重试排查的时候我一般按这个顺序走命中率最高先看errMsg是不是配置类问题隐私协议、未开通服务再看errcode是不是 token 相关的最后才怀疑前端传参。很多同学一上来就怀疑前端代码翻半天发现是后台余额用完了白白浪费时间。另外要提醒一句40029有一种很迷惑的场景——用户点击按钮后页面因为网络慢卡了几秒用户以为没反应就退出去重新进两次点击产生的两个 code 都在路上先到的那个成功了后到的那个自然就40029。所以后端对同一个用户的并发请求做一次幂等合并是很有必要的。5. 实时验证组件什么时候值得多花那次短信5.1 快速验证和实时验证的选型依据微信其实提供了两个手机号组件名字很像适用场景差别不小。手机号快速验证组件就是前面讲的getPhoneNumber。它拿到的是用户在微信里绑定的那个号码整个过程没有短信速度极快用户点一下就行转化率最高。它的局限在于这个号码是微信认为属于这个用户的号码但不代表它此刻一定在用户手上。用户换了号没更新微信绑定、号码是家里人的副卡、号码已经停机但没解绑这些情况下快速验证拿到的号码可能是失效的。手机号实时验证组件会走一次短信验证码流程用户点击后输入收到的验证码验证通过才返回结果。因为多了一次真实验证号码的实时有效性是有保证的。对比维度快速验证实时验证open-type 值getPhoneNumbergetRealtimePhoneNumber用户操作成本一次点击点击 填写短信验证码号码有效性微信绑定号可能已停用实时验证有效性有保证交互耗时1 秒内视短信到达速度通常数秒适用场景登录、注册、普通下单实名、支付验证、找回账号、风控场景选型逻辑其实很简单只要业务对这个号码现在能不能收到短信/电话有要求就上实时验证只是需要一个账号标识和联系方式快速验证就够了。我经手的一个项目同时用了两个主登录流程走快速验证提现前的大额操作走实时验证成本和体验都平衡得不错。5.2 接入差异其实只有两处好消息是实时验证的接入改动非常小和快速验证共用同一个换取接口。!-- 实时验证组件 -- button classphone-btn open-typegetRealtimePhoneNumber getrealtimephonenumberhandleRealtimePhone 短信验证并绑定手机号 /buttonconst handleRealtimePhone (e) { const { errMsg, code } e.detail || {}; if (errMsg ! getRealtimePhoneNumber:ok || !code) { uni.showToast({ title: 验证未完成, icon: none }); return; } // 注意换取的接口和快速验证完全一样 submitPhoneCode(code); };注意两点事件名从getphonenumber变成了getrealtimephonenumber成功时的errMsg前缀也跟着变判断成功的时候别硬编码写死getPhoneNumber:ok否则实时验证那条路会一直判成失败。另外拿到的code依然走/wxa/business/getuserphonenumber这个接口后端代码一行都不用改只是计费口径不同。顺便说一个我在实际项目里发现的现象实时验证的短信到达率在不同运营商和不同时段会有波动。如果用户点了按钮但十分钟没收到短信他的第一反应是反复点这时候前端必须做节流同一用户 60 秒内只能触发一次否则用户以为没发出去实际上后台已经扣了好几次费用。6. 成本控制与工程化落地别让一次点击变成一笔账6.1 去重、缓存与幂等把每一次计费都花在刀刃上既然每次调用都计费那怎么少调就是工程问题。我一般在项目里落三层防护。第一层是数据库层面的唯一约束。用openid做主键或者唯一索引存一条openid → phone的映射。用户第二次进来登录时先查这张表命中就直接签发登录态根本不触发前端授权按钮。这一层能省掉绝大部分重复调用因为真实业务里用户登录是高频动作但手机号绑定只需要一次。第二层是前端防抖加按钮置灰。前面提过用户在网络慢的时候容易重复点。除了disabled我还会加一个本地的请求锁let submitting false; async function submitPhoneCode(code) { if (submitting) return; submitting true; try { const res await uni.request({ url: ${BASE_URL}/api/auth/phone, method: POST, data: { code }, timeout: 8000 }); // 处理登录成功逻辑 } finally { // 无论成败都要解锁避免用户永远点不动 submitting false; } }第三层是服务端的幂等键。用openid 手机号组合成幂等键写进 Redis有效期设个 60 秒。同一用户带着两个不同code打过来时第二次直接返回第一次的结果不再向微信发起请求。这一层是应对并发重复请求的最后一道闸。6.2 条件编译一份代码怎么照顾 App 和 H5用 uniapp 最大的诱惑就是一套代码多端跑但手机号这个功能是小程序独有的App 和 H5 上open-type完全不起作用。合理的做法是在组件层用条件编译把三个平台的入口分开但向上层暴露同一个回调。template view classauth-entry !-- #ifdef MP-WEIXIN -- button classphone-btn open-typegetPhoneNumber getphonenumberhandleGetPhoneNumber 微信一键登录/button !-- #endif -- !-- #ifdef APP-PLUS || H5 -- button classphone-btn clickgoSmsLogin手机号验证码登录/button !-- #endif -- /view /template这样组织的好处是业务层只关心拿到手机号这个结果至于这个号码是微信给的还是短信验证来的通过一个统一的onPhoneResolved(phone)回调消化掉。App 端走自己的短信服务H5 端也走短信只不过各自对接的短信通道不同和小程序这条链路完全解耦。顺带提一句manifest.json里的配置。小程序的appid是在mp-weixin节点下配置的如果这里填错了或者填的是测试号手机号接口是调不通的——测试号不支持这个能力。还有一个细节mp-weixin节点下可以设置最低基础库版本这个值会影响你能否使用新链路最低建议设到 2.21.2 以上。6.3 上线前的自检清单这个清单是我从一个项目复盘里留下来的每次上线前照着过一遍能挡掉九成以上的低级事故小程序主体是非个人主体且微信认证已通过后台功能 → 手机号验证已开通账号余额充足并已配置低余额告警用户隐私保护指引中已声明收集手机号且审核已通过前端按钮是原生button事件名为全小写的getphonenumber前端不做code的任何持久化存储只在内存里传给后端后端access_token走缓存提前 300 秒刷新多实例共用同一份后端校验watermark.appid是否等于自身appidopenid → phone映射表有唯一索引登录优先查表而不是先授权接口级限流已配置单用户 60 秒内最多触发一次错误码分类处理40029和40001有独立的日志与告警我在实际项目里踩过一次比较典型的坑隐私协议审核通过后我们只在开发环境测了测试环境用的还是老版本的后台配置结果灰度发布时测试环境的用户全部授权失败。那之后我就养成了一个习惯——把这个自检清单拆成开发环境、测试环境、生产环境三份每个环境单独过一遍因为小程序后台的配置是跟着 appid 走的不同环境用不同 appid 就等于三套配置。最后分享一个我觉得挺实用的小技巧。手机号授权失败的原因里配置类问题占了绝大多数但前端拿到的errMsg是英文的、也挺长用户看不懂开发者看日志又容易漏。我的做法是在前端捕获失败后把errMsg一起打点上报同时在后端维护一张错误码 → 处置建议的映射表运维同学看到告警时能直接知道是该去补隐私声明还是该去充值。这个映射表不需要多复杂十几行配置但能把平均故障恢复时间从小时级压到分钟级。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

AI编码救星:superpowers如何让Codex看懂Java项目 2026/9/29 10:45:28

AI编码救星:superpowers如何让Codex看懂Java项目

最近我在重度使用Codex CLI改造一个老的Spring Boot项目,踩了不少坑。最典型的坑是:AI能听懂“帮我重构这个方法”,但听不懂“这个Controller里的事务粒度已经失控,需要先做依赖分析再动手”。直到我把superpowers这套技能集接进来…

阅读更多 →
RECOVERY蓝屏修复实战:从引导重建到Windows 10重装全指南 2026/9/29 10:45:07

RECOVERY蓝屏修复实战:从引导重建到Windows 10重装全指南

简介:针对Windows 10开机出现RECOVERY蓝屏并提示「你的PC/设备需要修复」的情况,无论是系统更新失败还是驱动冲突所致,这份PDF文档都梳理了完整的故障排查路径,面向普通用户、电脑新手及需要快速恢复系统的办公人员。文档重点讲解…

阅读更多 →
华为平板搭建ESP32开发环境:TaoToken统一Key接入与串口烧录验证 2026/9/29 10:44:47

华为平板搭建ESP32开发环境:TaoToken统一Key接入与串口烧录验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
MergeKit 贡献指南:从环境搭建、CLA 签署到 Pull Request 合入的完整开发者流程 2026/9/29 10:44:40

MergeKit 贡献指南:从环境搭建、CLA 签署到 Pull Request 合入的完整开发者流程

大模型模型优化AI 应用 【免费下载链接】mergekit Tools for merging pretrained large language models. 项目地址: https://gitcode.com/gh_mirrors/me/mergekit 点击查看 免费下载 MergeKit 是一套面向预训练大语言模型合并(merging)的工…

阅读更多 →
GitAgent内存系统深度解析:Git提交式记忆如何让AI的历史可追溯 2026/9/29 10:44:34

GitAgent内存系统深度解析:Git提交式记忆如何让AI的历史可追溯

GitAgent内存系统深度解析:Git提交式记忆如何让AI的历史可追溯 【免费下载链接】opengap A framework-agnostic, git-native standard for defining AI agents 项目地址: https://gitcode.com/gh_mirrors/git/opengap GitAgent(GitAgent内存系统&…

阅读更多 →
OpenClaw 2.7.9 新手配置全流程:从 settings.json 骨架到 TaoToken 接入 2026/9/29 10:44:27

OpenClaw 2.7.9 新手配置全流程:从 settings.json 骨架到 TaoToken 接入

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉