当 Kotlin 爱上 React,会产生什么化学反应?

Kotlin与Java虚拟机五:Lambda 与高阶函数 Kotlin 的 Lambda 与高阶函数是其简洁语法的核心。函数可作为一等值,支持 it 隐式参数、尾随 Lambda 语法,并通过 inline 优化性能。集合操作如 filter、map、reduce 等均为扩展函数,支持链式调用,配合 asSequence() 可实现惰性求值。函数引用 :: 和 SAM 转换简化接口调用,闭包机制允许访问外部变量。这些特性使 Kotlin 代码更短、更清晰,且运行高效。 阅读详情

点击蓝色字关注我们!

一个努力中的公众号

长的好看的人都关注了

c72cd3f419f7f50ac9afa789bf3081e8.png

说起 Kotlin,听说过的大部人第一反应是一门开发 Android 的语言。不得不说 Google 对 Kotlin 的宣传远远的大于了 Kotlin 的创始公司 Jetbrains 。

Kotlin 不仅仅是能写Android,而且可以写服务端,可以说只要可以写Java的地方,就可以用Kotlin来进行替换。当然Kotlin远不止这些。目前Kotlin可以做到以下平台的开发和使用。jvm,android,js和native(beta)。正因为可以开发 Native ,所以Kotlin现在对 ios 开发也是支持的,虽然看起来效果不是那么的好。

f8cc1ef43c9499fc07c7255b98f4ef54.jpeg

今天进行的 Kotlin 进行 react 开发,这只是对 KotlinJs 的一次尝试和使用。

环境

  • 浏览器:Microsoft Edge 77.0.235.5 (官方内部版本) dev(64 位)

  • Kotlin: 1.3.12

安装环境

很早以前,kotlin 官网就出现了两个库,这两个库基本就是来完善 react 在 kotlin 中的生态,第一个是 JetBrains/create-react-kotlin-app ,一个生成 React 工程的脚手架;第二个是JetBrains/kotlin-wrappers ,该库里存放了 react 周边生态组件,比如说 router,redux 等等。

按照脚手架文档,一行代码便能生成我们的项目。

npx create-react-kotlin-app my-app

在项目生成后,打开项目,使用 npm start 或者 yarn start 启动项目。

be600092f0a4be9329ade036aeb74e42.gif

项目会自动打开浏览器,然后看到我们的项目,说明我们的项目便启动了。

我们可以看看官方提供的 wrappers 库里有什么,好像什么都有就是缺少一个成熟的 UI 组件库。难道说所有的样式都要我们自己写?

当然不是。

kotlinjs 是可以调用到 nodejs 模块的,只不过啊,有点烦。

安装 ant

ant 是 阿里前端 开发的一套 UI 组件,其中有着 React 版本。首先要安装该库。

通过 yarn add antd 进行安装,等待安装完成。

安装结束后,我们将 ant的演示引入到我们的项目中,打开 index.css 引入项目。

@import '~antd/dist/antd.css';

我们尝试的引入一个 Button 看看。

然后我们看到页面上,还是原来的button ,和 ant 的风格没有半毛钱关系。说明我们还是没有讲ant的组件使用到我们的项目中去。

绑定 UI 控件

新建立一个包 ui/ant 来存放我们与 ant 组件的绑定类。

如何编写我们的绑定类?这是我遇到的第一个难题。

首先要找到我们的 node_modules中的antd目录,找到我们要绑定的控件目录。

b3d8339bea2268091209e2838e15522d.jpeg

我们用 @file:JsModule("antd/lib/button") 来表明绑定目录。通过 @JsName("default") 来进行绑定,此时需要该控件是以 exportdefaultButton;

@file:JsModule("antd/lib/button")
package ui.ant
import react.RClass
import react.RProps
@JsName("default")
external val button: RClass<RProps>

在我们的 Main.kt 中重新引入我们刚刚写的 button。此时的 button 不同于 react.dom.button ,我们所声明的 button 无法直接设置任何属性,方法等。当然我们会给他设置的。

package app
import react.RBuilder
import react.RComponent
import react.RProps
import react.RState
import react.dom.div
class App : RComponent<RProps, RState>() {
    override fun RBuilder.render() {
        div {
            ui.ant.button {
                +"Button"
            }
        }
    }
}
fun RBuilder.app() = child(App::class) {}

等待自动编译和刷新完成,查看我们的页面。

474ac65f7282381f8dd3d3dd38129cb5.gif

终于见到了我们的按钮了,而且样式和 Ant 官方文档上所示一致。

说明我们的绑定现在已经成功。

然后如法炮制,把其他的要使用控件进行同样的绑定。

我们 input 和 search 进行绑定。

此次绑定与上次有所改变,我们对这两个属性可以设置一些值,以便我们在 HTML DSL 中直接使用。这里我们通过实现 RProps 来重新定制了我们要使用的属性,对 Input 设置了一个 placeholder,而 SeacherRPorps 可以直接继承 InputRProps 来进行属性上的扩展。

@file:JsModule("antd/lib/input/Input")
package ui.antd
import react.*
external interface InputRProps:RProps {
    var placeholder:String
}
@JsName("default")
external val input: RClass<InputProps>

对 search 控件进行绑定

@file:JsModule("antd/lib/input/Search")
package ui.antd
import org.w3c.dom.events.Event
import react.*
external interface SearchProps : InputRProps {
    var onChange: (Event) -> Unit
    var value: String
}
@JsName("default")
external val search: RClass<SearchProps>

新建独立控件

我们重新独立出来一个组件,叫做 Home.kt 。将我们的 Button 存放进去,然后在 App.kt 上 使用 Home.kt。

package home
import react.RBuilder
import react.RComponent
import react.RProps
import react.RState
import react.dom.div
import ui.ant.button
class Home : RComponent<RProps, RState>() {
    override fun RBuilder.render() {
        div{
           button{
               +"Button"
           }
        }
    }
}
fun RBuilder.home() = child(Home::class) {}

等待页面刷新完毕,仍旧是原来的样子。

添加点击事件

因为我们的按钮中没有声明点击事件,所以无法直接调用,但是 kotlinjs 可以动态属性调用,通过 asDynamic 便可以调用我们想要使用的任何 html 中存在的属性。

button {
    +"Button"
    attrs {
        asDynamic().onClick = {
            console.log("on click")
        }
    }
}

此时就完成了按钮的点击事件,当我们在页面中点击时,可以看到浏览器控制台上的信息进行打印。

进行双向绑定

对我们要使用的数据存放在 RState 中,我们重写来写 RState。 state 和 props 是 react 中两个重要的属性,state 是用来进行内部数据的修改更新,而 props 是可以将外部数据进行传入。

自定义 HomeState 来继承 RState,来进行对内部数据的修改。

interface HomeState : RState {
    var inputValue: Int
}

将我们 Home 中的 RState 类型改为 HomeState

class Home : RComponent<RProps, HomeState>()

这样便可以在下方使用 state 中的数据。

用一个 div 来写我们的数据。

div {
    +"${state.inputValue}"
}

对 button 中的点击事件进行微小的修改,在 react 中对数据进行修改都需要调用 setState 。

button {
    +"Button"
    attrs.asDynamic().onClick ={
        setState {
            inputValue += 1
        }
    }
}

然而,页面刷新后却是一个报错。TypeError: Cannot read property 'toString' of undefined 无法读取 undefinded 的 toString 方法。

原来,单在 HomeState 中声明是不够的,还需要在 HomeState.init() 的重写方法中进行初始值的赋值。

重新等待页面刷新,出现了我们预想的结果。每次点击按钮就会数字就会进行累加。

配合 Axios

axios 是前端中常用的一个 http 请求框架,kotlin 官方已经对它进行了一次简单的封装。我们这里直接就可以使用。

首先仍旧是先安装 axios

yarn add axios

安装完成后新建一个文件夹axios,再新建Axios.js。

package axios
import kotlin.js.Promise
@JsModule("axios")
external fun <T> axios(config: AxiosConfigSettings): Promise<AxiosResponse<T>>
// Type definition
external interface AxiosConfigSettings {
    var url: String
    var method: String
    var baseUrl: String
    var timeout: Number
    var data: dynamic
    var transferRequest: dynamic
    var transferResponse: dynamic
    var headers: dynamic
    var params: dynamic
    var withCredentials: Boolean
    var adapter: dynamic
    var auth: dynamic
    var responseType: String
    var xsrfCookieName: String
    var xsrfHeaderName: String
    var onUploadProgress: dynamic
    var onDownloadProgress: dynamic
    var maxContentLength: Number
    var validateStatus: (Number) -> Boolean
    var maxRedirects: Number
    var httpAgent: dynamic
    var httpsAgent: dynamic
    var proxy: dynamic
    var cancelToken: dynamic
}
external interface AxiosResponse<T> {
    val data: T
    val status: Number
    val statusText: String
    val headers: dynamic
    val config: AxiosConfigSettings
}

我们做一个简单的应用,通过输入名字,到 Github 上寻找相关的仓库。

首先定义我们的数据类,来存放我们请求获取到的数据。

data class Result(
        val total_count: Int,
        val items: Array<Item>
)
data class Item(val id: Long,
                val node_id: String,
                val name: String,
                val full_name: String,
                val html_url: String)

然后在 HomeState 中声明所使用的类。

interface HomeState : RState {
    var inputValue: String
    var repos: Array<Item>
}

下面是界面定义的代码

div(classes = "search-input") {
    search {
        attrs {
            onChange = {
                val element = it.target as HTMLInputElement
                setState {
                    inputValue = element.value
                }
            }
            placeholder = "请输入 Github 仓库名称"
        }
    }
}
button {
    +"搜索"
    attrs {
        asDynamic().onClick = {
        }
    }
}
div(classes = "list") {
    if (state.repos.isNotEmpty()) {
        ul {
            for ((index, item) in state.repos.withIndex()) {
                li {
                    a(href = item.html_url) {
                        +"${index + 1} / ${item.full_name}"
                    }
                }
            }
        }
    }
}

不过当我们此时预览界面的时候,发现又又又错了,提示 repos 无法进行迭代。

明明是数组为什么无法迭代呢?原来是在 Home 组件创建的时候,还没有对 repos 进行初始化。这里需要了解 react 的生命周期,在组件建立前,对数据进行初始化。

override fun componentWillMount() {
        setState {
            repos = emptyArray()
        }
    }

此时页面可以正常展示了。

完善 button 的点击事件。

button {
    +"搜索"
    attrs {
        asDynamic().onClick = {
            //https://api.github.com/search/repositories?q=
            val config: AxiosConfigSettings = jsObject {
                url = "https://api.github.com/search/repositories?q=${state.inputValue}"
            }
            axios.axios<Result>(config).then { response ->
                setState {
                    repos = response.data.items
                }
            }.catch { error ->
                console.log("error", error)
            }
        }
    }
}

此时便完成了项目。

6038ce03d7e6d9f6444f685300ad6e73.jpeg

总结

首先声明 作者并非专业前端,所以文章中有错误还望指正;本文更重要的目的是为了表明 kotlin 在其他领域的使用和使用体验。

kotlin 写 react,在书写习惯上应该和 ts 写是差不多的,而且给后端同学带来了写前端的体验。但是生态不完整,就一个 UI 组件库没有一个官方用 kt 写的,而且编译速度慢,报错有时不明显,可查阅资料少。不过要说硬着头皮开发,个人感觉是完全可以的,不过估计要自己造不少轮子。

4a435d6dcb4ffbfbe2ec29e01469df06.jpeg

好看的人才能点

b3f8882b9add8742995d3698a5c96594.gif

FI 应计对象的创建和摊销 创建/修改:ACACTREE01/ACACTREE02 应计对象的过账:ACACPOSTING 阅读详情

相关推荐

HBuilderX远程开发环境配置:Windows SSH连接实操

详细介绍如何在Windows系统中通过SSH连接远程服务器,配置HBuilderX进行高效远程开发,提升编码协作效率。全程实操演示,轻松掌握hbuilderx与远程环境的无缝对接。

weixin_35751194的博客 1194

使用 React 和 Kotlin/JS 构建 Web 应用程序

本文通过相关的数据,演示如何使用和Kotlin/JS构建一个Web全栈应用。通过本文,你将学到如下内容:完成与构建典型 React 应用程序相关的常见任务。探索如何使用 Kotlin 的 DSL 来帮助简洁、统一地表达概念,同时又不牺牲可读性,从而允许您完全用 Kotlin 编写完整的应用程序。了解如何使用现成的 npm 组件、使用外部库以及发布最终应用程序。demo代码地址如下:最终的完整功能,请查看finish分支。

虎哥LoveDroid 1387

Coze平台智能体开发实战:打造测试工程师的AI助手

智能体是具备特定身份和能力的AI实体,由大语言模型(LLM)驱动,并通过插件、知识库和工作流扩展其能力。明确的角色定位:可以设定特定身份和专业领域扩展能力:通过插件连接外部工具和服务记忆能力:通过知识库存储和检索专属信息流程化处理:通过工作流执行复杂多步任务通过Coze平台创建测试工程师智能体,可以显著提升测试效率和质量。明确的角色定位:聚焦测试工程师的实际需求丰富的知识库:提供准确的领域知识支持合适的工具集成:通过插件连接现有工具链持续迭代优化:根据反馈不断改进智能体行为自动化测试生成。

hogwarts_beibei的博客 1005

当 Kotlin 爱上 React

本文首发于 youngxhui.top 说起 Kotlin,听说过的大部人第一反应是一门开发 Android 的语言。不得不说 Google 对 Kotlin 的宣传远远的大于了 Kotlin 的创始公司 Jetbrains 。 Kotlin 不仅仅是能写Android,而且可以写服务端,可以说只要可以写Java的地方,就可以用Kotlin来进行替换。当然Kotlin远不止这些。目前Kotlin可...

代码猎奇站 1785

伪距单点定位算法原理以及利用最小二乘法计算实例

即该公式:建立下列代价函数满足最佳估值的条件为:即:可解得未知参数 X 的最佳估值由于每个可见卫星观测量的精度可能不一样,这时,需要对不同观测量赋予一个权重系数,称为加权。

qq_43381998的博客 1万+

《python神经网络编程》程序实现

《python神经网络编程》程序实现 通过对这本书的学习,结合这本书的内容: 完成了以下代码: 其中NN.py实现了一个类,这个类可以完成神经网络的初始化,训练以及输入数据并得出结果。 而run.py则是对神经网络类的使用 NN.py: import numpy import scipy.special #神经网络类的实现 class neuralNetwork: #构造函数初始化类 def __init__(self,inputnodes,hiddennodes,outputnodes

ms8617484的博客 620

Android使用Ant自动打包,kotlin中文教程

<property name=“out-signed-package” value="outdir/{outdir}/outdir/{ant 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》 【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整资料开源分享 .project.name}-signed.apk" /> Creating all o...

m0_65511857的博客 1798

防火墙——双机热备理论讲解

本端和对端协商失败。有可能是因为本端和对端设备的软件版本不一致、某端配置错误、对端设备的心跳接口状态为Down、HRP源或目的端口号被修改、或者底层链路不通等原因导致。会话表、SeverMap表、IP监控表、分片缓存表、GTP表、黑名单、PAT方式端口映射表、NO-PAT方式地址映射表。两台设备一主一备,正常情况下业务流量由主设备处理,当主设备故障时,业务流量平滑切换到备用设备进行处理,业务不中断。开启自动备份后,当主用设备配置了一条可备份的命令或产生了可备份的会话表状态时,会直接备份到备用设备上。

m0_49864110的博客 2万+

Kotlin中级——callbackFlow

如下新增扩展函数,将监听过程转为Flow,可以流式处理,并绑定相关生命周期。转为Flow,并在Flow取消时同步取消监听。如下,是一个温度监听类,每隔1秒发送数据。若普通调用,需要自行注册和取消注册。

松仔Log的博客 164

AutoSAR系列讲解(入门篇)2.2-SWC的类型

该章讲解了两种SWC的分类,并且映射到实际的工程中应该是具体的什么文件类型

雪云飞星的博客 4万+

使用 ABAP 代码动态创建新的 Function Module

通过内表。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 6912

KXBuild 实战,Windows 编译 Kotlin Multiplatform iOS,真机调试、打包 IPA

KMP 的 iOS 端只能在 macOS 上编?在 Windows 上也能跑,iosApp 的构建、安装和调试交给 KXBuild,Gradle 脚本和 iosApp 工程都不用改。Kotlin Multiplatform iOS 流程:体检、在 Config.xcconfig 填 Team ID 和 Bundle ID、连 iPhone 按 F5,在 VS Code 里同时给 Swift 和 Kotlin 打断点、看跨语言调用栈,Compose Multiplatform 同样适用,打出可上架 ipa。

2501_91373349的博客 234

华为OD机试真题2025双机位B卷 Java&Go 实现【朋友圈个数】

给定一组朋友关系,统计一下该朋友关系网中的朋友圈个数。 朋友圈的定义:一个朋友圈至少由3个朋友组成,且要求同一个朋友圈中的任意两个人都具有直接的朋友关系。 输入描述 输入一个朋友关系列表,如 Fiends =A.B],[A.C],IB,DI,其中的每一个元素 Friendsi表示 Friends[i][0)和 Friends [i][1] 是朋友关系 先输入一个数字 N 代表关系的总数,后面每条关系一行,两个成员以逗号分隔

MISAYAONE的博客 520

Go1.27升后Errorf指针%w怎么改

Go1.27起printf vet会报告用%w包装*E(E已实现error);应改传值型error,并用errors.Is/As回归验收。

Amax3747的博客 497

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

kotlin常见面试题

Kotlin通过类型系统区分可空与不可空类型,编译期防止空指针异常。?.、?:(Elvis)、!!、lateinit、let等机制实现安全操作与延迟初始化。协程是轻量级、可挂起的计算,基于状态机实现,非线程,支持协作式调度。run、apply、let等作用域函数各有返回值与接收者引用差异。data class 自动生成equals、hashCode、toString、copy及解构。扩展函数为静态方法,不能访问私有成员,成员函数优先于扩展。inline内联消除高阶函数开销,crossinline限制非局部返

u012116089的博客 104

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

中文提问搜不到英文文档:查询翻译、多语言向量与语言感知融合怎么选,用跨语言 Recall@10 和单语回归验收

把决策压成几条。语料是单语言的,这套东西一个都不需要装。跨语言检索的全部成本都在「查询语言和文档语言不一致」这个前提上。跨语言查询是长尾、且现有系统在同语言上已经不错的,先做 LID 加分语言词法索引,不要先上翻译。前者是确定性的结构改动,不引入模型调用,能立刻消掉「中文查询打英文 BM25 返回空」这个静默退化;翻译是第二步。领域术语密集的场景,词表不是可选项。没有词表的查询翻译在技术文档上大概率是负收益:它把查询变得通顺,同时把唯一能匹配上的标识符弄丢了。

争者留其名 208

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

kotlin中的闭包特性

Kotlin 闭包允许函数捕获并修改外部作用域的变量,通过 Ref 包装类实现,比 Java 的“仅读取 final 变量”更灵活。闭包是函数式编程(如柯里化、高阶函数)的基础,但修改捕获变量不保证线程安全,需手动同步(如 Mutex、Atomic)。Java 限制修改捕获变量以避免并发陷阱,而 Kotlin 将选择权交予开发者:语法自由,责任自担。

YB1375的专栏 226

Gson + Kotlin 数据类的空安全陷阱:一次线上 NPE 崩溃的完整分析与修复

服务端返回 {"provinceName": null, ...}│▼Gson 反射赋值,绕过主构造函数和空检查│▼data.provinceName 实际是 null(但类型仍标记为非空 String)│▼主线程 lambda 拿着这个"假的非空值"传给非空参数│▼Intrinsics.checkNotNullParameter 抛出 NPE,主线程崩溃项目内容崩溃现象根本原因Gson 反射赋值绕过 Kotlin 构造函数与空检查,服务端返回显式null。

学习的最佳方式,就是把它写下来! 223

Android 开发入门实践(十):用 Kotlin 协程完成模拟加载、取消与异常处理

本文记录第118—125课对NewsApp的协程改造:从viewModelScope.launch启动协程,逐步加入delay模拟等待、suspend函数封装、withContext(Dispatchers.IO)切换线程并观察上下文恢复机制。通过日志验证withContext完成后恢复原执行上下文(非自动回主线程),纠正了初始理解偏差。引入Job管理任务,实现加载取消与重复点击防护,结合异常处理与状态管理,完成可观察、可中断的异步流程。

Easyhood的博客 404

Kotlin 对象声明与对象表达式详解

Kotlin 的 object 机制提供对象声明(单例)、对象表达式(匿名对象)、伴生对象与数据对象等特性,简洁实现单例模式、接口匿名实现及静态成员。对象声明延迟初始化且线程安全,可实现接口或继承类;对象表达式每次创建新实例,适用于临时对象。结合 Spring Boot、策略模式等场景,提升代码可读性与灵活性。

tangPHP的博客 121

IntelliJ IDEA 下载安装:写 Java 和 Kotlin 主流的开发工具

IntelliJ IDEA 是写 Java 和 Kotlin 最主流的开发工具。代码补全准、跳转快、重构稳,报错能直接给出改法。——个人写 Java 项目、学习、做课程作业,都能用完整的一套。

科小怪的博客 193

手搓 AI Agent 02:让模型第一次“动手“——Agent Loop 与 ReAct

本系列第2篇实现Agent核心循环——ReAct模式,让模型从“被动回答”变为“主动执行”。通过LLMClient工具调用能力,构建think → act → observe闭环,首次实现模型自主调用计算器、读取结果并继续推理。代码支持安全算术、步数限制与错误隔离,升级终端REPL为可观察的智能体交互界面。每步操作清晰可见,为后续多工具、规划与协作打下基础。

快乐来自程序里 117

21st.dev 上手教程:12000+ React 组件库怎么用,6 步从挑样式到接进 Cursor

21st.dev 收录 12000+ 个 React + Tailwind 组件、297 套整站模板与 979 套主题。本文是手把手流程版:按页面用途找组件、用复制或 shadcn CLI 落地、接入 MCP 让 Cursor 自己取代码、用整站模板开局,附 10 张真实组件截图和 4 个常见坑。

懿轩的技术小窝 207

从 0 实现 ReAct:消息顺序、工具参数分片与失败回填

上一篇讲四种 Loop 的总览,这篇把 ReAct 拆开,讲清楚"为什么你的工具调用总是不生效"。适合:已经能调通大模型 API、但一接工具就各种报错的同学。

Java 后端学习者。写 JUC 并发、Redis 实战,也记录 LLM 应用开发(Spring AI / RAG / Agent)的学习实录——少讲概念,多讲过程和踩坑。 442

【SpringAI Alibaba】ReactAgent响应SSE实时流式传输案例实战

SSE 是单向推送(服务器→客户端),无法客户端→服务器实时发送;双向需用 WebSocket。SSE 是 HTML5 标准,服务器向客户端推送事件流,单向通信,适合流式输出场景。流式输出让用户看到 AI "边想边写",首字响应时间大幅降低(通常在 1 秒内)大模型响应通常需要几秒甚至几十秒,如果等全部生成完再返回,用户体验极差。流式输出中发生异常,要及时关闭流并通知客户端,避免客户端永久等待。,否则 SSE 不会实时推送。

qq_38196449的博客 217

React 鉴权路由守卫直觉

用布局路由 + Navigate 做登录拦截和登录回跳,并说明前端守卫不是安全边界。

JudithHuang的博客 127
上一篇: Gin 大结局
下一篇: 当 Kotlin 爱上 React
代码猎奇站
博客等级 码龄7年 39粉丝 29原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值