Taro小程序dva框架開(kāi)發(fā)學(xué)習(xí)
本篇介紹的是Taro小程序開(kāi)發(fā),僅供學(xué)習(xí)參考。使用的框架是Taro(3.3.9)+dva+Taro UI架構(gòu)開(kāi)發(fā),其中封裝了dva框架來(lái)管理整個(gè)應(yīng)用的state,基于Taro UI進(jìn)行組建開(kāi)發(fā),封裝了網(wǎng)絡(luò)請(qǐng)求方法(大部分借鑒網(wǎng)絡(luò)),網(wǎng)絡(luò)返回值進(jìn)行了處理,方便數(shù)據(jù)處理。
Taro鏈接:https://taro-docs.jd.com/taro/docs/README/index.html
Taro UI鏈接:https://taro-ui.jd.com/#/docs/introduction
Taro Demo鏈接(Gitee):TaroDvaDemo: Taro+dva demo
Taro Demo鏈接(Github):https://github.com/coffeelife/TaroDvaDemo.git
Taro新舊版本遷移說(shuō)明:https://taro-docs.jd.com/taro/docs/next/migration
項(xiàng)目組成介紹
UI:Taro UI和原生組件組成
dva:使用redux來(lái)進(jìn)行state同意管理,網(wǎng)絡(luò)請(qǐng)求過(guò)程封裝etc
http.js封裝taro請(qǐng)求,統(tǒng)一處理參數(shù)和返回結(jié)果
版本詳情
"dependencies": { "@babel/runtime": "^7.7.7", "@tarojs/async-await": "^2.2.10", "@tarojs/cli": "3.3.9", "@tarojs/components": "3.3.9", "@tarojs/react": "3.3.9", "@tarojs/redux-h5": "^2.2.10", "@tarojs/runtime": "3.3.9", "@tarojs/taro": "3.3.9", "babel-runtime": "^6.26.0", "dva-core": "^1.6.0-beta.7", "dva-loading": "^3.0.22", "jsrsasign": "^10.4.1", "lodash": "4.17.15", "nervjs": "^1.5.7", "react": "^17.0.0", "react-dom": "^17.0.0", "react-redux": "^7.2.5", "redux": "^4.1.1", "redux-logger": "^3.0.6", "redux-thunk": "^2.3.0", "regenerator-runtime": "^0.13.9", "taro-ui": "^3.0.0-alpha.3", "vconsole": "^3.9.1" },
說(shuō)明
因?yàn)槠綍r(shí)都是使用dva+antd開(kāi)發(fā)網(wǎng)頁(yè)、dva+antd mobile開(kāi)發(fā)手機(jī)網(wǎng)頁(yè)、dva+react native開(kāi)發(fā)手機(jī)app,所以在考慮這個(gè)框架的時(shí)候會(huì)在想自己如何無(wú)感知來(lái)切換到小程序開(kāi)發(fā)當(dāng)中,所以就像使用Taro(react) + dva的方式來(lái)開(kāi)發(fā)小程序,然后Taro提供了自己的UI庫(kù)Taro UI,所以順便使用taro UI作為組件庫(kù)。
開(kāi)發(fā)前準(zhǔn)備
taro cli工具安裝(copy官網(wǎng))
# 使用 npm 安裝 CLI
$ npm install -g @tarojs/cli
# OR 使用 yarn 安裝 CLI
$ yarn global add @tarojs/cli
# OR 安裝了 cnpm,使用 cnpm 安裝 CLI
$ cnpm install -g @tarojs/cli
項(xiàng)目初始化(copy官網(wǎng))
taro init mypp
[圖片上傳失敗...(image-17a4dd-1642501786545)]
項(xiàng)目目錄結(jié)構(gòu)
[圖片上傳失敗...(image-d0abae-1642501786545)]
pages//目錄頁(yè)面目錄
app.config,js//目錄主配置文件
app.js//主入口文件
app.less//主樣式文件
project.config.js//小程序配置文件
package.json依賴(lài)配置文件
project.config.js將該文件的appid改成自己的appid
{
"miniprogramRoot": "dist/",
"projectname": "myapp",
"description": "測(cè)試app",
"appid": "自己的appId",
"setting": {
"urlCheck": true,
"es6": false,
"postcss": false,
"preloadBackgroundData": false,
"minified": false,
"newFeature": true,
"autoAudits": false,
"coverView": true,
"showShadowRootInWxmlPanel": false,
"scopeDataCheck": false,
"useCompilerModule": false
},
"compileType": "miniprogram",
"simulatorType": "wechat",
"simulatorPluginLibVersion": {},
"condition": {}
}
npm run dev:weapp運(yùn)行查看效果
[圖片上傳失敗...(image-5667e7-1642501786545)]
dva框架的搭建
安裝redux、 react-redux 等依賴(lài)包(老版)
npm install --save react-redux
npm install --save redux @tarojs/redux @tarojs/redux-h5 redux-thunk redux-logger
安裝redux、 react-redux 等依賴(lài)包(新版)
Taro新舊版本遷移說(shuō)明:Menu
使用第三方 React庫(kù)#
如果你需要引入 React 相關(guān)生態(tài)的庫(kù),直接通過(guò) npm install 安裝然后引入使用即可,Taro 不會(huì)再維護(hù)類(lèi)似于 taro-redux 、taro-mobx 之類(lèi)的庫(kù)。
// 當(dāng)使用了 JSX 時(shí),babel 會(huì)隱式地調(diào)用 React.createElement
// 因此只要你使用了 JSX,就要把 React 或 Nerv 引入
import React from 'react'
import { useSelector } from 'react-redux'
// 如果是使用的是 Nerv
// import { useSelector } from 'nerv-redux'
function Excited () {
const counter = useSelector(state => state.counter)
return ...
}
npm install --save react-redux
npm install --save redux @tarojs/redux @tarojs/redux-h5 redux-thunk redux-logger
安裝dva
dva-core:封裝了 redux 和 redux-saga 的一個(gè)插件
dva-loading:管理頁(yè)面的 loading 狀態(tài)
npm install --save dva-core dva-loading
package.json依賴(lài)詳情
"dependencies": {
"@babel/runtime": "^7.7.7",
"@tarojs/cli": "3.3.9",
"@tarojs/components": "3.3.9",
"@tarojs/react": "3.3.9",
"@tarojs/redux": "^2.2.10",
"@tarojs/redux-h5": "^2.2.10",
"@tarojs/runtime": "3.3.9",
"@tarojs/taro": "3.3.9",
"dva-core": "^1.6.0-beta.7",
"dva-loading": "^3.0.22",
"lodash": "4.17.15",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-redux": "^7.2.6",
"redux": "^4.1.2",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.4.0",
"taro-ui": "^3.0.0-alpha.3"
},
在項(xiàng)目中配置dva
在src目錄下新建一個(gè)utils目錄,新建一個(gè)dva.js文件,文件代碼如下
import { create } from 'dva-core';
import { createLogger } from 'redux-logger';
import createLoading from 'dva-loading';
let app;
let store;
let dispatch;
function createApp(opt) {
// redux日志
// opt.onAction = [createLogger()];
app = create(opt);
app.use(createLoading({}));
if (!global.registered) opt.models.forEach(model => app.model(model));
global.registered = true;
app.start();
store = app._store;
app.getStore = () => store;
dispatch = store.dispatch;
app.dispatch = dispatch;
return app;
}
export default {
createApp,
getDispatch() {
return app.dispatch;
}
}
在src下新建目錄models目錄,里面新建一個(gè)默認(rèn)的index.js和model.js文件
index.js
import model from "./model"
export default [model];
model.js
import * as indexApi from './service';
export default {
namespace: 'index',
state: {
},
effects: {
* effectsDemo(_, { call, put }) {
const { status, data } = yield call(indexApi.demo, {});
if (status === 'ok') {
yield put({ type: 'save',
payload: {
topData: data,
} });
}
},
},
reducers: {
save(state, { payload }) {
return { ...state, ...payload };
},
},
};
在src下新建service文件夾,在目錄下新建index.js文件
service.js文件
import httpRequest from '../../utils/http';
export const demo = (data) => {
return httpRequest.get("",data);
};
app.js改造
將utils下的dva.js引入,然后使用dva和model
app.js
import "@tarojs/async-await";
import React, { Component } from "react";
import dva from "./utils/dva";
import models from "./models";
import { Provider } from "react-redux";
import "./app.less";
const dvaApp = dva.createApp({
initialState: {},
models: models
});
const store = dvaApp.getStore();
// 如果需要在 h5 環(huán)境中開(kāi)啟 React Devtools
// 取消以下注釋?zhuān)?// if (process.env.NODE_ENV !== 'production' && process.env.TARO_ENV === 'h5') {
// require('nerv-devtools')
// }
export default class App extends Component {
config={}
componentDidMount() {}
componentDidShow() {}
componentDidHide() {}
componentDidCatchError() {}
// this.props.children 是將要會(huì)渲染的頁(yè)面
render() {
return (
<Provider store={store}>
{this.props.children}
</Provider>
);
}
}
新建login和index目錄然后新建對(duì)應(yīng)的less、js和config.js文件,如圖
login下的index.js文件
import React, { Component } from "react";
import Taro from "@tarojs/taro";
import { View, Text, Button } from "@tarojs/components";
import "./index.less";
import { connect } from "react-redux";
import { AtButton } from "taro-ui";
@connect(({ index }) => ({
index
}))
export default class Login extends Component {
componentDidMount() {
console.log(this.props);
}
goMain = () => {
Taro.redirectTo({ url: "/pages/index/index?a=1&b=2&c=3" });
};
render() {
return (
<View className="login-page">
登錄頁(yè)面
<Button className="loginBtn" onClick={this.goMain}>
登錄
</Button>
</View>
);
}
}
index下的index.js文件
import React, { Component } from "react";
import Taro, { getCurrentInstance } from "@tarojs/taro";
import { View, Text, OpenData, Button, Textarea } from "@tarojs/components";
import { connect } from "react-redux";
import "./index.less";
@connect(({ index }) => ({
index
}))
export default class Index extends Component {
state = {
userInfo: {},
hasUserInfo: false
};
componentDidMount = () => {
Taro.hideHomeButton();
let { router } = getCurrentInstance();
let { a, b, c } = router.params;
console.log(this.props, router, a, b, c);
};
getUserProfile = () => {
// 推薦使用wx.getUserProfile獲取用戶(hù)信息,開(kāi)發(fā)者每次通過(guò)該接口獲取用戶(hù)個(gè)人信息均需用戶(hù)確認(rèn)
// 開(kāi)發(fā)者妥善保管用戶(hù)快速填寫(xiě)的頭像昵稱(chēng),避免重復(fù)彈窗
Taro.getUserProfile({
desc: "用于完善會(huì)員資料", // 聲明獲取用戶(hù)個(gè)人信息后的用途,后續(xù)會(huì)展示在彈窗中,請(qǐng)謹(jǐn)慎填寫(xiě)
success: res => {
this.setState(
{
userInfo: res.userInfo,
hasUserInfo: true
},
() => {
Taro.showModal({
title: "用戶(hù)信息",
content: JSON.stringify(res.userInfo),
showCancel: false,
confirmText: "確定"
});
}
);
}
});
};
render() {
let { hasUserInfo, userInfo } = this.state;
return (
<View className="index-page">
<View className="userInfo">
<OpenData className="userAvatar" type="userAvatarUrl" />
<View className="userDetail">
<OpenData type="userNickName" lang="zh_CN" />
<View>
<OpenData type="userGender" lang="zh_CN" />{" "}
<OpenData type="userCountry" lang="zh_CN" />{" "}
<OpenData type="userProvince" lang="zh_CN" />{" "}
<OpenData type="userCity" lang="zh_CN" />
</View>
</View>
</View>
<Button className="userBtn" onClick={this.getUserProfile}>
獲取用戶(hù)信息
</Button>
</View>
);
}
}
在 index的主文件中打印this.props就可以看到對(duì)應(yīng)的state數(shù)據(jù),這里的獲取上一頁(yè)傳遞使用過(guò)let { router } = getCurrentInstance();let { a, b, c } = router.params;來(lái)獲取。這樣dva框架就已經(jīng)加入到小程序當(dāng)中,就可以像使用網(wǎng)頁(yè)開(kāi)發(fā)的思維來(lái)開(kāi)發(fā)小程序。
[圖片上傳失敗...(image-9fbb73-1642501786544)]
taro網(wǎng)絡(luò)請(qǐng)求的封裝
此處借鑒的是網(wǎng)上的demo,可以根據(jù)自己的需求來(lái)進(jìn)行開(kāi)發(fā)調(diào)整。主要設(shè)計(jì)請(qǐng)求方式get、post、put、delete的封裝,攔截器的封裝、返回結(jié)果的封裝,統(tǒng)一返回taro.request這部分我們會(huì)做修改,上代碼
http.js
import Taro from "@tarojs/taro";
import getBaseUrl from "./baseUrl";
import interceptors from "./interceptors";
interceptors.forEach(interceptorItem => Taro.addInterceptor(interceptorItem));
class httpRequest {
baseOptions(params, method = "GET") {
let { url, data } = params;
const BASE_URL = getBaseUrl(url);
let contentType = "application/json";
contentType = params.contentType || contentType;
const option = {
url: BASE_URL + url, //地址
data: data, //傳參
method: method, //請(qǐng)求方式
timeout: 50000, // 超時(shí)時(shí)間
header: {
"content-type": contentType,
Authorization: Taro.getStorageSync("Authorization")
}
};
return Taro.request(option);
}
get(url, data = "") {
let option = { url, data };
return this.baseOptions(option);
}
post(url, data, contentType) {
let params = { url, data, contentType };
return this.baseOptions(params, "POST");
}
put(url, data = "") {
let option = { url, data };
return this.baseOptions(option, "PUT");
}
delete(url, data = "") {
let option = { url, data };
return this.baseOptions(option, "DELETE");
}
}
export default new httpRequest();
攔截器封裝interceptors.js
import Taro from "@tarojs/taro"
import { pageToLogin } from "./utils"
import { HTTP_STATUS } from './config'
const customInterceptor = (chain) => {
const requestParams = chain.requestParams
return chain.proceed(requestParams).then(res => {
// 只要請(qǐng)求成功,不管返回什么狀態(tài)碼,都走這個(gè)回調(diào)
if (res.statusCode === HTTP_STATUS.NOT_FOUND) {
return Promise.reject("請(qǐng)求資源不存在")
} else if (res.statusCode === HTTP_STATUS.BAD_GATEWAY) {
return Promise.reject("服務(wù)端出現(xiàn)了問(wèn)題")
} else if (res.statusCode === HTTP_STATUS.FORBIDDEN) {
Taro.setStorageSync("Authorization", "")
pageToLogin()
// TODO 根據(jù)自身業(yè)務(wù)修改
return Promise.reject("沒(méi)有權(quán)限訪(fǎng)問(wèn)");
} else if (res.statusCode === HTTP_STATUS.AUTHENTICATE) {
Taro.setStorageSync("Authorization", "")
pageToLogin()
return Promise.reject("需要鑒權(quán)")
} else if (res.statusCode === HTTP_STATUS.SUCCESS) {
return res.data
}
})
}
// Taro 提供了兩個(gè)內(nèi)置攔截器
// logInterceptor - 用于打印請(qǐng)求的相關(guān)信息
// timeoutInterceptor - 在請(qǐng)求超時(shí)時(shí)拋出錯(cuò)誤。
const interceptors = [customInterceptor, Taro.interceptors.logInterceptor]
export default interceptors
獲取鏈接的方式baseUrl.js
const getBaseUrl = (url) => {
let BASE_URL = '';
if (process.env.NODE_ENV === 'development') {
//開(kāi)發(fā)環(huán)境 - 根據(jù)請(qǐng)求不同返回不同的BASE_URL
if (url.includes('/api/')) {
BASE_URL = ''
} else if (url.includes('/iatadatabase/')) {
BASE_URL = ''
}
} else {
// 生產(chǎn)環(huán)境
if (url.includes('/api/')) {
BASE_URL = ''
} else if (url.includes('/iatadatabase/')) {
BASE_URL = ''
}
}
return BASE_URL
}
export default getBaseUrl;
請(qǐng)求狀態(tài)文件config.js
export const HTTP_STATUS = {
SUCCESS: 200,
CREATED: 201,
ACCEPTED: 202,
CLIENT_ERROR: 400,
AUTHENTICATE: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
SERVER_ERROR: 500,
BAD_GATEWAY: 502,
SERVICE_UNAVAILABLE: 503,
GATEWAY_TIMEOUT: 504
}
叮叮,這樣整體的封裝就完成了
擴(kuò)展
添加分包
[圖片上傳失敗...(image-7e87db-1642501786544)]
app.config.js
export default {
pages: [
'pages/login/index',
'pages/index/index'
],
subPackages: [
{ root: "modules", pages: ["test/index", "detail/index"] }
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: 'black'
}
}
http返回修改
結(jié)果返回從promise修改為現(xiàn)在直接返回結(jié)果
http.js新
import Taro from "@tarojs/taro";
import getBaseUrl from "./baseUrl";
import { HTTP_STATUS } from "./config";
import { STORAGE_DATA } from "./constant";
import interceptors from "./interceptors";
import { createNonceSt, MD5withRSA, pageToLogin, signjs } from "./utils";
interceptors.forEach(interceptorItem => Taro.addInterceptor(interceptorItem));
class httpRequest {
baseOptions(params, method = "GET") {
let { url, data } = params;
//添加token 隨機(jī)數(shù) sign
if (Taro.getStorageSync(STORAGE_DATA.TOKEN))
data.token = Taro.getStorageSync(STORAGE_DATA.TOKEN);
if (!data.retrieveIntervalTime) data.retrieveIntervalTime = 60000;
if (!data.retrieveTime) data.retrieveTime = new Date().getTime();
data.nonce = createNonceSt();
data.sign = MD5withRSA(signjs(data));
const BASE_URL = getBaseUrl(url);
let contentType = "application/json";
contentType = params.contentType || contentType;
const option = {
url: BASE_URL + url, //地址
data: data, //傳參
method: method, //請(qǐng)求方式
timeout: 50000, // 超時(shí)時(shí)間
header: {
"content-type": contentType,
Authorization: `Bearer ${Taro.getStorageSync(STORAGE_DATA.TOKEN)}`
}
};
// return Taro.request(option);
if (process.env.NODE_ENV === "development") {
console.log(
`${new Date().toLocaleString()}【 M=${option.url} 】P=${JSON.stringify(
option.data
)}`
);
}
return Taro.request(option)
.then(res => {
console.log("resres", res);
if (res) {
if (
res.code >= HTTP_STATUS.SUCCESS &&
res.code < HTTP_STATUS.MULTI_SELECT
) {
if (process.env.NODE_ENV === "development") {
console.log(
`${new Date().toLocaleString()}【 M=${
option.url
} 】【接口響應(yīng):】`,
res
);
}
return res;
} else {
throw new Error(
`${res.desc || "網(wǎng)絡(luò)請(qǐng)求錯(cuò)誤,狀態(tài)碼"}(${res.code})`
);
}
} else {
throw new Error(`數(shù)據(jù)返回異常,請(qǐng)重試`);
}
})
.catch(error => {
console.log("http error", error);
Taro.showToast({ title: error.message || "服務(wù)異常", icon: "none" });
// pageToLogin()
// return null;
});
}
get(url, data = "") {
let option = { url, data };
return this.baseOptions(option);
}
post(url, data, contentType) {
let params = { url, data, contentType };
return this.baseOptions(params, "POST");
}
put(url, data = "") {
let option = { url, data };
return this.baseOptions(option, "PUT");
}
delete(url, data = "") {
let option = { url, data };
return this.baseOptions(option, "DELETE");
}
}
export default new httpRequest();
驗(yàn)簽過(guò)程添加
//公共驗(yàn)簽
1 公共驗(yàn)簽方法是用于生成訪(fǎng)問(wèn)api的sign簽的一個(gè)公共方法,生成的sign將用于api接口有效性與權(quán)限的驗(yàn)證,以保證接口互通的安全性。
2 簽名算法:
簽名生成的通用步驟如下:
第一步,設(shè)所有發(fā)送或者接收到的數(shù)據(jù)為集合M(不包含sign),將集合M內(nèi)非空參數(shù)值的參數(shù)按照參數(shù)名ASCII碼從小到大排序(字典序),使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特別注意以下重要規(guī)則:
1.◆ 參數(shù)名ASCII碼從小到大排序(字典序);
2.◆ 如果參數(shù)的值為空不參與簽名;
3.◆ 參數(shù)名區(qū)分大小寫(xiě);
4.◆ 驗(yàn)證調(diào)用返回或主動(dòng)通知簽名時(shí),傳送的sign參數(shù)不參與簽名,將生成的簽名與該sign值作校驗(yàn)。
5.◆ 接口可能增加字段,驗(yàn)證簽名時(shí)必須支持增加的擴(kuò)展字段
第二步,在stringA最后拼接上apikey得到stringSignTemp字符串,并對(duì)stringSignTemp進(jìn)行MD5運(yùn)算,再將得到的字符串所有字符轉(zhuǎn)換為大寫(xiě),得到signMD5,對(duì)signMD5值進(jìn)行RSA運(yùn)算得到sign。
舉例:
假設(shè)傳送的參數(shù)如下:
appid: wxd930ea5d5a258f4f
mch_id: 10000100
device_info: 1000
body: test
nonce: ibuaiVcKdpRxkhJA
第一步:對(duì)參數(shù)按照key=value的格式,并按照參數(shù)名ASCII字典序排序如下:
stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce=ibuaiVcKdpRxkhJA";
第二步:拼接API密鑰:
MD5簽名方式:
stringSignTemp=stringA+"&key=192006250b4c09247ec02edce69f6a2d" //注:key為用戶(hù)的userCode
signMD5=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7" //注:MD5簽名方式
RSA簽名方式:
sign=RSA(signMD5,appSecret)="6A9AE1657590FD6257D693A078E1C3E4BB6BA4DC30B23E0EE2496E54170DACD6"
注:
1 RSA簽名方式中的加密算法為RSA,簽名算法為MD5withRSA
2生成隨機(jī)數(shù)算法
API接口協(xié)議中包含字段nonce,主要保證簽名不可預(yù)測(cè)。我們推薦生成隨機(jī)數(shù)算法如下:調(diào)用隨機(jī)數(shù)函數(shù)生成,將得到的值轉(zhuǎn)換為字符串。
2.1.3字段介紹:
nonce 隨機(jī)數(shù)
key 為用戶(hù)的userCode
utils.js中的MD5withRSA、signjs、sortObj、createNonceSt為封裝的驗(yàn)簽加密算法(已驗(yàn)證可以使用)
import Taro from "@tarojs/taro";
import md5 from "./md5js";
import { STORAGE_DATA } from "./constant";
import { hextob64, KEYUTIL, KJUR, RSAKey } from "jsrsasign";
/**
* @description 獲取當(dāng)前頁(yè)url
*/
export const getCurrentPageUrl = () => {
let pages = Taro.getCurrentPages();
let currentPage = pages[pages.length - 1];
let url = currentPage.route;
return url;
};
export const pageToLogin = () => {
let path = getCurrentPageUrl();
Taro.clearStorageSync();
if (!path.includes("login")) {
Taro.reLaunch({
url: "/pages/login/index"
});
}
};
/** 對(duì)象轉(zhuǎn)url參數(shù) */
export const stringify = params => {
const obj = typeof params === "object" && params !== null ? params : {};
const isEmpty = v => v === "" || v === null || v === undefined;
return Object.keys(obj)
.filter(k => !isEmpty(obj[k]))
.map(key => `${key}=${encodeURIComponent(obj[key])}`)
.join("&");
};
export const repeat = (str = "0", times) => new Array(times + 1).join(str);
// 時(shí)間前面 +0
export const pad = (num, maxLength = 2) =>
repeat("0", maxLength - num.toString().length) + num;
/** 時(shí)間格式的轉(zhuǎn)換 */
export const formatTime = time =>
`${pad(time.getHours())}:${pad(time.getMinutes())}:${pad(
time.getSeconds()
)}.${pad(time.getMilliseconds(), 3)}`;
export var globalData = {}; // 全局公共變量
// 使用es6的padStart()方法來(lái)補(bǔ)0
export const getYMDHMS = timestamp => {
let time = timestamp ? new Date(timestamp) : new Date();
let year = time.getFullYear();
const month = (time.getMonth() + 1).toString().padStart(2, "0");
const date = time
.getDate()
.toString()
.padStart(2, "0");
const hours = time
.getHours()
.toString()
.padStart(2, "0");
const minute = time
.getMinutes()
.toString()
.padStart(2, "0");
const second = time
.getSeconds()
.toString()
.padStart(2, "0");
return (
year +
"年" +
month +
"月" +
date +
"日 " +
hours +
"時(shí)" +
minute +
"分" +
second +
"秒"
);
};
export function getFileName(filePath) {
if (!filePath) return null;
let index = filePath.lastIndexOf("/");
const name = filePath.substring(index + 1);
return name;
}
// 使用es6的padStart()方法來(lái)補(bǔ)0
export const getYMDHMS2 = timestamp => {
let time = timestamp ? new Date(timestamp) : new Date();
let year = time.getFullYear();
const month = (time.getMonth() + 1).toString().padStart(2, "0");
const date = time
.getDate()
.toString()
.padStart(2, "0");
const hours = time
.getHours()
.toString()
.padStart(2, "0");
const minute = time
.getMinutes()
.toString()
.padStart(2, "0");
const second = time
.getSeconds()
.toString()
.padStart(2, "0");
return (
year + "-" + month + "-" + date + " " + hours + ":" + minute + ":" + second
);
};
export function dateStr() {
let date = new Date();
if (date.getHours() >= 0 && date.getHours() < 12) {
return "上午好";
} else if (date.getHours() >= 12 && date.getHours() < 18) {
return "下午好";
} else {
return "晚上好";
}
}
/**
* 校驗(yàn)手機(jī)號(hào)是否正確
* @param phone 手機(jī)號(hào)
*/
export const verifyPhone = phone => {
const reg = /^1[0-9]{10}$/;
const _phone = phone.toString().trim();
let toastStr =
_phone === ""
? "手機(jī)號(hào)不能為空~"
: !reg.test(_phone) && "請(qǐng)輸入正確手機(jī)號(hào)~";
return {
errMsg: toastStr,
done: !toastStr,
value: _phone
};
};
export const verifyStr = (str, text) => {
const _str = str.toString().trim();
const toastStr = _str.length ? false : `請(qǐng)?zhí)顚?xiě)${text}~`;
return {
errMsg: toastStr,
done: !toastStr,
value: _str
};
};
// 截取字符串
export const sliceStr = (str, sliceLen) => {
if (!str) {
return "";
}
let realLength = 0;
const len = str.length;
let charCode = -1;
for (var i = 0; i < len; i++) {
charCode = str.charCodeAt(i);
if (charCode >= 0 && charCode <= 128) {
realLength += 1;
} else {
realLength += 2;
}
if (realLength > sliceLen) {
return `${str.slice(0, i)}...`;
}
}
return str;
};
/**
* JSON 克隆
* @param {Object | Json} jsonObj json對(duì)象
* @return {Object | Json} 新的json對(duì)象
*/
export function objClone(jsonObj) {
var buf;
if (jsonObj instanceof Array) {
buf = [];
var i = jsonObj.length;
while (i--) {
buf[i] = objClone(jsonObj[i]);
}
return buf;
} else if (jsonObj instanceof Object) {
buf = {};
for (var k in jsonObj) {
buf[k] = objClone(jsonObj[k]);
}
return buf;
} else {
return jsonObj;
}
}
export function MD5withRSA(signMd5) {
if (!Taro.getStorageSync(STORAGE_DATA.PRIVATEKEY)) return;
const pem = `-----BEGIN PRIVATE KEY-----${Taro.getStorageSync(
STORAGE_DATA.PRIVATEKEY
)}-----END PRIVATE KEY-----`;
let key = KEYUTIL.getKey(pem);
// 創(chuàng)建 Signature 對(duì)象
let signature = new KJUR.crypto.Signature({ alg: "MD5withRSA" });
// 傳入key實(shí)例, 初始化signature實(shí)例
signature.init(key);
// 傳入待簽明文
signature.updateString(signMd5);
const signInfo = signature.sign();
const sign = hextob64(signInfo);
console.log("signature:", signInfo, sign);
// 簽名, 得到16進(jìn)制字符結(jié)果
return sign;
}
// 支付md5加密獲取sign
export function signjs(jsonobj) {
var signstr = obj2str(jsonobj);
if (Taro.getStorageSync(STORAGE_DATA.USERID)) {
signstr = signstr + "&key=" + Taro.getStorageSync(STORAGE_DATA.USERID);
}
var sign = md5(signstr); //驗(yàn)證調(diào)用返回或微信主動(dòng)通知簽名時(shí),傳送的sign參數(shù)不參與簽名,將生成的簽名與該sign值作校驗(yàn)。
console.log("signstrkey:", signstr, signstr.length, sign, sign.toUpperCase());
return sign.toUpperCase();
}
export function sortObj(obj) {
if (obj instanceof Array) {
let newArr = [];
newArr = obj.map((item, index) => {
return sortObj(item);
});
return newArr;
} else if (obj instanceof Object) {
let keys = Object.keys(obj);
if (!(keys && keys.length > 0)) return null;
keys = keys.sort(); //參數(shù)名ASCII碼從小到大排序(字典序);
let newObj = {};
keys.forEach(function(key) {
if (obj[key] !== "" && obj[key] !== undefined && obj[key] !== null) {
//如果參數(shù)的值為空不參與簽名;
newObj[key] = sortObj(obj[key]); //參數(shù)名區(qū)分大小寫(xiě);
}
});
return newObj;
} else {
return obj;
}
}
//object轉(zhuǎn)string,用于簽名計(jì)算
export function obj2str(args) {
if (!args) return;
let newArgs = sortObj(args);
console.log("sortObj\n", args, "\n", newArgs);
let string = "";
for (let k in newArgs) {
string +=
"&" +
k +
"=" +
(typeof newArgs[k] == "object" ? JSON.stringify(newArgs[k]) : newArgs[k]);
}
string = string.substr(1);
return string;
}
//隨機(jī)函數(shù)的產(chǎn)生:
export function createNonceSt() {
return Math.random()
.toString(36)
.substr(2, 15); //隨機(jī)小數(shù),轉(zhuǎn)換36進(jìn)制,去掉0.,保留余下部分
}
//時(shí)間戳產(chǎn)生的函數(shù), 當(dāng)前時(shí)間以證書(shū)表達(dá),精確到秒的字符串
export function createTimeStamp() {
return parseInt(new Date().getTime() / 1000) + "";
}
export function sort_ASCII(obj) {
var arr = new Array();
var num = 0;
for (var i in obj) {
arr[num] = i;
num++;
}
var sortArr = arr.sort();
var sortObj = {};
for (var i in sortArr) {
sortObj[sortArr[i]] = obj[sortArr[i]];
}
return sortObj;
}
/** 檢查更新 */
export function checkForUpdate() {
if (Taro.canIUse("getUpdateManager")) {
const updateManager = Taro.getUpdateManager();
updateManager.onCheckForUpdate(res => {
// 請(qǐng)求完新版本信息的回調(diào)
if (res.hasUpdate) {
updateManager.onUpdateReady(() => {
Taro.showModal({
title: "更新提示",
content: "新版本已經(jīng)準(zhǔn)備好,是否重啟應(yīng)用?",
showCancel: false,
confirmColor: "#40A3FF",
success: res => {
if (res.confirm) {
// 新的版本已經(jīng)下載好,調(diào)用 applyUpdate 應(yīng)用新版本并重啟
updateManager.applyUpdate();
}
}
});
});
updateManager.onUpdateFailed(() => {
// 新版本下載失敗
Taro.showModal({
title: "更新提示",
content: "新版本已經(jīng)上線(xiàn),請(qǐng)刪除當(dāng)前小程序,重新掃碼進(jìn)入",
confirmColor: "#40A3FF",
showCancel: false
});
});
}
});
}
}