z-paging-utils.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. // [z-paging]工具类
  2. import zLocalConfig from '../config/index'
  3. import c from './z-paging-constant'
  4. const storageKey = 'Z-PAGING-REFRESHER-TIME-STORAGE-KEY';
  5. let config = null;
  6. let configLoaded = false;
  7. let cachedSystemInfo = null;
  8. const timeoutMap = {};
  9. // 获取默认配置信息
  10. function gc(key, defaultValue) {
  11. // 这里return一个函数以解决在vue3+appvue中,props默认配置读取在main.js之前执行导致uni.$zp全局配置无效的问题。相当于props的default中传入一个带有返回值的函数
  12. return () => {
  13. // 处理z-paging全局配置
  14. _handleDefaultConfig();
  15. // 如果全局配置不存在,则返回默认值
  16. if (!config) return defaultValue;
  17. const value = config[key];
  18. // 如果全局配置存在但对应的配置项不存在,则返回默认值;反之返回配置项
  19. return value === undefined ? defaultValue : value;
  20. };
  21. }
  22. // 获取最终的touch位置
  23. function getTouch(e) {
  24. let touch = null;
  25. if (e.touches && e.touches.length) {
  26. touch = e.touches[0];
  27. } else if (e.changedTouches && e.changedTouches.length) {
  28. touch = e.changedTouches[0];
  29. } else if (e.datail && e.datail != {}) {
  30. touch = e.datail;
  31. } else {
  32. return { touchX: 0, touchY: 0 }
  33. }
  34. return {
  35. touchX: touch.clientX,
  36. touchY: touch.clientY
  37. };
  38. }
  39. // 判断当前手势是否在z-paging内触发
  40. function getTouchFromZPaging(target) {
  41. if (target && target.tagName && target.tagName !== 'BODY' && target.tagName !== 'UNI-PAGE-BODY') {
  42. const classList = target.classList;
  43. if (classList && classList.contains('z-paging-content')) {
  44. // 此处额外记录当前z-paging是否是页面滚动、是否滚动到了顶部、是否是聊天记录模式以传给renderjs。避免不同z-paging组件renderjs内部判断数据互相影响导致的各种问题
  45. return {
  46. isFromZp: true,
  47. isPageScroll: classList.contains('z-paging-content-page'),
  48. isReachedTop: classList.contains('z-paging-reached-top'),
  49. isUseChatRecordMode: classList.contains('z-paging-use-chat-record-mode')
  50. };
  51. } else {
  52. return getTouchFromZPaging(target.parentNode);
  53. }
  54. } else {
  55. return { isFromZp: false };
  56. }
  57. }
  58. // 递归获取z-paging所在的parent,如果查找不到则返回null
  59. function getParent(parent) {
  60. if (!parent) return null;
  61. if (parent.$refs.paging) return parent;
  62. return getParent(parent.$parent);
  63. }
  64. // 打印错误信息
  65. function consoleErr(err) {
  66. console.error(`[z-paging]${err}`);
  67. }
  68. // 延时操作,如果key存在,调用时清除对应key之前的延时操作
  69. function delay(callback, ms = c.delayTime, key) {
  70. const timeout = setTimeout(callback, ms);;
  71. if (!!key) {
  72. timeoutMap[key] && clearTimeout(timeoutMap[key]);
  73. timeoutMap[key] = timeout;
  74. }
  75. return timeout;
  76. }
  77. // 设置下拉刷新时间
  78. function setRefesrherTime(time, key) {
  79. const datas = getRefesrherTime() || {};
  80. datas[key] = time;
  81. uni.setStorageSync(storageKey, datas);
  82. }
  83. // 获取下拉刷新时间
  84. function getRefesrherTime() {
  85. return uni.getStorageSync(storageKey);
  86. }
  87. // 通过下拉刷新标识key获取下拉刷新时间
  88. function getRefesrherTimeByKey(key) {
  89. const datas = getRefesrherTime();
  90. return datas && datas[key] ? datas[key] : null;
  91. }
  92. // 通过下拉刷新标识key获取下拉刷新时间(格式化之后)
  93. function getRefesrherFormatTimeByKey(key, textMap) {
  94. const time = getRefesrherTimeByKey(key);
  95. const timeText = time ? _timeFormat(time, textMap) : textMap.none;
  96. return `${textMap.title}${timeText}`;
  97. }
  98. // 将文本的px或者rpx转为px的值
  99. function convertToPx(text) {
  100. const dataType = Object.prototype.toString.call(text);
  101. if (dataType === '[object Number]') return text;
  102. let isRpx = false;
  103. if (text.indexOf('rpx') !== -1 || text.indexOf('upx') !== -1) {
  104. text = text.replace('rpx', '').replace('upx', '');
  105. isRpx = true;
  106. } else if (text.indexOf('px') !== -1) {
  107. text = text.replace('px', '');
  108. }
  109. if (!isNaN(text)) {
  110. if (isRpx) return Number(rpx2px(text));
  111. return Number(text);
  112. }
  113. return 0;
  114. }
  115. // rpx => px,预留的兼容处理
  116. function rpx2px(rpx) {
  117. return uni.upx2px(rpx);
  118. }
  119. // 同步获取系统信息,兼容不同平台
  120. function getSystemInfoSync(useCache = false) {
  121. if (useCache && cachedSystemInfo) {
  122. return cachedSystemInfo;
  123. }
  124. // 目前只用到了deviceInfo、appBaseInfo和windowInfo中的信息,因此仅整合这两个信息数据
  125. const infoTypes = ['DeviceInfo', 'AppBaseInfo', 'WindowInfo'];
  126. const { deviceInfo, appBaseInfo, windowInfo } = infoTypes.reduce((acc, key) => {
  127. const method = `get${key}`;
  128. if (uni[method] && uni.canIUse(method)) {
  129. acc[key.charAt(0).toLowerCase() + key.slice(1)] = uni[method]();
  130. }
  131. return acc;
  132. }, {});
  133. // 如果deviceInfo、appBaseInfo和windowInfo都可以从各自专属的api中获取,则整合它们的数据
  134. if (deviceInfo && appBaseInfo && windowInfo) {
  135. cachedSystemInfo = { ...deviceInfo, ...appBaseInfo, ...windowInfo };
  136. } else {
  137. // 使用uni.getSystemInfoSync兜底,确保能获取到最终的系统信息
  138. cachedSystemInfo = uni.getSystemInfoSync();
  139. }
  140. return cachedSystemInfo;
  141. }
  142. // 获取当前时间
  143. function getTime() {
  144. return (new Date()).getTime();
  145. }
  146. // 获取z-paging实例id,随机生成10位数字+字母
  147. function getInstanceId() {
  148. const s = [];
  149. const hexDigits = "0123456789abcdef";
  150. for (let i = 0; i < 10; i++) {
  151. s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
  152. }
  153. return s.join('') + getTime();
  154. }
  155. // 等待一段时间
  156. function wait(ms) {
  157. return new Promise(resolve => {
  158. setTimeout(resolve, ms);
  159. });
  160. }
  161. // 是否是promise
  162. function isPromise(func) {
  163. return Object.prototype.toString.call(func) === '[object Promise]';
  164. }
  165. // 添加单位
  166. function addUnit(value, unit) {
  167. if (Object.prototype.toString.call(value) === '[object String]') {
  168. let tempValue = value;
  169. tempValue = tempValue.replace('rpx', '').replace('upx', '').replace('px', '');
  170. if (value.indexOf('rpx') === -1 && value.indexOf('upx') === -1 && value.indexOf('px') !== -1) {
  171. tempValue = parseFloat(tempValue) * 2;
  172. }
  173. value = tempValue;
  174. }
  175. return unit === 'rpx' ? value + 'rpx' : (value / 2) + 'px';
  176. }
  177. // 深拷贝
  178. function deepCopy(obj) {
  179. if (typeof obj !== 'object' || obj === null) return obj;
  180. let newObj = Array.isArray(obj) ? [] : {};
  181. for (let key in obj) {
  182. if (obj.hasOwnProperty(key)) {
  183. newObj[key] = deepCopy(obj[key]);
  184. }
  185. }
  186. return newObj;
  187. }
  188. // 对短时间内重复插入的数据进行整合,并一次性插入
  189. function useBufferedInsert(fn, delay = 50) {
  190. let buffer = [];
  191. let timer = null;
  192. let latestArgs = [];
  193. return function insertBuffered(data, ...args) {
  194. const newData = Object.prototype.toString.call(data) !== '[object Array]' ? [data] : data;
  195. buffer.push(...newData);
  196. latestArgs = args;
  197. if (!timer) {
  198. timer = setTimeout(() => {
  199. fn(buffer.length === 1 ? buffer[0] : buffer, ...latestArgs);
  200. buffer = [];
  201. timer = null;
  202. }, buffer.length === 1 ? 10 : delay);
  203. }
  204. };
  205. }
  206. // ------------------ 私有方法 ------------------------
  207. // 处理全局配置
  208. function _handleDefaultConfig() {
  209. // 确保只加载一次全局配置
  210. if (configLoaded) return;
  211. // 优先从config.js中读取
  212. if (zLocalConfig && Object.keys(zLocalConfig).length) {
  213. config = zLocalConfig;
  214. }
  215. // 如果在config.js中读取不到,则尝试到uni.$zp读取
  216. if (!config && uni.$zp) {
  217. config = uni.$zp.config;
  218. }
  219. // 将config中的短横线写法全部转为驼峰写法,使得读取配置时可以直接通过key去匹配,而非读取每个配置时候再去转,减少不必要的性能开支
  220. config = config ? Object.keys(config).reduce((result, key) => {
  221. result[_toCamelCase(key)] = config[key];
  222. return result;
  223. }, {}) : null;
  224. configLoaded = true;
  225. }
  226. // 时间格式化
  227. function _timeFormat(time, textMap) {
  228. const date = new Date(time);
  229. const currentDate = new Date();
  230. // 设置time对应的天,去除时分秒,使得可以直接比较日期
  231. const dateDay = new Date(time).setHours(0, 0, 0, 0);
  232. // 设置当前的天,去除时分秒,使得可以直接比较日期
  233. const currentDateDay = new Date().setHours(0, 0, 0, 0);
  234. const disTime = dateDay - currentDateDay;
  235. let dayStr = '';
  236. const timeStr = _dateTimeFormat(date);
  237. if (disTime === 0) {
  238. dayStr = textMap.today;
  239. } else if (disTime === -86400000) {
  240. dayStr = textMap.yesterday;
  241. } else {
  242. dayStr = _dateDayFormat(date, date.getFullYear() !== currentDate.getFullYear());
  243. }
  244. return `${dayStr} ${timeStr}`;
  245. }
  246. // date格式化为年月日
  247. function _dateDayFormat(date, showYear = true) {
  248. const year = date.getFullYear();
  249. const month = date.getMonth() + 1;
  250. const day = date.getDate();
  251. return showYear ? `${year}-${_fullZeroToTwo(month)}-${_fullZeroToTwo(day)}` : `${_fullZeroToTwo(month)}-${_fullZeroToTwo(day)}`;
  252. }
  253. // data格式化为时分
  254. function _dateTimeFormat(date) {
  255. const hour = date.getHours();
  256. const minute = date.getMinutes();
  257. return `${_fullZeroToTwo(hour)}:${_fullZeroToTwo(minute)}`;
  258. }
  259. // 不满2位在前面填充0
  260. function _fullZeroToTwo(str) {
  261. str = str.toString();
  262. return str.length === 1 ? '0' + str : str;
  263. }
  264. // 驼峰转短横线
  265. function _toKebab(value) {
  266. return value.replace(/([A-Z])/g, "-$1").toLowerCase();
  267. }
  268. // 短横线转驼峰
  269. function _toCamelCase(value) {
  270. return value.replace(/-([a-z])/g, (_, group1) => group1.toUpperCase());
  271. }
  272. export default {
  273. gc,
  274. setRefesrherTime,
  275. getRefesrherFormatTimeByKey,
  276. getTouch,
  277. getTouchFromZPaging,
  278. getParent,
  279. convertToPx,
  280. getTime,
  281. getInstanceId,
  282. consoleErr,
  283. delay,
  284. wait,
  285. isPromise,
  286. addUnit,
  287. deepCopy,
  288. rpx2px,
  289. getSystemInfoSync,
  290. useBufferedInsert
  291. };