淺談微信小程序轉支付寶

項目不大或新開項目导坟,可優(yōu)先嘗試使用AntMove 一鍵搬家

新項目也可以使用uniapp

本篇是主要針對比較龐大的項目,使用的兼容方式是代碼兼容 + 自定義工具轉換(篇尾附錄使用oc些的轉換工具核心源碼),下文主要記錄一些思路與兼容關鍵點

1. 文件拓展名轉換

微信 支付寶
wxml axml
axss acss
wxs sjs

2. API兼容

2.1 使用AntMove 一鍵搬家對已有項目進行轉換萧豆,然后提取__antmove/api中的兼容文件放入項目中样傍,然后在轉換為支付寶小程序時將使用了wx.的js文件中引入index.js如:const wx = require('/utils/my/api/index.js')(my)

  • 本篇用到的__antmove /api文件缸榄, 對應項目中的utils/my/api
__antmove/api/index.js
__antmove/api/desc.js
__antmove/api/log.js
__antmove/api/my.js
__antmove/api/propsPolyfill.js
__antmove/api/runtimeProcess.js
__antmove/api/utils.js
  • 可根據項目實際情況在api/my.js補充和修改API兼容源碼 (建議修改部分進行標記、新增放到最好云矫,方便迭代
    如對getAccountInfoSynccheckSession進行自定義兼容

2.2 支付寶小程序不能在Page({})之外和page.data中訪問app全局數(shù)據舍沙,只能通過在app中定義好的函數(shù)訪問。 如下:

   // 正確方式
   const iPhoneX = getApp().isIPhoneXOrLater() 
   // 錯誤方式1
   const iPhoneX = getApp().data.iPhoneX
   // 錯誤方式2
   Page({
      data: {
         iPhoneX: getApp().data.iPhoneX
      }
   })

2.3 wx.getMenuButtonBoundingClientRect()

  • 由于支付寶不支持獲取膠囊按鈕屬性,所以在項目中禁止使用該方法迈喉。有下面方法取代
  • const navBarInfo = getApp().getNavBarInfo() // 盡可能使用getApp()來調用方法,支付寶對使用app調用會有個別情況不兼容
// app.js
// mark: 導航欄信息
/// navBarInfo參數(shù)說明 { top: 狀態(tài)欄高度 width:寬度 height:標題bar高度 H:狀態(tài)欄+標題欄 }
getNavBarInfo: function () {
   if (this.data.navBarInfo !== null)  return this.data.navBarInfo

   var info = wx.getSystemInfoSync()
   var barInfo = {}
   barInfo.top = info.statusBarHeight || 44
   barInfo.width = info.windowWidth || 375
   if (info.app == 'alipay') { // 支付寶
       barInfo.height = info.titleBarHeight || 48
       barInfo.menuRect = null
   }else { // 微信
       var rect = wx.getMenuButtonBoundingClientRect()
       barInfo.height = rect.bottom + 10 - barInfo.top
       barInfo.menuRect = rect
   }
   barInfo.H = barInfo.top + barInfo.height
   this.data.navBarInfo = barInfo
   return barInfo
},

3. json配置字段轉換<主要字段>

將微信小程序json轉換為支付寶時只需一些核心字段轉換即可茄茁,然后再對個別進行單獨配置(具體需求根據項目而定
3.1app.json字段轉換對應關系

  • pagse : 直接將pages對象賦值給支付寶pages即可
  • subpackages : 直接將subpackages對象賦值給支付寶subpackages即可
  • preloadRule : 直接將preloadRule對象賦值給支付寶preloadRule即可
  • window:在支付寶創(chuàng)一個同名window對象鹰祸,然后對子元素逐一轉換(如下表)
內容 微信條件/字段 支付寶目標
導航欄背景顏色 navigationBarBackgroundColor存在 titleBarColor
導航欄標題顏色 navigationBarTextStyle=white barButtonTheme = light
導航欄標題文字內容 navigationBarTitleText defaultTitle
導航欄樣式 navigationStyle =custom transparentTitle=always titlePenetrate=YES
頂部窗口的背景色 backgroundColorTop backgroundImageColor
窗口的背景色 backgroundColor backgroundColor
是否開啟當前頁面下拉刷新 enablePullDownRefresh pullRefresh
頁面上拉觸底事件觸發(fā)時距頁面底部距離,單位為px onReachBottomDistance onReachBottomDistance
設置為 true 則頁面整體不能上下滾動 disableScroll=True allowsBounceVertical=NO
使用組件集合 usingComponents usingComponents
是否為組件 component component
  • tabBar:在支付寶創(chuàng)一個同名window對象跟继,然后對子元素逐一轉換
內容 微信條件/字段 支付寶目標
自定義tabbar custom 不支持
文字顏色 color textColor
文字選中時的顏色 selectedColor selectedColor
背景色 backgroundColor backgroundColor
tab 的列表 list items
list.item:設置頁面路徑 pagePath pagePath
list.item:按鈕文字 text name
list.item:圖片路徑 iconPath icon
list.item:高亮圖標路徑 selectedIconPath activeIcon
  • 其它:可根據需求進行定制轉換

3.2 page.json頁面和組件json: 轉換關系同上文window子元素轉換一致

3.3 ext.json:三方平臺調試文件配置對應關系(非三方平臺小程序可忽略)

微信 支付寶
extEnable extEnable 根據實際情況設置
extPages subPackages 根據實際情況設置
ext ext 根據實際情況設置
window window 子元素的轉換見上文window
tabBar tabBar 子元素的轉換見上文tabBar

3.4 mini.project.json是支付寶的配置管理文件种冬,可以進行單獨管理

4. wxmlaxml

4.1 常用元素轉換關系
// 文件引用
"wxml" : "axml",

// 條件語句與for循環(huán)語句
"wx:" : "a:",

// 事件綁定
"bindtap=" : "onTap=",
"catchtap=" : "catchTap=",
"bindchange=" : "onChange=",
"bindinput=" : "onInput=",
"bindfocus=" : "onFocus=",
"bindblur=" : "onBlur=",
"bindconfirm=" : "onConfirm=",
"bindscroll=" : "onScroll=",
"longpress=" : "onLongTap=",
"touchstart=" : "touchStart=",
"touchmove=" : "touchMove=",
"touchend=" : "touchEnd=",
"touchcancel=" : "touchCancel=",
"bindsubmit=": "onSubmit=",
"bindreset=": "onReset=",

// wxs
"<wxs src=" :  "<import-sjs from=",
"wxs>" : "import-sjs>",
"module=" : "name=",
".wxs" : ".sjs",

// button
"open-type=\"getUserInfo\"" : "open-type=\"getAuthorize\" scope=\"userInfo\"",
"open-type=\"getPhoneNumber\"" : "open-type=\"getAuthorize\" scope=\"phoneNumber\"",
"bindgetuserinfo=" : "onGetAuthorize=",
"bindgetphonenumber=" : "onGetAuthorize=",

// canvas
"canvas-id=" : "id="
4.1 paddingmargin
  • 支付寶中image使用 padding無效果,設置的padding會加到寬高上舔糖,所以要避免使用
  • 支付寶中axml中給第一個元素設置margin-top效果會轉移到page上去娱两,導致顯示異常。最簡單的解決辦法是在第一個元素前插入一個<view stye="height: 1rpx;"/>,注意高度必須設置(可有用高度替換margin-top)金吗,否則無效
  • 支付寶支持自定義標簽十兢,如:<food></food> <cell></cell>等
4.3 input

支付寶不支持頁內樣式趣竣,所以不能在input中使用style

4.4 button
  • open-type 兼容適配 (代碼兼容,工具轉換兼容)
功能 微信 支付寶 工具轉換 代碼兼容 備注
打開客服會話 contact 不支持 NO YES -
分享 share share / contactShare NO YES 分享到通訊錄好友需要兼容
授權設置 openSetting 不支持 NO YES 可以通過openSetting API打開
意見反饋 feedback 不支持 NO YES -
打開APP launchApp 不支持 NO YES -
關注生活號 不支持 lifestyle NO YES -
獲取用戶信息 getUserInfo getAuthorize + scope YES YES 見ps1
獲取手機號 getPhoneNumber getAuthorize + scope YES YES 見ps2

ps1: 獲取用戶信息

  1. 工具轉換說明
    • open-type="getUserInfo" ---> open-type="getAuthorize" scope="userInfo" // 注意用雙引號
    • bindgetuserinfo= ---> onGetAuthorize=
  2. 代碼適配說明
    • 2.1 由于支付寶通過按鈕只能獲取授權旱物,我們需要在取得授權后手動調用api獲取信息my.getOpenUserInfo(),
      參考代碼:pages/main/user/userInfo.js 中的getUserInfo函數(shù)
    • 2.2 支付寶授權錯誤回調兼容(可選操作):onError="onAuthError"遥缕, 在onAuthError中處理授權失敗回調(包括用戶拒絕和系統(tǒng)異常)問題
  3. 支付寶文檔:https://opendocs.alipay.com/mini/api/ch8chh

ps2: 獲取手機號 轉換說明

  1. 工具轉換說明
    • open-type="getPhoneNumber" ---> open-type="getAuthorize" scope="phoneNumber" // 注意用雙引號
    • bindgetphonenumber= ---> onGetAuthorize
  2. 代碼適配說明:
    • 2.1 由于支付寶通過按鈕只能獲取授權,我們需要在取得授權后手動調用api獲取信息my.getPhoneNumber(),
      參考代碼:pages/main/user/signup.js 中的getPhoneNumberFromWechat函數(shù)
    • 2.2 支付寶授權錯誤回調兼容(可選操作):onError="onAuthError"宵呛, 在onAuthError中處理授權失敗回調(包括用戶拒絕和系統(tǒng)異常)問題
  3. 支付寶文檔:https://opendocs.alipay.com/mini/api/getphonenumber

5. js文件兼容

5.1 在使用了wx.的js中插入兼容api對象单匣,到js文件的首行

"const wx = require('/utils/my/api/index.js')(my)\n"

5.2 組件js兼容轉換(逐一替換)

"/****** alipay begin" : "http:///****** alipay begin",
"*///****** alipay end" : "http:///****** alipay end",
"http:///****** wx begin" : "/****** wx begin",
"http:///****** wx end" : "*///****** wx end",
"properties" : "props"

5.3 config.js配置文件設置小程序標識符為my

"MINI_APP_TYPE = 'wx' 替換成 MINI_APP_TYPE = 'my'

或使用
const MINI_APP_TYPE = wx.getSystemInfoSync().app == 'alipay' ?  'wx' : 'wx'

6. wxssacss

// 文件引用轉換
"wxss" : @"acss"

7. wxssjs

7.1 支付寶不支持頁內sjs,所以為了方便將wxs轉化為sjs烤蜕,需要寫到一個專門的.wxs文件中
7.2 支付寶不支持"module.exports = ..." 的寫法封孙,只支持"export default"。 所以為了方便將wxs轉化為sjs需要按如下規(guī)定書寫:
7.2.1. module.exports = {}讽营,要注意空格虎忌,然后轉換時將"module.exports =" 替換為 @"export default"
7.2.2. 不管輸出對象有多少個都只能以module.exports = {}的形式書寫,如module.exports.msg = msg;是錯誤的寫法橱鹏,會導致轉換失敗

// 正確參考:
module.exports = {
  takeFoodTypeName: takeFoodTypeName,
  stepIconName: stepIconName,
  stateIconName: stateIconName,
  showBtn: showBtn,
  fmtText: text
}

7.3. axml中將wxs(使用)轉換為sjs見上文 wxml與axml

8. 自定義組件

8.0 兼容方式:

    1. AntMove組件兼容方式(對組件進行更細致的封裝膜蠢,統(tǒng)一兼容,實測對特俗組件兼容度不夠好)
    1. 對自定義組件進行逐一兼容(本篇采用方式莉兰,更具針對性)挑围,如下:為了兼容支付寶,需要做一些適配糖荒、增加一些設定

8.1 支付寶組件中的樣式沒有區(qū)域隔離杉辙,會對使用組件的頁面生效,導致顯示異常捶朵,所以需要自行進行區(qū)域隔離(如:可以加上__組件名稱簡寫__類似的前綴蜘矢、 mp__classname等)

8.2 事件綁定:微信是提供triggerEvent方法進行事件傳遞,而支付寶是提供props中的屬性函數(shù)综看。 兼容方式如下:

    1. 微信綁定事件的方法名統(tǒng)一首字母大寫: 如 this.triggerEvent('FuncName', data) >>> 對應支付寶 >>> this.props.onFuncName({detail: data})
    1. 支付寶的綁定事件需要寫在props中品腹,且以on開始駝峰寫法onFuncName, 通過在組件內使用this.props.onFuncName({detail: data})進行事件觸發(fā), 參數(shù)傳遞時需要以{detail: data}這樣的固定格式,detail是固定key红碑,data是自定義返回參數(shù)
    1. 在使用組件時舞吭,統(tǒng)一以on開始駝峰寫法onFuncName="bindFuncName"進行事件綁定,方便同時兼容支付寶和微信
    1. 綁定事件的實現(xiàn)之固定兼容方式
    // 1. 在methods中實現(xiàn)下面函數(shù)
    bindEvent: function (name, detail, options) {
       name = name.substring(0, 1).toUpperCase() + name.substring(1) // 首字轉換為母大寫
       if (getApp().data.config.app === 'wx') { // wx
          // 固定寫法
          this.triggerEvent(name, detail) 
       }else { // my
          // 根據實際情況調用對應的函數(shù)
          if (name === 'EventFuncName') { // 注意EventName首字大寫
             this.props.onEventFuncName({detail: detail}) // 參數(shù)固定寫法
          }
       }
    
       // 2. 在事件觸發(fā)的地方調用bindEvent
       this.bindEvent('EventFuncName', { key: value, ... }); 
    },
    

8.3 固定寫法:代碼分為微信析珊、支付寶羡鸥、公共,并用固定注釋標記區(qū)域(如下)唾琼,方便轉換工具進行轉換(特定格式進行隔離兄春,方便轉換是進行切換)
```
///****** wx begin
wx code...
///****** wx end

  /****** alipay begin
     my code...
  *///****** alipay end
  ```  

8.4 支付寶不支持通過setData的方法設置props的屬性,所以直接使用如this.properties.curStore = curStore的方法修改properties屬性。 如果使用setData方法取修改props屬性會導致數(shù)據渲染異常(會在data中新建一個)

8.5 在微信自定義組件中不支持直接使用wx調用createSelectorQuery锡溯,而需要使用this,但在支付寶中不支持使用this赶舆。所以需要兼容哑姚,如下

const isWX = getApp().app() == 'wx' 
// 方法1
var query = isWX ? this.createSelectorQuery() : wx.createSelectorQuery()

// 方法2
var query = wx.createSelectorQuery()
if (isWX)    query = query.in(this)

query.select('#marquee').boundingClientRect()
...

附錄: object-c實現(xiàn)參考源碼

// ======================== .h文件 =============================
@interface wx2my : NSObject
/// from wx minapp folder path
@property(nonatomic, copy) NSString *fromPath;
/// to my minapp folder path, default toPath = fromPath
@property(nonatomic, copy) NSString *toPath;
/// 獲取單例
+ (instancetype)shared;

/// 轉換:微信小程序轉支付寶小程序
/// @param schedule 處理進度回調
- (void)transform:(void(^)(CGFloat progress, NSInteger qty, NSInteger failQty))schedule;

// MARK: - 錯誤日志
@property(nonatomic, copy) void(^logsInput)(NSMutableArray *logs, NSString *log);
- (void)clearLogs;

@end



// ======================== .m文件 =============================

#import "wx2my.h"

@interface wx2my () {
    /// 總文件數(shù)
    NSInteger totalFiles;
}
/// app.json 、page.json 和component.json文件絕對路徑集合
@property(nonatomic, strong) NSArray *jsonFiles;
/// js文件絕對路徑集合
@property(nonatomic, strong) NSArray *jsFiles;
/// wxml文件絕對路徑集合
@property(nonatomic, strong) NSArray *wxmlFiles;
/// wxss文件絕對路徑集合
@property(nonatomic, strong) NSArray *wxssFiles;
/// wxs文件絕對路徑集合
@property(nonatomic, strong) NSArray *wxsFiles;
/// 其他文件絕對路徑集合
@property(nonatomic, strong) NSArray *otherFiles;

// MARK: - 錯誤日志
@property(nonatomic, strong) NSMutableArray *logs;
@end

@implementation wx2my
static wx2my *instance = nil;
+ (instancetype)shared {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[wx2my alloc] init];
    });
    return instance;
}


/// MARK: - 獲取所用文件路徑
- (void)initPaths {
    NSFileManager *defaultManager = [NSFileManager defaultManager];
    
    [self addLog:[defaultManager fileExistsAtPath:_fromPath isDirectory:nil] ? @"YES" : @"NO" error:nil actionPrefix:@"文件是否存在"];
    [self addLog:[defaultManager isReadableFileAtPath:_fromPath] ? @"YES" : @"NO" error:nil actionPrefix:@"文件讀取權限"];
    [self addLog:[defaultManager isWritableFileAtPath:_fromPath] ? @"YES" : @"NO" error:nil actionPrefix:@"文件修改權限"];
    
    NSArray *subPaths = [defaultManager subpathsAtPath:_fromPath];
    if (subPaths.count == 0) {
        [self addLog:@"" error:nil actionPrefix:@"文件讀取權失敗"];
        return;
    }else {
        [self addLog:@"" error:nil actionPrefix:@"文件讀取權成功"];
    }
    NSMutableArray *jsonFiles = [NSMutableArray array];
    NSMutableArray *jsFiles = [NSMutableArray array];
    NSMutableArray *wxmlFiles = [NSMutableArray array];
    NSMutableArray *wxssFiles = [NSMutableArray array];
    NSMutableArray *wxsFiles = [NSMutableArray array];
    NSMutableArray *otherFiles = [NSMutableArray array];
    for (NSString *path in subPaths) {
        /// 忽略git文件
        if ([path containsString:@".git"])    continue;
        
        // page.json
        if ([path hasSuffix:@".json"]) {
            if ([path hasSuffix:@"app.json"] || [path containsString:@"pages"] || [path containsString:@"components"]) {
                [jsonFiles addObject:path];
                continue;
            }
        }
        if ([path hasSuffix:@".js"]) {
            [jsFiles addObject:path];
            continue;
        }
        if ([path hasSuffix:@".wxml"]) {
            [wxmlFiles addObject:path];
            continue;
        }
        if ([path hasSuffix:@".wxss"]) {
            [wxssFiles addObject:path];
            continue;
        }
        if ([path hasSuffix:@".wxs"]) {
            [wxsFiles addObject:path];
            continue;
        }
        if ([path containsString:@"."] && ![path hasSuffix:@".DS_Store"]) { // 去掉文件夾和.DS_Store文件
            [otherFiles addObject:path];
        }
    }
    totalFiles = jsonFiles.count + jsFiles.count + wxmlFiles.count + wxssFiles.count + wxsFiles.count + otherFiles.count;
    self.jsonFiles = jsonFiles;
    self.jsFiles = jsFiles;
    self.wxmlFiles = wxmlFiles;
    self.wxssFiles = wxssFiles;
    self.wxsFiles = wxsFiles;
    self.otherFiles = otherFiles;
}

// MARK: - 開始轉換
- (void)transform:(void(^)(CGFloat progress, NSInteger qty, NSInteger failQty))schedule {
    if (!self.fromPath) {
//        [TLToast showToast:@"請先設置微信小程序路徑" inView:nil];
        return;
    }
    [self initPaths];
    NSInteger __block total = totalFiles;       // 已處理數(shù)量
    NSInteger __block qty = 0;                  // 已處理數(shù)量
    NSInteger __block failQty = 0;              // 失敗文件數(shù)量
    CGFloat __block progress = 0.00;            // 總進度
    NSArray <NSArray *>*groups = @[_jsonFiles, _jsFiles, _wxmlFiles, _wxssFiles, _wxsFiles, _otherFiles];
    [groups enumerateObjectsUsingBlock:^(NSArray * _Nonnull paths, NSUInteger idx, BOOL * _Nonnull stop) {
        [paths enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            [self fileTransform:obj complate:^(BOOL success) {
                qty++;
                if (!success) failQty++;
                progress = qty * 1.00 / total;
                if (schedule) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        schedule(progress, qty, failQty);
                    });
                }
                   
                if (idx == paths.count - 1) {
                    [self addLog:@"" error:nil actionPrefix:[NSString stringWithFormat:@"%@文件轉換完成", [obj pathExtension]]];
                }
                
                if (qty >= total || idx == paths.count-1) {
                    NSString *msg = nil;
                    if (qty >= total) {
                        msg = [NSString stringWithFormat:@"轉換完成 失敗%zi個文件", failQty];
                    }else {
                        msg = [NSString stringWithFormat:@"完成進度%.2f%%", progress * 100.f];
                    }
//                    [TLToast showToast:msg inView:nil];
                    NSLog(@"%@", msg);
                }
            }];
        }];
    }];
}

// MARK: - 文件轉換與導出
- (void)fileTransform:(NSString *)wxFile complate:(void (^)(BOOL success))complate {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 文件讀取
        NSError *err;
        NSString *wxFullPath = [NSString stringWithFormat:@"%@/%@", self.fromPath, wxFile];
        BOOL icCopy = [wxFile hasSuffix:@".png"] || [wxFile hasSuffix:@".jpg"];
        BOOL needRename = NO;
        NSData *data = nil;
        if (!icCopy) {
            NSString *dataString = [NSString stringWithContentsOfFile:wxFullPath
                                                             encoding:NSUTF8StringEncoding
                                                                error:&err];
//            if (err) {
//                err = nil;
//                dataString = [NSString stringWithContentsOfFile:wxFullPath
//                                                       encoding:NSMacOSRomanStringEncoding
//                                                          error:&err];
//            }
            if (err) {
                [self addLog:wxFile error:err actionPrefix:@"FILE READ FAIL"];
                if (complate) {
                    complate(NO);
                }
                return;
            }
            data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
            
            // 文件處理
            if ([wxFile hasSuffix:@".json"]) {
                data = [self jsonFileTransform:wxFile fileData:data error:&err];
            }else if ([wxFile hasSuffix:@".js"]) {
                data = [self jsFileTransform:wxFile fileData:data error:&err];
            }else if ([wxFile hasSuffix:@".wxml"]) {
                needRename = YES;
                data = [self wxmlFileTransform:wxFile fileData:data error:&err];
            }else if ([wxFile hasSuffix:@".wxss"]) {
                needRename = YES;
                data = [self wxssFileTransform:wxFile fileData:data error:&err];
            }else if ([wxFile hasSuffix:@".wxs"]) {
                needRename = YES;
                data = [self wxsFileTransform:wxFile fileData:data error:&err];
            }
            if (err) {
                [self addLog:wxFile error:err actionPrefix:dataString];//@"FILE TRANSFORM FAIL"];
                if (complate) complate(NO);
                return;
            }
        }
        
        
        // 文件導出
        // 新文件名(相對路徑)
        NSString *myFile= needRename ? [self fileNameWithFile:wxFile] : wxFile;
        NSString *savePath = [NSString stringWithFormat:@"%@/%@", self.toPath, myFile];
        NSFileManager *fileManager = [[NSFileManager alloc] init];
        // 創(chuàng)建文件夾
        [fileManager createDirectoryAtPath:[savePath stringByDeletingLastPathComponent]
               withIntermediateDirectories:YES
                                attributes:nil
                                     error:&err];
        if (err) {
            [self addLog:wxFile error:err actionPrefix:@"CREAT FOLDER FAIL"];
            if (complate) {
                complate(NO);
            }
            return;
        }
        
        // 保存
        if (icCopy) {
            if ([fileManager fileExistsAtPath:savePath]) {
                [fileManager removeItemAtPath:savePath error:&err];
                if (err) {
                    [self addLog:wxFile error:err actionPrefix:@"REMOVE FILE FAIL"];
                    if (complate) {
                        complate(NO);
                    }
                    return;
                }
            }

            [[NSFileManager defaultManager] copyItemAtPath:wxFullPath toPath:savePath error:&err];
            if (err) {
                [self addLog:wxFile error:err actionPrefix:@"COPY FILE FAIL"];
                if (complate) {
                    complate(NO);
                }
                return;
            }
        }else if (![data writeToFile:savePath atomically:YES]) {
            [self addLog:wxFile error:err actionPrefix:@"MY_FILE WRITING FAIL"];
            if (complate) {
                complate(NO);
            }
            return;
        }
        if (complate) {
            complate(YES);
        }
    });
}

// MARK: - json 文件轉換處理
- (NSData *)jsonFileTransform:(NSString *)wxFile fileData:(NSData *)data error:(NSError **)error {
    // 文件內容轉字典
    NSError *err;
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data
                                                         options:NSJSONReadingAllowFragments
                                                           error:error];
    if (err) {
        [self addLog:wxFile error:err actionPrefix:@"FILE TO DATA(READ) FAIL"];
        return data;
    }
    
    // 字典處理
    NSMutableDictionary *myDict = [NSMutableDictionary dictionary];
    if ([wxFile hasSuffix:@"app.json"]) {
        if (dict[@"pages"]) {
            myDict[@"pages"] = dict[@"pages"];
        }
        if (dict[@"subpackages"]) {
            myDict[@"subPackages"] = dict[@"subpackages"];
        }
        if (dict[@"preloadRule"]) {
            myDict[@"preloadRule"] = dict[@"preloadRule"];
        }
        
        if (dict[@"window"]) {
            NSMutableDictionary *myWindowDict = [NSMutableDictionary dictionary];
            [self wxWindowParans:dict[@"window"] toMyParams:myWindowDict];
            myDict[@"window"] = myWindowDict;
        }
        if (dict[@"tabBar"]) {
            NSMutableDictionary *myTabBarDict = [NSMutableDictionary dictionary];
            [self wxTabBarParans:dict[@"tabBar"] toMyParams:myTabBarDict];
            myDict[@"tabBar"] = myTabBarDict;
        }
    }else if ([wxFile hasSuffix:@"/ext.json"]) {
        if (dict[@"extEnable"]) {
            myDict[@"extEnable"] = dict[@"extEnable"];
        }
        if (dict[@"extPages"]) {
            myDict[@"subPackages"] = dict[@"extPages"];
        }
        if (dict[@"ext"]) {
            myDict[@"ext"] = dict[@"ext"];
        }
        if (dict[@"window"]) {
            NSMutableDictionary *myWindowDict = [NSMutableDictionary dictionary];
            [self wxWindowParans:dict[@"window"] toMyParams:myWindowDict];
            myDict[@"window"] = myWindowDict;
        }
        if (dict[@"tabBar"]) {
            NSMutableDictionary *myTabBarDict = [NSMutableDictionary dictionary];
            [self wxTabBarParans:dict[@"tabBar"] toMyParams:myTabBarDict];
            myDict[@"tabBar"] = myTabBarDict;
        }
    }else if ([wxFile hasSuffix:@"mini.project.json"]) {
        myDict = [dict mutableCopy];
    }else {
        [self wxWindowParans:dict toMyParams:myDict];
    }

    // json to data
    NSData *jsonData =[NSJSONSerialization dataWithJSONObject:myDict
                                                      options:NSJSONWritingPrettyPrinted
                                                        error:error];
    if (err) {
        [self addLog:wxFile error:err actionPrefix:@"MY_JSON TO DATA FAIL"];
        return data;
    }
    
    //NSJSONSerialization converts a URL string from http://... to http:\/\/... remove the extra escapes
    NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"];
    jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    return jsonData;
}

// MARK: - json文件 window 字段屬性轉換處理
- (void)wxWindowParans:(NSDictionary *)wxParams toMyParams:(NSMutableDictionary *)myDict {
    [wxParams enumerateKeysAndObjectsUsingBlock:^(NSString *key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        if ([key isEqualToString:@"navigationBarBackgroundColor"]) { // 導航欄背景顏色
            myDict[@"titleBarColor"] = obj;
        }
        if ([key isEqualToString:@"navigationBarTextStyle"] && [obj isEqualToString:@"white"]) { //導航欄標題顏色
            myDict[@"barButtonTheme"] = @"light";
        }
        if ([key isEqualToString:@"navigationBarTitleText"]) { // 導航欄標題文字內容
            myDict[@"defaultTitle"] = obj;
        }
        if ([key isEqualToString:@"navigationStyle"] && [obj isEqualToString:@"custom"]) { // 導航欄樣式
            myDict[@"transparentTitle"] = @"always";
            myDict[@"titlePenetrate"] = @"YES";
        }
        if ([key isEqualToString:@"backgroundColorTop"]) { // 頂部窗口的背景色 --> 下拉露出顯示背景圖的底色
            myDict[@"backgroundImageColor"] = obj;
        }
        if ([key isEqualToString:@"backgroundColor"]) { // 窗口的背景色
            myDict[@"backgroundColor"] = obj;
        }
        if ([key isEqualToString:@"enablePullDownRefresh"]) { // 是否開啟當前頁面下拉刷新
            myDict[@"pullRefresh"] = obj;
        }
        if ([key isEqualToString:@"onReachBottomDistance"]) { // 頁面上拉觸底事件觸發(fā)時距頁面底部距離芜茵,單位為px
            myDict[@"onReachBottomDistance"] = obj;
        }
        if ([key isEqualToString:@"disableScroll"] && [obj boolValue]) { // 設置為 true 則頁面整體不能上下滾動
            myDict[@"allowsBounceVertical"] = @"NO";
        }
        if ([key isEqualToString:@"usingComponents"]) { // 使用組件集合
            myDict[@"usingComponents"] = obj;
        }
        if ([key isEqualToString:@"component"]) { // 是否為組件
            myDict[@"component"] = obj;
        }
    }];
}

// MARK: - json文件 tabbar 字段屬性轉換處理
- (void)wxTabBarParans:(NSDictionary *)wxParams toMyParams:(NSMutableDictionary *)myDict {
    [wxParams enumerateKeysAndObjectsUsingBlock:^(NSString *key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        if ([key isEqualToString:@"custom"] && [obj boolValue]) { // 設置為 true 則頁面整體不能上下滾動
            NSLog(@"\n\n============== WARING ===============\n\n"
                   "      支付寶小程序不支持自定義tabbar類型      "
                   "\n\n============== WARING ===============\n\n");
        }
        
        if ([key isEqualToString:@"color"]) { // 文字顏色
            myDict[@"textColor"] = obj;
        }
        
        if ([key isEqualToString:@"selectedColor"]) { // 文字選中時的顏色
            myDict[@"selectedColor"] = obj;
        }
        if ([key isEqualToString:@"backgroundColor"]) { // 背景色
            myDict[@"backgroundColor"] = obj;
        }
        if ([key isEqualToString:@"list"]) { // tab 的列表
            NSArray <NSDictionary *>*list = obj;
            NSMutableArray *items = [NSMutableArray arrayWithCapacity:list.count];
            [list enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                NSMutableDictionary *item = [NSMutableDictionary dictionary];
                // 設置頁面路徑
                if(obj[@"pagePath"]) item[@"pagePath"] = obj[@"pagePath"];
                // 按鈕文字
                if(obj[@"text"]) item[@"name"] = obj[@"text"];
                // 圖片路徑
                if(obj[@"iconPath"]) item[@"icon"] = obj[@"iconPath"];
                // 高亮圖標路徑
                if(obj[@"selectedIconPath"]) item[@"activeIcon"] = obj[@"selectedIconPath"];
                [items addObject:item];
            }];
            myDict[@"items"] = items;
        }
    }];
}

// MARK: - js 文件處理
- (NSData *)jsFileTransform:(NSString *)wxFile fileData:(NSData *)data error:(NSError **)error {
    NSString __block *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSArray *ignoreFiles = @[@"bmap.js", @"date.js", @"config.js"]; // 要忽略的文件
    // 插入api對象引用
    NSString *fileName = [wxFile lastPathComponent];
    if (![ignoreFiles containsObject:fileName] && ![wxFile containsString:@"utils/my/api/"]) {
        if ([content containsString:@"wx."]) {
            NSMutableString *newContent = [[NSMutableString alloc] initWithString:content];
            [newContent insertString:@"const wx = require('/utils/my/api/index.js')(my)\n" atIndex:0];
            content = newContent;
        }
        
        if ([wxFile hasPrefix:@"component/"]) { // 組件
            NSDictionary *temp = @{
                @"/****** alipay begin" : @"http:///****** alipay begin",
                @"*///****** alipay end" : @"http:///****** alipay end",
                @"http:///****** wx begin" : @"/****** wx begin",
                @"http:///****** wx end" : @"*///****** wx end",
                @"properties" : @"props"
            };
            
            [temp enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL * _Nonnull stop) {
                if ([content containsString:key]) {
                    content = [content stringByReplacingOccurrencesOfString:key withString:obj];
                }
            }];
        }
    }
    if ([fileName isEqualToString:@"config.js"]) {
        content = [content stringByReplacingOccurrencesOfString:@"MINI_APP_TYPE = 'wx'"
                                                     withString:@"MINI_APP_TYPE = 'my'"];
    }
    return [content dataUsingEncoding:NSUTF8StringEncoding];
}

// MARK: - wxml 文件處理
- (NSData *)wxmlFileTransform:(NSString *)wxFile fileData:(NSData *)data error:(NSError **)error {
    NSString __block *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSDictionary *temp = @{
        @"wxml" : @"axml",
        @"wx:" : @"a:",
        
        @"bindtap=" : @"onTap=",
        @"catchtap=" : @"catchTap=",
        @"bindchange=" : @"onChange=",
        @"bindinput=" : @"onInput=",
        @"bindfocus=" : @"onFocus=",
        @"bindblur=" : @"onBlur=",
        @"bindconfirm=" : @"onConfirm=",
        @"bindscroll=" : @"onScroll=",
        @"longpress=" : @"onLongTap=",
        @"touchstart=" : @"touchStart=",
        @"touchmove=" : @"touchMove=",
        @"touchend=" : @"touchEnd=",
        @"touchcancel=" : @"touchCancel=",
        @"bindsubmit=": @"onSubmit=",
        @"bindreset=": @"onReset=",
        
        @"<wxs src=" :  @"<import-sjs from=",
        @"wxs>" : @"import-sjs>",
        @"module=" : @"name=",
        @".wxs" : @".sjs",
        
        @"open-type=\"getUserInfo\"" : @"open-type=\"getAuthorize\" scope=\"userInfo\"",
        @"open-type=\"getPhoneNumber\"" : @"open-type=\"getAuthorize\" scope=\"phoneNumber\"",
        @"bindgetuserinfo=" : @"onGetAuthorize=",
        @"bindgetphonenumber=" : @"onGetAuthorize=",
        @"canvas-id=" : @"id="
    };
    [temp enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL * _Nonnull stop) {
        if ([content containsString:key]) {
            content = [content stringByReplacingOccurrencesOfString:key withString:obj];
        }
    }];
    
    return [content dataUsingEncoding:NSUTF8StringEncoding];
}

// MARK: - wxml 文件處理
- (NSData *)wxssFileTransform:(NSString *)wxFile fileData:(NSData *)data error:(NSError **)error {
    NSString __block *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSDictionary *temp = @{
        @"wxss" : @"acss"
    };
    [temp enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL * _Nonnull stop) {
        if ([content containsString:key]) {
            content = [content stringByReplacingOccurrencesOfString:key withString:obj];
        }
    }];
    return [content dataUsingEncoding:NSUTF8StringEncoding];
}

// MARK: - wxs 文件處理
- (NSData *)wxsFileTransform:(NSString *)wxFile fileData:(NSData *)data error:(NSError **)error {
    NSString __block *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSDictionary *temp = @{
        @"module.exports =" : @"export default"
    };
    [temp enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL * _Nonnull stop) {
        if ([content containsString:key]) {
            content = [content stringByReplacingOccurrencesOfString:key withString:obj];
        }
    }];
    return [content dataUsingEncoding:NSUTF8StringEncoding];
}


// MARK: - 文件拓展名修改
- (NSString *)fileNameWithFile:(NSString *)wxFile {
    NSString *myFile = wxFile;
    if ([wxFile hasSuffix:@".wxml"]) {
        myFile = [wxFile stringByReplacingOccurrencesOfString:@".wxml" withString:@".axml"];
    }else if ([wxFile hasSuffix:@".wxss"]) {
        myFile = [wxFile stringByReplacingOccurrencesOfString:@".wxss" withString:@".acss"];
    }else if ([wxFile hasSuffix:@".wxs"]) {
        myFile = [wxFile stringByReplacingOccurrencesOfString:@".wxs" withString:@".sjs"];
    }
    return myFile;
}

- (void)addLog:(NSString *)file error:(NSError *)err actionPrefix:(NSString *)prefix{
    NSString *fileName = [[file componentsSeparatedByString:@"/"] lastObject];
    NSString *log = [NSString stringWithFormat:@"%@%@ %@\n%@\n", prefix, fileName.length ? @":" : @"", fileName, err ? err : @""];
    NSLog(@"%@", log);
    if (!self.logs) {
        self.logs = [NSMutableArray array];
    }
    if ([log isKindOfClass:[NSString class]]) {
        [self.logs addObject:log];
    }else {
        NSLog(@"%@", log);
    }
    
    if (self.logsInput) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.logsInput(self.logs, log);
        });
    }
}

- (void)clearLogs {
    [self.logs removeAllObjects];
    if (self.logsInput) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.logsInput(self.logs, @"日志清除成功");
        });
    }
}
@end

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末叙量,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子九串,更是在濱河造成了極大的恐慌绞佩,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猪钮,死亡現(xiàn)場離奇詭異品山,居然都是意外死亡,警方通過查閱死者的電腦和手機烤低,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門肘交,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扑馁,你說我怎么就攤上這事涯呻。” “怎么了腻要?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵复罐,是天一觀的道長。 經常有香客問我雄家,道長效诅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任趟济,我火速辦了婚禮填帽,結果婚禮上,老公的妹妹穿的比我還像新娘咙好。我一直安慰自己,他們只是感情好褐荷,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布勾效。 她就那樣靜靜地躺著,像睡著了一般叛甫。 火紅的嫁衣襯著肌膚如雪层宫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天其监,我揣著相機與錄音萌腿,去河邊找鬼。 笑死抖苦,一個胖子當著我的面吹牛毁菱,可吹牛的內容都是我干的米死。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贮庞,長吁一口氣:“原來是場噩夢啊……” “哼峦筒!你這毒婦竟也來了?” 一聲冷哼從身側響起窗慎,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤物喷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后遮斥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體峦失,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年术吗,在試婚紗的時候發(fā)現(xiàn)自己被綠了尉辑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡藐翎,死狀恐怖材蹬,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情吝镣,我是刑警寧澤堤器,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站末贾,受9級特大地震影響闸溃,放射性物質發(fā)生泄漏。R本人自食惡果不足惜拱撵,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一辉川、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拴测,春花似錦乓旗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至务荆,卻和暖如春妆距,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背函匕。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工娱据, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盅惜。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓中剩,卻偏偏與公主長得像忌穿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子咽安,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內容