Part 10:iOS的数据持久化(2),Sqlite,CoreData

此处接 Part 10:iOS的数据持久化(1),文件,归档

Sqlite方式:NoteModelSQlite.swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

import Foundation

//Sqlite数据库,需要添加 libsqlite3.tbd 的库,然后创建 .h 头文件,并在头文件中引入 #import "sqlite3.h"

//.h 头文件,简单作法:可以先创建一个OC的文件,会提示是否创建头文件,点击确定创建后,头文件会自动创建

class NoteModelSQlite: NSObject {

     

    //定义数据库文件名称的常量

    let DB_FILE = "/NotesList.sqlite3"

    //sqlitedb指针 Swift COpaquePointer

    var db:OpaquePointer?  = nil

     

    let dateFormatter : DateFormatter = DateFormatter()

     

    private static let sharedInstance = NoteModelSQlite() //单例的实例保存这个属性中

    class var sharedFoo: NoteModelSQlite { //swift中的静态计算属性

         

        //初始化

        sharedInstance.plistCopyDocument()

         

        return sharedInstance

    }

     

     

    //修改Note方法

    public func modify(model: Note) {

         

        //得到数据库的文件路径

        let path:NSString = self.getDocumentPath() as NSString

        //将path:NSString转换为C的字符串(char*)

        let cpath = path.cString(using: String.Encoding.utf8.rawValue)

         

        /*1、sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开

         @param1 文件路径,

         @param2 db指针类型

         &db是传递db地址*/

        if sqlite3_open(cpath, &db) != SQLITE_OK {

            sqlite3_close(db)//关闭链接

            assert(false, "数据库连接失败")

        else {

            let sql = "update note set content = ? where cdate = ?"//sql语句

            //sql语句也需要转换成C的字符串

            let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))

             

            var statement : OpaquePointer?  = nil//statement指针对象

            /*2、使用sqlite3_prepare_v2函数预处理SQL语句;

             @param1 db指针

             @param2 要执行的sql语句

             @param3 要传递sql语句的大小,传递所有 -1

             @param4 statement指针对象

             @param5 会告诉你那此没有被传递sql语句

             */

            if sqlite3_prepare_v2(db, cSql, -1, &statement, nil) == SQLITE_OK {

                 

                //3、使用sqlite3_bind_text函数绑定参数;sql语句中的条件会用 ? 号来代替

                self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

                let strDate = self.dateFormatter.string(from: model.Date as Date)//把model.Date转为字符串

                let cDate = strDate.cString(using: String.Encoding.utf8)//转换为C的字符串(char*)

                let cContent = model.Content.cString(using: String.Encoding.utf8.rawValue)//转换为C的字符串(char*)

                /* sqlite3_bind_text 绑定参数

                 @param1 statement指针对象

                 @param2 绑定参数的索引 1开始

                 @param3 正真要绑定的参数

                 @param4 要传递的最大字节数

                 @param5 回调函数

                 */

                sqlite3_bind_text(statement, 1, cContent, -1, nil)//绑定content

                sqlite3_bind_text(statement, 2, cDate, -1, nil)//绑定cdate

                 

                //4、使用sqlite3_step函数执行SQL语句,遍历结果集;

                if sqlite3_step(statement) != SQLITE_DONE { //如果没有执行完成

                    sqlite3_close(db)//关闭链接

                    assert(false, "信息修改失败")

                }

            }

            //  6、使用sqlite3_finalize和sqlite3_close函数释放资源。

            sqlite3_finalize(statement)//释放sql语句对象

            sqlite3_close(db)

        }

    }

     

     

    //插入Note方法

    public func create(model: Note) {

        //得到数据库的文件路径

        let path:NSString = self.getDocumentPath() as NSString

        //将path:NSString转换为C的字符串(char*)

        let cpath = path.cString(using: String.Encoding.utf8.rawValue)

         

        /*1、sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开

         @param1 文件路径,

         @param2 db指针类型

         &db是传递db地址*/

        if sqlite3_open(cpath, &db) != SQLITE_OK {

            sqlite3_close(db)//关闭链接

            assert(false, "数据库连接失败")

        else {

            let sql = "insert into note (cdate,content) values (?,?)"//sql语句

            //sql语句也需要转换成C的字符串

            let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))

             

            var statement : OpaquePointer?  = nil//statement指针对象

            /*2、使用sqlite3_prepare_v2函数预处理SQL语句;

             @param1 db指针

             @param2 要执行的sql语句

             @param3 要传递sql语句的大小,传递所有 -1

             @param4 statement指针对象

             @param5 会告诉你那此没有被传递sql语句

             */

            if sqlite3_prepare_v2(db, cSql, -1, &statement, nil) == SQLITE_OK {

                 

                //3、使用sqlite3_bind_text函数绑定参数;sql语句中的条件会用 ? 号来代替

                self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

                let strDate = self.dateFormatter.string(from: model.Date as Date)//把model.Date转为字符串

                let cDate = strDate.cString(using: String.Encoding.utf8)//转换为C的字符串(char*)

                let cContent = model.Content.cString(using: String.Encoding.utf8.rawValue)//转换为C的字符串(char*)

                /* sqlite3_bind_text 绑定参数

                 @param1 statement指针对象

                 @param2 绑定参数的索引 1开始

                 @param3 正真要绑定的参数

                 @param4 要传递的最大字节数

                 @param5 回调函数

                 */

                sqlite3_bind_text(statement, 1, cDate, -1, nil)//绑定cdate

                sqlite3_bind_text(statement, 2, cContent, -1, nil)//绑定content

                 

                //4、使用sqlite3_step函数执行SQL语句,遍历结果集;

                if sqlite3_step(statement) != SQLITE_DONE { //如果没有执行完成

                    sqlite3_close(db)//关闭链接

                    assert(false, "信息添加失败")

                }

            }

            //  6、使用sqlite3_finalize和sqlite3_close函数释放资源。

            sqlite3_finalize(statement)//释放sql语句对象

            sqlite3_close(db)

        }

    }

     

    //删除Note方法

    public func remove(model: Note) {

        //得到数据库的文件路径

        let path:NSString = self.getDocumentPath() as NSString

        //将path:NSString转换为C的字符串(char*)

        let cpath = path.cString(using: String.Encoding.utf8.rawValue)

         

        /*1、sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开

         @param1 文件路径,

         @param2 db指针类型

         &db是传递db地址*/

        if sqlite3_open(cpath, &db) != SQLITE_OK {

            sqlite3_close(db)//关闭链接

            assert(false, "数据库连接失败")

        else {

            let sql = "delete from note where cdate = ?"//sql语句

            //sql语句也需要转换成C的字符串

            let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))

             

            var statement : OpaquePointer?  = nil//statement指针对象

            /*2、使用sqlite3_prepare_v2函数预处理SQL语句;

             @param1 db指针

             @param2 要执行的sql语句

             @param3 要传递sql语句的大小,传递所有 -1

             @param4 statement指针对象

             @param5 会告诉你那此没有被传递sql语句

             */

            if sqlite3_prepare_v2(db, cSql, -1, &statement, nil) == SQLITE_OK {

                 

                //3、使用sqlite3_bind_text函数绑定参数;sql语句中的条件会用 ? 号来代替

                self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

                let strDate = self.dateFormatter.string(from: model.Date as Date)//把model.Date转为字符串

       

                let cDate = strDate.cString(using: String.Encoding.utf8)//转换为C的字符串(char*)

                /* sqlite3_bind_text 绑定参数

                 @param1 statement指针对象

                 @param2 绑定参数的索引 1开始

                 @param3 正真要绑定的参数

                 @param4 要传递的最大字节数

                 @param5 回调函数

                 */

                sqlite3_bind_text(statement, 1, cDate, -1, nil)//绑定cdate

                 

                //4、使用sqlite3_step函数执行SQL语句,遍历结果集;

                if sqlite3_step(statement) != SQLITE_DONE { //如果没有执行完成

                    sqlite3_close(db)//关闭链接

                    assert(false, "数据库删除失败")

                }

            }

            //  6、使用sqlite3_finalize和sqlite3_close函数释放资源。

            sqlite3_finalize(statement)//释放sql语句对象

            sqlite3_close(db)

        }

    }

     

    //根据主键查询一条数据

    func findByid(model:Note) -> Note? {

         

        //得到数据库的文件路径

        let path:NSString = self.getDocumentPath() as NSString

        //将path:NSString转换为C的字符串(char*)

        let cpath = path.cString(using: String.Encoding.utf8.rawValue)

         

        /*1、sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开

         @param1 文件路径,

         @param2 db指针类型

         &db是传递db地址*/

        if sqlite3_open(cpath, &db) != SQLITE_OK {

            sqlite3_close(db)//关闭链接

            assert(false, "数据库连接失败")

        else {

            let sql = "select cdate,content from note where cdate = ?"//sql语句

            //sql语句也需要转换成C的字符串

            let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))

             

            var statement : OpaquePointer?  = nil//statement指针对象

            /*2、使用sqlite3_prepare_v2函数预处理SQL语句;

             @param1 db指针

             @param2 要执行的sql语句

             @param3 要传递sql语句的大小,传递所有 -1

             @param4 statement指针对象

             @param5 会告诉你那此没有被传递sql语句

             */

            if sqlite3_prepare_v2(db, cSql, -1, &statement, nil) == SQLITE_OK {

                 

                //3、使用sqlite3_bind_text函数绑定参数;sql语句中的条件会用 ? 号来代替

                let strDate = dateFormatter.string(from: model.Date as Date)//把model.Date转为字符串

                let cDate = strDate.cString(using: String.Encoding.utf8)//转换为C的字符串(char*)

                /* sqlite3_bind_text 绑定参数

                 @param1 statement指针对象

                 @param2 绑定参数的索引 1开始

                 @param3 正真要绑定的参数

                 @param4 要传递的最大字节数

                 @param5 回调函数

                 */

                sqlite3_bind_text(statement, 1, cDate, -1, nil)

                 

                //4、使用sqlite3_step函数执行SQL语句,遍历结果集;

                if sqlite3_step(statement) == SQLITE_ROW { //有记录返回

                     

                    /*5、使用sqlite3_column_text等函数提取字段数据;

                     @param1 statement指针对象

                     @param2 字段索引,比如 cdate==0,content==1

                     @return 返回提char * 需要 转为 String

                     */

                    //char* -> String   UnsafePointer相当于 char*

                    let bufDate = sqlite3_column_text(statement, 0)//查询cdate

                    let strDate = String(cString: bufDate!)//转为String

                    self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

                    //String->NSDate

                    let date : NSDate = self.dateFormatter.date(from: strDate)! as NSDate

                     

                    let bufContent = sqlite3_column_text(statement, 1)//查询content

                    let strContent = String(cString: bufContent!)//转为String

                     

                    //  6、使用sqlite3_finalize和sqlite3_close函数释放资源。

                    sqlite3_finalize(statement)//释放sql语句对象

                    sqlite3_close(db)

                     

                    let note = Note(datedate, content: strContent as NSString)//合为Note对象

                     

                    return note

                }

            }

        }

        return nil

    }

     

     

    //查询所有数据方法

    func findAll() -> NSMutableArray {

         

        let listData  = NSMutableArray()

         

        //得到数据库的文件路径

        let path:NSString = self.getDocumentPath() as NSString

        //将path:NSString转换为C的字符串(char*)

        let cpath = path.cString(using: String.Encoding.utf8.rawValue)

         

        /*1、sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开

         @param1 文件路径,

         @param2 db指针类型

         &db是传递db地址*/

        if sqlite3_open(cpath, &db) != SQLITE_OK {

            sqlite3_close(db)//关闭链接

            assert(false, "数据库连接失败")

        else {

            let sql = "select cdate,content from note"//sql语句

            //sql语句也需要转换成C的字符串

            let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))

             

            var statement : OpaquePointer?  = nil//statement指针对象

            /*2、使用sqlite3_prepare_v2函数预处理SQL语句;

             @param1 db指针

             @param2 要执行的sql语句

             @param3 要传递sql语句的大小,传递所有 -1

             @param4 statement指针对象

             @param5 会告诉你那此没有被传递sql语句

             */

            if sqlite3_prepare_v2(db, cSql, -1, &statement, nil) == SQLITE_OK {

             

                //3、使用sqlite3_bind_text函数绑定参数;sql语句中的条件会用 ? 号来代替

                 

                //4、使用sqlite3_step函数执行SQL语句,遍历结果集;

                while sqlite3_step(statement) == SQLITE_ROW { //有记录返回

                     

                    /*5、使用sqlite3_column_text等函数提取字段数据;

                     @param1 statement指针对象

                     @param2 字段索引,比如 cdate==0,content==1

                     @return 返回提char * 需要 转为 String

                     */

                    let bufDate = sqlite3_column_text(statement, 0)//查询cdate

                    let strDate = String(cString:bufDate!)//cString转为String

                    self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

                    //String->NSDate

                    let date : NSDate = self.dateFormatter.date(from: strDate)! as NSDate

                     

                    let bufContent = sqlite3_column_text(statement, 1)//查询content

                    let strContent = String(cString: bufContent!)//转为String

                     

                    let note = Note(datedate, content: strContent as NSString)//合为Note对象

                     

                    listData.add(note)//添加到可变数组中

                }

            }

            //  6、使用sqlite3_finalize和sqlite3_close函数释放资源。

            sqlite3_finalize(statement)//释放sql语句对象

            sqlite3_close(db)

        }

        return listData

    }

     

    //初始化 创建数据表 与 数据库文件

    func plistCopyDocument() {

        //得到数据库的文件路径

        let path:NSString = self.getDocumentPath() as NSString

        print(path)

        //将path:NSString转换为C的字符串(char*)

        let cpath = path.cString(using: String.Encoding.utf8.rawValue)

         

        /*sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开

        @param1 文件路径,

        @param2 db指针类型

        &db是传递db地址*/

        if sqlite3_open(cpath, &db) != SQLITE_OK {

            sqlite3_close(db)//关闭链接

            assert(false, "数据库连接失败")

        else {

            //sql语句:如果Note数据表不存在则创建,cdate 是主键,content为字符类型

            let sql = "create table if not exists Note (cdate text primary key, content text)"

            //sql语句也需要转换成C的字符串

            let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))

             

            /*sqlite3_exec执行sql,但sqlite3_exec没有返回结果集

             @param1 指针,

            @param2 sql语句,

            @param3 回调函数,

            @param4 为回调函数传递参数的指针

            @param5 errmsg错误信息*/

            if sqlite3_exec(db, cSql, nil, nil, nil) != SQLITE_OK {

                sqlite3_close(db)//关闭链接

                assert(false, "SQL语句执行失败")

            else {

                sqlite3_close(db)//执行成功关闭链接

            }

        }

    }

     

    //获取系统沙箱Document目录方法

    func getDocumentPath() -> String {

        let documentDirectory: NSArray = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray

        let myDocPath = documentDirectory[0] as! NSString

        //在myDocPath字符串的基础上累加另一个字符串NotesList.plist,目录+文件路径

        let wtFile = myDocPath.appending(DB_FILE) as String

        return wtFile

    }

     

}

CoreData方式:NoteDAO.swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

//如果使用CoreData,下以步骤

//1,创建CoreData文件,在对应文件夹中右击选择CoreData..

//2,创建CoreData模型,进入CoreData文件中,左下方的加号

//3,生成实例文件,注意命名不要和现有类冲突,选中Model.xcdatamodeld->Editor->Create NSManagedObject subclass

//4,拷贝堆栈代码(创建新项目 use CoreData ,AppDelegate.swift中查看)或创建到指定文件位置,注意命名..

//5,要引入 import CoreData

//注:这个是一种convenience方法,即快速实现。所以并不需要新建对应于entity的class,上面第4步可省略

import UIKit

import Foundation

import CoreData

//DAO类,一般按表名命名,相当于PHP框架加的 Model

class NoteDAO: UIViewController {

     

    //保存数据列var,没用数据,数据保存到内存中

    var listData: NSMutableArray! //可变数组类型

     

    //只进行和数据库的交互,不需要保持任何状态,所以可以使用单例模式

    private static let sharedInstance = NoteDAO() //单例的实例保存这个属性中

    class var sharedFoo: NoteDAO { //swift中的静态计算属性

         

        return sharedInstance

    }

     

    //插入Note方法

    func create(model: Note) {

         

        let context = self.getContext()

        // 定义一个entity,这个entity一定要在xcdatamodeld中做好定义

        let entity = NSEntityDescription.entity(forEntityName: "CoreDataNote", in: context)

         

        let person = NSManagedObject(entity: entity!, insertInto: context)

         

        person.setValue(model.Content, forKey: "cContent")

        person.setValue(model.Date, forKey: "cDate")

         

        do {

            try context.save()

            print("saved")

        }catch{

            print(error)

        }

         

    }

     

    //删除Note方法

    func remove(model: Note) {

        //定义查询主体

        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "CoreDataNote")

        let entity:NSEntityDescription = NSEntityDescription.entity(forEntityName: "CoreDataNote", in: getContext())!

        //指定要查询的实体

        fetchRequest.entity = entity

        //添加查询条件

        fetchRequest.predicate = NSPredicate(format: "cDate = %@", model.Date)

         

        do {

            //进行查询

            let searchResults = try getContext().fetch(fetchRequest)

             

            if searchResults.count != 0 {

                //删除操作

                self.getContext().delete(searchResults[0] as! NSManagedObject)

                //结果保存

                try getContext().save()

                print("delete success ~ ~")

            }

             

        catch  {

            print(error)

        }

    }

     

    //修改Note.Content方法

    func modify(model: Note) {

        //定义查询主体

        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "CoreDataNote")

        let entity:NSEntityDescription = NSEntityDescription.entity(forEntityName: "CoreDataNote", in: getContext())!

        //指定要查询的实体

        fetchRequest.entity = entity

        //添加查询条件

        fetchRequest.predicate = NSPredicate(format: "cDate = %@", model.Date)

         

        do {

            //进行查询

            let searchResults = try getContext().fetch(fetchRequest)

             

            if searchResults.count != 0 {

                //到得查询出来的对象

                let cNote = searchResults[0] as! CoreDataNote

                //修改对应的字段

                cNote.cContent = model.Content as String

                //结果保存

                try getContext().save()

                print("update success ~ ~")

            }

             

        catch  {

            print(error)

        }

    }

     

    //查询所有数据方法

    func findAll() -> NSMutableArray {

        //定义可变数组

        let listData = NSMutableArray()

        //定义查询主体

        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "CoreDataNote")

        do {

            //进行查询

            let searchResults = try getContext().fetch(fetchRequest)

            //print("numbers of \(searchResults.count)")//获得总数

             

            //遍历结果集

            for p in (searchResults as! [NSManagedObject]){

                //获取对应字段

                let date = p.value(forKey: "cDate"as! NSDate

                let content = p.value(forKey: "cContent"as! NSString

                //创建一个 note 对像

                let note = Note(datedate, content: content)

                //加入可变数组中

                listData.add(note)

            }

        catch  {

            print(error)

        }

        return listData

         

    }

     

    //查询一条数据方法

    func findById(model: Note) -> String! {//返回类型为可选的,说明Note可以返回nil

        //定义查询主体

        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "CoreDataNote")

        let entity:NSEntityDescription = NSEntityDescription.entity(forEntityName: "CoreDataNote", in: getContext())!

        //指定要查询的实体

        fetchRequest.entity = entity

        //添加查询条件

        fetchRequest.predicate = NSPredicate(format: "cDate = %@", model.Date)

         

        do {

            //进行查询

            let searchResults = try getContext().fetch(fetchRequest)

             

            if searchResults.count != 0 {

                //到得查询出来的对象

                let cNote = searchResults[0] as! CoreDataNote

                //查询需要的字段值

                let cContent = cNote.value(forKey: "cContent")!

                //返回

                return cContent as! String

            }

             

        catch  {

            print(error)

        }

        return nil

    }

     

    // 获取Context上下文

    func getContext () -> NSManagedObjectContext {

        let appDelegate = UIApplication.shared.delegate as! AppDelegate

        return appDelegate.persistentContainer.viewContext

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程工人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值