util.js 13 KB

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