uni-app下SQLite应用初探备忘录

uni-app是什么东东,这里就不是多讲了,大家可以看下官网:https://uniapp.dcloud.net.cn/。总之以后,但有实体程序,大多用它来书写。SQLite是什么东西?我认为就是一个本地数据库,可以用SQL语句来完成数据库的操作。写本文是为了备忘,也是为积累知识。

一、创建uni-app项目

打开HBuilder,新建一个支持SQLite的项目。具体操作如下:
1、新建一个空项目

在这里插入图片描述
选默认模板,就可以了。也就是空的,不要选其他。

2、自动生成项目之后,在左侧找开项目,找到个manifest.json,如图:

在这里插入图片描述
这个文件就是项目的配置文件,一定要看好

3、如果你生成的项目,没有AppID,则点“生新获取"。如果有AppID,则直接略过:

在这里插入图片描述
操作之后结果,如下图所示,但是注意你生成的AppID可能不一样:
在这里插入图片描述

4、在App模块配置之中,选中SQLite,操作如图所示:

在这里插入图片描述
这个文件通常是自动保存的,也可以手工保存一下。理论上讲,我们现在已经可以操作SQLite了,但是我们还要先了解一下操作的API;看一下,系统为我们提供那些接口,来操作SQLite

二、操作SQLite的API

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来讲,它的处理速度比他们都快。SQLite第一个Alpha版本诞生于2000年5月。 至2023年已经接近有23个年头,SQLite也迎来了一个版本 SQLite 3已经发布。

我们的API,还要记得一个网址:https://www.html5plus.org/doc/zh_cn/sqlite.html,打后之后如图所示:

在这里插入图片描述
下面我们先来了解一下这个文档

1、打开数据库

void plus.sqlite.openDatabase(options);

说明: \color{red}说明: 说明:
如果数据库存在则打开,不存在则创建。

参数: \color{red}参数: 参数:
options参数为json类型,包含以下属性:

  • name: ( String ) 必选 数据库名称 如chat
  • path: ( String ) 必选 数据库路径 如_doc/chat.db
  • success: ( SQLiteSuccessCallback ) 可选 打开数据库成功回调函数,回调函数无返回参数。
  • fail: ( SQLiteFailCallback ) 可选 打开数据库失败回调函数

例子:

// 打开数据库
function openDB(){
	plus.sqlite.openDatabase({
		name: 'first',
		path: '_doc/test.db',
		success: function(e){
			console.log('openDatabase success!');
		},
		fail: function(e){
			console.log('openDatabase failed: '+JSON.stringify(e));
		}
	});
}

2、判断数据库是否打开

Boolean plus.sqlite.isOpenDatabase(options);

说明: \color{red}说明: 说明:
数据库已经打开则返回true,数据库没有打开则返回false。

参数: \color{red}参数: 参数:
options参数为json类型,包含以下属性:

  • name: ( String ) 必选 数据库名称
  • path: ( String ) 必选 数据库路径

返回值: \color{red}返回值: 返回值:
Boolean : true表示数据库已打开,false表示数据库没有打开。

例子:

if (plus.sqlite.isOpenDatabase({name: 'first',path: '_doc/test.db'})){
    console.log('已经打开数据库。');
}else{
    console.log('没有打开数据库!');
}

3、关闭数据库

void plus.sqlite.closeDatabase(options);

说明: \color{red}说明: 说明:
完成数据库操作后,必须关闭数据库,否则可能会导致系统资源无法释放。

参数: \color{red}参数: 参数:
options参数为json类型,包含以下属性:

  • name: ( String ) 必选 数据库名称
  • success: ( SQLiteSuccessCallback ) 可选 关闭数据库成功回调函数,回调函数无返回参数。
  • fail: ( SQLiteFailCallback ) 可选 关闭数据库失败回调函数

示例:

// 关闭数据库
function closeDB(){
	plus.sqlite.closeDatabase({
		name: 'first',
		success: function(e){
			console.log('closeDatabase success!');
		},
		fail: function(e){
			console.log('closeDatabase failed: '+JSON.stringify(e));
		}
	});
}

4、执行事务

void plus.sqlite.transaction(options);

参数: \color{red}参数: 参数:
options参数为json类型,包含以下属性:

  • name: ( String ) 必选 数据库名称
  • operation: ( String ) 必选 需要执行的事务操作
    • begin(开始事务)
    • commit(提交)
    • rollback(回滚)
  • success: ( SQLiteSuccessCallback ) 可选 执行事务成功回调函数,回调函数无返回参数。
  • fail: ( SQLiteFailCallback ) 可选 执行事务失败回调函数

返回值: \color{red}返回值: 返回值:
void : 无

示例:

// 执行事务
function transactionDB(){
	plus.sqlite.transaction({
		name: 'first',
		operation: 'begin',
		success: function(e){
			console.log('transaction success!');
		},
		fail: function(e){
			console.log('transaction failed: '+JSON.stringify(e));
		}
	});
}

5、执行增删改等操作的SQL语句

void plus.sqlite.executeSql(options);

参数: \color{red}参数: 参数:
options参数为json类型,包含以下属性:

  • name: ( String ) 必选 数据库名称
  • sql: ( Array[String] | String ) 必选 需要执行的SQL语句
    • 参数为字符串时,表示执行单条SQL语句;
    • 参数为字符串数组时,表示执行多条SQL语句,按数组顺序执行,某条SQL语句执行错误则终止。
      . + 注意:不支持SQL语句中使用“;”分割多条命令,要运行多条命令请使用字符串数组参数。
  • success: ( SQLiteSuccessCallback ) 可选 执行SQL语句成功回调函数
    • 回调函数无返回参数。
  • fail: ( SQLiteFailCallback ) 可选 执行SQL语句失败回调函数

示例:

// 执行SQL语句
function executeSQL(){
	plus.sqlite.executeSql({
		name: 'first',
		sql: 'create table if not exists database("where" CHAR(110),"location" CHAR(100),"age" INT(11))',
		success: function(e){
			console.log('executeSql success!');
			plus.sqlite.executeSql({
				name: 'first',
				sql: "insert into database values('北京','安乐林','11')",
				success: function(e){
					console.log('executeSql success!');
				},
				fail: function(e){
					console.log('executeSql failed: '+JSON.stringify(e));
				}
			});
		},
		fail: function(e){
			console.log('executeSql failed: '+JSON.stringify(e));
		}
	});
}

6、执行查询的SQL语句

void plus.sqlite.selectSql(options);

参数: \color{red}参数: 参数:
options参数为json类型,包含以下属性:

  • name: ( String ) 必选 数据库名称
  • sql: ( String ) 必选 需要查询的SQL语句
  • success: ( SQLiteSuccessCallback ) 可选 执行SQL语句成功回调函数
    • 回调函数返回参数为JSON对象数组,其中JSON对象为查询的结果。 如果未查询到数据则返回参数为空数组。
  • fail: ( SQLiteFailCallback ) 可选 执行SQL语句失败回调函数

示例:

// 查询SQL语句
function selectSQL(){
	plus.sqlite.selectSql({
		name: 'first',
		sql: 'select * from database',
		success: function(data){
			console.log('selectSql success: ');
			for(var i in data){
				console.log(data[i]);
			}
		},
		fail: function(e){
			console.log('selectSql failed: '+JSON.stringify(e));
		}
	});
}

三、直接引用

我们现在来制作原始的接口,俱体的方法是找到我们的主文件:pages\index\index.vue。在这个文件开头,写入以下代码:

在这里插入图片描述
源码如下:

		<view class="title">原始接口0</view>
		<view style="display: flex;flex-direction: row;flex-wrap: wrap;justify-content:flex-start;justify-items: auto;">
			<button style="width: 30%;margin-bottom: 10rpx;" @click="openDB">打开DB</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="isopen">是否打开</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="closeDB">关闭DB</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="executeSQL">执行SQL</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="selectSQL">查询SQL</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="transBegin">事务开始</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="editDB">岁数+1</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="transCommit">事务提交</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="transRollback">事务回滚</button>
		</view>

methods: 处,加上我们改写的代码。
1、打开数据库接口:

			//打开数据库
			openDB(){
				plus.sqlite.openDatabase({
					name: 'first',
					path: '_doc/test.db',
					success: function(e){
						console.log('openDatabase success!');
					},
					fail: function(e){
						console.log('openDatabase failed: '+JSON.stringify(e));
					}
				});
			},

拷上面代码,去function,最后再上加**,**就可以了。以后的代码也是这样改。

2、数据库是否打开:

//db是否打开
			isopen(){
				if (plus.sqlite.isOpenDatabase({name: 'first',path: '_doc/test.db'})){
				    console.log('已经打开数据库。');
					return true;
				}else{
				    console.log('没有打开数据库!');
					return false;
				}
			},

这应该算一个同步函数

3、关闭数据库:

            closeDB(){
				plus.sqlite.closeDatabase({
					name: 'first',
					success: function(e){
						console.log('closeDatabase success!');
					},
					fail: function(e){
						console.log('closeDatabase failed: '+JSON.stringify(e));
					}
				});
			},

每次操作数据库,都要关闭!
4、执行SQL语句:

			executeSQL(){
				plus.sqlite.executeSql({
					name: 'first',
					sql: 'create table if not exists database("where" CHAR(110),"location" CHAR(100),"age" INT(11))',
					success: function(e){
						console.log('executeSql success!');
						plus.sqlite.executeSql({
							name: 'first',
							sql: "insert into database values('北京','安乐林','11')",
							success: function(e){
								console.log('executeSql success!');
							},
							fail: function(e){
								console.log('executeSql failed: '+JSON.stringify(e));
							}
						});
					},
					fail: function(e){
						console.log('executeSql failed: '+JSON.stringify(e));
					}
				});
			},

这个函数有点长,不过不用改逻辑,我们要试一下,能不能运行,接口是不是OK!
5、查询SQL语句

			selectSQL(){
				plus.sqlite.selectSql({
					name: 'first',
					sql: 'select * from database',
					success: function(data){
						console.log('selectSql success: ');
						for(var i in data){
							console.log(data[i]);
						}
					},
					fail: function(e){
						console.log('selectSql failed: '+JSON.stringify(e));
					}
				});
			},

这种函数,没有通用性
6、执行事务(开始)

			transBegin(){
				plus.sqlite.transaction({
					name: 'first',
					operation: 'begin',
					success: function(e){
						console.log('transaction begin success!');
					},
					fail: function(e){
						console.log('transaction begin failed: '+JSON.stringify(e));
					}
				});
			},

事务接口,必须扩一下,扩成三个开始、提交、回滚

7、执行事务(提交)

			transCommit(){
				plus.sqlite.transaction({
					name: 'first',
					operation: 'commit',
					success: function(e){
						console.log('transaction commit success!');
					},
					fail: function(e){
						console.log('transaction commit failed: '+JSON.stringify(e));
					}
				});
			},

8、执行事务(回滚)

			transRollback(){
				plus.sqlite.transaction({
					name: 'first',
					operation: 'rollback',
					success: function(e){
						console.log('transaction rollback success!');
					},
					fail: function(e){
						console.log('transaction rollback failed: '+JSON.stringify(e));
					}
				});
			},

9、修改数据事务用

			editDB(){
				plus.sqlite.executeSql({
					name: 'first',
					sql: "update database set age=age+1 ",
					success: function(e){
						console.log('executeSql update success!');
					},
					fail: function(e){
						console.log('executeSql update failed: '+JSON.stringify(e));
					}
				});
			},

这是事务中要用的,只要开启事务,修改数据,在提交后有效。如果回滚则无效!

10、小结:

这个接口是可以用的,特别是事务也是可以用!这对于关键业务相当有用。但是缺点也常非明显,不方便使用,我们在目前的基础之上,可以进化到一个单独的js文件中。

四、第1代接口

1、我们改template,增加如下代码:

<hr>
		<view class="title">第1代接口</view>
		<view style="display: flex;flex-direction: row;flex-wrap: wrap;justify-content:flex-start;justify-items: auto;">
			<button style="width: 30%;margin-bottom: 10rpx;" @click="clickOpenDB">打开DB</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="clickIsOpen">是否打开</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="clickCloseDB">关闭DB</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="clickExecuteSQL">执行SQL</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="clickSelectSQL">查询SQL</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="clickTransBegin">事务开始</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="clickEditDB">岁数+1</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="clickTransCommit">事务提交</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="clickTransRollback">事务回滚</button>
		</view>

2、我们新建目录common,并在其目录下建立新js文件:SQLite31.js,效果如图所示:

//第一代SQLite接口,作者:天一先生,时间:2023-8-12
//打开数据库
function openDB(config){
	plus.sqlite.openDatabase({
		...config,
		success: function(e){
			console.log('openDatabase success!');
				},
		fail: function(e){
			console.log('openDatabase failed: '+JSON.stringify(e));
				}
	});
}

//db是否打开
function isOpen(config){
	if (plus.sqlite.isOpenDatabase(config)){
		console.log('已经打开数据库。');
		return true;
	}else{
		console.log('没有打开数据库!');
		return false;
	}
}

// 关闭数据库
function closeDB(config){
	plus.sqlite.closeDatabase({
		...config,
		success: function(e){
			console.log('closeDatabase success!');
			},
		fail: function(e){
			console.log('closeDatabase failed: '+JSON.stringify(e));
			}
	});
}

// 执行SQL语句
function executeSQL(config,sql){
	plus.sqlite.executeSql({
		...config,
		sql: sql,
		success: function(e){
			console.log('executeSql success!');
			},
		fail: function(e){
			console.log('executeSql failed: '+JSON.stringify(e));
			}
	});
}

// 查询SQL语句
function selectSQL(config,sql){
	plus.sqlite.selectSql({
		...config,
		sql: sql,
		success: function(data){
				console.log('selectSql success: ');
				for(var i in data){
					console.log(data[i]);
					}
				},
				fail: function(e){
					console.log('selectSql failed: '+JSON.stringify(e));
					}
		});
}

// 执行事务(开始)begin,commit,rollback
function trans(config,oper){
	plus.sqlite.transaction({
		...config,
		operation:oper,
		success: function(e){
				console.log('transaction '+oper+' success!');
			},
		fail: function(e){
				console.log('transaction '+oper+' failed: '+JSON.stringify(e));
			}
		});
}


module.exports = {trans,selectSQL,executeSQL,closeDB,isOpen,openDB};

3、在主程序中我们引用这个js,在pages/index/index.vue的script

<script>
	import sql1 from '@/common/SQLite31.js'
	export default {
		data() {
			return {
				title: 'Hello',
				config:{name: 'first',path: '_doc/test.db'}
			}
		},
		onLoad() {
			//this.openDB();
		},

4、在methods之中增加事件如下:

//==========================第1代接口=======================
			clickOpenDB(){
				sql1.openDB(this.config)
			},
			clickIsOpen(){
				sql1.isOpen(this.config)
			},
			clickCloseDB(){
				sql1.closeDB(this.config);
			},
			clickExecuteSQL(){
				let sqltxt=['create table if not exists database("where" CHAR(110),"location" CHAR(100),"age" INT(11))',
							"insert into database values('北京','安乐林','11')"];
				sql1.executeSQL(this.config,sqltxt);
			},
			clickSelectSQL(){
				let sqltxt='select * from database';
				sql1.selectSQL(this.config,sqltxt);
			},
			clickTransBegin(){
				sql1.trans(this.config,'begin');
			},
			clickEditDB(){
				let sqltxt="update database set age=age+1 ";
				sql1.executeSQL(this.config,sqltxt);
			},
			clickTransCommit(){
				sql1.trans(this.config,'commit');
			},
			clickTransRollback(){
				sql1.trans(this.config,'rollback');
			},

5、小结:我们已经做了接口,也用独立文件来表达。但是使用还是不方便,比如不能方便的使用连续两条SQL,必须要在回调中才安全。如果遇上复杂一点的SQL,我们就难以搞定。所以必须继续进行同步化!

五、第2代接口

1、同步化我们采用 async/promise/await来处理所有的已知函数,以isOpen为例说明,未同化前:

//db是否打开
function isOpen(config){
	if (plus.sqlite.isOpenDatabase(config)){
		console.log('已经打开数据库。');
		return true;
	}else{
		console.log('没有打开数据库!');
		return false;
	}
}

同化之后

//db是否打开
async function isOpen(config){
	return new Promise((callback)=>{
		if (plus.sqlite.isOpenDatabase(config)){
			callback({code:1,msg:'已经打开数据库。'});
		}else{
			callback({code:0,msg:'没有打开数据库!'});
		}
	});
}

如何使用同化后的函数呢?
在模板之中:

<button style="width: 30%;margin-bottom: 10rpx;" @click="onclickIsOpen">是否打开</button>

在脚本之中:

<script>

import sql2 from '@/common/SQLite32.js'
    ......
    methods: {
        ...
        		async onclickIsOpen(){
				let res=await sql2.isOpen(this.config);
				if (res.code==1) console.log('已经打开');
				else console.log(res.msg);
			},

事实上我们规定,所有同步函数都返回:{code:code,msg:msg,data:[]}
code,msg是必须的,data是可选的;code=1是操作成功,code=0是操作失败,msg:是成功信息或出错信息,data是数据

2、我们在主页面的template,增加如下代码:

		<hr>
		<view class="title">第2代接口</view>
		<view style="display: flex;flex-direction: row;flex-wrap: wrap;justify-content:flex-start;justify-items: auto;">
			<button style="width: 30%;margin-bottom: 10rpx;" @click="onclickOpenDB">打开DB</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="onclickIsOpen">是否打开</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="onclickCloseDB">关闭DB</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="onclickExecuteSQL">执行SQL</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="onclickSelectSQL">查询SQL</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="onclickTransBegin">事务开始</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="onclickEditDB">岁数+1</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="onclickTransCommit">事务提交</button>
			<button style="width: 30%;margin-bottom: 10rpx;" @click="onclickTransRollback">事务回滚</button>
		</view>

3、新建common/SQLite32.js如下:

/*
名称:	SQLite32.js
作者:	天一
功能:	SQLite3的同步接口
开创:	2023-08-12
最新:	2023-08-13
*/
// 打开数据库
async function openDB(config){
	return new Promise((callback)=>{
		plus.sqlite.openDatabase({
			...config,
			success: function(e){
				callback({code:1,msg:'打开数据库 成功!'})
			},
			fail: function(e){
				callback({code:0,msg:'打开数据库 失败: '+JSON.stringify(e)})
			}
		});
	})
}

//db是否打开
async function isOpen(config){
	return new Promise((callback)=>{
		if (plus.sqlite.isOpenDatabase(config)){
			callback({code:1,msg:'已经打开数据库。'});
		}else{
			callback({code:0,msg:'没有打开数据库!'});
		}
	});
}

// 关闭数据库
async function closeDB(config){
	return new Promise((callback)=>{
		plus.sqlite.closeDatabase({
			...config,
			success: function(e){
					console.log('closeDatabase success!');
					callback({code:1,msg:'关闭数据库成功。'});
				},
			fail: function(e){
					callback({code:0,msg:'关闭数据库失败:'+JSON.stringify(e)});
				}
		});
	});
}

// 执行SQL语句
async function executeSQL(config,sql){
	return new Promise((callback)=>{
		plus.sqlite.executeSql({
			...config,
			sql: sql,
			success: function(e){
					callback({code:1,msg:e});
				},
			fail: function(e){
					callback({code:0,msg:'执行SQL失败:'+JSON.stringify(e)});
				}
		});
	});
}

// 查询SQL语句
async function selectSQL(config,sql){
	return new Promise((callback)=>{
		plus.sqlite.selectSql({
			...config,
			sql: sql,
			success: function(data){
					callback({code:1,data:data,msg:'查询SQL成功'})
				},
			fail: function(e){
					callback({code:0,data:[],msg:'查询SQL失败: '+JSON.stringify(e)})
				}
			});
	});

}

// 执行事务(oper)begin,commit,rollback
async function transaction(config,oper){
	return new Promise((callback)=>{
		plus.sqlite.transaction({
			...config,
			operation:oper,
			success: function(e){
					callback({code:1,msg:'事务 '+oper+' 成功'});
			},
			fail: function(e){
					callback({code:0,msg:'事务 '+oper+' 失败:'+JSON.stringify(e)});
			}
		});
	});

}

module.exports = {closeDB,openDB,isOpen,executeSQL,selectSQL,transaction};

4、在主程序中我们引用这个js,在pages/index/index.vue的script

<script>
	import sql2 from '@/common/SQLite32.js'
	export default {
		data() {
			return {
				title: 'Hello',
				config:{name: 'first',path: '_doc/test.db'}
			}
		},
		onLoad() {
			//this.openDB();
		},

5、在methods之中增加事件如下:

			//==========================第2代接口==================
			async onclickOpenDB(){
				let res=await sql2.openDB(this.config);
				if (res.code==1) console.log('打开成功');
				else console.log(res.msg);
			},
			//
			async onclickIsOpen(){
				let res=await sql2.isOpen(this.config);
				if (res.code==1) console.log('已经打开');
				else console.log(res.msg);
			},
			async onclickCloseDB(){
				let res=await sql2.closeDB(this.config);
				console.log(res);
			},
			async onclickExecuteSQL(){
				let sqltxt=['create table if not exists database("where" CHAR(110),"location" CHAR(100),"age" INT(11))',
							"insert into database values('北京','安乐林','11')"];
				let res=await sql2.executeSQL(this.config,sqltxt);
				console.log(res);
			},
			async onclickSelectSQL(){
				let sqltxt='select * from database';
				let res=await sql2.selectSQL(this.config,sqltxt);
				console.log(res);
			},
			async onclickTransBegin(){
				let res=await sql2.transaction(this.config,'begin');
				console.log(res);
			},
			async onclickEditDB(){
				let sqltxt="update database set age=age+1 ";
				let res=await sql2.executeSQL(this.config,sqltxt);
				console.log(res);
			},
			async onclickTransCommit(){
				let res=await sql2.transaction(this.config,'commit');
				console.log(res);
			},
			async onclickTransRollback(){
				let res=await sql2.transaction(this.config,'rollback');
				console.log(res);
			},
		

6、小结:本文只是初谈,所有代码,已上传 https://gitcode.net/sinat_26699091/SQLiteDemo

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值