简介:面向Qt与QSS学习者的智能家居界面完整项目包,可作为课程设计、毕业设计或Qt入门综合练手参考。压缩包共81个文件,整体仅1.66MB,涵盖34张PNG图片、11个C++源文件、11个头文件、9个UI布局文件、9张JPG背景图及QRC资源管理文件等,便于直接查看界面设计与样式代码。已有61人学习下载。项目按客厅、卧室、厨房、音乐、统计等模块拆分布局,代码中可见QSS样式定制、QCustomPlot图表集成、系统设置对话框等典型实现。压缩包内还附有设计思路笔记、Qt工程配置文件与用户环境设置记录,便于追溯完整开发流程。通过学习这套代码,可快速掌握Qt控件布局、资源文件管理、QSS换肤以及多页面交互的控制逻辑,同时也可参考其中图片素材用于美化自有界面。适合有一定C++基础、希望了解智能家居界面实现细节的开发者。
1. 拿到「智能家居界面 - 副本 (2).zip」之后,先别急着解压
做智能家居方案集成的人,对这种命名大概率不陌生:从外包手里收来的界面包、同事离职交接的压缩包、甚至是从某个论坛付费拿到的Demo,名字往往是「项目名 - 副本 (2).zip」这类格式。这个标题背后传递的信息非常直白:这是一份智能家居界面的前端交付物,可能是中控大屏、管理后台、或者App嵌页,而且经历了不止一次复制粘贴,版本状态未知。我要说的是,这类压缩包的解压只是开始,真正的难点在于判断它能不能用、怎么跑起来、以及怎么跟真实的设备数据打通。
这篇笔记就围绕「智能家居界面」这个交付方向展开:从拿到压缩包后的技术评估讲起,到本地跑通、再到接上真实设备数据的改造路径,最后把常见的落地坑挨个排掉。适合正在做智能家居项目集成、需要接手别人界面工程的前端或全栈工程师,也适合被外包交付折磨的嵌入式开发者。你能得到一套完整的判断流程和可执行的启动、对接、排查方案。
2. 拆开压缩包:判断界面技术栈与工程形态的三步评估法
2.1 用文件清单判断项目类型:静态原型还是工程化应用
压缩包还没解开之前,不要急着双击解压。先看包内清单,这是评估成本最低的一步。在命令行里用unzip -l查看压缩包内部结构,不会把文件释放到磁盘,速度也快。
unzip -l "智能家居界面 - 副本 (2).zip" | head -50看输出内容时重点关注三类标记:第一,如果清单里有package.json、node_modules或src目录,说明这是工程化前端项目,需要安装依赖才能启动;第二,如果只有index.html、css、js、images这类目录,没有构建配置,大概率是静态原型页面,可以直接用浏览器或简单静态服务器打开;第三,如果出现.py、.exe、.apk这类文件,那这个「界面」的交付形态可能不是纯前端,需要另行评估运行环境。
我一般还会用unzip -l统计文件总数和总大小,辅助判断项目体量。少于20个文件的包,基本就是单页面Demo,不要对功能完整度抱太高期望;超过50个文件且有明显分层目录的包,才值得花时间继续评估。这一步能帮你快速过滤掉那些「界面Demo」级别的交付物,避免在空壳项目上浪费时间。
2.2 识别界面技术栈:原生H5、Vue还是可视化大屏框架
拿到解压后的目录,下一步就是识别技术栈。最直接的方式是看package.json里的dependencies字段,以及页面对应的入口文件引用了哪些脚本。智能家居界面这个方向,技术栈高度集中在三类:原生H5+jQuery、Vue 2/3全家桶、以及可视化大屏场景下常用的数据可视化库组合。
# 进入解压目录后,快速查看项目命名 cd "智能家居界面 - 副本 (2)" && ls -la # 有 package.json 时,直接看依赖清单 cat package.json | python3 -c "import sys, json; print(json.load(sys.stdin).get('dependencies', {}))"从依赖名就能读出很多信息:出现vue、vue-router、vuex说明是Vue生态;出现echarts、@antv/g2、three.js说明界面里有图表或3D场景,常见于智能家居的可视化大屏;出现jquery、bootstrap说明是较早期的原生项目,可能依赖第三方插件较多。再看devDependencies里有没有webpack、vite、vue-cli-service,这决定了你的启动命令是npm run dev还是npm run serve。
如果压缩包里没有package.json,那就打开index.html看<script>标签的引入方式。直接引CDN链接的是原生H5居多,里面如果大量使用window.xxx全局变量,说明这个界面写得比较粗糙,后期接数据时需要做一定程度的重构。还有一个细节值得检查:<meta name="viewport">是否存在,以及是否有rem或vw适配逻辑,这决定了界面是不是为触屏或大屏做过专门的适配。
2.3 评估界面可用性:从入口文件与依赖清单反推交付完整度
技术栈确认后,紧接着要回答一个问题:这个界面交付得完不完整?我通常会给压缩包里的项目做一次「完整度体检」,核心是检查四件事:入口文件能不能找到、页面路由是否指向真实存在的组件、有没有写死的mock数据、以及静态资源是否齐全。
# 查看项目里所有的 HTML 文件 find . -name "*.html" -type f # 查看 JS 文件里的 mock 数据标记(常见关键字) grep -rn "mock\|fake\|testData\|模拟数据" --include="*.js" --include="*.vue" --include="*.json" . | head -20 # 查看是否存在接口配置文件(常见命名) find . -type f \( -name "api*.js" -o -name "request*.js" -o -name "config*.json" \) | head -20find命令第一行用来确认入口文件的位置,如果项目根目录连index.html都没有,那大概率交付物缺了关键文件,或者你拿到的只是源码包的一部分。grep那行用来定位mock数据,这是智能家居界面里最常藏数据的地方——设备列表、温湿度值、开关状态往往被写死在data()返回对象或单独的JSON文件里。如果api*.js或request*.js存在,说明原作者有意识地做了接口层封装,改造起来成本低;如果所有请求都零散写在组件里,后面接真实设备时就要下决心动刀。
另外一个评估维度是看package.json里的scripts字段,start、dev、serve、build这些命令是否齐全。只有build没有dev的项目,调试起来会比较别扭,需要每次改代码都重新打包。完整度评估做完,你就对该不该继续投入有了明确判断:文件结构完整、有接口封装、有启动脚本的包,值得跑通;结构残缺、资源缺失、代码耦合度极高的包,及时止损比强行救活更划算。
3. 本地跑通:从解压到界面可点的最小启动流程
3.1 环境自检:Node、Python与浏览器的角色分工
把压缩包里的项目在本地跑起来,涉及三个运行时角色,分工常常被人搞混。Node.js 服务的是工程化前端项目的构建与开发服务器,启动Vue或React项目必须有它;Python 用来启动最简单的静态文件服务器,适合没有构建步骤的纯HTML项目;浏览器则是最终呈现界面和运行交互逻辑的容器。分清这三者的关系,后面遇到启动报错时才知道去查谁。
# 检查 Node.js 是否安装及版本 node -v npm -v # 检查 Python 是否可用 python3 --version输出结果里如果node或npm提示 command not found,说明环境缺Node;如果node有版本但npm没有,说明Node安装不完整。智能家居界面项目常见于老外包团队交付,Node 版本可能差异很大:Vue 2 项目配 Node 16 以下能跑顺,Vite 5 项目则需要 Node 18 以上。我自己的习惯是:先跑node -v,如果主版本号小于等于14,碰到偏新的项目时直接考虑用 nvm 切换版本,别跟环境较劲,这方面已经有太多玄学问题,版本第一、排错第二。
3.2 静态界面直接预览:最简单的一条命令
如果第2章评估时发现项目没有package.json,只有index.html和静态资源,那就别折腾构建工具了。直接在当前目录起一个静态文件服务器,是最快的跑通路径。这里不推荐用浏览器直接双击打开文件,因为很多界面里的AJAX请求用file://协议根本无法发出,接口会全部报跨域错误。
cd "智能家居界面 - 副本 (2)/package目录" # 进入包含 index.html 的目录 python3 -m http.server 8080启动后浏览器访问http://localhost:8080就能看到界面。8080是端口参数,可以换成别的,比如python3 -m http.server 3000。如果8080被占用,启动时会报Address already in use,换一个端口即可。用这种方式跑起来的界面,适合做视觉走查和交互体验测试,但要注意:浏览器地址栏里的路径必须准确指向index.html,如果你进入的是子目录,就需要手动拼上子目录名,这是新手经常卡住的地方。
3.3 工程化项目启动:npm install 与 dev server 的正确顺序
有package.json的项目,跑通的流程相对固定但暗坑不少。核心顺序是:安装依赖 → 启动开发服务器。很多人卡在依赖安装这一关,原因多半是网络问题或Node版本不匹配。安装依赖时如果速度极慢或报ETIMEDOUT,可以用国内镜像源解决。
# 进入项目根目录 cd "智能家居界面 - 副本 (2)/项目根目录" # 安装依赖(使用镜像源,避免官方源超时) npm install --registry=https://registry.npmmirror.com # 启动开发服务器 npm run devnpm install里的--registry参数指定了本次安装使用的源地址,只在当前命令生效,不会改写全局配置,避免污染你其他项目的下载源。启动命令npm run dev实际上是执行package.json里scripts.dev定义的那条命令,通常是vite或vue-cli-service serve。启动成功后终端会输出访问地址,格式一般是http://localhost:5173或http://localhost:8080,Vite 默认端口是5173,Vue CLI 默认是8080。如果启动时提示端口被占用,Vite 会自动加1避让,但 Vue CLI 会直接报错,这时需要手动指定端口,比如npm run dev -- --port 8081。
依赖安装中还有一种值得注意的场景:npm install执行到最后报了若干WARN deprecated或WARN vulnerabilities信息。这类警告大多不影响项目启动,不用逐条处理。真正会让项目起不来的错误集中在ERESOLVE和MODULE_NOT_FOUND两类:前者通常是依赖版本冲突,可以尝试npm install --legacy-peer-deps绕过;后者是某个包没装上,重跑一次npm install解决的概率很大。跑完这些,界面基本能出来。
3.4 启动失败时先看这三类日志
启动命令敲下去之后,终端弹出的红色报错信息就是你的第一手排障线索,不要急着去改代码。根据我的经验,智能家居界面项目启动失败大概率逃不出三类问题:缺少模块、语法错误、端口冲突。
# 查看启动日志的最后 30 行,确定错误类型 npm run dev 2>&1 | tail -30输出里如果包含Module not found: Error: Can't resolve 'xxx',说明项目代码里引用了某个未安装的依赖,可能是package.json里没写、也可能是安装过程中被跳过了,直接npm install xxx补装即可。如果报错信息里有具体的.vue或.js文件路径和行号,且伴随SyntaxError或Unexpected token,那是源码里有语法问题,通常是压缩包在复制过程中文件损坏,回去检查对应文件是否完整传输。如果报错是EADDRINUSE,就是端口被占用,换个端口或者关掉占用进程即可。排查启动问题时,我习惯先把日志完整读一遍再动手,很多时候报错原因在倒数第5行而不是倒数第1行,不要只看最下面那段。
4. 把静态界面接上真实设备:mock数据到智能家居API的替换路径
4.1 找到数据入口:从写死的JSON到接口层的重构
本地跑通只是第一步,智能家居界面的价值在于真实数据。大多数交付包里,设备列表、场景模式、环境数据都是写死在前端组件的data()或独立JSON文件里的。要换成真实数据,首先得定位所有数据入口。常见做法是搜索data()返回对象里有没有deviceList、temperature、switchStatus这类语义明确的字段,它们就是需要替换的靶点。
// 重构前:数据写死在组件里(常见于交付包) export default { data() { return { deviceList: [ { id: 1, name: "客厅空调", type: "ac", status: "on" }, { id: 2, name: "卧室窗帘", type: "curtain", status: "off" } ], temperature: 26 }; } };改造成接口请求的形态时,我建议先在项目里建一个独立的api模块,把请求统一收口,而不是在组件里到处写fetch。这样做的直接好处是:后续更换接口域名、加统一鉴权、做请求拦截都只改一个文件。
// api/device.js:统一的接口层 import request from "./request"; // 拉取设备列表 export function getDeviceList() { return request.get("/api/v1/devices"); } // 切换设备开关状态 export function setDeviceStatus(deviceId, status) { return request.post(`/api/v1/devices/${deviceId}/status`, { status }); }request模块内部封装了axios实例,可以统一配置baseURL和超时时间。改造组件时,把原先直接赋值的deviceList替换成created钩子里调用getDeviceList()然后赋值给this.deviceList。这里有一个容易忽略的参数要点:setDeviceStatus的status字段取值最好与后端协议约定明确,比如"on"/"off"还是1/0,前端做一次映射转换后传给后端,避免把界面里的展示值和接口协议值混在一起,这是后续联调时最容易反复翻车的点。
4.2 HTTP轮询与WebSocket的选择:场景与参数
设备状态从「界面手动刷新」变成「自动更新」,就需要考虑数据推送机制。智能家居界面里,选择HTTP轮询还是WebSocket,取决于设备状态变化的实时性要求和项目总体架构。大屏展示场景适合WebSocket,因为数据实时刷新且推送频率高;管理后台的列表页面用HTTP轮询更简单,因为状态变化不频繁,轮询够用且容易调试。
// HTTP 轮询:适合低频状态刷新,实现简单 setInterval(async () => { const res = await getDeviceList(); this.deviceList = res.data; }, 5000); // 5秒一次,参数可根据设备数量调整轮询间隔参数有讲究:设得太短比如1秒,设备数量多时后端压力大;设得太长比如30秒,状态同步不及时。我一般从5到10秒起步,压测后根据服务器负载调整。WebSocket方案相对复杂,但实时性更好,而且能覆盖设备主动上报的场景,比如网关状态变化、告警推送。
// WebSocket 方案:建立连接与断线重连 const ws = new WebSocket("ws://192.168.1.100:8080/ws/device"); ws.onopen = () => { // 连接成功后订阅设备状态主题 ws.send(JSON.stringify({ action: "subscribe", topic: "device/status" })); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); // data.payload 里携带设备ID和最新状态,就地更新界面 updateDeviceStatus(data.payload); }; ws.onclose = () => { // 断线后延时重连,避免死循环 setTimeout(connectWebSocket(), 3000); };WebSocket的地址参数需要和后端网关约定,常见端口是8080、8083,路径/ws/device要看后端定义。重连延时3秒是折中值:太短会打爆服务端,太长用户会感觉设备失联。另外,智能家居网关的WebSocket往往要求连接后先发一个鉴权消息,你需要在onopen里带上token字段,服务端校验通过后才会推送数据,这点容易被忽视,导致连接一直建立成功但收不到任何消息。
4.3 本地跑通设备下发的调试方法
界面接上数据之后,下一步是下发控制,也就是在界面上点开关,设备要真的动作。没有真实设备时,可以用模拟服务先验证链路。常见做法是用Node.js写一个极简的WebSocket服务,模拟设备的上下线、状态变更和回复确认。
# 先安装一个 http 服务调试工具 npm install -G wscat # 注意是全局安装 # 启动一个测试用的 WebSocket 服务 wscat -l 8080wscat -l 8080在本地监听8080端口,界面里的WebSocket连接地址改成ws://localhost:8080后,你可以在终端手动发送格式化的JSON模拟设备上报。比如发送{"deviceId": "ac_1", "status": "off"},界面如果正确响应为「空调已关闭」状态,说明前端的数据链路是通的。这种方式能让前端开发在设备硬件到位之前就完成大半联调工作,你需要的只是和后端确定好JSON的字段结构,不要等到硬件环境齐了才开始调界面,那时候时间成本高得多。
5. 智能家居界面落地避坑:五条常见的血泪经验
5.1 副本命名导致的版本混乱
现象:压缩包叫「智能家居界面 - 副本 (2).zip」,解压出来的项目代码里有多个重复文件,比如index_final_v3.html、index_最终版.html,你改了其中一个,刷新页面却发现改动没生效。
原因:这类带「副本(2)」标志的交付包,几乎都是手工复制目录产生的,原作者的版本管理要么没有、要么没执行。多个文件并存,入口文件指向混乱,改错文件是必然的。
解决:解压后第一件事是确认index.html里script标签实际引用的JS文件路径,以引用关系为准,不要以文件名里带没带final为准。如果发现多个同功能文件,保留index.html实际引用那条链路,其余全部移到一个_backup目录里,从物理上避免改错。这个动作做在前面,能省下后面至少半天的排查时间。
5.2 资源文件404:路径大小写与编码问题
现象:界面能打开,但页面里的图标、背景图全部空白,浏览器控制台报一堆404 (Not Found),仔细看路径只是大小写不一致,比如Images/logo.png但磁盘上的目录是小写的images。
原因:Windows 文件系统不区分路径大小写,macOS和Linux区分。压缩包大概率是在Windows上打的包,代码里写的是Images/logo.png,而你本地环境是Linux或macOS,大小写对不上就404。还有一种情况是文件名里有中文或空格,解压工具或传输过程把编码搞乱了。
解决:在项目根目录跑一次大小写差异扫描,把所有引用路径修正为磁盘实际的大小写。批量处理可以用find列出实际文件名,再结合grep找到代码里的引用路径,逐一修正。如果文件数量太多,直接用脚本重写引用路径。这类问题修复起来不复杂,但排查很费时间,建议在解压后第一时间统一检查资源引用,不要等到界面打开一片空白才去猜原因。
5.3 跨域拦截:界面能开但数据不动
现象:界面启动正常,但所有设备列表、环境数据的请求全部报错,控制台显示Access to XMLHttpRequest at 'http://192.168.1.100:8080/...' from origin 'http://localhost:5173' has been blocked by CORS policy,页面表格一直转圈没数据。
原因:前端页面跑在localhost:5173,后端接口在另一台机器的192.168.1.100:8080,跨域了。智能家居场景尤其常见:后端网关和前端开发机不在同一个机器上,甚至不在同一个网段。交付包里的代码一般没有配置代理,后端的CORS也没放开。
解决:开发阶段最省事的方案是在前端工程里配代理,让浏览器认为请求是同源的。Vite项目修改vite.config.js,Vue CLI项目修改vue.config.js。
// vite.config.js:开发环境代理配置 export default { server: { proxy: { "/api": { target: "http://192.168.1.100:8080", // 后端接口地址 changeOrigin: true } } } };changeOrigin: true让请求头里的Host字段改成目标域名,很多后端服务校验这个字段,不设可能会出现奇怪的403。配置后,前端请求路径保持/api/v1/devices,代理服务会把请求转发到192.168.1.100:8080/api/v1/devices,浏览器看到的始终是同源请求,跨域问题消失。上线部署时再用Nginx做同样的反向代理配置,一套思路走到底。
5.4 大屏适配翻车:固定尺寸与缩放方案的取舍
现象:界面在开发者的笔记本浏览器里显示完美,一旦投到1080P或4K的触控大屏上,布局错乱、字体偏小、图表拉伸变形。这是智能家居中控大屏项目中最常见也最容易被低估的问题。
原因:交付包里的界面大概率是用px固定尺寸写的,或者只适配了电脑浏览器窗口。大屏的分辨率高、长宽比特殊,固定像素方案必然翻车。
解决:我验证过比较稳的方案是两步走。第一步,用rem代替px,以设计稿基准宽度计算出根字体大小;第二步,在大屏容器最外层加一个scale缩放容器,动态计算浏览器视口与设计稿宽高的比例,整体做缩放。
// 大屏适配:根据视口动态缩放 function autoScale() { const designWidth = 1920; // 设计稿宽度 const designHeight = 1080; // 设计稿高度 const scaleX = window.innerWidth / designWidth; const scaleY = window.innerHeight / designHeight; const scale = Math.min(scaleX, scaleY); // 取较小值保证不溢出 document.getElementById("screen").style.transform = `scale(${scale})`; } window.addEventListener("resize", autoScale); autoScale();这个方案用Math.min(scaleX, scaleY)让界面在任意比例下都能完整显示,不会裁切。但要注意:缩放后界面占据的实际区域可能小于视口,剩余区域需要用纯色背景或背景图填充,不然会出现黑边。如果你对交付要求更高,可以改用按宽度等比例缩放、纵向上允许滚动,但这种做法在大屏上交互体验差一些。关键是在项目启动第一天就决定适配策略,不要等界面全部做完再回头改尺寸单位,那时候工作量几乎等于重做。
5.5 WebSocket掉线后界面假死
现象:界面和设备数据都正常运行,但运行一段时间后,设备状态不再更新,重启浏览器后恢复。打开控制台发现WebSocket连接已经关闭,且没有重连日志。
原因:智能家居网关的WebSocket服务一般都有空闲超时机制,连接一段时间没有任何消息就主动断开。前端代码里只写了onopen和onmessage,没写onclose重连逻辑,或者写了重连但没处理重连后的重新订阅,导致连接虽然恢复了但设备状态不再推送。
解决:给WebSocket封装加上心跳和重连机制。心跳的作用是保持连接活跃,每隔30秒发一个ping消息,服务端收到后回pong,如果连续几次没收到回包就主动断开重连。重连时要注意先清理旧的onmessage监听器,避免多次重连后消息处理函数重复绑定,导致同一条数据被界面更新多次。
// WebSocket 心跳与自动重连 let heartbeatTimer = null; let reconnectTimer = null; function connectWebSocket() { const ws = new WebSocket("ws://192.168.1.100:8080/ws/device"); ws.onopen = () => { ws.send(JSON.stringify({ action: "subscribe", topic: "device/status" })); // 每 30 秒发一次心跳 heartbeatTimer = setInterval(() => { ws.send(JSON.stringify({ action: "ping" })); }, 30000); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.action === "pong") return; // 心跳回包不处理业务 updateDeviceStatus(data.payload); }; ws.onclose = () => { clearInterval(heartbeatTimer); // 3 秒后重连 reconnectTimer = setTimeout(connectWebSocket, 3000); }; } connectWebSocket();心跳间隔30秒和重连延时3秒是常用参数,具体值要看网关配置和网络环境:如果网关的心跳超时是60秒,前端心跳至少要比它快一倍,最好30秒;重连延时太短会造成连接风暴,太长用户会明显感觉到设备离线。另外,重连次数过多导致网关封IP的情况我也遇到过,保险的做法是在重连逻辑里加一个最大次数限制,达到阈值后页面弹窗提示用户手动刷新,而不是无限循环下去。
6. 从「能跑」到「能交付」:打包、验收与交接清单
界面接上真实设备、跑通全链路之后,就到了交付环节。很多工程师在这个阶段栽跟头:开发环境一切正常,打包部署后却各种问题。这里有一套我验证过的落地清单,照着走能少走弯路。
# 工程化项目打包生产版本 npm run build # 打包完成后检查产物目录 ls -la dist/npm run build执行后生成dist目录,这就是要部署的产物。检查时重点看三件事:index.html里的资源路径是不是绝对路径(如果是,大概率部署到子目录时会404,需要在构建配置里改base或publicPath);dist目录里有没有完整的static或assets资源目录;以及压缩后的JS文件名是否带哈希值,带哈希说明构建完整,不带则可能走了缓存的旧代码。
# 本地验证打包产物 cd dist && python3 -m http.server 8081这一步很容易被跳过,但它能提前暴露大部分部署问题。浏览器访问http://localhost:8081,把设备的增删改、开关控制、场景切换全部过一遍,确认界面功能和开发环境表现一致,再移交到服务器。做智能家居项目还有一个专项检查:界面里的设备图标和设备类型是否一一对应——空调、灯、窗帘、插座,图标和文案错位是交付时最尴尬的细节问题。
验收阶段建议用表格逐项过,不用写复杂的测试报告,但核心项不能漏:设备列表加载速度、控制指令响应时间、断网重连表现、长时间运行的稳定性、以及大屏分辨率下的显示效果。这几项在智能家居界面里是用户感知最强的,每一项出问题都会被直接投诉。
最后是交接。光给一个压缩包,不做任何说明,这份交付的价值会大打折扣。我在交接项目时,习惯在压缩包根目录加一个README.md,写清楚启动命令、后端接口地址需要在哪里改、WebSocket鉴权怎么配、大屏适配参数在哪里调。这份文档给接手的工程师省下的时间,往往比写它花的时间多得多。这也是为什么我说,拿到「副本(2).zip」这种包时,先别急着骂交付方,把上面这套流程跑通,很多问题其实都能用工程手段兜住。
最后说一个我做项目留下的习惯:任何外部交付的界面包,我绝不会在原目录里直接改代码。解压后先复制一份,命名为「项目名_接手表_日期」,然后在这个副本上做评估和改造。原包永远保持不动,每个改动都能对照原版差异,出问题时有后悔药可以吃。用这个习惯接手的智能家居界面项目,还没有哪次因为版本混乱而翻车的。希望帮到你。
本文还有配套的精品资源,点击获取