添加 h5

This commit is contained in:
saplf
2019-09-07 14:39:03 +08:00
parent 71a3719bcf
commit 49fe983181
9 changed files with 507 additions and 13 deletions

View File

@@ -5,12 +5,16 @@ import { isDev } from '@/utils/utils';
import splash from '@/assets/splash.png'; import splash from '@/assets/splash.png';
import styles from './index.css'; import styles from './index.css';
import { init, destory } from '@/utils/h5plus';
const BasicLayout: React.FC = props => { const BasicLayout: React.FC = props => {
const [isReady, cancel] = useTimeout(isDev ? 100 : 1500); const [isReady, cancel] = useTimeout(isDev ? 100 : 1500);
useEffect(() => { useEffect(() => {
init();
return () => { return () => {
destory();
if (!isReady()) cancel(); if (!isReady()) cancel();
}; };
}, []); }, []);

View File

@@ -65,7 +65,6 @@
.reset { .reset {
height: 100%; height: 100%;
padding-top: @header-height;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }

View File

@@ -12,6 +12,7 @@ import { PageState } from '@/types/common';
import Header from '@/components/Header'; import Header from '@/components/Header';
import styles from './style.less'; import styles from './style.less';
import h5plus from '@/utils/h5plus';
const Option = Select.Option; const Option = Select.Option;
@@ -44,8 +45,17 @@ const DrawPage: React.FC<DispatchProp & DrawModelState> = ({
); );
} else if (pageState === PageState.Success) { } else if (pageState === PageState.Success) {
const coinLabel = coin && coin.label.toUpperCase(); const coinLabel = coin && coin.label.toUpperCase();
const onScan = () => { const onScan = async () => {
message.warn(formatMessage({ id: 'draw.qrPrompt' })); if (h5plus.isApp()) {
const result = await h5plus.scan();
if (result) {
dispatch({ type: 'draw/onUpdateState', address: result });
} else {
message.warn(formatMessage({ id: 'draw.qrPrompt' }));
}
} else {
message.warn(formatMessage({ id: 'draw.qrPrompt' }));
}
} }
const confirmContent = ( const confirmContent = (
<Modal <Modal

View File

@@ -49,7 +49,7 @@ const MinePage: React.FC<HomeMineModelState & DispatchProp> = ({
</span> </span>
<span className="line"> <span className="line">
<span className="title2">{formatMessage({ id: 'home.mine.valid' })}</span> <span className="title2">{formatMessage({ id: 'home.mine.valid' })}</span>
<span className="value">{info ? info.stoneAll : 0} KG</span> <span className="value">{info ? info.activeFund : 0} KG</span>
</span> </span>
</div> </div>
@@ -66,7 +66,7 @@ const MinePage: React.FC<HomeMineModelState & DispatchProp> = ({
const renderItem = (it: Mine) => ( const renderItem = (it: Mine) => (
<div className={styles.item} key={it.id}> <div className={styles.item} key={it.id}>
<div className="line"> <div className="line">
<div>{formatMessage({ id: 'home.mine.validTime' })}{it.validTime}</div> <div>{formatMessage({ id: 'home.mine.validTime' })}{it.createdAt}</div>
<div className="amount">+{it.amount} KG</div> <div className="amount">+{it.amount} KG</div>
</div> </div>
<div className="line"> <div className="line">
@@ -74,7 +74,7 @@ const MinePage: React.FC<HomeMineModelState & DispatchProp> = ({
{formatMessage({ id: 'home.mine.status' })} {formatMessage({ id: 'home.mine.status' })}
{formatMessage({ id: `home.mine.status${it.status}` })} {formatMessage({ id: `home.mine.status${it.status}` })}
</div> </div>
<div className="small">{formatMessage({ id: 'home.mine.invalidTime' })}{it.invalidTime}</div> <div className="small">{formatMessage({ id: 'home.mine.invalidTime' })}{it.expiresAt}</div>
</div> </div>
</div> </div>
); );

View File

@@ -112,7 +112,7 @@ export interface MineBasic {
/** /**
* 当前有效矿晶 * 当前有效矿晶
*/ */
stoneAll: string; activeFund: string;
} }
export enum MineStatus { export enum MineStatus {
@@ -132,12 +132,12 @@ export interface Mine {
/** /**
* 获赠日期 * 获赠日期
*/ */
validTime: string; createdAt: string;
/** /**
* 到期日期 * 到期日期
*/ */
invalidTime: string; expiresAt: string;
/** /**
* 矿晶状态 * 矿晶状态

403
src/types/global.d.ts vendored
View File

@@ -1,3 +1,406 @@
interface BaseView { }
interface BarcodeSuccessCallback {
/**
* @param type 识别到的条码类型 与Barcode对象定义的条码类型常量一致。
* @param code 识别到的条码数据 扫码识别出的数据内容字符串类型采用UTF8编码格式。
* @param file 扫码成功的截图文件路径 扫码识别到的截图png格式文件如果设置为不保存截图则返回undefined。
*/
(type: number, code: string, file?: string): any;
}
interface BarcodeErrorCallback {
(error: DOMException): any;
}
interface BarcodeStyles {
/**
* 条码识别控件背景颜色
*
* @description 颜色值支持(参考CSS颜色规范):颜色名称(参考CSS Color Names)/十六进制值/rgb值默认值为红色。
*/
background?: string;
/**
* 扫码框颜色
*
* @description 颜色值支持(参考CSS颜色规范):颜色名称(参考CSS Color Names)/十六进制值/rgb值默认值为红色。
*/
frameColor?: string;
/**
* 扫码条颜色
*
* @description 颜色值支持(参考CSS颜色规范):颜色名称(参考CSS Color Names)/十六进制值/rgb值默认值为红色。
*/
scanbarColor?: string;
/**
* Barcode扫码控件左上角的垂直偏移量
*
* @description 可取值: 像素值,如"100px" 百分比,如"10%"相对于父Webview窗口的高度 自动计算,如"auto",根据height值自动计算相对于父Webview窗口垂直居中。
*/
top?: string;
/**
* Barcode扫码控件左上角的水平偏移量
*
* @description 可取值: 像素值,如"100px" 百分比,如"10%"相对于父Webview窗口的高度 自动计算,如"auto",根据height值自动计算相对于父Webview窗口垂直居中。
*/
left?: string;
/**
* Barcode扫码控件的宽度
*
* @description 可取值: 像素值,如"100px" 百分比,如"10%"相对于父Webview窗口的宽度。 默认值为"100%"。
*/
width?: string;
/**
* Barcode扫码控件的高度
*
* @description 可取值: 像素值,如"100px" 百分比,如"10%"相对于父Webview窗口的宽度。 默认值为"100%"。
*/
height?: string;
/**
* Barcode扫码控件在Webview窗口的布局模式
*
* @description 可取值: "static" - 静态布局模式,如果页面存在滚动条则随窗口内容滚动; "absolute" - 绝对布局模式,如果页面存在滚动条不随窗口内容滚动; 默认值为"static"。
*/
position?: 'static' | 'absolute';
}
interface BarcodeOptions {
/**
* 是否保存扫码成功时的截图
*
* @description 如果设置为true则在扫码成功时将图片保存并通过onmarked回调函数的file参数返回保存文件的路径。 默认值为false不保存截图。
*/
conserve?: boolean;
/**
* 保存扫码成功时图片保存路径
*
* @description 可通过此参数设置保存截图的路径和名称,如果设置图片文件名称则必须指定文件的后缀名(必须是.png否则认为是指定目录文件名称则自动生成。
*/
filename?: string;
/**
* 扫码成功时是否需要震动提醒
*
* @description 如果设置为true则在扫码成功时震动设备false则不震动。 默认值为true。
*/
vibrate?: boolean;
/**
* 扫码成功时播放的提示音
*
* @description 可取值: "none" - 不播放提示音; "default" - 播放默认提示音5+引擎内置)。 默认值为"default"。
*/
sound?: 'none' | 'default';
}
interface Barcode extends BaseView {
/**
* 取消扫码识别
*
* @description 结束对摄像头获取图片数据进行条码识别操作,同时关闭摄像头的视频捕获。 结束后可调用start方法重新开始识别。
*/
cancel: () => any;
/**
* 关闭条码识别控件
*
* @description 释放控件占用系统资源调用close方法后控件对象将不可使用。
*/
close: () => any;
/**
* 操作闪光灯
*
* @description 设置扫码识别控件在扫码时是否开启摄像头的闪光灯,默认值为不开启闪光灯。
*
* @param open true表示打开闪光灯false表示关闭闪光灯。
*/
setFlash: (open: boolean) => any;
/**
* 设置扫码识别控件的样式
*
* @description 用于动态更新扫码识别控件的显示样式参数。
*/
setStyle: (styles: BarcodeStyles) => any;
/**
* 开始扫码识别
*
* @description 调用设备的摄像头在控件中预览并获取捕获数据进行扫码识别当识别出条码二维码数据时触发onmarked事件返回扫码结果。
*
* @param options 扫码识别的参数
*/
start: (options?: BarcodeOptions) => any;
/**
* 扫码识别成功事件
*/
onmarked: BarcodeSuccessCallback;
/**
* 扫码识别错误事件
*/
onerror: BarcodeErrorCallback;
}
interface BarcodeWrapper {
/**
* QR二维码
*
* @description 1994年由日本Denso-Wave公司发明QR来自英文Quick Response的缩写即快速反应的意思源自发明者希望QR码可让其内容快速被解码。 目前使用最广泛的二维码。
*/
QR: 0;
/**
* EAN条形码标准版
*
* @description 国际物品编码协会在全球推广应用的商品条码是由13位数字组成。 目前使用最广泛的一维条形码。
*/
EAN13: 1;
/**
* ENA条形码简版
*
* @description 国际物品编码协会在全球推广应用的商品条码是由8位数字组成。
*/
EAN8: 2;
/**
* 扫码识别图片中的条码
*
* @description 输入图片文件进行扫码识别成功扫描到条码一维码或二维码后通过successCallback回调返回失败则通过errorCallback回调返回。
*
* @param path 要扫码的图片路径 必须是本地文件路径如URLType类型如以"_www"、"_doc"、"_documents"、"_downloads"开头的相对URL路径或者系统绝对路径。
* @param successCB 扫码识别成功回调函数
* @param errorCB 扫码识别失败回调函数
* @param filters 条码类型过滤器
*/
scan: (path: string, successCB: BarcodeSuccessCallback, errorCB?: BarcodeErrorCallback, filters?: number[]) => any;
/**
* 创建扫码识别控件对象
*
* @description 此方法创建扫码识别控件并不会显示在页面中需要调用plus.webview.Webview窗口对象的append方法将其添加到Webview窗口中才能显示。 注意需要设置styles参数的top/left/width/height属性指定扫码识别控件的位置及大小否则可能无法正确显示。
*
* @param id 扫码识别控件的标识 可用于通过plus.barcode.getBarcodeById()方法查找已经创建的扫码识别控件对象。
* @param filters 条码类型过滤器 条码类型常量数组默认情况支持QR、EAN13、EAN8类型。 通过此参数可设置扫码识别支持的条码类型(注意:设置支持的条码类型越多,扫描识别速度可能将会降低)。
* @param styles 扫码识别控件样式
*/
create: (id: string, filters?: number[], styles?: BarcodeStyles) => Barcode;
/**
* 查找扫码识别控件对象
*
* @description 根据指定的id标识查找扫码控件对象可跨页面进行查找。
*
* @param id 扫码识别控件的标识
*/
getBarcodeById: (id: string) => Barcode | null;
}
interface WebviewObject extends BaseView {
/**
* Webview窗口的标识
*
* @description 在打开或创建Webview窗口时设置如果没有设置窗口标识此属性值为undefined。
*/
id: string;
/**
* 在Webview窗口中添加子窗口
*
* @description 将另一个Webview窗口作为子窗口添加到当前Webview窗口中添加后其所有权归父Webview窗口当父窗口关闭时子窗口自动关闭。
*/
append: (view: BaseView) => any;
/**
* 移除子Webview窗口
*
* @description 从当前Webview窗口移除指定的子Webview窗口若指定的webview对象不是当前窗口的子窗口则无任何作用。 移除后子Webview窗口不会关闭需要调用其close方法才能真正关闭并销毁。
*/
remove: (view: BaseView) => any;
}
interface WebviewWrapper {
/**
* 获取所有Webview窗口
*
* @description 获取应用中已创建的所有Webview窗口包括所有未显示的Webview窗口。 返回WebviewObject对象在数组中按创建的先后顺序排列即数组中第一个WebviewObject对象用是加载应用的入口页面。
*/
all: () => WebviewObject[];
/**
* 获取当前窗口的WebviewObject对象
*/
currentWebview: () => WebviewObject;
/**
* 获取屏幕所有可视的Webview窗口
*
* @description 仅在屏幕区域显示的Webview窗口如果Webview窗口显示了但被其它Webview窗口盖住则认为不可视。
*/
getDisplayWebview: () => WebviewObject[];
/**
* 查找指定标识的WebviewObject窗口
*
* @description 在已创建的窗口列表中查找指定标识的Webview窗口并返回。 若没有查找到指定标识的窗口则返回null若存在多个相同标识的Webview窗口则返回第一个创建的Webview窗口。 如果要获取应用入口页面所属的Webview窗口其标识为应用的%APPID%可通过plus.runtime.appid获取。
*/
getWebviewById: (id: string) => WebviewObject | null;
/**
* 获取应用首页WebviewObject窗口对象
*/
getLaunchWebview: () => WebviewObject;
}
interface RuntimeWrapper {
/**
* 当前应用的APPID
*/
appid: string;
/**
* 客户端的版本名称
*
* @description 字符串类型在编译环境中设置的apk/ipa版本名称。 Android平台对应AndroidManifest.xml中的android:versionName属性值 iOS平台对应Info.plist中的CFBundleShortVersionString字段值。
*/
version: string;
/**
* 客户端的版本号
*
* @description 字符串类型,仅包含数字。 Android平台对应AndroidManifest.xml中的android:versionCode属性值 iOS平台对应Info.plist中CFBundleVersion字段值。
*/
versionCode: string;
/**
* 退出应用
*
* @description 5+APP退出应用返回系统桌面 流应用:在“流应用”中调用则退出当前流应用,返回进入应用前的界面(如应用列表)。
*/
quit: () => any;
/**
* 重启当前的应用
*/
restart: () => any;
/**
* 设置程序快捷方式图标上显示的角标数字
*/
setBadgeNumber: (num: number) => any;
/**
* 调用第三方程序打开指定的URL
*/
openURL: (url: string, err?: (e: DOMException) => any, identity?: string) => any;
/**
* 使用内置Webview窗口打开URL
*/
openWeb: (url: string) => any;
}
interface ShareMessage {
/**
* 内容见 https://www.html5plus.org/doc/zh_cn/share.html#plus.share.ShareMessage
*/
type?: 'web' | 'text' | 'image' | 'music' | 'video';
/**
* 分享消息的文字内容
*/
content?: string;
/**
* 分享消息的图片
*
* @description 分享消息中包含的图片路径,仅支持本地路径。 若分享平台仅支持提交一张图片,传入多张图片则仅提交第一张图片。 如果未指定type类型优先级顺序为pictures>content即设置了pictures则认为分享图片类型
*/
pictures?: string[];
/**
* 分享消息的缩略图
*
* @description 分享消息中包含的缩略图路径,支持本地路径及网络路径。 若分享平台仅支持提交一张图片,传入多张图片则仅提交第一张图片。 如果分享平台的信息不支持缩略图若没有设置消息的图片pictures则使用缩略图否则忽略其属性值。 注意图片有大小限制推荐图片小于20Kb。
*/
thumbs?: string[];
/**
* 分享的多媒体资源
*
* @description 分享的多媒体资源地址当type值为"music"、"video"时有效。 注意: 微信分享平台支持音乐、视频类型仅支持网络地址以http://或https://开头); QQ分享平台支持音乐类型仅支持网络路径以http://或https://开头); 新浪微博分享平台支持视频类型,仅支持本地文件路径。
*/
media?: string;
/**
* 分享独立的链接
*
* @description 分享资源地址仅支持网络地址以http://或https://开头)。 如果未指定type类型优先级顺序为href>pictures>content即设置了href则认为分享网页类型
*/
href?: string;
/**
* 分享消息的标题
*
* @description 仅微信分享网页、音频、视频、小程序类型时支持。
*/
title?: string;
}
interface ShareWrapper {
/**
* 使用系统组件发送分享
*/
sendWithSystem: (message: ShareMessage, successCB: () => any, errorCB: (err: DOMException) => any) => any;
}
type AppKeyType =
| 'backbutton';
interface AppKeyEvent {
keyCode: number;
keyType: AppKeyType;
}
interface KeyWrapper {
/**
* 添加按键事件监听器
*/
addEventListener: (event: AppKeyType, listener: (event: AppKeyEvent) => any) => any;
/**
* 移除按键事件监听器
*/
removeEventListener: (event: AppKeyType, listener: (event: AppKeyEvent) => any) => any;
}
interface H5Plus {
barcode: BarcodeWrapper;
webview: WebviewWrapper;
runtime: RuntimeWrapper;
share: ShareWrapper;
net: { XMLHttpRequest: XMLHttpRequest };
key: KeyWrapper;
}
interface Window { interface Window {
g_app: any; g_app: any;
plus: H5Plus;
} }

75
src/utils/h5plus.ts Normal file
View File

@@ -0,0 +1,75 @@
import { dispatch, getState } from './utils';
import { routerRedux } from 'dva';
const plus = window.plus;
const isApp = () => typeof plus !== 'undefined';
const barcodeId = 'barcode'
const scan = () => {
if (!isApp()) return Promise.resolve('');
return new Promise<string>(resolve => {
const { barcode, webview } = plus;
const bc = barcode.create(barcodeId, [barcode.QR], {
position: 'absolute',
left: '0px',
top: '100px',
width: '100%',
height: '500px',
});
bc.onmarked = (_, code) => {
resolve(code);
bc.close();
};
bc.onerror = () => {
resolve('');
bc.close();
}
webview.currentWebview().append(bc);
bc.start();
});
}
const firstPaths = [
'/check',
'/home/estate',
'/home/fund',
'/home/market',
'/home/profile',
];
const backButtonHandler = () => {
const barcode = plus.barcode.getBarcodeById(barcodeId);
if (barcode) {
barcode.close();
return;
}
const { pathname } = getState().router.location;
if (firstPaths.includes(pathname)) {
plus.runtime.quit();
} else {
dispatch(routerRedux.goBack());
}
};
const init = () => {
if (!isApp()) return;
plus.key.addEventListener('backbutton', backButtonHandler);
}
const destory = () => {
if (!isApp()) return;
plus.key.removeEventListener('backbutton', backButtonHandler);
}
export {
init,
isApp,
scan,
destory,
};
export default {
init,
isApp,
scan,
destory,
};

View File

@@ -20,7 +20,7 @@ const errorMap = {
}; };
const baseURL = 'https://server.fiv.tacever.org/v1'; const baseURL = 'https://server.fiv.tacever.org/v1';
// const baseURL = '//yapi.faronear.org/mock/20/v1'; // const baseURL = 'http://yapi.faronear.org/mock/20/v1';
const instance = axios.create({ const instance = axios.create({
baseURL, baseURL,
}); });
@@ -57,7 +57,8 @@ async function filter<T>(
} else { } else {
instance.defaults.headers = _.omit(instance.defaults.headers, 'token'); instance.defaults.headers = _.omit(instance.defaults.headers, 'token');
} }
const { status, data } = await (instance[method] as (...args: any[]) => Promise<AxiosResponse<AppResp>>)(...args); const { status, data } =
await (instance[method] as (...args: any[]) => Promise<AxiosResponse<AppResp>>)(...args);
replaceId(data); replaceId(data);
ret = { ret = {
ok: status >= 200 && status < 300 && data && data.code === 0, ok: status >= 200 && status < 300 && data && data.code === 0,

View File

@@ -10,6 +10,8 @@ interface Action {
const dispatch = (action: Action) => window.g_app._store.dispatch(action); const dispatch = (action: Action) => window.g_app._store.dispatch(action);
export { isDev, dispatch }; const getState = () => window.g_app._store.getState();
export default { isDev, dispatch }; export { isDev, dispatch, getState };
export default { isDev, dispatch, getState };