mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-09-06 07:19:47 +08:00
feat: pages > page and h5 scroll
This commit is contained in:
parent
5beef07281
commit
552bc4e9ad
@ -8,6 +8,7 @@ module.exports = {
|
|||||||
// 这里值为 false 表示这个全局变量不允许被重新赋值,比如:
|
// 这里值为 false 表示这个全局变量不允许被重新赋值,比如:
|
||||||
//
|
//
|
||||||
// Vue: false
|
// Vue: false
|
||||||
|
__DEV__: false
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'vue/comment-directive': 'off',
|
'vue/comment-directive': 'off',
|
||||||
|
@ -13,7 +13,7 @@ const headPkgs = [
|
|||||||
"fes-plugin-access",
|
"fes-plugin-access",
|
||||||
"fes-plugin-model",
|
"fes-plugin-model",
|
||||||
"fes-plugin-layout",
|
"fes-plugin-layout",
|
||||||
"fes-plugin-icon",
|
"fes-plugin-icon"
|
||||||
];
|
];
|
||||||
const tailPkgs = [];
|
const tailPkgs = [];
|
||||||
// const otherPkgs = readdirSync(join(__dirname, 'packages')).filter(
|
// const otherPkgs = readdirSync(join(__dirname, 'packages')).filter(
|
||||||
|
12
packages/fes-plugin-request/README.md
Normal file
12
packages/fes-plugin-request/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# request 封装
|
||||||
|
|
||||||
|
为了尽可能减少包的,提供可定制化能力
|
||||||
|
|
||||||
|
## TODO 待实现能力
|
||||||
|
|
||||||
|
* formData 控制
|
||||||
|
* 轮询
|
||||||
|
* 并行请求 >> 通过定义 key 区分
|
||||||
|
* 防抖 & 节流
|
||||||
|
* 缓存 & SWR & 预加载
|
||||||
|
* loadingDelay
|
@ -14,10 +14,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"axios": "0.21.1",
|
||||||
"@webank/fes": "^2.0.0"
|
"@webank/fes": "^2.0.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"axios": "^0.20.0",
|
|
||||||
"throttle-debounce": "^2.3.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@ export function typeOf(obj) {
|
|||||||
'[object RegExp]': 'regExp',
|
'[object RegExp]': 'regExp',
|
||||||
'[object Undefined]': 'undefined',
|
'[object Undefined]': 'undefined',
|
||||||
'[object Null]': 'null',
|
'[object Null]': 'null',
|
||||||
'[object Object]': 'object'
|
'[object Object]': 'object',
|
||||||
|
'[object URLSearchParams]': 'URLSearchParams'
|
||||||
};
|
};
|
||||||
return map[Object.prototype.toString.call(obj)];
|
return map[Object.prototype.toString.call(obj)];
|
||||||
}
|
}
|
||||||
@ -38,13 +39,14 @@ export function isObject(obj) {
|
|||||||
return typeOf(obj) === 'object';
|
return typeOf(obj) === 'object';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isHtmlElement(node) {
|
export function isURLSearchParams(obj) {
|
||||||
return node && node.nodeType === Node.ELEMENT_NODE;
|
return typeOf(obj) === 'URLSearchParams';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
export const isUndefined = val => val === undefined;
|
export const isUndefined = val => val === undefined;
|
||||||
|
|
||||||
export const isDefined = val => val !== undefined && val !== null;
|
export const isDefined = val => val != null;
|
||||||
|
|
||||||
|
|
||||||
export function checkHttpRequestHasBody(method) {
|
export function checkHttpRequestHasBody(method) {
|
||||||
@ -88,3 +90,16 @@ export function trimObj(obj) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 唯一定位一个请求(url, data | params, method)
|
||||||
|
* 其中请求参数(data, params)根据请求方法,只使用其中一个
|
||||||
|
* 一个请求同时包含 data | params 参数的设计本身不合理
|
||||||
|
* 不对这种情况进行兼容
|
||||||
|
*/
|
||||||
|
export function genRequestKey(url, data, method) {
|
||||||
|
if (isURLSearchParams(data)) {
|
||||||
|
return `${url}${data.toString()}${method}`;
|
||||||
|
}
|
||||||
|
return `${url}${JSON.stringify(data)}${method}`;
|
||||||
|
}
|
||||||
|
12
packages/fes-plugin-request/src/template/paramsProcess.js
Normal file
12
packages/fes-plugin-request/src/template/paramsProcess.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { checkHttpRequestHasBody, trimObj } from 'helpers';
|
||||||
|
|
||||||
|
export default (instance) => {
|
||||||
|
instance.interceptors.request.use((config) => {
|
||||||
|
if (checkHttpRequestHasBody(config.method)) {
|
||||||
|
config.data = trimObj(config.data);
|
||||||
|
} else {
|
||||||
|
config.params = trimObj(config.params);
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
};
|
@ -1,31 +1,14 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { ApplyPluginsType, plugin } from '@webank/fes';
|
||||||
// import { debounce } from 'throttle-debounce';
|
|
||||||
import { ApplyPluginsType, plugin, router } from '@webank/fes';
|
|
||||||
import {
|
import {
|
||||||
checkHttpRequestHasBody,
|
checkHttpRequestHasBody,
|
||||||
trimObj,
|
isFunction
|
||||||
isFunction,
|
|
||||||
isObject
|
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
|
|
||||||
/**
|
import setDataField from './setDataField';
|
||||||
* 统一错误处理
|
import paramsProcess from './paramsProcess';
|
||||||
* @param {object | string | function} errorStruct
|
import resDataAdaptor from './resDataAdaptor';
|
||||||
* {
|
import resErrorProcess from './resErrorProcess';
|
||||||
* errorMessage: '', // 错误地址
|
|
||||||
* errorPage: '', // 错误页面地址
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
function _errorHandler(error, customerErrorHandler) {
|
|
||||||
if (isFunction(error)) {
|
|
||||||
error();
|
|
||||||
} else if (error.errorPage) {
|
|
||||||
router.replace(error.errorPage);
|
|
||||||
} else {
|
|
||||||
customerErrorHandler && customerErrorHandler(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addInterceptors(instance, interceptors, type = 'request') {
|
function addInterceptors(instance, interceptors, type = 'request') {
|
||||||
interceptors.forEach((fn) => {
|
interceptors.forEach((fn) => {
|
||||||
@ -59,90 +42,27 @@ function getRequestInstance() {
|
|||||||
initialValue: {}
|
initialValue: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
const _errorConfig = Object.assign({
|
|
||||||
401: {
|
|
||||||
showType: 9,
|
|
||||||
errorPage: '/login'
|
|
||||||
},
|
|
||||||
403: '用户得到授权,但访问是禁止的'
|
|
||||||
}, errorConfig);
|
|
||||||
|
|
||||||
const _requestInterceptors = [].concat([
|
|
||||||
(config) => {
|
|
||||||
config.method = config.method.toUpperCase();
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
(config) => {
|
|
||||||
if (checkHttpRequestHasBody(config.method)) {
|
|
||||||
config.data = trimObj(config.data);
|
|
||||||
} else {
|
|
||||||
config.params = trimObj(config.params);
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
], requestInterceptors);
|
|
||||||
|
|
||||||
const _responseInterceptors = [].concat([
|
|
||||||
[
|
|
||||||
function (response) {
|
|
||||||
if (isObject(response.data) && response.data.code !== '0') {
|
|
||||||
_errorHandler(_errorConfig[response.data.code] || response.data.msg || response.data.errorMessage || response.data.errorMsg || '服务异常', errorHandler);
|
|
||||||
return Promise.reject(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}, function (error) {
|
|
||||||
if (error.response) {
|
|
||||||
// The request was made and the server responded with a status code
|
|
||||||
// that falls out of the range of 2xx
|
|
||||||
if (_errorConfig[error.response.status]) {
|
|
||||||
_errorHandler(_errorConfig[error.response.status], errorHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
]
|
|
||||||
], responseInterceptors);
|
|
||||||
if (responseDataAdaptor && isFunction(responseDataAdaptor)) {
|
|
||||||
_responseInterceptors.unshift((response) => {
|
|
||||||
// 响应内容可能是个文件流 or 普通文本
|
|
||||||
if (isObject(response.data)) {
|
|
||||||
response.data = responseDataAdaptor(response.data);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 只把响应数据暴露出去
|
|
||||||
_responseInterceptors.push((response) => {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const dataField = REPLACE_DATA_FIELD;
|
|
||||||
if (isObject(response.data) && dataField) {
|
|
||||||
return response.data[dataField];
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
|
|
||||||
const instance = axios.create(Object.assign({
|
const instance = axios.create(Object.assign({
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
withCredentials: true
|
withCredentials: true
|
||||||
}, otherConfigs));
|
}, otherConfigs));
|
||||||
|
|
||||||
addRequestInterceptors(instance, _requestInterceptors);
|
// eslint-disable-next-line
|
||||||
addResponseInterceptors(instance, _responseInterceptors);
|
const dataField = REPLACE_DATA_FIELD;
|
||||||
|
addRequestInterceptors(requestInterceptors);
|
||||||
|
addResponseInterceptors(responseInterceptors);
|
||||||
|
|
||||||
|
|
||||||
|
paramsProcess(instance);
|
||||||
|
resDataAdaptor(instance, { responseDataAdaptor });
|
||||||
|
resErrorProcess(instance, { errorConfig, errorHandler });
|
||||||
|
setDataField(instance, dataField);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
instance
|
instance
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 待实现能力
|
|
||||||
// formData 控制
|
|
||||||
// 轮询
|
|
||||||
// 并行请求 >> 通过定义 key 区分
|
|
||||||
// 防抖 & 节流
|
|
||||||
// 缓存 & SWR & 预加载
|
|
||||||
// loadingDelay
|
|
||||||
|
|
||||||
let currentRequestInstance = null;
|
let currentRequestInstance = null;
|
||||||
export const request = (url, data, options = {}) => {
|
export const request = (url, data, options = {}) => {
|
||||||
if (!currentRequestInstance) {
|
if (!currentRequestInstance) {
|
||||||
@ -150,7 +70,7 @@ export const request = (url, data, options = {}) => {
|
|||||||
currentRequestInstance = instance;
|
currentRequestInstance = instance;
|
||||||
}
|
}
|
||||||
options.url = url;
|
options.url = url;
|
||||||
options.method = options.method || 'post';
|
options.method = (options.method || 'post').toUpperCase();
|
||||||
if (checkHttpRequestHasBody(options.method)) {
|
if (checkHttpRequestHasBody(options.method)) {
|
||||||
options.data = data;
|
options.data = data;
|
||||||
} else {
|
} else {
|
||||||
|
11
packages/fes-plugin-request/src/template/resDataAdaptor.js
Normal file
11
packages/fes-plugin-request/src/template/resDataAdaptor.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { isFunction, isObject, isString } from './helpers';
|
||||||
|
|
||||||
|
export default (instance, { responseDataAdaptor }) => {
|
||||||
|
instance.interceptors.response.use((response) => {
|
||||||
|
// 响应内容可能是个文件流 or 普通文本
|
||||||
|
if (isFunction(responseDataAdaptor) && (isObject(response.data) || isString(response.data))) {
|
||||||
|
response.data = responseDataAdaptor(response.data);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
};
|
37
packages/fes-plugin-request/src/template/resErrorProcess.js
Normal file
37
packages/fes-plugin-request/src/template/resErrorProcess.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { isObject, isFunction } from 'helpers';
|
||||||
|
|
||||||
|
function resErrorProcess(error, customerErrorHandler) {
|
||||||
|
if (isFunction(error)) {
|
||||||
|
error();
|
||||||
|
} else {
|
||||||
|
customerErrorHandler && customerErrorHandler(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (instance, { errorConfig, errorHandler }) => {
|
||||||
|
const _errorConfig = Object.assign({
|
||||||
|
401: {
|
||||||
|
showType: 9,
|
||||||
|
errorPage: '/login'
|
||||||
|
},
|
||||||
|
403: '用户得到授权,但访问是禁止的'
|
||||||
|
}, errorConfig);
|
||||||
|
|
||||||
|
instance.interceptors.response.use((response) => {
|
||||||
|
if (isObject(response.data) && response.data.code !== '0') {
|
||||||
|
resErrorProcess(_errorConfig[response.data.code] || response.data.msg || response.data.errorMessage || response.data.errorMsg || '服务异常', errorHandler);
|
||||||
|
return Promise.reject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}, (error) => {
|
||||||
|
if (error.response) {
|
||||||
|
// The request was made and the server responded with a status code
|
||||||
|
// that falls out of the range of 2xx
|
||||||
|
if (_errorConfig[error.response.status]) {
|
||||||
|
resErrorProcess(_errorConfig[error.response.status], errorHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
};
|
10
packages/fes-plugin-request/src/template/setDataField.js
Normal file
10
packages/fes-plugin-request/src/template/setDataField.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { isObject } from './helpers';
|
||||||
|
|
||||||
|
export default (instance, { dataField }) => {
|
||||||
|
instance.interceptors.response.use((response) => {
|
||||||
|
if (isObject(response.data) && dataField) {
|
||||||
|
return response.data[dataField];
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
};
|
@ -1,5 +1 @@
|
|||||||
# fes vue3 模版
|
# fes vue3 模版
|
||||||
|
|
||||||
## TODO
|
|
||||||
|
|
||||||
* 屏幕适配
|
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
import { requestWrap } from '@webank/fes';
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
// 响应体控制
|
|
||||||
// formData 控制
|
|
||||||
// 错误控制
|
|
||||||
// 跳错误页面 || 或者重新登录
|
|
||||||
// 段时间内不能重复发送的请求
|
|
||||||
|
|
||||||
// or
|
|
||||||
export default requestWrap({
|
|
||||||
login: {
|
|
||||||
url: '',
|
|
||||||
throttle: 300,
|
|
||||||
options: {
|
|
||||||
method: 'get'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
66
packages/fes-template-h5/src/common/utils.js
Normal file
66
packages/fes-template-h5/src/common/utils.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// TODO
|
||||||
|
// 时间格式化
|
||||||
|
// js 数字精度计算
|
||||||
|
// 手机号、身份证号 等的校验
|
||||||
|
// 数字分割
|
||||||
|
|
||||||
|
|
||||||
|
export function resetContainerHeight(dom) {
|
||||||
|
const originalHeight = document.body.clientHeight || document.documentElement.clientHeight;
|
||||||
|
|
||||||
|
window.onresize = function () {
|
||||||
|
const resizeHeight = document.documentElement.clientHeight || document.body.clientHeight;
|
||||||
|
if (resizeHeight < originalHeight) {
|
||||||
|
// 恢复内容区域高度
|
||||||
|
const container = document.querySelector(dom);
|
||||||
|
container.style.height = originalHeight;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function resetInputBlur() {
|
||||||
|
const isWechat = window.navigator.userAgent.match(/MicroMessenger\/([\d.]+)/i);
|
||||||
|
if (!isWechat) return;
|
||||||
|
const wechatVersion = isWechat[1];
|
||||||
|
const version = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
|
||||||
|
|
||||||
|
// 如果设备类型为iOS 12+ 和wechat 6.7.4+,恢复成原来的视口
|
||||||
|
if (+wechatVersion.replace(/\./g, '') >= 674 && +version[1] >= 12) {
|
||||||
|
window.scrollTo(0, Math.max(document.body.clientHeight, document.documentElement.clientHeight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQueryString(name) {
|
||||||
|
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i');
|
||||||
|
const r = window.location.search.substr(1).match(reg);
|
||||||
|
if (r != null) {
|
||||||
|
return decodeURIComponent(r[2]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function simpleRequest(options) {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.timeout = 3000;
|
||||||
|
if (options.type === 'GET') {
|
||||||
|
xhr.open(options.type, options.url, options.async || true);
|
||||||
|
xhr.send(null);
|
||||||
|
} else if (options.type === 'POST') {
|
||||||
|
xhr.open(options.type, options.url, options.async || true);
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
|
xhr.send(JSON.stringify(options.data || {}));
|
||||||
|
}
|
||||||
|
xhr.onreadystatechange = function () {
|
||||||
|
if (xhr.readyState === 4) {
|
||||||
|
if (xhr.status >= 200 && xhr.status < 300) {
|
||||||
|
options.successed(xhr.responseText);
|
||||||
|
} else {
|
||||||
|
options.failed && options.failed(xhr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.ontimeout = function () {
|
||||||
|
options.failed && options.failed(xhr);
|
||||||
|
};
|
||||||
|
}
|
@ -57,6 +57,6 @@ div {
|
|||||||
}
|
}
|
||||||
.onepiece {
|
.onepiece {
|
||||||
.hairline("top");
|
.hairline("top");
|
||||||
// background: url('../images/male.png');
|
background: url('../images/male.png');
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -3,6 +3,9 @@ img {
|
|||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
touch-action: manipulation; // 处理 IOS10+,click点击 300ms 问题
|
||||||
|
}
|
||||||
body {
|
body {
|
||||||
background-color: #f7f7f7;
|
background-color: #f7f7f7;
|
||||||
}
|
}
|
||||||
@ -20,3 +23,23 @@ a {
|
|||||||
* {
|
* {
|
||||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 适配 iPhone X 顶部填充*/
|
||||||
|
@supports (top: env(safe-area-inset-top)){
|
||||||
|
body,
|
||||||
|
.alien-screen-header {
|
||||||
|
padding-top: constant(safe-area-inset-top, 40px);
|
||||||
|
padding-top: env(safe-area-inset-top, 40px);
|
||||||
|
padding-top: var(safe-area-inset-top, 40px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 判断iPhoneX 将 footer 的 padding-bottom 填充到最底部 */
|
||||||
|
@supports (bottom: env(safe-area-inset-bottom)){
|
||||||
|
body,
|
||||||
|
.alien-screen-footer {
|
||||||
|
padding-bottom: constant(safe-area-inset-bottom, 20px);
|
||||||
|
padding-bottom: env(safe-area-inset-bottom, 20px);
|
||||||
|
padding-top: var(safe-area-inset-bottom, 20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
4
packages/fes-template-h5/src/styles/mixins/scroll.less
Normal file
4
packages/fes-template-h5/src/styles/mixins/scroll.less
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.scroll() {
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
export default {
|
export default {
|
||||||
base: '/foo/',
|
base: '/foo/',
|
||||||
define: {
|
define: {
|
||||||
FOO: 'bar'
|
__DEV__: false
|
||||||
},
|
},
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
access: {
|
access: {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { access } from '@webank/fes';
|
import { access } from '@webank/fes';
|
||||||
import PageLoading from '@/components/PageLoading.vue';
|
import PageLoading from '@/components/PageLoading';
|
||||||
import UserCenter from '@/components/UserCenter.vue';
|
import UserCenter from '@/components/UserCenter';
|
||||||
|
|
||||||
export const beforeRender = {
|
export const beforeRender = {
|
||||||
loading: <PageLoading />,
|
loading: <PageLoading />,
|
||||||
|
@ -3,3 +3,7 @@
|
|||||||
export function main() {
|
export function main() {
|
||||||
console.log('hello world');
|
console.log('hello world');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isHtmlElement(node) {
|
||||||
|
return node && node.nodeType === Node.ELEMENT_NODE;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user