util.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /**
  2. * 以下为 uni-stat 的工具方法
  3. */
  4. // 千分位
  5. function regexHandleNum(num) {
  6. return String(num).replace(/\B(?=(\d{3})+(?!\d))/g, ','); // 3是千分位,4是万分位
  7. }
  8. // 新版格式化字段数据函数
  9. function formatterData(object) {
  10. let {
  11. fieldsMap,
  12. data,
  13. formatter = true
  14. } = object;
  15. let rows = JSON.parse(JSON.stringify(data));
  16. rows.map((row) => {
  17. for (let key in row) {
  18. let fieldItem = fieldsMap.find((item) => {
  19. return item.field == key;
  20. });
  21. if (typeof fieldItem === "object") {
  22. let {
  23. fix = 0,
  24. } = fieldItem;
  25. if (typeof fieldItem.multiple === "number" && typeof row[key] === "number") {
  26. row[key] = Number((row[key] * fieldItem.multiple).toFixed(fix));
  27. }
  28. if (formatter && fieldItem.formatter && typeof row[key] === "number") {
  29. if (fieldItem.formatter === ",") {
  30. row[key] = regexHandleNum(row[key]);
  31. } else if (fieldItem.formatter === "%") {
  32. row[key] = `${(row[key] * 100).toFixed(fix)}%`
  33. } else if (fieldItem.formatter === "-") {
  34. // 时分秒格式
  35. row[key] = parseDateTime(row[key]);
  36. }
  37. }
  38. }
  39. }
  40. });
  41. return rows;
  42. }
  43. // 补全趋势图的数据
  44. function fillTrendChartData(data, query, fieldsMap) {
  45. let { start_time, dimension } = query;
  46. if (["hour","day"].indexOf(dimension)>-1){
  47. let timeArr = [];
  48. let oneTime;
  49. if (dimension === "hour"){
  50. oneTime = 1000*3600;
  51. } else if (dimension === "day"){
  52. oneTime = 1000*3600*24;
  53. }
  54. let start = start_time[0];
  55. let end = start_time[1];
  56. let nowTime = start;
  57. timeArr = [start];
  58. while ((nowTime+oneTime)<=end){
  59. nowTime += oneTime;
  60. timeArr.push(nowTime);
  61. }
  62. let newData = [];
  63. for (let i = 0; i < timeArr.length; i++) {
  64. let time = timeArr[i];
  65. let dataItem = data.find((item, index) => {
  66. return item.start_time === time;
  67. });
  68. if (dataItem) {
  69. newData.push(dataItem);
  70. } else {
  71. let obj = {
  72. start_time: time
  73. };
  74. fieldsMap.map((item, index) => {
  75. obj[item.field] = 0;
  76. });
  77. newData.push(obj);
  78. }
  79. }
  80. return newData
  81. } else {
  82. return data;
  83. }
  84. }
  85. // 将查询条件拼接为字符串
  86. function stringifyQuery(query, dimension = false, delArrs = []) {
  87. const queryArr = []
  88. const keys = Object.keys(query)
  89. const time = query.start_time
  90. keys.forEach(key => {
  91. if (key === 'time_range' || delArrs.indexOf(key) !== -1) return
  92. let val = query[key]
  93. if (val) {
  94. if (typeof val === 'string' && val.indexOf(key) > -1) {
  95. queryArr.push(val)
  96. } else {
  97. if (typeof val === 'string') {
  98. val = `"${val}"`
  99. }
  100. if (Array.isArray(val)) {
  101. if (val.length === 2 && key.indexOf('time') > -1) {
  102. queryArr.push(`${key} >= ${val[0]} && ${key} <= ${val[1]}`)
  103. } else {
  104. val = val.map(item => `${key} == "${item}"`).join(' || ')
  105. val && queryArr.push(`(${val})`)
  106. }
  107. } else if (dimension && key === 'dimension') {
  108. if (maxDeltaDay(time)) {
  109. queryArr.push(`dimension == "hour"`)
  110. } else {
  111. // if (val && val !== `"hour"`) {
  112. // queryArr.push(`${key} == ${val}`)
  113. // } else {
  114. // queryArr.push(`dimension == "day"`)
  115. // }
  116. // 放开按小时查询的时间限制
  117. queryArr.push(`${key} == ${val}`)
  118. }
  119. } else {
  120. queryArr.push(`${key} == ${val}`)
  121. }
  122. }
  123. }
  124. })
  125. const queryStr = queryArr.join(' && ')
  126. return queryStr || {}
  127. }
  128. // 根据页面字段配置 fieldsMap 数据计算、格式化字段
  129. function mapfields(map, data = {}, goal, prefix = '', prop = 'value') {
  130. const goals = [],
  131. argsGoal = goal
  132. map = JSON.parse(JSON.stringify(map))
  133. const origin = JSON.parse(JSON.stringify(data))
  134. for (const mapper of map) {
  135. let {
  136. field,
  137. computed,
  138. formatter,
  139. disable,
  140. fix
  141. } = mapper
  142. if (!disable) {
  143. goal = argsGoal || mapper
  144. const hasValue = goal.hasOwnProperty(prop)
  145. const preField = prefix + field
  146. if (data) {
  147. const value = data[preField]
  148. if (computed) {
  149. const computedFields = computed.split('/')
  150. let [dividend, divisor] = computedFields
  151. dividend = Number(origin[prefix + dividend])
  152. divisor = Number(origin[prefix + divisor])
  153. const val = format(division(dividend, divisor), formatter, fix)
  154. if (hasValue && field === goal.field) {
  155. goal[prop] = val
  156. } else {
  157. goal[field] = val
  158. }
  159. } else {
  160. if (value) {
  161. const val = format(value, formatter, fix)
  162. if (hasValue) {
  163. if (goal.field === field) {
  164. goal[prop] = val
  165. }
  166. } else {
  167. goal[field] = val
  168. }
  169. }
  170. }
  171. }
  172. if (hasValue) {
  173. goals.push(goal)
  174. }
  175. }
  176. }
  177. return goals
  178. }
  179. // 将查询条件对象拼接为字符串,给 client db 的 field 属性消费
  180. function stringifyField(mapping, goal, prop) {
  181. if (goal) {
  182. mapping = mapping.filter(f => f.field === goal)
  183. }
  184. if (prop) {
  185. mapping = mapping.filter(f => f.field && f.hasOwnProperty(prop))
  186. }
  187. const fieldString = mapping.map(f => {
  188. let fields = []
  189. if (f.computed) {
  190. fields = f.computed.split('/')
  191. } else {
  192. fields.push(f.field)
  193. }
  194. fields = fields.map(field => {
  195. if (f.stat === -1) {
  196. return field
  197. } else {
  198. return `${field} as ${ 'temp_' + field}`
  199. }
  200. })
  201. return fields.join()
  202. })
  203. return fieldString.join()
  204. }
  205. // 将查询条件对象拼接为字符串,给 client db 的 groupField 属性消费
  206. function stringifyGroupField(mapping, goal, prop) {
  207. if (goal) {
  208. mapping = mapping.filter(f => f.field === goal)
  209. }
  210. if (prop) {
  211. mapping = mapping.filter(f => f.field && f.hasOwnProperty(prop))
  212. }
  213. const groupField = mapping.map(f => {
  214. const stat = f.stat
  215. let fields = []
  216. if (f.computed) {
  217. fields = f.computed.split('/')
  218. } else {
  219. fields.push(f.field)
  220. }
  221. fields = fields.map(field => {
  222. if (stat !== -1) {
  223. return `${stat ? stat : 'sum' }(${'temp_' + field}) as ${field}`
  224. }
  225. })
  226. return fields.filter(Boolean).join()
  227. })
  228. .filter(Boolean)
  229. .join()
  230. return groupField
  231. }
  232. // 除法函数
  233. function division(dividend, divisor) {
  234. if (divisor) {
  235. return dividend / divisor
  236. } else {
  237. return 0
  238. }
  239. }
  240. // 对数字进行格式化,格式 type 配置在页面 fieldMap.js 中
  241. function format(num, type = ',', fix) {
  242. // if (!type) return num
  243. if (typeof num !== 'number') return num
  244. if (type === '%') {
  245. // 注意浮点数精度
  246. num = (num * 100)
  247. if (String(num).indexOf('.') > -1) {
  248. num = num.toFixed(2)
  249. }
  250. num = num ? num + type : num
  251. return num
  252. } else if (type === '%%') {
  253. num = Number(num)
  254. return num.toFixed(2) + '%'
  255. } else if (type === '-') {
  256. return formatDate(num, 'day')
  257. } else if (type === ':') {
  258. num = Math.ceil(num)
  259. let h, m, s
  260. h = m = s = 0
  261. const wunH = 60 * 60,
  262. wunM = 60 // 单位秒, wun 通 one
  263. if (num >= wunH) {
  264. h = Math.floor(num / wunH)
  265. const remainder = num % wunH
  266. if (remainder >= wunM) {
  267. m = Math.floor(remainder / wunM)
  268. s = remainder % wunM
  269. } else {
  270. s = remainder
  271. }
  272. } else if (wunH >= num && num >= wunM) {
  273. m = Math.floor(num / wunM)
  274. s = num % wunM
  275. } else {
  276. s = num
  277. }
  278. const hms = [h, m, s].map(i => i < 10 ? '0' + i : i)
  279. return hms.join(type)
  280. } else if (type === ',') {
  281. return num.toLocaleString()
  282. } else {
  283. if (String(num).indexOf('.') > -1) {
  284. if (Math.abs(num) > 1) {
  285. num = num.toFixed(fix || 0)
  286. } else {
  287. num = num.toFixed(fix || 2)
  288. }
  289. }
  290. return num
  291. }
  292. }
  293. // 格式化日期,返回其所在的范围
  294. function formatDate(date, type) {
  295. let d = new Date(date)
  296. if (type === 'hour') {
  297. let h = d.getHours()
  298. h = h < 10 ? '0' + h : h
  299. let str = `${h}:00 ~ ${h}:59`
  300. if (h === 0) {
  301. // 0 点的时候,显示为 yyyy-mm-dd(00:00 ~ 00:59)
  302. let firstday = parseDateTime(d)
  303. str = firstday + "(00:00 ~ 00:59)";
  304. }
  305. return str
  306. } else if (type === 'week') {
  307. const first = d.getDate() - d.getDay() + 1; // First day is the day of the month - the day of the week
  308. const last = first + 6; // last day is the first day + 6
  309. let firstday = new Date(d.setDate(first));
  310. firstday = parseDateTime(firstday)
  311. let lastday = new Date(d.setDate(last));
  312. lastday = parseDateTime(lastday)
  313. return `${firstday} ~ ${lastday}`
  314. } else if (type === 'month') {
  315. let firstday = new Date(d.getFullYear(), d.getMonth(), 1);
  316. firstday = parseDateTime(firstday)
  317. let lastday = new Date(d.getFullYear(), d.getMonth() + 1, 0);
  318. lastday = parseDateTime(lastday)
  319. return `${firstday} ~ ${lastday}`
  320. } else {
  321. return parseDateTime(d)
  322. }
  323. }
  324. // 格式化日期,返回其 yyyy-mm-dd 格式
  325. function parseDateTime(datetime, type, splitor = '-') {
  326. let d = datetime
  327. if (typeof d !== 'object') {
  328. d = new Date(d)
  329. }
  330. const year = d.getFullYear()
  331. const month = d.getMonth() + 1
  332. const day = d.getDate()
  333. const hour = d.getHours()
  334. const minute = d.getMinutes()
  335. const second = d.getSeconds()
  336. const date = [year, lessTen(month), lessTen(day)].join(splitor)
  337. const time = [lessTen(hour), lessTen(minute), lessTen(second)].join(':')
  338. if (type === "dateTime") {
  339. return date + ' ' + time
  340. }
  341. return date
  342. }
  343. function lessTen(item) {
  344. return item < 10 ? '0' + item : item
  345. }
  346. // 获取指定日期当天或 n 天前零点的时间戳,丢弃时分秒
  347. function getTimeOfSomeDayAgo(days = 0, date = Date.now()) {
  348. const d = new Date(date)
  349. const oneDayTime = 24 * 60 * 60 * 1000
  350. let ymd = [d.getFullYear(), d.getMonth() + 1, d.getDate()].join('/')
  351. ymd = ymd + ' 00:00:00'
  352. const someDaysAgoTime = new Date(ymd).getTime() - oneDayTime * days
  353. return someDaysAgoTime
  354. }
  355. // 判断时间差值 delta,单位为天
  356. function maxDeltaDay(times, delta = 2) {
  357. if (!times.length) return true
  358. const wunDay = 24 * 60 * 60 * 1000
  359. const [start, end] = times
  360. const max = end - start < wunDay * delta
  361. return max
  362. }
  363. // 查询 总设备数、总用户数, 通过 field 配置
  364. function getFieldTotal(query = this.query, field = "total_devices") {
  365. let fieldTotal
  366. if (typeof query === 'object') {
  367. query = stringifyQuery(query, false, ['uni_platform'])
  368. }
  369. const db = uniCloud.database()
  370. return db.collection('uni-stat-result')
  371. .where(query)
  372. .field(`${field} as temp_${field}, start_time`)
  373. .groupBy('start_time')
  374. .groupField(`sum(temp_${field}) as ${field}`)
  375. .orderBy('start_time', 'desc')
  376. .get()
  377. .then(cur => {
  378. const data = cur.result.data
  379. fieldTotal = data.length && data[0][field]
  380. fieldTotal = format(fieldTotal)
  381. this.panelData && this.panelData.forEach(item => {
  382. if (item.field === field) {
  383. item.value = fieldTotal
  384. }
  385. })
  386. return Promise.resolve(fieldTotal)
  387. })
  388. }
  389. // 防抖函数
  390. function debounce(fn, time = 100) {
  391. let timer = null
  392. return function(...args) {
  393. if (timer) clearTimeout(timer)
  394. timer = setTimeout(() => {
  395. fn.apply(this, args)
  396. }, time)
  397. }
  398. }
  399. const files = {}
  400. function fileToUrl(file) {
  401. for (const key in files) {
  402. if (files.hasOwnProperty(key)) {
  403. const oldFile = files[key]
  404. if (oldFile === file) {
  405. return key
  406. }
  407. }
  408. }
  409. let url = (window.URL || window.webkitURL).createObjectURL(file)
  410. files[url] = file
  411. return url
  412. }
  413. /**
  414. * 获取两个时间戳之间的所有时间
  415. * let start = new Date(1642694400000) // 2022-01-21 00:00:00
  416. * let end = new Date(1643644800000) // 2022-02-01 00:00:00
  417. * dateList = getAllDateCN(date1, date2)
  418. * @param {*} startTime
  419. * @param {*} endTime
  420. */
  421. function getAllDateCN(startTime, endTime) {
  422. let date_all = [];
  423. let i = 0;
  424. while (endTime.getTime() - startTime.getTime() >= 0) {
  425. // 获取日期和时间
  426. // let year = startTime.getFullYear()
  427. // let month = startTime.getMonth() + 1
  428. // let day = startTime.getDate()
  429. // let time = startTime.toLocaleTimeString()
  430. date_all[i] = startTime.getTime()
  431. // 获取每天00:00:00的时间戳
  432. // date_all[i] = new Date(startTime.toLocaleDateString()).getTime() / 1000;
  433. // 天数+1
  434. startTime.setDate(startTime.getDate() + 1);
  435. i += 1;
  436. }
  437. return date_all;
  438. }
  439. function createUniStatQuery(object) {
  440. return Object.assign({}, object, {
  441. type: "native_app",
  442. create_env: "uni-stat"
  443. })
  444. }
  445. export {
  446. formatterData,
  447. fillTrendChartData,
  448. stringifyQuery,
  449. stringifyField,
  450. stringifyGroupField,
  451. mapfields,
  452. getTimeOfSomeDayAgo,
  453. division,
  454. format,
  455. formatDate,
  456. parseDateTime,
  457. maxDeltaDay,
  458. debounce,
  459. fileToUrl,
  460. getFieldTotal,
  461. getAllDateCN,
  462. createUniStatQuery
  463. }