首页 web前端 js教程 简介。使用 JavaScript Web构建 Cardano 钱包检查器。

简介。使用 JavaScript Web构建 Cardano 钱包检查器。

Jan 08, 2025 am 08:29 AM

这个项目是我的第一个 Web3 应用程序,它通过 Yoroi 钱包连接到 Cardano 区块链。实际上,这非常简单,只是检查钱包余额的一种方法,但它标志着许多令人兴奋的项目的开始。我想在学习本教程时与您分享我的学习过程。

?我们正在建设什么?

在开始之前,让我们先明确一下我们正在创建什么。这是一个简单的工具,可以让您:

  • 查看任何卡尔达诺钱包的余额(当然是合法的!?)
  • 连接到您的 Yoroi 钱包
  • 在 ADA 中显示余额

Intro. to WebBuilding a Cardano Wallet Checker with JavaScript.

?你需要什么

基本的 JavaScript 知识(如果你会 console.log("hello world"),就很好了!)

  1. - 文本编辑器(VS Code、Sublime,如果您喜欢冒险,甚至可以使用记事本)
  2. - Yoroi 钱包扩展已安装(我们需要它进行测试)
  3. - Blockfrost API 密钥(别担心,我将向您展示如何获取它) 让我们开始吧!

第 1 步:设置项目

在计算机上创建一个名为 CardanoWalletExplorer 的新文件夹(如果您愿意,也可以是 junky justyk,名称并不重要)。在代码编辑器中打开该文件夹(我使用 Visual Studio Code)。
在文件夹内,创建两个文件:
index.html 和 style.css
现在打开index.html并粘贴以下内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cardano Wallet Explorer</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div>



<p>Let's add some style to our creation. Inside the style.css file paste this in:<br>
</p>

<pre class="brush:php;toolbar:false">:root {
    --primary-color: #0033ad;
    --secondary-color: #2a71d4;
    --accent-color: #17d1aa;
    --background-color: #f8faff;
    --card-background: #ffffff;
    --text-primary: #1a202c;
    --text-secondary: #4a5568;
    --border-color: #e2e8f0;
    --shadow: 0 4px 6px rgba(0,0,0,0.1);
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Inter', system-ui, sans-serif;
    background: var(--background-color);
    color: var(--text-primary);
    line-height: 1.5;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 2rem;
}

.container {
    background: var(--card-background);
    border-radius: 1.5rem;
    box-shadow: var(--shadow);
    width: 100%;
    max-width: 800px;
    overflow: hidden;
}

.app-header {
    background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
    color: white;
    padding: 2rem;
    text-align: center;
}

.logo {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin-bottom: 0.5rem;
}

.logo i { font-size: 2rem; }
h1 { font-size: 1.8rem; font-weight: 600; }
.subtitle { opacity: 0.9; }

.content { padding: 2rem; }

.search-section {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.5rem;
    margin-bottom: 2rem;
}

.search-box {
    display: flex;
    gap: 1rem;
    width: 100%;
    max-width: 600px;
}

#wallet-search {
    flex: 1;
    padding: 0.875rem 1rem;
    border: 2px solid var(--border-color);
    border-radius: 12px;
    font-size: 0.95rem;
    transition: 0.3s ease;
}

#wallet-search:focus {
    outline: none;
    border-color: var(--accent-color);
    box-shadow: 0 0 0 3px rgba(23, 209, 170, 0.1);
}

button {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.875rem 1.5rem;
    border: none;
    border-radius: 12px;
    font-size: 0.95rem;
    font-weight: 500;
    cursor: pointer;
    transition: 0.3s ease;
}

#search-button {
    background: var(--accent-color);
    color: white;
}

#search-button:hover {
    background: #15bea0;
    transform: translateY(-2px);
}

.divider {
    width: 100%;
    max-width: 600px;
    text-align: center;
    position: relative;
    color: var(--text-secondary);
}

.divider::before,
.divider::after {
    content: '';
    position: absolute;
    top: 50%;
    width: calc(50% - 2rem);
    height: 1px;
    background: var(--border-color);
}

.divider::before { left: 0; }
.divider::after { right: 0; }

.connect-button {
    background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
    color: white;
    padding: 1rem 2rem;
}

.connect-button:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow);
}

.wallet-info {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 1.5rem;
    margin-top: 2rem;
}

.info-card {
    background: var(--card-background);
    border: 1px solid var(--border-color);
    border-radius: 1rem;
    padding: 1.5rem;
    transition: 0.3s ease;
}

.info-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow);
}

.card-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1rem;
    color: var(--text-secondary);
}

.card-header i {
    font-size: 1.25rem;
    color: var(--primary-color);
}

.card-content {
    font-size: 0.95rem;
    color: var(--text-primary);
    word-break: break-all;
}

.app-footer {
    text-align: center;
    padding: 1.5rem;
    background: var(--background-color);
    color: var(--text-secondary);
    font-size: 0.9rem;
}

.app-footer a {
    color: var(--primary-color);
    text-decoration: none;
}

.app-footer a:hover {
    text-decoration: underline;
}

@media (max-width: 640px) {
    body {
        padding: 1rem;
    }

    .container {
        border-radius: 1.5rem;
    }

    .app-header {
        padding: 1.5rem;
    }

    .content {
        padding: 1.5rem;
    }

    .search-box {
        flex-direction: column;
    }

    button {
        width: 100%;
        justify-content: center;
    }

    .wallet-info {
        grid-template-columns: 1fr;
    }
}

登录后复制
登录后复制

第 2 步:如何获取您的 Blockfrost API 密钥?

为了获取钱包余额,我们需要 Blockfrost,它允许我们与 Cardano 区块链进行交互。以下是获取 API 密钥的方法:

  • 前往 Blockfrost.io 并注册。
  • 登录后,单击“创建新项目”。
  • 选择主网进行真正的 ADA 或选择测试网进行测试。
  • 项目创建后,您将获得一个API Key。

第三步:手术的大脑?

现在到了有趣的部分,让它一切正常!在我们创建的文件夹中创建一个名为 script.js 的文件。

function checkYoroiInstalled() {
    return window.cardano && window.cardano.yoroi;
}
登录后复制
登录后复制

?此功能检查您的浏览器中是否安装了 Yoroi 钱包扩展。 Window.cardano 是像 Yoroi 这样的 Cardano 钱包公开的对象。我们检查它是否存在以及 window.cardano.yoroi 是否可用于确认 Yoroi 钱包已安装。
如果两者都为 true,则该函数返回 true;否则,返回 false。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cardano Wallet Explorer</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div>



<p>Let's add some style to our creation. Inside the style.css file paste this in:<br>
</p>

<pre class="brush:php;toolbar:false">:root {
    --primary-color: #0033ad;
    --secondary-color: #2a71d4;
    --accent-color: #17d1aa;
    --background-color: #f8faff;
    --card-background: #ffffff;
    --text-primary: #1a202c;
    --text-secondary: #4a5568;
    --border-color: #e2e8f0;
    --shadow: 0 4px 6px rgba(0,0,0,0.1);
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Inter', system-ui, sans-serif;
    background: var(--background-color);
    color: var(--text-primary);
    line-height: 1.5;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 2rem;
}

.container {
    background: var(--card-background);
    border-radius: 1.5rem;
    box-shadow: var(--shadow);
    width: 100%;
    max-width: 800px;
    overflow: hidden;
}

.app-header {
    background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
    color: white;
    padding: 2rem;
    text-align: center;
}

.logo {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin-bottom: 0.5rem;
}

.logo i { font-size: 2rem; }
h1 { font-size: 1.8rem; font-weight: 600; }
.subtitle { opacity: 0.9; }

.content { padding: 2rem; }

.search-section {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.5rem;
    margin-bottom: 2rem;
}

.search-box {
    display: flex;
    gap: 1rem;
    width: 100%;
    max-width: 600px;
}

#wallet-search {
    flex: 1;
    padding: 0.875rem 1rem;
    border: 2px solid var(--border-color);
    border-radius: 12px;
    font-size: 0.95rem;
    transition: 0.3s ease;
}

#wallet-search:focus {
    outline: none;
    border-color: var(--accent-color);
    box-shadow: 0 0 0 3px rgba(23, 209, 170, 0.1);
}

button {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.875rem 1.5rem;
    border: none;
    border-radius: 12px;
    font-size: 0.95rem;
    font-weight: 500;
    cursor: pointer;
    transition: 0.3s ease;
}

#search-button {
    background: var(--accent-color);
    color: white;
}

#search-button:hover {
    background: #15bea0;
    transform: translateY(-2px);
}

.divider {
    width: 100%;
    max-width: 600px;
    text-align: center;
    position: relative;
    color: var(--text-secondary);
}

.divider::before,
.divider::after {
    content: '';
    position: absolute;
    top: 50%;
    width: calc(50% - 2rem);
    height: 1px;
    background: var(--border-color);
}

.divider::before { left: 0; }
.divider::after { right: 0; }

.connect-button {
    background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
    color: white;
    padding: 1rem 2rem;
}

.connect-button:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow);
}

.wallet-info {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 1.5rem;
    margin-top: 2rem;
}

.info-card {
    background: var(--card-background);
    border: 1px solid var(--border-color);
    border-radius: 1rem;
    padding: 1.5rem;
    transition: 0.3s ease;
}

.info-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow);
}

.card-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1rem;
    color: var(--text-secondary);
}

.card-header i {
    font-size: 1.25rem;
    color: var(--primary-color);
}

.card-content {
    font-size: 0.95rem;
    color: var(--text-primary);
    word-break: break-all;
}

.app-footer {
    text-align: center;
    padding: 1.5rem;
    background: var(--background-color);
    color: var(--text-secondary);
    font-size: 0.9rem;
}

.app-footer a {
    color: var(--primary-color);
    text-decoration: none;
}

.app-footer a:hover {
    text-decoration: underline;
}

@media (max-width: 640px) {
    body {
        padding: 1rem;
    }

    .container {
        border-radius: 1.5rem;
    }

    .app-header {
        padding: 1.5rem;
    }

    .content {
        padding: 1.5rem;
    }

    .search-box {
        flex-direction: column;
    }

    button {
        width: 100%;
        justify-content: center;
    }

    .wallet-info {
        grid-template-columns: 1fr;
    }
}

登录后复制
登录后复制

此函数以用户友好的方式格式化 ADA 的余额。
Cardano使用lovelace作为最小单位(1 ADA = 1,000,000 lovelace),所以我们需要将其除以1,000,000转换为ADA。
它还确保余额显示为小数点后 6 位(例如 1.234567 ADA),或者如果余额无效或为空,则返回“0.000000”。

function checkYoroiInstalled() {
    return window.cardano && window.cardano.yoroi;
}
登录后复制
登录后复制

我们使用 Fetch API 向 Blockfrost 发出 GET 请求。 Blockfrost 提供了一个 API 来与 Cardano 区块链进行交互。我们向特定钱包地址的端点发送 GET 请求,并使用 API 密钥进行授权。该函数通过向 Blockfrost 的 API 发出请求来获取特定钱包地址的余额。

如果响应成功,我们将解析 JSON 数据并返回该地址中 ADA 的数量。
如果出现错误(例如,无效地址或 API 问题),它将抛出错误并将其记录到控制台。端点 URL 包括我们要检查的钱包地址。确保将 checkWalletBalance 函数中的 YOUR_API_KEY 占位符替换为您的 API 密钥。

function formatBalance(lovelaceBalance) {
    if (!lovelaceBalance || isNaN(lovelaceBalance)) {
        return "0.000000";
    }
    const adaBalance = parseFloat(lovelaceBalance) / 1_000_000;
    return adaBalance.toFixed(6);
}

登录后复制
  • 此 UI 更新功能使用钱包地址和余额更新用户界面 (UI)。
  • 它会缩短地址以显示前 8 个和后 8 个字符(以使其更具可读性)并将其显示在“钱包地址”卡中。
  • 它使用 formatBalance 函数格式化余额并将其显示在“钱包余额”卡中。
  • 当您将鼠标悬停在地址文本上时,它还会设置包含完整地址的工具提示。
**Fetching Wallet Balance Using Blockfrost API
async function checkWalletBalance(address) {
    try {
        const API_KEY = 'YOUR_API_KEY';
        const response = await fetch(`https://cardano-mainnet.blockfrost.io/api/v0/addresses/${address}`, {
            headers: {
                'project_id': API_KEY
            }
        });

        if (!response.ok) {
            throw new Error('Invalid address or API error');
        }

        const data = await response.json();
        return data.amount[0].quantity;
    } catch (error) {
        console.error('Error fetching balance:', error);
        throw error;
    }
}

登录后复制

此代码将事件侦听器附加到 HTML 元素:
当点击“连接 Yoroi Wallet”按钮时,它会调用 connectWallet 函数。
当点击“检查余额”按钮时,它会调用handleWalletSearch函数。
如果用户在钱包地址输入栏中按下“Enter”键,也会触发余额检查。

恭喜,你做到了! ?

您现在距离掌握 Web3 又近了一步! ?这个项目不仅是一项技术成就,更是迈向区块链开发广阔世界的激动人心的一步! ?
现在,您可以无缝连接到 Yoroi 钱包、检查余额,并将区块链的力量带到您的指尖。

?测试时间!

  1. 在 VS Code 上安装 liveserver 扩展,并通过单击“上线”按钮确保其正在运行。
  2. 这应该在您的浏览器中打开 HTML 文件
  3. 交叉手指并测试一下!

?您从这个项目中学到了什么

  • 深入了解 Yoroi 等区块链钱包如何运作以及如何与去中心化网络交互。
  • 掌握了使用 window.cardano API 将浏览器钱包安全连接到网络应用程序的过程。
  • 探索了 Blockfrost API 以从 Cardano 区块链获取钱包余额和地址详细信息。
  • 增强了异步和等待、错误处理和数据格式化方面的知识,以确保流畅的用户体验。
  • 学会根据实时区块链数据动态更新网页元素。
  • 了解将加密货币单位从 Lovelace 转换为 ADA 并格式化它们以供用户阅读的机制。
  • 认识到对 API 请求和用户输入进行稳健错误处理以防止中断的重要性。
  • 获得了对区块链上的钱包结构、地址管理和交易表示的宝贵见解。
  • 实现了加载旋转器、工具提示和警报等功能,以增强用户交互。
  • 解决了边缘情况和调试挑战,加强了分析和编码技能。

?升级的想法

这个项目是进入区块链开发世界的垫脚石。以下是一些进一步推进的想法:

  • 添加交易历史查看器以显示钱包地址过去的交易。
  • 实现多钱包支持,允许用户在 Yoroi、Nami 或 Eternal 等钱包之间切换。
  • 创建一个显示钱包活动趋势的仪表板,包括传入和传出交易摘要。
  • 整合实时价格数据以显示美元或欧元等法定货币的 ADA 余额。
  • 通过集成交易构建功能,启用直接从应用程序发送 ADA 的功能。
  • 添加深色模式切换,以获得视觉上吸引人且易于访问的设计。
  • 使界面响应式以确保与移动设备的兼容性。
  • 纳入更好的错误消息,指导用户解决问题,例如无效的钱包地址。

?您如何做出贡献

这个项目是我进入 Web3 的第一步,我渴望向最好的人学习。您的经验、见解和建议可以使这个项目变得更好,同时也帮助我成长为一名开发人员。您可以通过以下方式做出贡献:

  • 在 GitHub 上分叉该项目并添加新功能或改进。
  • 报告您在使用该工具时遇到的错误或问题并提出解决方案。
  • 通过在 GitHub 存储库上提出功能请求或讨论来提出改进建议。
  • 为您添加的任何新功能编写详细文档,以便其他人轻松使用和构建。

✨ 值得考虑的新功能

  • 添加二维码支持,方便钱包地址共享和扫描。
  • 对余额更新或交易确认等事件实施通知。
  • 包括一个教育部分,为初学者解释区块链基础知识。
  • 引入游戏化元素,例如频繁使用钱包或探索新功能的成就。
  • 开发质押功能,显示质押奖励和委托状态。

让我们继续构建和创新——您的贡献和创造力可以塑造这个工具的未来! ?

?需要帮助吗?

卡住了吗?发现错误?想聊天吗?在下面发表评论或在 Twitter 上找到我!
请记住,我们都是从某个地方开始的,唯一愚蠢的问题就是你没有问的问题!

区块链探索者们,编码愉快! ?

以上是简介。使用 JavaScript Web构建 Cardano 钱包检查器。的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

<🎜>:泡泡胶模拟器无穷大 - 如何获取和使用皇家钥匙
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系统,解释
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆树的耳语 - 如何解锁抓钩
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1676
14
CakePHP 教程
1429
52
Laravel 教程
1333
25
PHP教程
1278
29
C# 教程
1257
24
Python vs. JavaScript:学习曲线和易用性 Python vs. JavaScript:学习曲线和易用性 Apr 16, 2025 am 12:12 AM

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

JavaScript和Web:核心功能和用例 JavaScript和Web:核心功能和用例 Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

JavaScript在行动中:现实世界中的示例和项目 JavaScript在行动中:现实世界中的示例和项目 Apr 19, 2025 am 12:13 AM

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

了解JavaScript引擎:实施详细信息 了解JavaScript引擎:实施详细信息 Apr 17, 2025 am 12:05 AM

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python vs. JavaScript:社区,图书馆和资源 Python vs. JavaScript:社区,图书馆和资源 Apr 15, 2025 am 12:16 AM

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

Python vs. JavaScript:开发环境和工具 Python vs. JavaScript:开发环境和工具 Apr 26, 2025 am 12:09 AM

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

C/C在JavaScript口译员和编译器中的作用 C/C在JavaScript口译员和编译器中的作用 Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

Python vs. JavaScript:比较用例和应用程序 Python vs. JavaScript:比较用例和应用程序 Apr 21, 2025 am 12:01 AM

Python更适合数据科学和自动化,JavaScript更适合前端和全栈开发。1.Python在数据科学和机器学习中表现出色,使用NumPy、Pandas等库进行数据处理和建模。2.Python在自动化和脚本编写方面简洁高效。3.JavaScript在前端开发中不可或缺,用于构建动态网页和单页面应用。4.JavaScript通过Node.js在后端开发中发挥作用,支持全栈开发。

See all articles