From 552bc4e9ad875600a41b9328b9b6ddd2fa6635a1 Mon Sep 17 00:00:00 2001 From: bac-joker Date: Sun, 27 Dec 2020 19:57:02 +0800 Subject: [PATCH] feat: pages > page and h5 scroll --- .eslintrc.js | 1 + .fatherrc.js | 2 +- packages/fes-plugin-request/README.md | 12 ++ packages/fes-plugin-request/package.json | 5 +- .../src/template/helpers.js | 23 +++- .../src/template/paramsProcess.js | 12 ++ .../src/template/request.js | 114 +++--------------- .../src/template/resDataAdaptor.js | 11 ++ .../src/template/resErrorProcess.js | 37 ++++++ .../src/template/setDataField.js | 10 ++ packages/fes-template-h5/README.md | 4 - .../fes-template-h5/src/common/service.js | 19 --- packages/fes-template-h5/src/common/utils.js | 66 ++++++++++ .../src/{pages => page}/index.vue | 2 +- .../src/{pages => page}/onepiece.vue | 0 .../fes-template-h5/src/styles/common.less | 23 ++++ .../src/styles/mixins/scroll.less | 4 + packages/fes-template/.fes.js | 2 +- packages/fes-template/src/app.js | 4 +- .../src/{pages => page}/index.vue | 0 .../src/{pages => page}/onepiece.vue | 0 packages/fes-utils/index.js | 4 + 22 files changed, 222 insertions(+), 133 deletions(-) create mode 100644 packages/fes-plugin-request/README.md create mode 100644 packages/fes-plugin-request/src/template/paramsProcess.js create mode 100644 packages/fes-plugin-request/src/template/resDataAdaptor.js create mode 100644 packages/fes-plugin-request/src/template/resErrorProcess.js create mode 100644 packages/fes-plugin-request/src/template/setDataField.js create mode 100644 packages/fes-template-h5/src/common/utils.js rename packages/fes-template-h5/src/{pages => page}/index.vue (96%) rename packages/fes-template-h5/src/{pages => page}/onepiece.vue (100%) create mode 100644 packages/fes-template-h5/src/styles/mixins/scroll.less rename packages/fes-template/src/{pages => page}/index.vue (100%) rename packages/fes-template/src/{pages => page}/onepiece.vue (100%) diff --git a/.eslintrc.js b/.eslintrc.js index 7d69d0f0..74c6bf29 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -8,6 +8,7 @@ module.exports = { // 这里值为 false 表示这个全局变量不允许被重新赋值,比如: // // Vue: false + __DEV__: false }, rules: { 'vue/comment-directive': 'off', diff --git a/.fatherrc.js b/.fatherrc.js index fcb12e40..56414d04 100644 --- a/.fatherrc.js +++ b/.fatherrc.js @@ -13,7 +13,7 @@ const headPkgs = [ "fes-plugin-access", "fes-plugin-model", "fes-plugin-layout", - "fes-plugin-icon", + "fes-plugin-icon" ]; const tailPkgs = []; // const otherPkgs = readdirSync(join(__dirname, 'packages')).filter( diff --git a/packages/fes-plugin-request/README.md b/packages/fes-plugin-request/README.md new file mode 100644 index 00000000..5d547918 --- /dev/null +++ b/packages/fes-plugin-request/README.md @@ -0,0 +1,12 @@ +# request 封装 + +为了尽可能减少包的,提供可定制化能力 + +## TODO 待实现能力 + +* formData 控制 +* 轮询 +* 并行请求 >> 通过定义 key 区分 +* 防抖 & 节流 +* 缓存 & SWR & 预加载 +* loadingDelay diff --git a/packages/fes-plugin-request/package.json b/packages/fes-plugin-request/package.json index aec079d0..7cd33fef 100644 --- a/packages/fes-plugin-request/package.json +++ b/packages/fes-plugin-request/package.json @@ -14,10 +14,7 @@ "author": "", "license": "MIT", "peerDependencies": { + "axios": "0.21.1", "@webank/fes": "^2.0.0" - }, - "dependencies": { - "axios": "^0.20.0", - "throttle-debounce": "^2.3.0" } } diff --git a/packages/fes-plugin-request/src/template/helpers.js b/packages/fes-plugin-request/src/template/helpers.js index e28d7de7..8b4944ee 100644 --- a/packages/fes-plugin-request/src/template/helpers.js +++ b/packages/fes-plugin-request/src/template/helpers.js @@ -13,7 +13,8 @@ export function typeOf(obj) { '[object RegExp]': 'regExp', '[object Undefined]': 'undefined', '[object Null]': 'null', - '[object Object]': 'object' + '[object Object]': 'object', + '[object URLSearchParams]': 'URLSearchParams' }; return map[Object.prototype.toString.call(obj)]; } @@ -38,13 +39,14 @@ export function isObject(obj) { return typeOf(obj) === 'object'; } -export function isHtmlElement(node) { - return node && node.nodeType === Node.ELEMENT_NODE; +export function isURLSearchParams(obj) { + return typeOf(obj) === 'URLSearchParams'; } +// eslint-disable-next-line export const isUndefined = val => val === undefined; -export const isDefined = val => val !== undefined && val !== null; +export const isDefined = val => val != null; 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}`; +} diff --git a/packages/fes-plugin-request/src/template/paramsProcess.js b/packages/fes-plugin-request/src/template/paramsProcess.js new file mode 100644 index 00000000..47ce5a85 --- /dev/null +++ b/packages/fes-plugin-request/src/template/paramsProcess.js @@ -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; + }); +}; diff --git a/packages/fes-plugin-request/src/template/request.js b/packages/fes-plugin-request/src/template/request.js index 135f4a94..7ab218cf 100644 --- a/packages/fes-plugin-request/src/template/request.js +++ b/packages/fes-plugin-request/src/template/request.js @@ -1,31 +1,14 @@ import axios from 'axios'; - -// import { debounce } from 'throttle-debounce'; -import { ApplyPluginsType, plugin, router } from '@webank/fes'; +import { ApplyPluginsType, plugin } from '@webank/fes'; import { checkHttpRequestHasBody, - trimObj, - isFunction, - isObject + isFunction } from './helpers'; -/** - * 统一错误处理 - * @param {object | string | function} errorStruct - * { - * errorMessage: '', // 错误地址 - * errorPage: '', // 错误页面地址 - * } - */ -function _errorHandler(error, customerErrorHandler) { - if (isFunction(error)) { - error(); - } else if (error.errorPage) { - router.replace(error.errorPage); - } else { - customerErrorHandler && customerErrorHandler(error); - } -} +import setDataField from './setDataField'; +import paramsProcess from './paramsProcess'; +import resDataAdaptor from './resDataAdaptor'; +import resErrorProcess from './resErrorProcess'; function addInterceptors(instance, interceptors, type = 'request') { interceptors.forEach((fn) => { @@ -59,90 +42,27 @@ function getRequestInstance() { 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({ timeout: 10000, withCredentials: true }, otherConfigs)); - addRequestInterceptors(instance, _requestInterceptors); - addResponseInterceptors(instance, _responseInterceptors); + // eslint-disable-next-line + const dataField = REPLACE_DATA_FIELD; + addRequestInterceptors(requestInterceptors); + addResponseInterceptors(responseInterceptors); + + + paramsProcess(instance); + resDataAdaptor(instance, { responseDataAdaptor }); + resErrorProcess(instance, { errorConfig, errorHandler }); + setDataField(instance, dataField); return { instance }; } -// TODO 待实现能力 -// formData 控制 -// 轮询 -// 并行请求 >> 通过定义 key 区分 -// 防抖 & 节流 -// 缓存 & SWR & 预加载 -// loadingDelay - let currentRequestInstance = null; export const request = (url, data, options = {}) => { if (!currentRequestInstance) { @@ -150,7 +70,7 @@ export const request = (url, data, options = {}) => { currentRequestInstance = instance; } options.url = url; - options.method = options.method || 'post'; + options.method = (options.method || 'post').toUpperCase(); if (checkHttpRequestHasBody(options.method)) { options.data = data; } else { diff --git a/packages/fes-plugin-request/src/template/resDataAdaptor.js b/packages/fes-plugin-request/src/template/resDataAdaptor.js new file mode 100644 index 00000000..896a85a4 --- /dev/null +++ b/packages/fes-plugin-request/src/template/resDataAdaptor.js @@ -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; + }); +}; diff --git a/packages/fes-plugin-request/src/template/resErrorProcess.js b/packages/fes-plugin-request/src/template/resErrorProcess.js new file mode 100644 index 00000000..53de4c8b --- /dev/null +++ b/packages/fes-plugin-request/src/template/resErrorProcess.js @@ -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); + }); +}; diff --git a/packages/fes-plugin-request/src/template/setDataField.js b/packages/fes-plugin-request/src/template/setDataField.js new file mode 100644 index 00000000..ec06af0a --- /dev/null +++ b/packages/fes-plugin-request/src/template/setDataField.js @@ -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; + }); +}; diff --git a/packages/fes-template-h5/README.md b/packages/fes-template-h5/README.md index 4de8a6ec..6f2e62a7 100644 --- a/packages/fes-template-h5/README.md +++ b/packages/fes-template-h5/README.md @@ -1,5 +1 @@ # fes vue3 模版 - -## TODO - -* 屏幕适配 diff --git a/packages/fes-template-h5/src/common/service.js b/packages/fes-template-h5/src/common/service.js index 06447dc2..e69de29b 100644 --- a/packages/fes-template-h5/src/common/service.js +++ b/packages/fes-template-h5/src/common/service.js @@ -1,19 +0,0 @@ -import { requestWrap } from '@webank/fes'; - -// TODO -// 响应体控制 -// formData 控制 -// 错误控制 -// 跳错误页面 || 或者重新登录 -// 段时间内不能重复发送的请求 - -// or -export default requestWrap({ - login: { - url: '', - throttle: 300, - options: { - method: 'get' - } - } -}); diff --git a/packages/fes-template-h5/src/common/utils.js b/packages/fes-template-h5/src/common/utils.js new file mode 100644 index 00000000..97d6e99c --- /dev/null +++ b/packages/fes-template-h5/src/common/utils.js @@ -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); + }; +} diff --git a/packages/fes-template-h5/src/pages/index.vue b/packages/fes-template-h5/src/page/index.vue similarity index 96% rename from packages/fes-template-h5/src/pages/index.vue rename to packages/fes-template-h5/src/page/index.vue index 60c4add9..3e5f081e 100644 --- a/packages/fes-template-h5/src/pages/index.vue +++ b/packages/fes-template-h5/src/page/index.vue @@ -57,6 +57,6 @@ div { } .onepiece { .hairline("top"); - // background: url('../images/male.png'); + background: url('../images/male.png'); } diff --git a/packages/fes-template-h5/src/pages/onepiece.vue b/packages/fes-template-h5/src/page/onepiece.vue similarity index 100% rename from packages/fes-template-h5/src/pages/onepiece.vue rename to packages/fes-template-h5/src/page/onepiece.vue diff --git a/packages/fes-template-h5/src/styles/common.less b/packages/fes-template-h5/src/styles/common.less index fe0e8573..8d2bd816 100644 --- a/packages/fes-template-h5/src/styles/common.less +++ b/packages/fes-template-h5/src/styles/common.less @@ -3,6 +3,9 @@ img { -webkit-touch-callout: none; } +html { + touch-action: manipulation; // 处理 IOS10+,click点击 300ms 问题 +} body { background-color: #f7f7f7; } @@ -20,3 +23,23 @@ a { * { -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); + } +} diff --git a/packages/fes-template-h5/src/styles/mixins/scroll.less b/packages/fes-template-h5/src/styles/mixins/scroll.less new file mode 100644 index 00000000..7fa92c44 --- /dev/null +++ b/packages/fes-template-h5/src/styles/mixins/scroll.less @@ -0,0 +1,4 @@ +.scroll() { + -webkit-overflow-scrolling: touch; + overflow-y: auto; +} \ No newline at end of file diff --git a/packages/fes-template/.fes.js b/packages/fes-template/.fes.js index f92b43db..73b2812f 100644 --- a/packages/fes-template/.fes.js +++ b/packages/fes-template/.fes.js @@ -4,7 +4,7 @@ export default { base: '/foo/', define: { - FOO: 'bar' + __DEV__: false }, publicPath: '/', access: { diff --git a/packages/fes-template/src/app.js b/packages/fes-template/src/app.js index e837a94a..d48e6033 100644 --- a/packages/fes-template/src/app.js +++ b/packages/fes-template/src/app.js @@ -1,6 +1,6 @@ import { access } from '@webank/fes'; -import PageLoading from '@/components/PageLoading.vue'; -import UserCenter from '@/components/UserCenter.vue'; +import PageLoading from '@/components/PageLoading'; +import UserCenter from '@/components/UserCenter'; export const beforeRender = { loading: , diff --git a/packages/fes-template/src/pages/index.vue b/packages/fes-template/src/page/index.vue similarity index 100% rename from packages/fes-template/src/pages/index.vue rename to packages/fes-template/src/page/index.vue diff --git a/packages/fes-template/src/pages/onepiece.vue b/packages/fes-template/src/page/onepiece.vue similarity index 100% rename from packages/fes-template/src/pages/onepiece.vue rename to packages/fes-template/src/page/onepiece.vue diff --git a/packages/fes-utils/index.js b/packages/fes-utils/index.js index 6728bacc..13d22cb4 100644 --- a/packages/fes-utils/index.js +++ b/packages/fes-utils/index.js @@ -3,3 +3,7 @@ export function main() { console.log('hello world'); } + +export function isHtmlElement(node) { + return node && node.nodeType === Node.ELEMENT_NODE; +}