美文网首页
浅谈微信小程序转支付宝

浅谈微信小程序转支付宝

作者: Gxdy | 来源:发表于2021-02-01 20:28 被阅读0次

    项目不大或新开项目,可优先尝试使用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全局数据,只能通过在app中定义好的函数访问。 如下:

       // 正确方式
       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参数说明 { top: 状态栏高度 width:宽度 height:标题bar高度 H:状态栏+标题栏 }
    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:在支付宝创一个同名window对象,然后对子元素逐一转换(如下表)
    内容 微信条件/字段 支付宝目标
    导航栏背景颜色 navigationBarBackgroundColor存在 titleBarColor
    导航栏标题颜色 navigationBarTextStyle=white barButtonTheme = light
    导航栏标题文字内容 navigationBarTitleText defaultTitle
    导航栏样式 navigationStyle =custom transparentTitle=always titlePenetrate=YES
    顶部窗口的背景色 backgroundColorTop backgroundImageColor
    窗口的背景色 backgroundColor backgroundColor
    是否开启当前页面下拉刷新 enablePullDownRefresh pullRefresh
    页面上拉触底事件触发时距页面底部距离,单位为px onReachBottomDistance onReachBottomDistance
    设置为 true 则页面整体不能上下滚动 disableScroll=True allowsBounceVertical=NO
    使用组件集合 usingComponents usingComponents
    是否为组件 component component
    • tabBar:在支付宝创一个同名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循环语句
    "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函数
      • 2.2 支付宝授权错误回调兼容(可选操作):onError="onAuthError", 在onAuthError中处理授权失败回调(包括用户拒绝和系统异常)问题
    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函数
      • 2.2 支付宝授权错误回调兼容(可选操作):onError="onAuthError", 在onAuthError中处理授权失败回调(包括用户拒绝和系统异常)问题
    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" : "///****** alipay begin",
    "*///****** alipay end" : "///****** alipay end",
    "///****** wx begin" : "/****** wx begin",
    "///****** 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需要按如下规定书写:
    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组件兼容方式(对组件进行更细致的封装,统一兼容,实测对特俗组件兼容度不够好)
      1. 对自定义组件进行逐一兼容(本篇采用方式,更具针对性),如下:为了兼容支付宝,需要做一些适配、增加一些设定

    8.1 支付宝组件中的样式没有区域隔离,会对使用组件的页面生效,导致显示异常,所以需要自行进行区域隔离(如:可以加上__组件名称简写__类似的前缀、 mp__classname等)

    8.2 事件绑定:微信是提供triggerEvent方法进行事件传递,而支付宝是提供props中的属性函数。 兼容方式如下:

      1. 微信绑定事件的方法名统一首字母大写: 如 this.triggerEvent('FuncName', data) >>> 对应支付宝 >>> this.props.onFuncName({detail: data})
      1. 支付宝的绑定事件需要写在props中,且以on开始驼峰写法onFuncName, 通过在组件内使用this.props.onFuncName({detail: data})进行事件触发, 参数传递时需要以{detail: data}这样的固定格式,detail是固定key,data是自定义返回参数
      1. 在使用组件时,统一以on开始驼峰写法onFuncName="bindFuncName"进行事件绑定,方便同时兼容支付宝和微信
      1. 绑定事件的实现之固定兼容方式
      // 1. 在methods中实现下面函数
      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
            // 根据实际情况调用对应的函数
            if (name === 'EventFuncName') { // 注意EventName首字大写
               this.props.onEventFuncName({detail: detail}) // 参数固定写法
            }
         }
      
         // 2. 在事件触发的地方调用bindEvent
         this.bindEvent('EventFuncName', { key: value, ... }); 
      },
      

    8.3 固定写法:代码分为微信支付宝公共,并用固定注释标记区域(如下),方便转换工具进行转换(特定格式进行隔离,方便转换是进行切换)
    ```
    ///****** wx begin
    wx code...
    ///****** wx end

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

    8.4 支付宝不支持通过setData的方法设置props的属性,所以直接使用如this.properties.curStore = curStore的方法修改properties属性。 如果使用setData方法取修改props属性会导致数据渲染异常(会在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实现参考源码

    // ======================== .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 () {
        /// 总文件数
        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;       // 已处理数量
        NSInteger __block qty = 0;                  // 已处理数量
        NSInteger __block failQty = 0;              // 失败文件数量
        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];
            // 创建文件夹
            [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"]) { // 页面上拉触底事件触发时距页面底部距离,单位为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" : @"///****** alipay begin",
                    @"*///****** alipay end" : @"///****** alipay end",
                    @"///****** wx begin" : @"/****** wx begin",
                    @"///****** 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
    
    

    相关文章

      网友评论

          本文标题:浅谈微信小程序转支付宝

          本文链接:https://www.haomeiwen.com/subject/ijgjtltx.html