支付寶小程式開發準備工作

逸樂太子發表於2021-10-27

在正式開發支付寶小程式之前,我們要做一些準備工作,比如說封裝一下config檔案、request請求、api介面集中定義,自定義的工具指令碼等。文末還有彩蛋哈~

第一步:在支付寶開發者工具中新增支付寶小程式。

第二步:在小程式檔案的根目錄建立utils目錄。

建立自定義的相關js檔案(或者直接從以前定義好的專案中拷貝過來),如apis.js、config.js、tools.js、http.js、tools.sjs

1.小程式中呼叫介面的集中展示,加上備註資訊,這樣在後臺檢視的時候方便一些吧。

//apis.js
//此處為封裝的請求方法
import { getJSON } from './http'


const apis = {
    // 使用者登入
    userLogin(params, method = 'GET') {
        return getJSON('/api/login?r=' + new Date().getTime(), params, method)
    },
}

module.exports = apis

 

 2.配置檔案,開發的時候難免遇到介面地址切換開發環境、測試環境、生產環境等各種環境,我們事先配置好檔案,在切換的時候直接修改環境引數即可。

// 後臺不同環境
const ENV = {
    SIT: 'sit',
    UAT: 'uat',
    PRD: 'prd'
};

//後臺環境對應的介面目錄
const BUILD_INFO = {
    sit: {
        api: 'http://127.0.0.1:8080',
        domain: ''
    },
    uat: {
        api: 'https://**.com',
        domain: 'https://**.com'
    },
    prd: {
        api: 'https://**.com',
        domain: 'https://**.com'
    }
}
// 小程式APP_id
const APP_ID = {
    sit: '',
    uat: '',
    prd: ''
}

// 不同環境的版本
const VERSION = {
    sit: '1.2.1',
    uat: '1.0.1',
    prd: '1.0.16'
}

//實際使用的後臺環境
const env = ENV.PRD;

const config = {
    ENV: env,
    API_URL: BUILD_INFO[env].api,
    APP_ID: APP_ID[env],
    DOMAIN_URL: BUILD_INFO[env].domain,
    VERSION: VERSION[env]
};

module.exports = config;

 

 3.封裝一些常用的工具函式以便我們提升開發效率。比如小程式中的頁面跳轉一般情況下要繫結事件,然後定義事件方法,在其中定義跳轉的頁面,此處我封裝了一個go2page的方法,這樣就不用在小程式頁面的指令碼中定義各種跳轉事件了。不過就是需要在引用go2page的地方新增引數到data中。

在使用go2page方法進行頁面跳轉後,我們需要從query中提取引數。使用下面的curPagePath方法即可。

const tools = {
    isObject(obj) {
        if (Object.prototype.toString.call(obj) === '[object Object]') {
            return true
        }
        return false
    },
    isDefine(str) {
        if (str === 0) {
            return true
        }
        if (str == '' || (!str)) {
            return false
        }
        return true
    },
    isIDCard(str) {
        const reg = /^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$/
        return reg.test(str)
    },
    isPhone(str) {
        const myreg = /^1[3|4|5|6|7|8|9][0-9]\d{8}$/
        return myreg.test(str)
    },
    isFullName(str) {
        const reg = /^[\u4E00-\u9FA5\uf900-\ufa2d·s]{2,20}$/
        return reg.test(str)
    },
    getUrlParam(href) {
        const indexWen = href.indexOf('?')
        const indexJing = href.indexOf('#')
        const theRequest = {}
        if (indexWen === -1) {
            return theRequest
        }

        if (indexJing < indexWen) { // 如果#號在問好前面
            href = href.substring(indexWen + 1)
        } else {
            href = href.substring(indexWen + 1, indexJing)
        }
        const strArr = href.split('&')
        for (let i = 0; i < strArr.length; i++) {
            theRequest[strArr[i].split('=')[0]] = unescape(strArr[i].split('=')[1])
        }
        return theRequest
    },
    stringifyParam(param = {}) {
        let paramString = ''
        for (let key in param) {
            if (paramString === '') {
                paramString += key + '=' + param[key]
            } else {
                paramString += '&' + key + '=' + param[key]
            }
        }
        return paramString
    },
    // 生成隨機碼
    getRandom(num) {
        const char = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
        const result = []
        for (let i = 0; i < num - 1; i++) {
            const index = Math.random() * char.length - 1
            const value = char.charAt(index)
            result.push(value)
        }
        return result.join('')
    },
    desensitizing(str = '', from = 0, length = 0, replaceStr = '*') { // 隱藏某些字串
        if (str.length === 0) {
            return str
        }
        // 變成陣列
        const strArr = str.split('')
        // 更改陣列
        strArr.splice(from, length, replaceStr.repeat(length))
        return strArr.join('')
    },
    htmlEncode(str = '') {
        if (str === '') {
            return str
        }
        let s = str.replace(/&/g, '&')
        s = s.replace(/</g, '<')
        s = s.replace(/>/g, '>')
        s = s.replace(/\'/g, ''')
        s = s.replace(/\"/g, '"')
        return s
    },
    getDay(day) {
        var today = new Date();
        var targetday_milliseconds = today.getTime() + 1000 * 60 * 60 * 24 * day;
        today.setTime(targetday_milliseconds); //注意,這行是關鍵程式碼
        var tYear = today.getFullYear();
        var tMonth = today.getMonth();
        var tDate = today.getDate();
        tMonth = this.doHandleMonth(tMonth + 1);
        tDate = this.doHandleMonth(tDate);
        return tYear + "-" + tMonth + "-" + tDate;
    },
    doHandleMonth(month) {
        var m = month;
        if (month.toString().length == 1) {
            m = "0" + month;
        }
        return m;
    },
    getWeek(dateString) {
        var dateArray = dateString.split("-");
        var date = new Date(dateArray[0], parseInt(dateArray[1] - 1), dateArray[2]);
        return "周" + "日一二三四五六".charAt(date.getDay());
    },
    getUserInfo() {
        my.getStorage({
            key: 'userInfo',
            success: function (res) {
                console.log('獲取快取中使用者資訊', res)
                return res.data
            },
            fail: function (res) {
                console.log('獲取快取中使用者資訊失敗', res)
                return {}
            }
        });

    },
    checkToken(callback, param = {}) {
        let that = this
        var app = getApp()
        if (app.globalData.userId) {
            callback.call(this, param);
        } else {
            setTimeout(function () {
                param = Object.assign(param, app.globalData)
                that.checkToken(callback, param);
            }, 200);
        }
    },
    go2page(e) {
        const { dataset } = e.target
        let params = []
        for (let key in dataset) {
            if (key != 'url') {
                params.push(key + '=' + dataset[key])
            }
        }
        if (!this.isDefine(e.target.dataset.url)) {
            my.showToast({
                type: 'success',
                content: '建設中,敬請期待',
                duration: 3000,
                success: () => {
                },
            });
        } else {
            let url = e.target.dataset.url
            if (params.length > 0) {
                url = url + '?' + params.join('&')
            }

            my.navigateTo({
                url: url
            });
        }
    },
    round(str) {
        if(!str){return ''}
        return str.toFixed(2)
    },
    curPagePath(query){
        let pages = getCurrentPages();
        let currPage = null;
        // console.log(pages) 的到一個陣列
        if (pages.length) {
        // 獲取當前頁面的物件(上邊所獲得的陣列中最後一項就是當前頁面的物件)
        currPage = pages[pages.length - 1];
        }
        // 獲取當前頁面的路由
        let route = currPage.route
        let params = []
        if(query){
            for (let key in query) {
                params.push(key + '=' + query[key])
            }
        }
        if(params.length>0){
            route += '?' + params.join('&') 
        }
        console.log('當前頁面路徑==>',route)
    },
    getTimeList(start, end, step,restStart,restEnd,selectDate) {
        var curDate = new Date(),
            curFullYear = curDate.getFullYear(),
            curMonth = curDate.getMonth() + 1,
            curDay = curDate.getDate()
        curMonth = curMonth < 10 ? '0' + curMonth : curMonth
        curDay = curDay < 10 ? '0' + curDay : curDay
        var ymd = curFullYear + '/' + curMonth + '/' + curDay
        selectDate = selectDate.replace(/-/g,'/')

        console.log('selectDate',selectDate)

        var startTime = new Date(ymd + ' ' + start).getTime(),
            endTime = new Date(ymd + ' ' + end).getTime(),
            restStartTime = new Date(ymd + ' ' + restStart).getTime(),
            restEndTime = new Date(ymd + ' ' + restEnd).getTime()
        var returnArr = []
        while (startTime + step * 60 * 1000 <= endTime) {
            // 中途休息的時間階段
            if(startTime + step * 60 * 1000>restStartTime && startTime + step * 60 * 1000<=restEndTime){
                startTime += step * 60 * 1000
                continue
            }
            // 如果時間小於當前時間則跳過顯示
            if(startTime + step * 60 * 1000<new Date().getTime() && ymd == selectDate){
                startTime += step * 60 * 1000
                continue
            }
            returnArr.push(this.getTime(startTime) + '-' + this.getTime(startTime+step*60*1000))
            startTime += step * 60 * 1000
        }

        return returnArr

    },

    getTime(timestamp) {
        var date = new Date(timestamp);//時間戳為10位需*1000,時間戳為13位的話不需乘1000
        var Y = date.getFullYear() + '-';
        var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
        var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
        var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
        var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + '';
        var s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
        return h + m;
    },
    isIphoneX(){
        var u = navigator.userAgent;
        var isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
        if (isIOS) {
            if (screen.height == 812 && screen.width == 375) {
                return true;
            }
            else {
                return false;
            }
        }
    }

}
module.exports = tools;

 

4.常用的請求方法,根據實際專案中約定的介面規範進行修改。

import config from './config';

function isApiSuccess(result) {
    const status = result.success || result.stat || result.status || ''; // 介面狀態欄位
    if (status == true || status == '000' || status === 'ok' || status === 'success' || status === 'succeed') {
        return true;
    } else {
        return false;
    }
}

export function getJSON(url, params, method = 'GET', mockSetting = {}) {
    let path = '';
    const { on = false, mockData = {} } = mockSetting;
    if (url.indexOf('http') > -1 || url.indexOf('https') > -1) {
        path = url;
    } else {
        path = config.API_URL + url;
    }
    return new Promise((resolve, reject) => {
        my.showLoading();
        if (on) {
            console.log('-------返回 mock 資料-------', url, params, mockData);
            if (isApiSuccess(mockData)) {
                resolve(mockData);
            } else {
                reject(mockData);
            }
            my.hideLoading();
            return;
        }

        let token = params.token
        let headers = {
            "Content-Type": "application/json",
        }

        if (params.headers) {
            headers = Object.assign(headers, params.headers)
            delete params.headers
        }
        if (token) {
            headers['Authorization'] = 'Bearer ' + token
            delete params.token
        }
        if (method.toLocaleLowerCase() != 'get') {
            headers['content-type'] = 'application/json'
        }

        if (method.toLocaleLowerCase() == 'delete') {
            headers['content-type'] = 'application/x-www-form-urlencoded'
        }

        my.request({
            url: path,
            method: method,
            data: params,
            dataType: 'json',
            headers: headers,
            success: (result) => {
                // console.log(`--返回資料--介面地址=>${url},結果=>${JSON.stringify(result)}`);
                my.hideLoading();
                if (isApiSuccess(result.data)) {
                    resolve(result.data);
                } else {
                    reject(result.data);
                }
            },
            fail: (err) => {
                console.log('-------返回錯誤-------', url, err);
                my.hideLoading();
                if (err.status == 401) {
                    console.log('未登入許可權')
                }
                my.showToast({
                    type: 'fail',
                    content: '請求異常,請稍後再試!'
                });
                reject(err);
            },
        });
    });
}

 

5.在頁面中引用方法時需要用到sjs檔案。

const desensitizing = (str,from,length,replaceStr = '*')=>{
    if (str.length === 0) {
        return str
    }
    // 變成陣列
    const strArr = str.split('')
    let replace_str = ''
    for(var i=0;i<length;i++){
        replace_str += replaceStr
    }
    // 更改陣列
    strArr.splice(from, length, replace_str)
    return strArr.join('')
}
const repeat = (length,repeatStr='*') =>{
    var str = ''
    for(let i=0;i<length;i++){
        str += repeatStr
    }
    return str
}
const desensitizing2 = (str,type,isdes=false,replaceStr = '*')=>{
    if(!isdes){
        return str
    }
    if(str == '' || !str){
        return ''
    }
    switch(type){
        case 'name':
            str = str.replace(getRegExp('·','g'),replaceStr);
            if(str.length == 1){
                return replaceStr
            }
            if(str.length>=2){
                if(str.length>=5){
                    return repeat(4,replaceStr) + str[str.length-1]
                }
                return desensitizing(str,0,str.length-1)
            }
            return replaceStr;
            break;
        case 'sfz':
            return desensitizing(str,1,str.length-2)
            break;
        case 'phone':
            return desensitizing(str,3,6)
            break;
    }
}

const substring = (str,from,length=0)=>{
    if(str.length == 0){
        return ''
    }
    return str.substring(from,from+length)
}
const isDefine = (str)=>{
    if (str === '' || !str) {
            return false
    }
    return true
}
const round = (str,len=2)=>{
    if(!str){return ''}
    let point_index = str.lastIndexOf('.')
    if(len == 0){
        return str.substring(0,1) 
    }
    if(str.length > len + point_index + 1){
        return str.substring(0,len + point_index + 1)
    }
    return str
}
const str2date = (str,separater='-') => {
    if(str === '' || !str || str.length<8) {
        return ''
    }

    return str.substring(0,4) + separater + str.substring(4,6) + separater + str.substring(6,8)

}
export default {
    desensitizing,
    substring,
    isDefine,
    round,
    desensitizing2,
    str2date
}

 

 以上準備工作完成之後,需要引用一下支付寶小程式的ui元件,我用的是mini-ali-ui。

 這下終於可以開發愉快地寫頁面了吧。此時此刻你在想什麼呢。

作為一個經常使用mac、linux系統的程式猿,想借助於開發工具中的控制檯進行頁面建立,怎麼辦呢?

這時候要用到node的功能了,在根目錄下新增package.json

{
  "dependencies": {
    "iconv-lite": "^0.6.3"
  },
  "scripts": {
    "page": "node scripts/page"
  }
}

 

 然後在根目錄新增scripts目錄,在scripts目錄中新增page.js

示例程式碼如下:此程式僅支援 “pages/分組名稱/頁面名稱”這種結構的頁面建立,若需要減小層級或增加層級自行修改哈。

/**
 * pages頁面快速生成指令碼 
 * 用法:npm run tep `檔名`
 * npm run page product/ProductClass
 */

const fs = require('fs');
var iconv = require('iconv-lite');

const dirName = process.argv[2];
const cover = process.argv[3];
console.log(dirName)
const dirNameArr = dirName.split('/')
const folder = dirNameArr[0]
const fileName = dirNameArr[1]
const capPirName = dirName.substring(0, 1).toUpperCase() + dirName.substring(1);
console.log(folder, fileName, capPirName, __dirname)
if (!dirName) {
    console.log('資料夾名稱不能為空!');
    console.log('示例:npm run page product/ProductClas');
    process.exit(0);
}

var curPath = __dirname.substring(0, __dirname.lastIndexOf('/'))
console.log('curPath==', curPath)

function loadjson(filepath) {
    var data;
    try {
        var jsondata = iconv.decode(fs.readFileSync(curPath + '/' + filepath, "binary"), "utf8");
        data = JSON.parse(jsondata);
    }
    catch (err) {
        console.log(err);
    }

    return data;
}


function savejson(filepath, data) {
    var datastr = JSON.stringify(data, null, 4);

    if (datastr) {
        try {
            fs.writeFileSync(curPath + '/' + filepath, datastr);
        }
        catch (err) {

        }
    }
}

//model模板

const wxmlTemp = `
    <view>
    </view>
`
const jsTemp = `
import tools from '/utils/tools'
import { getJSON } from '/utils/http'
import apis from '/utils/apis'
const app = getApp()
Page({

    data: {

    },

    onLoad: function (options) {
        tools.curPagePath(options)
    },

    onShow: function () {

    },
    go2page(e){
        tools.go2page(e)
    }
})
`
const wxssTemp = `

`
const jsonTemp = `
{
    "usingComponents": {}
}
`
if (!fs.existsSync(`pages/${folder}`)) {
    fs.mkdirSync(`pages/${folder}`)
}

if (!fs.existsSync(`pages/${folder}/${fileName}`)) {
    fs.mkdirSync(`pages/${folder}/${fileName}`)
} else {
    // 如果資料夾存在則說明頁面已建立,不能再操作了,否則會覆蓋已有頁面
    if (cover && cover != 1) {
        process.exit(0);
    }
}

process.chdir(`pages/${folder}/${fileName}`); // cd $1


fs.writeFileSync(`${fileName}.axml`, wxmlTemp)
fs.writeFileSync(`${fileName}.json`, jsonTemp)
fs.writeFileSync(`${fileName}.acss`, wxssTemp)
fs.writeFileSync(`${fileName}.js`, jsTemp)

var jsonPath = 'app.json'
var appJson = loadjson(jsonPath)
if (appJson) {
    var pages = appJson.pages
    if (pages.indexOf(`pages/${folder}/${fileName}/${fileName}`) == -1) {
        pages.push(`pages/${folder}/${fileName}/${fileName}`)
    }

    savejson(jsonPath, appJson)


}

process.exit(0);

終於寫完了,以上是我個人在開發支付寶小程式時的經驗總結,僅供參考。不喜可以直接跳過~~~

相關文章