util.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. import CONSTANT from "@/constant";
  2. import { mobileReg } from "@/utils/tools/validate";
  3. import { getMiniAppUrl, isWxBrowser } from "@/utils/common";
  4. import permision from "@/utils/wa-permission_1.1/permission.js";
  5. import { afterAppLoginSuccess } from "@/utils/appDevice.js";
  6. // 是否为空
  7. const isEmpty = val => {
  8. if (val instanceof Array) {
  9. if (val.length === 0){
  10. return true
  11. }
  12. } else if (val instanceof Object) {
  13. if (Object.keys(val).length == 0){
  14. return true
  15. }
  16. } else {
  17. if ( val === "null" || val === null || val === "undefined" || val === undefined || val === "" || JSON.stringify(val) == "[]"){
  18. return true
  19. }
  20. return false;
  21. }
  22. return false;
  23. };
  24. /**
  25. * 深拷贝对象
  26. * @param {要拷贝对象} obj
  27. */
  28. export const copyObject = obj => {
  29. let str,
  30. newobj = Array.isArray(obj) === true ? [] : {};
  31. if (typeof obj !== "object") {
  32. return;
  33. } else if (JSON) {
  34. (str = JSON.stringify(obj)), //系列化对象
  35. (newobj = JSON.parse(str)); //还原
  36. } else {
  37. for (let i in obj) {
  38. newobj[i] = typeof obj[i] === "object" ? copyObject(obj[i]) : obj[i];
  39. }
  40. }
  41. return newobj;
  42. };
  43. const numberFormat = (number, type) => {
  44. if (number > 10000) {
  45. let num = (number / 10000 + "").split(".");
  46. return `${num[0]}.${("" + num[1]).slice(0, 1)} 万`;
  47. } else {
  48. return parseFloat(Number(number));
  49. }
  50. };
  51. // 截取图片上传字符串
  52. const imgSubstr = val => {
  53. if (isEmpty(val)) return [];
  54. if (isArray(val)) {
  55. return val.map(e => {
  56. if(e.indexOf("?") > -1){
  57. e = e.slice(0,e.indexOf("?"))
  58. }
  59. let index = e.indexOf("uploads/");
  60. e = e.substring(index);
  61. return e;
  62. });
  63. } else {
  64. if(e.indexOf("?") > -1){
  65. e = e.slice(0,e.indexOf("?"))
  66. }
  67. let index = val.indexOf("uploads/");
  68. return val.substring(index);
  69. }
  70. };
  71. // 是否为数组
  72. const isArray = arr => {
  73. return typeof arr == "object" && arr.constructor == Array;
  74. };
  75. // 是否为字符串
  76. const isString = str => {
  77. return typeof str == "string" && str.constructor == String;
  78. };
  79. // 是否为对象
  80. const isObject = obj => {
  81. return typeof obj == "object" && obj.constructor == Object;
  82. };
  83. // 是否为数字
  84. const isNumber = num => {
  85. return typeof num == "number" && num.constructor == Number;
  86. };
  87. // 是否为日期类型
  88. const isDate = date => {
  89. return typeof date == "object" && date.constructor == Date;
  90. };
  91. // 是否为函数
  92. const isFunction = obj => {
  93. return typeof obj == "object" && obj.constructor == Function;
  94. };
  95. const checkMobile = num => {
  96. return mobileReg.test(num);
  97. };
  98. const checkName = str => {
  99. return /^[\u4E00-\u9FA5A-Za-z\s]+(·[\u4E00-\u9FA5A-Za-z]+)*$/.test(str);
  100. };
  101. //是否金额
  102. const isMoney = money =>{
  103. return /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/.test(money)
  104. }
  105. /** *
  106. * 检查登录
  107. * @parmas query 拼接得参数对象
  108. * @parmas isFrist 为true时开头为&
  109. */
  110. export async function getCheckLogin() {
  111. let status = false;
  112. let token = uni.getStorageSync("token");
  113. if (token) {
  114. status = true;
  115. } else {
  116. // #ifdef H5
  117. if (isWxBrowser()) {
  118. let option = JSON.parse(uni.getStorageSync("currentQuery")) || {};
  119. console.log("option", option);
  120. // console.log('option', getMiniAppUrl(option, true))
  121. // return false
  122. location.href = `${CONSTANT.hostUrl}/auth/prepare?url=${encodeURIComponent(
  123. getMiniAppUrl(option, true)
  124. )}`;
  125. } else {
  126. // uni.showToast({
  127. // title: '请用微信浏览器打开!',
  128. // icon: 'none',
  129. // mask: true
  130. // })
  131. }
  132. // #endif
  133. // #ifndef H5
  134. // #endif
  135. }
  136. return status;
  137. }
  138. export const floatFormat = (f, n) => {
  139. let m = Math.pow(10, n);
  140. return parseInt(f * m, 10) / m;
  141. };
  142. /** *
  143. * 对象参数转为url参数
  144. * @parmas query 拼接得参数对象
  145. * @parmas isFrist 为true时开头为&
  146. */
  147. export const parse = (query, isFrist = false) => {
  148. let str = Object.keys(query)
  149. .filter(key => !isEmpty(query[key]))
  150. .reduce((result, key) => {
  151. const value = query[key];
  152. // in查询特殊处理
  153. if (Array.isArray(value) && !isEmpty(value)) {
  154. return `${result}&${value.reduce(
  155. (val, cVal) => `${val ? `${val}&` : val}${key}=${cVal}`,
  156. ""
  157. )}`;
  158. }
  159. // between查询做特殊处理
  160. if (typeof value === "object" && !isEmpty(value)) {
  161. const [start, end] = value;
  162. return `${result}&${key}[]=${start}&${key}[]=${end}`;
  163. }
  164. return `${result}&${key}=${value}`;
  165. }, "");
  166. return isFrist ? str : str.replace(/^&/, "?");
  167. };
  168. /** *
  169. * 全局时间转换
  170. * @parmas num 时间戳默认为11位时间戳
  171. * @parmas fmt 默认转换的时间格式
  172. * @parmas all 为true时time为13位时间戳
  173. */
  174. export const $formatDate = (num, fmt = "YYYY-MM-DD HH:mm", all) => {
  175. num = all ? num : num * 1000;
  176. let date = new Date();
  177. date.setTime(num);
  178. let o = {
  179. "M+": date.getMonth() + 1,
  180. "D+": date.getDate(),
  181. "h+": date.getHours() % 12 === 0 ? 12 : date.getHours() % 12,
  182. "H+": date.getHours(),
  183. "m+": date.getMinutes(),
  184. "s+": date.getSeconds(),
  185. "q+": Math.floor((date.getMonth() + 3) / 3),
  186. S: date.getMilliseconds()
  187. };
  188. let week = {
  189. "0": "\u65e5",
  190. "1": "\u4e00",
  191. "2": "\u4e8c",
  192. "3": "\u4e09",
  193. "4": "\u56db",
  194. "5": "\u4e94",
  195. "6": "\u516d"
  196. };
  197. if (/(Y+)/.test(fmt)) {
  198. fmt = fmt.replace(
  199. RegExp.$1,
  200. (date.getFullYear() + "").substr(4 - RegExp.$1.length)
  201. );
  202. }
  203. if (/(E+)/.test(fmt)) {
  204. fmt = fmt.replace(
  205. RegExp.$1,
  206. (RegExp.$1.length > 1
  207. ? RegExp.$1.length > 2
  208. ? "\u661f\u671f"
  209. : "\u5468"
  210. : "") + week[date.getDay() + ""]
  211. );
  212. }
  213. for (let k in o) {
  214. if (new RegExp("(" + k + ")").test(fmt)) {
  215. fmt = fmt.replace(
  216. RegExp.$1,
  217. RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
  218. );
  219. }
  220. }
  221. return fmt;
  222. };
  223. // 防重复导航的时间间隔(毫秒)
  224. let lastNavigateTime = 0;
  225. const NAVIGATE_THROTTLE_TIME = 600;
  226. /** *
  227. * 全局公共按钮跳转(已添加防重复点击功能)
  228. * @parmas item.funtion 为可执行函数
  229. * @parmas item.url 为跳转的路径
  230. * @parmas item.query 为跳转携带的参数
  231. * @parmas item.type 为跳转调用的函数 1为navigateTo, 2为redirectTo 关闭当前页,跳转到指定页, 3为reLaunch, 4为switchTab
  232. */
  233. export const pageTo = item => {
  234. // 防重复点击逻辑
  235. const currentTime = Date.now();
  236. if (currentTime - lastNavigateTime < NAVIGATE_THROTTLE_TIME) {
  237. console.log('导航操作过于频繁,已忽略');
  238. return false;
  239. }
  240. lastNavigateTime = currentTime;
  241. if (isNumber(item)) {
  242. uni.navigateBack({
  243. delta: item
  244. });
  245. return false;
  246. }
  247. if (isString(item)) {
  248. uni.navigateTo({
  249. url: item
  250. });
  251. return false;
  252. }
  253. if (item.funtion) {
  254. item.funtion();
  255. }
  256. if (item.url) {
  257. let query = item.query ? parse(item.query) : "";
  258. let allUrl = `${item.url}${query}`;
  259. if (item.type == 2) {
  260. uni.redirectTo({ url: allUrl })
  261. } else if (item.type == 3) {
  262. uni.reLaunch({ url: allUrl })
  263. } else if (item.type == 4) {
  264. uni.setStorageSync("switchTabQuery", item.query);
  265. uni.switchTab({ url: item.url })
  266. } else {
  267. uni.navigateTo({ url: allUrl })
  268. }
  269. }
  270. };
  271. /** *
  272. * 是否登录
  273. */
  274. export const isLogin = () => {
  275. let token = uni.getStorageSync("token");
  276. if (token) {
  277. return true;
  278. }
  279. return false;
  280. };
  281. /**
  282. * 去重
  283. * 姜枫 2021.05.05
  284. * **/
  285. export const unique = (arr) => {
  286. for(var i=0; i<arr.length; i++){
  287. for(var j=i+1; j<arr.length; j++){
  288. if(arr[i].classId==arr[j].classId){ //第一个等同于第二个,splice方法删除第二个
  289. arr.splice(j,1);
  290. j--;
  291. }
  292. }
  293. }
  294. return arr;
  295. };
  296. // 点击声效 - 缓存对象,避免重复创建
  297. let hitAudioContext = null
  298. let lastHitPlayTime = 0
  299. const HIT_MIN_INTERVAL = 100
  300. const initHitAudio = () => {
  301. if (hitAudioContext) return
  302. try {
  303. if (uni.getSystemInfoSync().platform === 'windows') return
  304. hitAudioContext = uni.createInnerAudioContext()
  305. hitAudioContext.autoplay = false
  306. hitAudioContext.src = '/static/hit.mp3'
  307. hitAudioContext.volume = 1
  308. } catch (e) {
  309. console.error('[hitRemind] 初始化失败:', e)
  310. hitAudioContext = null
  311. }
  312. }
  313. export const hitRemind = () => {
  314. try {
  315. // 防抖:100ms 内只播放一次
  316. const now = Date.now()
  317. if (now - lastHitPlayTime < HIT_MIN_INTERVAL) {
  318. return
  319. }
  320. // 初始化音频
  321. if (!hitAudioContext) {
  322. initHitAudio()
  323. }
  324. if (!hitAudioContext) {
  325. return
  326. }
  327. // 更新时间戳
  328. lastHitPlayTime = now
  329. // 停止之前的播放
  330. try {
  331. hitAudioContext.stop()
  332. } catch (e) {
  333. // 忽略停止失败
  334. }
  335. // 设置音量并播放
  336. hitAudioContext.volume = 1
  337. try {
  338. hitAudioContext.play()
  339. } catch (playError) {
  340. console.error('[hitRemind] 播放异常:', playError)
  341. hitAudioContext = null
  342. }
  343. } catch (e) {
  344. console.error('[hitRemind] 执行出错:', e)
  345. }
  346. }
  347. //#ifdef APP-PLUS
  348. const CLDialog=uni.requireNativePlugin("CL-Dialog")
  349. //#endif
  350. // 确认弹框
  351. export const confirmModal = (item,okCallback=null,cancelCallback=null) => {
  352. let title = item.title || '提示'
  353. let content = item.content || '确认操作?'
  354. let okText = item.okText || '确认'
  355. let cancelText = item.cancelText || '取消'
  356. let cacelTextColor = '#999999'
  357. let okTextColor = '#38ADFF'
  358. let singer = item.singer || false
  359. //#ifdef APP-PLUS
  360. let platform=uni.getSystemInfoSync().platform
  361. if(platform=='ios'){
  362. uni.showModal({
  363. title: title,
  364. content: content,
  365. showCancel: true,
  366. cancelText: cancelText,
  367. cancelColor: cacelTextColor,
  368. confirmText: okText,
  369. confirmColor: okTextColor,
  370. success: function (res) {
  371. if (res.confirm) {
  372. if(okCallback!=null){
  373. okCallback()
  374. }
  375. } else if (res.cancel) {
  376. if(cancelCallback!=null){
  377. cancelCallback()
  378. }
  379. }
  380. }
  381. })
  382. }else if(platform=='android'){
  383. let options={
  384. title:title,//内容(可选)但是标题和内容至少选择一个
  385. con:content,
  386. okTitle:okText,//确认按钮文字(可选)
  387. cancleTitle:cancelText,//取消按钮文字(可选)
  388. okTextColor:"#38ADFF",//确认按钮颜色(可选)
  389. cancleTextColor:"#999999",//取消按钮颜色(可选)
  390. singer:singer,//是否只显示确认按钮,默认false(可选)
  391. textAlign:"center",//对齐方式 //left居左,center居中,right 居右 默认居中
  392. conColor:"",
  393. bgColor:"#FFFFFF",//自定义弹框颜色
  394. titleColor:"#3d3d3d"//自定义title颜色
  395. }
  396. CLDialog.show(options,()=>{
  397. if(okCallback!=null){
  398. okCallback()
  399. }
  400. },()=>{
  401. if(cancelCallback!=null){
  402. cancelCallback()
  403. }
  404. })
  405. }
  406. //#endif
  407. // #ifdef MP-WEIXIN
  408. uni.showModal({
  409. title: title,
  410. content: content,
  411. showCancel: true,
  412. cancelText: cancelText,
  413. cancelColor: cacelTextColor,
  414. confirmText: okText,
  415. confirmColor: okTextColor,
  416. success: function (res) {
  417. if (res.confirm) {
  418. if(okCallback!=null){
  419. okCallback()
  420. }
  421. } else if (res.cancel) {
  422. if(cancelCallback!=null){
  423. cancelCallback()
  424. }
  425. }
  426. }
  427. })
  428. //#endif
  429. }
  430. //判断是否有扫码的条件
  431. export const isScanEnv = () =>{
  432. let isScanEnv = false
  433. //#ifdef APP-PLUS
  434. if(plus.device.vendor == 'Newland'){
  435. isScanEnv = true
  436. }
  437. //#endif
  438. return isScanEnv
  439. }
  440. // 库存不足提示音 - 缓存对象,避免重复创建
  441. let noStockAudioContext = null
  442. let lastNoStockPlayTime = 0
  443. const NO_STOCK_MIN_INTERVAL = 1000
  444. const initNoStockAudio = () => {
  445. if (noStockAudioContext) return
  446. try {
  447. if (uni.getSystemInfoSync().platform === 'windows') return
  448. noStockAudioContext = uni.createInnerAudioContext()
  449. noStockAudioContext.autoplay = false
  450. noStockAudioContext.src = "/static/noStock.mp3"
  451. noStockAudioContext.volume = 1
  452. } catch (e) {
  453. console.error('[noStockRemind] 初始化失败:', e)
  454. noStockAudioContext = null
  455. }
  456. }
  457. export const noStockRemind = () => {
  458. try {
  459. // 防抖:1秒内只播放一次
  460. const now = Date.now()
  461. if (now - lastNoStockPlayTime < NO_STOCK_MIN_INTERVAL) {
  462. return
  463. }
  464. // 初始化音频
  465. if (!noStockAudioContext) {
  466. initNoStockAudio()
  467. }
  468. if (!noStockAudioContext) {
  469. return
  470. }
  471. // 更新时间戳
  472. lastNoStockPlayTime = now
  473. // 停止之前的播放
  474. try {
  475. noStockAudioContext.stop()
  476. } catch (e) {
  477. // 忽略停止失败
  478. }
  479. // 设置音量并播放
  480. noStockAudioContext.volume = 1
  481. try {
  482. noStockAudioContext.play()
  483. } catch (playError) {
  484. console.error('[noStockRemind] 播放异常:', playError)
  485. noStockAudioContext = null
  486. }
  487. } catch (e) {
  488. console.error('[noStockRemind] 执行出错:', e)
  489. }
  490. }
  491. export async function callUp(mobile){
  492. //#ifdef MP-WEIXIN
  493. uni.makePhoneCall({phoneNumber: mobile})
  494. //#endif
  495. //#ifdef APP-PLUS
  496. let env = uni.getSystemInfoSync().platform
  497. if(env == 'android'){
  498. //Android是动态权限,请求权限状态时会触发弹框询问是否赋权(如果已经同意或永久禁止则不会询问),所以必须使用异步方式来处理
  499. var result = await permision.requestAndroidPermission('android.permission.CALL_PHONE')
  500. if(Number(result) == -1){
  501. confirmModal({content:'请在权限管理中开启【拨打电话】','okText':'去开启'},() => {
  502. permision.gotoAppPermissionSetting()
  503. })
  504. return
  505. }
  506. }
  507. uni.makePhoneCall({phoneNumber: mobile})
  508. //#endif
  509. }
  510. /** 海报/太阳码:开发构建用 develop,正式构建用 release */
  511. export const getMiniEnvVersion = () => {
  512. return process.env.NODE_ENV === 'development' ? 'develop' : 'release'
  513. }
  514. export default {
  515. isEmpty,
  516. copyObject,
  517. imgSubstr,
  518. isArray,
  519. parse,
  520. checkMobile,
  521. checkName,
  522. $formatDate,
  523. floatFormat,
  524. pageTo,
  525. isLogin,
  526. getCheckLogin,
  527. numberFormat,
  528. isString,
  529. unique,
  530. isMoney,
  531. confirmModal,
  532. isScanEnv,
  533. hitRemind,
  534. noStockRemind,
  535. callUp,
  536. afterAppLoginSuccess,
  537. getMiniEnvVersion
  538. };