tool.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. import Vue from 'vue'
  2. // 工具函数错误处理包装器
  3. function withErrorHandler(fn, fallback = null) {
  4. try {
  5. return fn();
  6. } catch (error) {
  7. console.error('工具函数执行错误:', error);
  8. return fallback;
  9. }
  10. }
  11. // 异步操作超时包装器
  12. function withTimeout(promise, timeoutMs = 10000) {
  13. return Promise.race([
  14. promise,
  15. new Promise((_, reject) =>
  16. setTimeout(() => reject(new Error('操作超时')), timeoutMs)
  17. )
  18. ]).catch(err => {
  19. if (err.message === '操作超时') {
  20. console.warn('操作超时,自动忽略');
  21. }
  22. throw err;
  23. });
  24. }
  25. function setBar() {
  26. // 使用异步方式获取系统信息,避免阻塞
  27. uni.getSystemInfoAsync ? uni.getSystemInfoAsync({
  28. success: handleSystemInfo,
  29. fail: () => {
  30. // 默认值处理
  31. Vue.prototype.StatusBar = 20;
  32. Vue.prototype.CustomBar = 64;
  33. }
  34. }) : uni.getSystemInfo({
  35. success: handleSystemInfo,
  36. fail: () => {
  37. Vue.prototype.StatusBar = 20;
  38. Vue.prototype.CustomBar = 64;
  39. }
  40. });
  41. }
  42. function handleSystemInfo(e) {
  43. // #ifndef MP
  44. Vue.prototype.usePort = 'h5';
  45. Vue.prototype.StatusBar = e.statusBarHeight || 20;
  46. if (e.platform == 'android') {
  47. Vue.prototype.CustomBar = (e.statusBarHeight || 0) + 50;
  48. } else {
  49. Vue.prototype.CustomBar = (e.statusBarHeight || 0) + 45;
  50. };
  51. // #endif
  52. // #ifdef MP-WEIXIN
  53. Vue.prototype.usePort = 'wechat';
  54. Vue.prototype.StatusBar = e.statusBarHeight || 20;
  55. withErrorHandler(() => {
  56. let custom = wx.getMenuButtonBoundingClientRect();
  57. Vue.prototype.Custom = custom;
  58. Vue.prototype.CustomBar = custom.bottom + custom.top - e.statusBarHeight;
  59. });
  60. // #endif
  61. // #ifdef MP-ALIPAY
  62. Vue.prototype.StatusBar = e.statusBarHeight || 20;
  63. Vue.prototype.CustomBar = (e.statusBarHeight || 0) + (e.titleBarHeight || 0);
  64. // #endif
  65. }
  66. function setLink(systemclient = "marketingtool") {
  67. return Vue.prototype.qrCodePrefix = 'https://meida.cnyunl.com';
  68. }
  69. function mount() {
  70. Vue.prototype.getLocation = (isHighAccuracy = false) => {
  71. return new Promise((resolve, reject) => {
  72. let that = this;
  73. handle()
  74. function handle() {
  75. uni.getLocation({
  76. isHighAccuracy,
  77. highAccuracyExpireTime: isHighAccuracy ? 8000 : '',
  78. success: res => {
  79. console.log("获取定位", res)
  80. resolve(res)
  81. },
  82. fail: err => {
  83. console.log("获取位置失败", err)
  84. uni.hideLoading();
  85. if (["getLocation:fail 系统错误,错误码:-13000,meet frequency limit, please slowdown and try again later", "getLocation:fail auth deny"].includes(err.errMsg)) return resolve();
  86. query()
  87. }
  88. })
  89. }
  90. function query() {
  91. uni.getSetting({
  92. success({
  93. authSetting
  94. }) {
  95. if (authSetting['scope.userLocation']) {
  96. handle()
  97. } else {
  98. uni.showModal({
  99. title: '提示',
  100. content: '需要获取您的地理位置,请确认授权,否则可能会定位门店不准确',
  101. cancelText: '下次再说',
  102. confirmText: '前去授权',
  103. success: ({
  104. confirm
  105. }) => {
  106. if (confirm) {
  107. uni.openSetting({
  108. success(res) {
  109. if (res.authSetting['scope.userLocation']) handle();
  110. }
  111. })
  112. } else {
  113. /* uni.showToast({
  114. title: "您未授权地理位置,",
  115. icon: "none",
  116. }) */
  117. resolve();
  118. }
  119. }
  120. })
  121. }
  122. }
  123. })
  124. }
  125. })
  126. };
  127. Vue.prototype.cutoff = (msg, title = "", mask = false, exitTime = 0, icon = 'none', duration = 2000,) => {
  128. if (msg != '成功' || title) uni.showToast({
  129. title: msg == '成功' ? title : msg,
  130. duration,
  131. icon,
  132. mask: mask || exitTime != 0
  133. })
  134. if (exitTime && msg == '成功') setTimeout(uni.navigateBack, exitTime)
  135. return msg != '成功';
  136. };
  137. Vue.prototype.paging = (content, init, update) => {
  138. if (update) {
  139. console.log("分页", content, init, update)
  140. let content1 = JSON.parse(JSON.stringify(content));
  141. content1.pageSize = (content1.pageNumber - 1) * content1.pageSize;
  142. content1.pageNumber = 1;
  143. return content1
  144. } else {
  145. if (content.pageTotal == undefined || !content.pageTotal) content.pageTotal = 1;
  146. if (content.pageNumber == undefined || !content.pageNumber) content.pageNumber = 1;
  147. if (content.pageSize == undefined || !content.pageSize) content.pageSize = 20;
  148. if (init) content.pageNumber = 1;
  149. return content.pageNumber > content.pageTotal;
  150. }
  151. }
  152. Vue.prototype.tovw = (num) => (num * 100 / 375).toFixed(3) + "vw";
  153. Vue.prototype.getCity = (obj, isAll = true) => obj.province + obj.city + obj.county + (isAll ? obj.address : '');
  154. Vue.prototype.getApps = (appRemark, route) => {
  155. const list = Object.values(uni.getStorageSync('authList')[appRemark])
  156. return route ? list.find(v => v.path == route || v.pathDetail == route) : list
  157. };
  158. Vue.prototype.getHeight = (even, that, calculate = true) => {
  159. return new Promise((resolve, reject) => {
  160. if (calculate) {
  161. uni.getSystemInfo({
  162. success(s) {
  163. uni.createSelectorQuery().in(that).select(even).boundingClientRect().exec(res => (!res[0]) ? reject('没有查询到元素') : resolve(s.windowHeight - res[0].bottom))
  164. }
  165. });
  166. } else {
  167. uni.createSelectorQuery().in(that).select(even).boundingClientRect().exec(res => (!res[0]) ? reject('没有查询到元素') : resolve(res[0]))
  168. }
  169. })
  170. };
  171. //compressed压缩图;thumbnail缩略图,hls转码视频,cover封面
  172. //maxSizeKB: 最大图片大小阈值(KB),默认200KB,大于此值则使用缩略图
  173. Vue.prototype.getSpecifiedImage = (item, type = 'thumbnail', maxSizeKB = 500) => {
  174. if (!item) return "";
  175. if (!item.subfiles || !item.subfiles.length) return item.url || "";
  176. // 如果原图存在且大小小于等于阈值,使用原图
  177. if (item.url && item.contentlength && item.contentlength <= maxSizeKB * 1024) {
  178. return item.url;
  179. }
  180. // 在 subfiles 中查找,优先级:compressed > thumbnail
  181. const priorityTypes = ['compressed', 'thumbnail'];
  182. let targetTypes = type === 'compressed' ? ['compressed'] : priorityTypes;
  183. for (const t of targetTypes) {
  184. const v = item.subfiles.find(v => v.type == t);
  185. if (v && v.url) return v.url;
  186. }
  187. return item.url || "";
  188. }
  189. // 专门处理视频文件的函数:优先取hls,若未取到取原本的url
  190. Vue.prototype.getVideoUrl = (item) => {
  191. if (!item) return item;
  192. item.customCache = true;
  193. // 优先查找hls格式
  194. if (item.subfiles && item.subfiles.length > 0) {
  195. const hlsFile = item.subfiles.find(v => v.type == 'hls');
  196. if (hlsFile && hlsFile.url) {
  197. item.customCache = false;
  198. item.url = hlsFile.url;
  199. return item;
  200. }
  201. }
  202. return item;
  203. }
  204. Vue.prototype.formatTime = (date = new Date(), j1 = '-', j2 = ':') => {
  205. const year = date.getFullYear()
  206. const month = date.getMonth() + 1
  207. const day = date.getDate()
  208. const hour = date.getHours()
  209. const minute = date.getMinutes()
  210. const second = date.getSeconds()
  211. const formatNumber = n => {
  212. n = n.toString()
  213. return n[1] ? n : `0${n}`
  214. }
  215. return `${[year, month, day].map(formatNumber).join(j1)} ${[hour, minute, second].map(formatNumber).join(j2)}`
  216. }
  217. Vue.prototype.getCustomClass = (typename) => {
  218. return new Promise((resolve, reject) => {
  219. Vue.prototype.$Http.basic({
  220. "classname": "sysmanage.develop.optiontype.optiontype",
  221. "method": "optiontypeselect",
  222. "content": {
  223. typename
  224. }
  225. }).then(res => {
  226. console.log("获取自定义分类" + typename, res)
  227. if (res.msg != '成功') return resolve([]);
  228. resolve(res.data);
  229. })
  230. })
  231. }
  232. Vue.prototype.CNY = (sum, symbol = '¥', strict = true, precision = 2) => {
  233. const currency = require("./currency.js");
  234. let num = currency(sum, {
  235. symbol,
  236. precision
  237. }).format();
  238. if (strict) {
  239. let decimals = num.split(".");
  240. decimals[1] = ('0.' + decimals[1] - 0 + '').substring(2)
  241. num = decimals[1] ? decimals.join(".") : decimals[0]
  242. }
  243. return num
  244. }
  245. Vue.prototype.callPhone = phoneNumber => {
  246. uni.makePhoneCall({
  247. phoneNumber: phoneNumber + '',
  248. complete: res => {
  249. console.log('makePhoneCall', res)
  250. }
  251. })
  252. }
  253. Vue.prototype.dye = (list, colors, num = 2) => {
  254. let count = num - 1,
  255. index = 0;
  256. return list.map((v, i) => {
  257. if (i > count) {
  258. count += num;
  259. index += 1;
  260. }
  261. v.color = colors[index % colors.length]
  262. return v
  263. })
  264. }
  265. Vue.prototype.getReg = name => {
  266. let obj = {
  267. phonenumber: {
  268. reg: '^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\\d{8}$',
  269. errText: "请输入正确的11位手机号码!"
  270. },
  271. email: {
  272. reg: '^([A-Za-z0-9_\\-\\.])+\\@([A-Za-z0-9_\\-\\.])+\\.([A-Za-z]{2,4})$',
  273. errText: "请输入正确的邮箱格式!"
  274. }
  275. }
  276. return obj[name] || ''
  277. }
  278. Vue.prototype.daysAgo = num => {
  279. let now = new Date().getTime() - 0,
  280. end = now + 86400000,
  281. beg = end - (num * 86400000);
  282. return {
  283. begindate: Vue.prototype.formatTime(new Date(beg)).split(' ')[0],
  284. enddate: Vue.prototype.formatTime(new Date(end)).split(' ')[0],
  285. }
  286. }
  287. Vue.prototype.getUrlParams = urlStr => {
  288. const url = decodeURIComponent(urlStr)
  289. let obj = {}
  290. let str = url.slice(url.indexOf('?') + 1),
  291. arr = str.split('&');
  292. obj.funName = url.slice(0, url.indexOf('?')).split("/").pop();
  293. for (let j = arr.length, i = 0; i < j; i++) {
  294. let arr_temp = arr[i].split('=')
  295. obj[arr_temp[0]] = arr_temp[1]
  296. }
  297. return obj
  298. }
  299. Vue.prototype.switchPage = app => {
  300. if (app.path) {
  301. uni.navigateTo({
  302. url: app.path,
  303. fail: (fail) => {
  304. console.log("跳转失败原因", fail)
  305. }
  306. })
  307. } else {
  308. if (app.name == 'index_design') {
  309. app.name = 'design';
  310. } else if (['index_6C', 'index_freeDesign'].includes(app.name)) {
  311. app.name = 'longText';
  312. };
  313. if (app.name == 'index_design') app.name = 'design';
  314. switch (app.name) {
  315. case 'design':
  316. Vue.prototype.$Http.changePage("index", "案例", {
  317. active: '实景案例'
  318. })
  319. break;
  320. case 'index_product':
  321. Vue.prototype.$Http.changePage("cloud", "单品")
  322. break;
  323. case 'index_commodity':
  324. Vue.prototype.$Http.changePage("index", "活动")
  325. break;
  326. case 'index_imgs':
  327. Vue.prototype.$Http.changePage("index", "案例", {
  328. active: '图库'
  329. })
  330. break;
  331. case 'index_dataBank':
  332. Vue.prototype.$Http.changePage("cloud", "资料库")
  333. break;
  334. case 'index_school':
  335. Vue.prototype.$Http.changePage("cloud", "商学院")
  336. break;
  337. case 'index_video':
  338. Vue.prototype.$Http.changePage("index", "视频")
  339. break;
  340. case 'index_cloud':
  341. Vue.prototype.$Http.changePage("cloud", "工作台")
  342. break;
  343. case 'longText':
  344. if (app.forms.path) {
  345. uni.navigateTo({
  346. url: app.forms.path.formcols[0].title
  347. })
  348. } else {
  349. console.log("长图文", app)
  350. }
  351. break;
  352. default:
  353. console.log("未配置路径", app.name)
  354. break;
  355. }
  356. }
  357. console.log(app);
  358. }
  359. Vue.prototype.isInitializeLogin = (fun) => {
  360. const systemInitIsComplete = getApp().globalData.systemInitIsComplete;
  361. if (!systemInitIsComplete || typeof systemInitIsComplete == 'object') {
  362. getApp().globalData.HomePageStartRendering.push(fun)
  363. } else {
  364. fun()
  365. }
  366. }
  367. const accountInfo = uni.getAccountInfoSync();
  368. Vue.prototype.isShow = accountInfo.miniProgram.envVersion.includes('tri');
  369. Vue.prototype.attinfosType = (postfix) => {
  370. postfix = postfix.toLowerCase()
  371. if (['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp', 'tiff', 'tga', 'psd', 'svg', 'ico'].includes(postfix)) {
  372. return '图片'
  373. } else if (postfix == 'mp4' || postfix == 'avi' || postfix == 'mov' || postfix == 'wmv' || postfix == 'flv' || postfix == 'mkv') {
  374. return '视频'
  375. } else if (postfix == 'docx' || postfix == 'doc' || postfix == 'xlsx' || postfix == 'xls' || postfix == 'pptx' || postfix == 'ppt' || postfix == 'pdf' || postfix == 'txt') {
  376. return '文档'
  377. } else if (postfix == 'richtext') {
  378. return '图文'
  379. } else {
  380. return '文档'
  381. }
  382. }
  383. }
  384. module.exports = {
  385. mount,
  386. setBar,
  387. setLink
  388. }