Swift 学习之Using Swift mix and match, network: 写rss阅读器

这里面使用到第三方库,因此需要使用mix and match特性。

请大家指出其中的错误,谢谢!

rss 阅读器,很简单的代码,只是为了学习Swift语言而写。


1、BaseViewController.swift:

[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import Foundation  
  2. import UIKit  
  3.   
  4. //  
  5. // @brief Each controller must directly or indirectly inherit from BaseViewController to  
  6. //        configure.  
  7. // @author huangyibiao  
  8. //  
  9. class BaseViewController : UIViewController {  
  10.     // use to config ios7 or later and below ios7  
  11.     var _originX: CGFloat = 0.0  
  12.     var _screenHeight: CGFloat = 480.0  
  13.     var _isIOS7Version = true  
  14.    
  15.     override func viewDidLoad() {  
  16.         super.viewDidLoad()  
  17.           
  18.         _isIOS7Version = UIDevice.currentDevice().systemVersion >= "7.0"  
  19.         // 难道坐标系统变化了?这么写法反面适配失败了  
  20.         //_originX = _isIOS7Version ? 64.0 : 0.0  
  21.         // 获取到的屏幕的高度怎么也只有self.view的高度,不是整个屏幕的高度了?  
  22.         _screenHeight = UIScreen.mainScreen().bounds.size.height  
  23.     }  
  24.       
  25.     override func viewWillAppear(animated: Bool) {  
  26.         super.viewWillAppear(animated)  
  27.         // config navigations  
  28.         let dict = [UITextAttributeTextColor: UIColor.whiteColor()]  
  29.         self.navigationController.navigationBar.titleTextAttributes = dict  
  30.         self.navigationController.navigationBar.barTintColor = UIColor.orangeColor()  
  31.     }  
  32. }  

2. need a model: FeedModel.swift

[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import Foundation  
  2.   
  3. //  
  4. // @brief Feed data model, just stores datas  
  5. // @author huangyibiao  
  6. //  
  7. class FeedModel : NSObject, NSCoding {  
  8.     var _name: String?  
  9.     var _url:  String?  
  10.       
  11.     // designated initializer  
  12.     init(name: String?, url: String?) {  
  13.         super.init()  
  14.         _name = name  
  15.         _url = url  
  16.     }  
  17.       
  18.     //  
  19.     // The following functions are NSCoding protocol methods  
  20.     //  
  21.     // encode  
  22.     func encodeWithCoder(aCoder: NSCoder!) {  
  23.         aCoder.encodeObject(_name, forKey: "_name")  
  24.         aCoder.encodeObject(_url, forKey: "_url")  
  25.     }  
  26.       
  27.     // decode  
  28.     init(coder aDecoder: NSCoder!) {  
  29.         _name = aDecoder.decodeObjectForKey("_name") as? String  
  30.         _url = aDecoder.decodeObjectForKey("_url") as? String  
  31.     }  
  32. }  

3. write an extension: Extension.swift

[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import Foundation  
  2. import UIKit  
  3.   
  4. //------------------------------------------------------------------------------------------  
  5. // @brief extension UIAlertView, making easily to use  
  6. // @author huangyibiao  
  7. //  
  8. extension UIAlertView {  
  9.     // @brief Shows an alert view, default showing with "cancel" and "ok" buttons  
  10.     // @param title: non-nil String object, shows the title  
  11.     // @param message: non-nil String object, shows the message  
  12.     // @return return non-nil UIAlertView object  
  13.     class func showAlertView(title: String!, message: String!) -> UIAlertView! {  
  14.         // in fact, swift provides UIAlerController to do the same thing  
  15.         // in the future, it may be replaced by UIAlertController  
  16.         let alert = UIAlertView()  
  17.         alert.title = title  
  18.         alert.message = message  
  19.         alert.addButtonWithTitle("Cancel")  
  20.         alert.addButtonWithTitle("Ok")  
  21.         alert.show()  
  22.           
  23.         return alert  
  24.     }  
  25. }  
  26.   
  27.   
  28. //------------------------------------------------------------------------------------------  
  29. // @brief 扩展String结构体,追加获取feed缓存路径的方法  
  30. // @author huangyibiao  
  31. //  
  32. extension String {  
  33.     // @brief 获取缓存路径  
  34.     // @param check: default value is true, meaning that needs to check is exist  
  35.     // @return Optional String type, if nil, means no value at all, otherwise, returns path  
  36.     //  
  37.     static func feedCachePath(isCheck check: Bool = true) -> String? {  
  38.         let path = NSHomeDirectory() + "/Documents/data"  
  39.         if check {  
  40.             if NSFileManager.defaultManager().fileExistsAtPath(path) {  
  41.                 return path  
  42.             }  
  43.         }  
  44.         return path  
  45.     }  
  46. }  

4. need a feed manager: FeedManager.swift

[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import Foundation  
  2.   
  3. //  
  4. // @brief Here FeedManager is a singleton, use to manage feed data objects  
  5. // @author huangyibiao  
  6. //  
  7.   
  8. // never use this global variable directly, because directly using it may not  
  9. // has a value.  
  10. // I don't know how to make it invisible outside in swift  
  11. var g_sharedFeedManager: FeedManager!  
  12.   
  13. class FeedManager {  
  14.     var _dataSource = NSMutableArray() // use to store FeedModel objects  
  15.       
  16.     // singleton  
  17.     class func sharedFeedManager() -> FeedManager! {  
  18.         if g_sharedFeedManager == nil { // no value  
  19.             g_sharedFeedManager = FeedManager()  
  20.         }  
  21.         return g_sharedFeedManager  
  22.     }  
  23.       
  24.     // designated initializer  
  25.     init() {  
  26.           
  27.     }  
  28.       
  29.     // return the numbers of feed models  
  30.     func count() -> Int {  
  31.         return _dataSource.count  
  32.     }  
  33.       
  34.     // add model  
  35.     func addFeedModel(feedModel: FeedModel!) {  
  36.         // found whether is exist  
  37.         for model: AnyObject in _dataSource {  
  38.             let feedModelObject = model as FeedModel  
  39.             if feedModelObject._name == feedModel._name {  
  40.                 return  
  41.             }  
  42.         }  
  43.         _dataSource.addObject(feedModel)  
  44.         saveAllFeeds()  
  45.     }  
  46.       
  47.     // remove model  
  48.     func removeFeedModel(atIndex index: Int) {  
  49.         assert(index >= 0 && index < count(), "out of range in array", file: "Extension.swift", line: 57)  
  50.           
  51.         _dataSource.removeObjectAtIndex(index)  
  52.         saveAllFeeds() // update local datas  
  53.     }  
  54.       
  55.     // obtain  
  56.     func feedModel(atIndex index: Int) -> FeedModel {  
  57.         assert(index >= 0 && index < count(), "out of range in array", file: "Extension.swift", line: 57)  
  58.           
  59.         return _dataSource[index] as FeedModel  
  60.     }  
  61.       
  62.     func saveAllFeeds() {  
  63.         let path = String.feedCachePath(isCheck: false)  
  64.         if path { // 若已经存在,先清除  
  65.             NSFileManager.defaultManager().removeItemAtPath(path, error: nil)  
  66.         }  
  67.         // 归档  
  68.         NSKeyedArchiver.archiveRootObject(_dataSource, toFile: path)  
  69.     }  
  70.       
  71.     func loadAllFeeds() {  
  72.         let path = String.feedCachePath()// default isCheck is true  
  73.         if path {  
  74.             let feedModels = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? NSArray  
  75.             if feedModels {  
  76.                 _dataSource.addObjectsFromArray(feedModels)  
  77.             }  
  78.         }  
  79.     }  
  80. }  

5.need a HttpRequest: HttpRequest.swift

[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import Foundation  
  2.   
  3. @objc protocol HttpRequestDelegate {  
  4.     // @brief when request finished, and will call this delegate method if comforms to  
  5.     // @param request: an non-nil HttpRequest object  
  6.     // @param downloadedData: you can do something with the parameter,  
  7.     //                        which is the data  downloaded from ns service  
  8.     @optional func requestFinished(request: HttpRequest!, downloadedData: NSMutableData!)  
  9.       
  10.     // @brief fail to download  
  11.     @optional func requestFailed(request: HttpRequest!)  
  12. }  
  13.   
  14. //  
  15. // @brief 网络请示类,这里只是简单实现,没有做缓存处理  
  16. // @author huangyibiao  
  17. //  
  18. class HttpRequest : NSObject, NSURLConnectionDelegate {  
  19.     var _delegate: HttpRequestDelegate?  
  20.     var _urlString: String?  
  21.     var _connection: NSURLConnection?  
  22.     var _downloadData: NSMutableData?  
  23.       
  24.     init()  {  
  25.         super.init()  
  26.         _downloadData = NSMutableData()  
  27.     }  
  28.       
  29.     // begin to request data  
  30.     func startRequest(delegate: HttpRequestDelegate?, urlString: String!) {  
  31.         cancelRequest() // cancel current request before a new request  
  32.           
  33.         _delegate = delegate  
  34.         _urlString = urlString  
  35.           
  36.         // clear _downloadData  
  37.         _downloadData!.resetBytesInRange(NSRange(location: 0, length: _downloadData!.length))  
  38.         _downloadData!.length = 0 // update length  
  39.           
  40.         var url = NSURL(string: _urlString)  
  41.         var request = NSURLRequest(URL: url)  
  42.         _connection = NSURLConnection(request: request, delegate: self)  
  43.         _connection!.start()  
  44.     }  
  45.       
  46.     func cancelRequest() {  
  47.         if _connection {  
  48.             _connection!.cancel()  
  49.             _connection = nil  
  50.         }  
  51.     }  
  52.       
  53.     //  
  54.     // The following funcs are NSURLConnectionDelegate methods  
  55.     // download fail  
  56.     func connection(connection: NSURLConnection!, didFailWithError error: NSError!){  
  57.         _downloadData!.resetBytesInRange(NSRange(location: 0,length: _downloadData!.length));  
  58.         _downloadData!.length = 0;  
  59.           
  60.         _delegate?.requestFailed?(self)  
  61.     }  
  62.       
  63.     // receive data and append to the downloaded data  
  64.     func connection(connection: NSURLConnection!, didReceiveData data: NSData!){  
  65.         _downloadData!.appendData(data);  
  66.     }  
  67.       
  68.     // download finished  
  69.     func connectionDidFinishLoading(connection: NSURLConnection!){  
  70.         _delegate?.requestFinished!(self, downloadedData: _downloadData!);  
  71.     }  
  72. }  

7.write a root controller: RootViewController.swift

[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import Foundation  
  2. import UIKit  
  3.   
  4. class RootViewController : BaseViewController, UITableViewDelegate, UITableViewDataSource {  
  5.     var _tableView: UITableView?  
  6.       
  7.     override func viewDidLoad() {  
  8.         self.title="网易Rss"  
  9.           
  10.         let addButton = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 40))  
  11.         addButton.setTitle("添加", forState: UIControlState.Normal)  
  12.         addButton.titleLabel.font = UIFont.systemFontOfSize(12);  
  13.         addButton.setTitleColor(UIColor.blackColor(), forState: .Normal);  
  14.         addButton.addTarget(self, action: "onAddButtonClicked:", forControlEvents: UIControlEvents.TouchUpInside)  
  15.         var addButtonItem = UIBarButtonItem(customView: addButton);  
  16.         super.navigationItem.rightBarButtonItem = addButtonItem;  
  17.           
  18.           
  19.         var aboutButton = UIButton(frame: CGRect(x: 0,y: 0,width: 50,height: 40))  
  20.         aboutButton.setTitle("关于", forState: .Normal)  
  21.         aboutButton.titleLabel.font = UIFont.systemFontOfSize(12);  
  22.         aboutButton.setTitleColor(UIColor.blackColor(), forState: .Normal);  
  23.         aboutButton.addTarget(self, action: "onAboutButtonClicked:", forControlEvents: .TouchUpInside)  
  24.         var aboutButtonItem = UIBarButtonItem(customView: aboutButton);  
  25.         super.navigationItem.leftBarButtonItem = aboutButtonItem;  
  26.           
  27.         _tableView = UITableView(frame: self.view.bounds);  
  28.         _tableView!.dataSource = self;  
  29.         _tableView!.delegate = self;  
  30.         self.view.addSubview(_tableView);  
  31.     }  
  32.       
  33.     override func viewWillAppear(animated: Bool) {  
  34.         super.viewWillAppear(animated)  
  35.         _tableView!.reloadData()  
  36.     }  
  37.       
  38.     //  
  39.     // The following are UIButton event methods  
  40.     //  
  41.     // clicke add button  
  42.     func onAddButtonClicked(sender: UIButton!) {  
  43.         let rss = FeedRssListViewController()  
  44.         self.navigationController.pushViewController(rss, animated: true)  
  45.     }  
  46.       
  47.     // clicked about button  
  48.     func onAboutButtonClicked(sender: UIButton!) {  
  49.         // call extension method  
  50.         UIAlertView.showAlertView("关于", message: "参考thilong,欢迎学习swift语言,请勿用于商业用途")  
  51.     }  
  52.       
  53.     //  
  54.     // The following are UITableViewDataSource protocol methods  
  55.     //  
  56.     func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int{  
  57.         return FeedManager.sharedFeedManager().count()  
  58.     }  
  59.       
  60.     func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!{  
  61.         let cellIdentifier = "cellIdentifier" ;  
  62.         var cell  = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell;  
  63.         if cell == nil {  
  64.             cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: cellIdentifier);  
  65.             cell!.selectionStyle = UITableViewCellSelectionStyle.None;  
  66.         }  
  67.           
  68.         let model = FeedManager.sharedFeedManager().feedModel(atIndex: indexPath.row)  
  69.         cell!.textLabel.text = model._name;  
  70.         return cell;  
  71.     }  
  72.       
  73.     //  
  74.     // The following are UITableViewDelegate protocol methods  
  75.     //  
  76.     func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!){  
  77.         let model = FeedManager.sharedFeedManager().feedModel(atIndex: indexPath.row)  
  78.         let detail = RssDetailViewController(model)  
  79.         detail.hidesBottomBarWhenPushed = true  
  80.         self.navigationController.pushViewController(detail, animated:true);  
  81.     }  
  82. }  

8.FeedRssListViewController.swift

[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import Foundation  
  2. import UIKit  
  3.   
  4. //  
  5. // @brief Rss contents for selected  
  6. // @author huangyibiao  
  7. //  
  8. class FeedRssListViewController : BaseViewController, UITableViewDelegate, UITableViewDataSource {  
  9.     var _tableView: UITableView?  
  10.     var _dataSources: Array<FeedModel> = [  
  11.         FeedModel(name:"网易新闻", url:"http://news.163.com/special/00011K6L/rss_newstop.xml"),  
  12.         FeedModel(name:"网易科技", url:"http://tech.163.com/special/000944OI/headlines.xml"),  
  13.         FeedModel(name:"网易NBA", url:"http://sports.163.com/special/00051K7F/rss_sportslq.xml"),  
  14.         FeedModel(name:"网易英超", url:"http://sports.163.com/special/00051K7F/rss_sportsyc.xml"),  
  15.         FeedModel(name:"网易娱乐", url:"http://ent.163.com/special/00031K7Q/rss_toutiao.xml"),  
  16.         FeedModel(name:"网易电影", url:"http://ent.163.com/special/00031K7Q/rss_entmovie.xml"),  
  17.         FeedModel(name:"网易互联网", url:"http://tech.163.com/special/000944OI/hulianwang.xml"),  
  18.         FeedModel(name:"网易IT界", url:"http://tech.163.com/special/000944OI/kejiyejie.xml"),  
  19.         FeedModel(name:"网易汽车", url:"http://auto.163.com/special/00081K7D/rsstoutiao.xml"),  
  20.         FeedModel(name:"网易数码", url:"http://tech.163.com/digi/special/00161K7K/rss_digixj.xml"),  
  21.         FeedModel(name:"网易笔记本", url:"http://tech.163.com/digi/special/00161K7K/rss_diginote.xml"),  
  22.         FeedModel(name:"网易手机", url:"http://mobile.163.com/special/001144R8/mobile163_copy.xml"),  
  23.         FeedModel(name:"网易时尚", url:"http://lady.163.com/special/00261R8C/ladyrss1.xml"),  
  24.         FeedModel(name:"网易星运", url:"http://lady.163.com/special/00261R8C/ladyrss4.xml"),  
  25.         FeedModel(name:"网易游戏", url:"http://game.163.com/special/003144N4/rss_gametop.xml"),  
  26.         FeedModel(name:"网易旅游", url:"http://travel.163.com/special/00061K7R/rss_hline.xml")  
  27.     ]  
  28.       
  29.     override func viewDidLoad() {  
  30.         super.viewDidLoad()  
  31.           
  32.         self.title = "Feed Models"  
  33.           
  34.         // config tableview  
  35.         _tableView = UITableView(frame: self.view.bounds);  
  36.         self.view.addSubview(_tableView);  
  37.         _tableView!.dataSource = self  
  38.         _tableView!.delegate = self;  
  39.     }  
  40.       
  41.     //  
  42.     // @brief The following funcs are UITableViewDataSource protocol methods  
  43.     //  
  44.     func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int{  
  45.         return _dataSources.count;  
  46.     }  
  47.       
  48.     func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!{  
  49.         let cellIdentifier = "cellIdentifier" ;  
  50.         var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell;  
  51.         if cell == nil {  
  52.             cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: cellIdentifier);  
  53.             cell!.selectionStyle = UITableViewCellSelectionStyle.None;  
  54.         }  
  55.         let model = _dataSources[indexPath.row] as FeedModel  
  56.         cell!.textLabel.text = model._name;  
  57.         return cell;  
  58.     }  
  59.       
  60.     //  
  61.     // @brief The following funcs are UITableViewDelegate protocol methods  
  62.     //  
  63.     func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!){  
  64.         let model = _dataSources[indexPath.row] as FeedModel;  
  65.         FeedManager.sharedFeedManager().addFeedModel(model)  
  66.         self.navigationController.popViewControllerAnimated(true);  
  67.     }  
  68. }  

9.RssDetailViewController.swift

[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. import Foundation  
  2. import UIKit  
  3.   
  4. //  
  5. // @brief Detail rss content   
  6. // @author huangyibiao  
  7. //  
  8. class RssDetailViewController : BaseViewController, HttpRequestDelegate {  
  9.     var _httpRequest: HttpRequest?  
  10.     var _webView: UIWebView?  
  11.     var _feedModel: FeedModel?  
  12.       
  13.     init(_ feedModel: FeedModel) {  
  14.         super.init(nibName: nil, bundle: nil)  
  15.           
  16.         _feedModel = feedModel  
  17.     }  
  18.       
  19.     override func viewDidLoad() {  
  20.         super.viewDidLoad()  
  21.           
  22.         self.title = _feedModel!._name  
  23.         _webView = UIWebView(frame: self.view.bounds)  
  24.         self.view.addSubview(_webView)  
  25.           
  26.         _httpRequest = HttpRequest()  
  27.         _httpRequest!.startRequest(self, urlString: _feedModel!._url)  
  28.     }  
  29.       
  30.     //  
  31.     // @brief The following funcs are HttpRequestDelegate protocol methods  
  32.     //  
  33.     func requestFinished(request: HttpRequest!, downloadedData: NSMutableData!) {  
  34.         let doc = GDataXMLDocument(data: downloadedData, options: 0, error: nil)  
  35.         if doc == nil {  
  36.             UIAlertView.showAlertView("error", message: "read xml file error")  
  37.             return;  
  38.         }  
  39.         var rootElement = doc.rootElement();  
  40.         var titleNode = rootElement.nodeForXPath("/rss/channel/title", error: nil);  
  41.         var linkNode = rootElement.nodeForXPath("/rss/channel/link", error: nil);  
  42.         var channelNode = rootElement.nodeForXPath("/rss/channel", error: nil);  
  43.         var items = doc.nodesForXPath("/rss/channel/item", error:nil);  
  44.         var entrysBuilder : NSMutableString = NSMutableString();  
  45.         var baseHTMLPath : NSString = NSBundle.mainBundle().pathForResource("FeedEntryList",ofType: "html");  
  46.         var finalHTML : NSString = NSString.stringWithContentsOfFile(baseHTMLPath,  
  47.             encoding: NSUTF8StringEncoding,error: nil);  
  48.         for (var i = 0; i < items.count; i++){  
  49.             var item = items[i] as GDataXMLElement;  
  50.             var itemTitleNode = item.elementsForName("title")[0] as GDataXMLElement;  
  51.             var itemDescriptionNode = item.elementsForName("description")[0] as GDataXMLElement;  
  52.             var itemUrlNode : GDataXMLElement = item.elementsForName("guid")[0] as GDataXMLElement;  
  53.               
  54.             var entryBuilder : NSMutableString = NSMutableString();  
  55.             entryBuilder.appendString("<a href='");  
  56.             // link here  
  57.             var urlString : NSString! = itemUrlNode.stringValue();  
  58.             var stringS = urlString.componentsSeparatedByString("/");  
  59.             var finalString : NSString? = stringS[stringS.count - 1] as? NSString;  
  60.             if finalString && finalString!.hasSuffix(".html"){  
  61.                 urlString = NSString(string:"http://3g.163.com/touch/article.html?docid=");  
  62.                 var docid : NSString = NSString(string:finalString!.stringByReplacingOccurrencesOfString(".html", withString:""));  
  63.                 urlString = urlString.stringByAppendingFormat("%@",docid);  
  64.             }  
  65.               
  66.               
  67.             entryBuilder.appendString(urlString);  
  68.             entryBuilder.appendString("'><div class='entry'><div class='entryTitle'>");  
  69.             //title here  
  70.             entryBuilder.appendString(itemTitleNode.stringValue());  
  71.             entryBuilder.appendString("</div><div class='entryDescription'>");  
  72.             //description here  
  73.             var description : NSString = itemDescriptionNode.stringValue();  
  74.             entryBuilder.appendString(description);  
  75.             entryBuilder.appendString("</div></div></a>");  
  76.               
  77.             entrysBuilder.appendString(entryBuilder);  
  78.         }  
  79.         finalHTML = finalHTML.stringByReplacingOccurrencesOfString("[[ENTRYLIST]]",  
  80.             withString: entrysBuilder);  
  81.           
  82.         _webView!.loadHTMLString(finalHTML,baseURL: nil);  
  83.     }  
  84.       
  85.     // fail to download  
  86.     func requestFailed(request: HttpRequest!) {  
  87.         println("request failed" + _feedModel!._url!)  
  88.     }  
  89. }  


10.mix and match: SwiftRssReader-Bridging-Header

[html]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #import "GDataXMLNode.h"  
  2. #import "JSONKit.h"  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值