wangxx пре 4 месеци
родитељ
комит
6d68774804

+ 13 - 0
src/utils/auth.js

@@ -0,0 +1,13 @@
1
+const TokenKey = 'App-Token'
2
+
3
+export function getToken() {
4
+  return uni.getStorageSync(TokenKey)
5
+}
6
+
7
+export function setToken(token) {
8
+  return uni.setStorageSync(TokenKey, token)
9
+}
10
+
11
+export function removeToken() {
12
+  return uni.removeStorageSync(TokenKey)
13
+}

+ 419 - 0
src/utils/common.js

@@ -0,0 +1,419 @@
1
+import config from '@/config'
2
+import { getToken } from '@/utils/auth'
3
+import { listCheckApprovalCcDetails, listCheckPendingTasks } from "@/api/myToDoList/myToDoList.js"
4
+import { listData } from "@/api/system/dict/data.js"
5
+/**
6
+* 显示消息提示框
7
+* @param content 提示的标题
8
+*/
9
+export function toast(content) {
10
+  uni.showToast({
11
+    icon: 'none',
12
+    title: content
13
+  })
14
+}
15
+
16
+/**
17
+* 显示模态弹窗
18
+* @param content 提示的标题
19
+*/
20
+export function showConfirm(content) {
21
+  return new Promise((resolve, reject) => {
22
+    uni.showModal({
23
+      title: '提示',
24
+      content: content,
25
+      cancelText: '取消',
26
+      confirmText: '确定',
27
+      success: function (res) {
28
+        resolve(res)
29
+      }
30
+    })
31
+  })
32
+}
33
+
34
+/**
35
+* 参数处理
36
+* @param params 参数
37
+*/
38
+export function tansParams(params) {
39
+  let result = ''
40
+  for (const propName of Object.keys(params)) {
41
+    const value = params[propName]
42
+    var part = encodeURIComponent(propName) + "="
43
+    if (value !== null && value !== "" && typeof (value) !== "undefined") {
44
+      if (typeof value === 'object') {
45
+        for (const key of Object.keys(value)) {
46
+          if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
47
+            let params = propName + '[' + key + ']'
48
+            var subPart = encodeURIComponent(params) + "="
49
+            result += subPart + encodeURIComponent(value[key]) + "&"
50
+          }
51
+        }
52
+      } else {
53
+        result += part + encodeURIComponent(value) + "&"
54
+      }
55
+    }
56
+  }
57
+  return result
58
+}
59
+
60
+export function getDate(date, AddDayCount = 0) {
61
+  if (!date) {
62
+    date = new Date()
63
+  }
64
+  if (typeof date !== 'object') {
65
+    date = date.replace(/-/g, '/')
66
+  }
67
+  const dd = new Date(date)
68
+
69
+  dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
70
+
71
+  const y = dd.getFullYear()
72
+  const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
73
+  const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
74
+  return {
75
+    fullDate: y + '-' + m + '-' + d,
76
+    year: y,
77
+    month: m,
78
+    date: d,
79
+    day: dd.getDay()
80
+  }
81
+}
82
+
83
+export function serializeData(obj) {
84
+  let str = '?'
85
+  Object.keys(obj).forEach(function (key) {
86
+    str += key + "=" + obj[key] + '&'
87
+  });
88
+
89
+  let reg = /&$/gi;
90
+  str = str.replace(reg, ""); //清除最后一个&符号
91
+  return str
92
+}
93
+
94
+
95
+export function buildTeamOptions(tree = []) {
96
+  const result = [];
97
+
98
+  function dfs(node, path = []) {
99
+    const currentPath = [...path, node.label];
100
+    // 如果是 TEAMS 叶子节点
101
+    if (node.deptType === 'TEAMS') {
102
+      result.push({
103
+        text: currentPath.join(' / '),
104
+        value: node.id
105
+      });
106
+    }
107
+    // 继续递归子节点
108
+    if (node.children && Array.isArray(node.children)) {
109
+      node.children.forEach(child => dfs(child, currentPath));
110
+    }
111
+  }
112
+
113
+  tree.forEach(root => dfs(root));
114
+  return result;
115
+}
116
+
117
+/**
118
+ * 找出deptType为DEPARTMENT的节点
119
+ * @param {Array} tree - 树形结构数据
120
+ * @returns {Array} 包含所有DEPARTMENT节点的数组
121
+ */
122
+export function buildDepartmentOptions(tree = []) {
123
+  const result = [];
124
+
125
+  function dfs(node, path = []) {
126
+    const currentPath = [...path, node.label];
127
+    // 如果是 DEPARTMENT 节点
128
+    if (node.deptType === 'DEPARTMENT') {
129
+      result.push({
130
+        text: currentPath.join(' / '),
131
+        value: node.id
132
+      });
133
+    }
134
+    // 继续递归子节点
135
+    if (node.children && Array.isArray(node.children)) {
136
+      node.children.forEach(child => dfs(child, currentPath));
137
+    }
138
+  }
139
+
140
+  tree.forEach(root => dfs(root));
141
+  return result;
142
+}
143
+
144
+//将数组格式的按照航站楼分成对象格式
145
+export function getHandleAreaData(data, notkezhang) {
146
+  let louObj = {};
147
+  let areaArr = [];
148
+  data.forEach(element => {
149
+
150
+    let name = `${element.terminlName}-${element.terminlCode}`
151
+    if (!louObj[name]) {
152
+      louObj[name] = [];
153
+    }
154
+    louObj[name].push({
155
+      ...element,
156
+      label: notkezhang ? element.channelName : element.regionalName,
157
+      code: notkezhang ? element.channelCode : element.regionalCode,
158
+    });
159
+  });
160
+
161
+  Object.keys(louObj).forEach(key => {
162
+    areaArr.push({
163
+      label: key.split('-')[0],
164
+      code: key.split('-')[1],
165
+      children: louObj[key]
166
+    });
167
+  });
168
+
169
+  return areaArr;
170
+}
171
+
172
+
173
+// 封装上传
174
+export function uploadFile(event) {
175
+  return new Promise((resolve, reject) => {
176
+    uni.uploadFile({
177
+      url: `${config.baseUrl}/common/upload`,
178
+      filePath: event.tempFilePaths[0],
179
+      name: 'file',
180
+      header: { Authorization: 'Bearer ' + getToken() },
181
+      success: (res) => resolve(JSON.parse(res.data)),
182
+      fail: reject
183
+    });
184
+  });
185
+}
186
+
187
+// 为消息tab显示红点
188
+export async function showMessageTabRedDot() {
189
+  try {
190
+    const query = {
191
+      pageNum: 1,
192
+      pageSize: 9999
193
+    };
194
+
195
+    let total = 0
196
+    let response = await listCheckPendingTasks(query);
197
+    total += response.total;
198
+    let response1 = await listCheckApprovalCcDetails(query);
199
+    total += response1.rows.filter(item => item.isRead == '0').length;
200
+    if (total > 0) {
201
+      // 消息tab在tabBar列表中的索引是1(从0开始)
202
+      uni.showTabBarRedDot({
203
+        index: 1,
204
+        success: () => {
205
+          // console.log('消息tab红点显示成功')
206
+        },
207
+        fail: (err) => {
208
+          // console.log('消息tab红点显示失败:', err)
209
+          // 如果使用自定义tabBar时原生API失败,可以在这里添加自定义处理逻辑
210
+        }
211
+      })
212
+    } else {
213
+      // 当total等于0时,取消消息tab上的红点显示
214
+      uni.hideTabBarRedDot({
215
+        index: 1,
216
+        success: () => {
217
+          console.log('消息tab红点隐藏成功')
218
+        },
219
+        fail: (err) => {
220
+          console.log('消息tab红点隐藏失败:', err)
221
+        }
222
+      })
223
+    }
224
+  } catch (error) {
225
+  }
226
+}
227
+
228
+/**
229
+ * 文字截断函数,超过指定长度显示省略号
230
+ * @param {string} text - 需要截断的文本
231
+ * @param {number} maxLength - 最大长度,默认10
232
+ * @param {string} suffix - 后缀,默认'...'
233
+ * @returns {string} 截断后的文本
234
+ */
235
+export function truncateText(text, maxLength = 10, suffix = '...') {
236
+  if (!text || typeof text !== 'string') {
237
+    return '';
238
+  }
239
+
240
+  // 如果文本长度小于等于最大长度,直接返回原文本
241
+  if (text.length <= maxLength) {
242
+    return text;
243
+  }
244
+
245
+  // 截取文本并添加后缀
246
+  return text.substring(0, maxLength) + suffix;
247
+}
248
+
249
+/**
250
+ * 智能文字截断函数,考虑中英文混合情况
251
+ * @param {string} text - 需要截断的文本
252
+ * @param {number} maxLength - 最大长度(按字符数计算),默认10
253
+ * @param {string} suffix - 后缀,默认'...'
254
+ * @returns {string} 截断后的文本
255
+ */
256
+export function smartTruncateText(text, maxLength = 10, suffix = '...') {
257
+  if (!text || typeof text !== 'string') {
258
+    return '';
259
+  }
260
+
261
+  // 计算字符数(中文字符算1个,英文字符算0.5个)
262
+  let charCount = 0;
263
+  let result = '';
264
+
265
+  for (let i = 0; i < text.length; i++) {
266
+    const char = text[i];
267
+    // 中文字符范围
268
+    if (char.charCodeAt(0) > 127) {
269
+      charCount += 1;
270
+    } else {
271
+      charCount += 0.5;
272
+    }
273
+
274
+    // 如果超过最大长度,添加后缀并返回
275
+    if (charCount > maxLength) {
276
+      return result + suffix;
277
+    }
278
+
279
+    result += char;
280
+  }
281
+
282
+  // 如果文本长度小于等于最大长度,直接返回原文本
283
+  return text;
284
+}
285
+
286
+/**
287
+ * 按单词截断文本(适用于英文文本)
288
+ * @param {string} text - 需要截断的文本
289
+ * @param {number} maxWords - 最大单词数,默认5
290
+ * @param {string} suffix - 后缀,默认'...'
291
+ * @returns {string} 截断后的文本
292
+ */
293
+export function truncateByWords(text, maxWords = 5, suffix = '...') {
294
+  if (!text || typeof text !== 'string') {
295
+    return '';
296
+  }
297
+
298
+  const words = text.trim().split(/\s+/);
299
+
300
+  if (words.length <= maxWords) {
301
+    return text;
302
+  }
303
+
304
+  return words.slice(0, maxWords).join(' ') + suffix;
305
+}
306
+
307
+/**
308
+ * 根据字典值获取对应的字典文字
309
+ * @param {string} dictType - 字典类型
310
+ * @param {string|number} dictValue - 字典值
311
+ * @param {string} defaultValue - 默认返回值,当找不到对应字典文字时返回
312
+ * @returns {Promise<string>} 字典文字
313
+ */
314
+export async function getDictLabelByValue(dictType, dictValue, defaultValue = '') {
315
+  if (!dictType || dictValue === undefined || dictValue === null) {
316
+    return defaultValue;
317
+  }
318
+
319
+  try {
320
+    // 调用listData接口获取字典数据
321
+    const response = await listData({
322
+      dictType: dictType,
323
+      dictValue: dictValue.toString()
324
+    });
325
+
326
+    // 如果接口返回成功且有数据
327
+    if (response.code === 200 && response.rows && response.rows.length > 0) {
328
+
329
+
330
+      return response.rows.find(item => item.dictValue === dictValue.toString())?.dictLabel || defaultValue;
331
+    }
332
+
333
+    return defaultValue;
334
+  } catch (error) {
335
+    console.error('获取字典文字失败:', error);
336
+    return defaultValue;
337
+  }
338
+}
339
+
340
+/**
341
+ * 检查角色是否有访问特定路径的权限
342
+ * @param {string} role - 角色名称
343
+ * @param {string} path - 访问路径
344
+ * @returns {boolean} 是否有权限
345
+ */
346
+export function checkRolePermission(role, path) {
347
+  // 角色权限配置对象
348
+  const rolePermissions = {
349
+    //安检员
350
+    SecurityCheck: [
351
+      '/pages/attendance/index',
352
+      `/pages/seizedReported/index?params=${encodeURIComponent(JSON.stringify({ type: 'add' }))}`,
353
+      '/pages/daily-exam/task-list/index',
354
+      '/pages/eikonStatistics/index',
355
+      '/pages/seizureRecord/index',
356
+      '/pages/seizeStatistics/index',
357
+      '/pages/voiceSubmissionDraft/index'
358
+    ],
359
+    //班组长
360
+    banzuzhang: [
361
+      '/pages/attendance/index',
362
+      `/pages/seizedReported/index?params=${encodeURIComponent(JSON.stringify({ type: 'add' }))}`,
363
+      '/pages/daily-exam/task-list/index',
364
+      '/pages/inspectionChecklist/index',
365
+      '/pages/eikonStatistics/index',
366
+      '/pages/seizureRecord/index',
367
+      '/pages/seizeStatistics/index',
368
+      '/pages/inspectionStatistics/index',
369
+      '/pages/questionStatistics/index',
370
+      '/pages/voiceSubmissionDraft/index'
371
+    ],
372
+    //站长
373
+    kezhang: [
374
+      '/pages/attendance/index',
375
+      '/pages/attendanceStatistics/index',
376
+      '/pages/inspectionChecklist/index',
377
+      '/pages/questionStatistics/index',
378
+      '/pages/inspectionStatistics/index',
379
+      '/pages/seizureRecord/index',
380
+      '/pages/seizeStatistics/index',
381
+      '/pages/eikonStatistics/index',
382
+      '/pages/workProfile/index',
383
+      '/pages/voiceSubmissionDraft/index'
384
+    ],
385
+    //站长
386
+    test: [
387
+      '/pages/attendanceStatistics/index',
388
+      '/pages/inspectionChecklist/index',
389
+      '/pages/questionStatistics/index',
390
+      '/pages/inspectionStatistics/index',
391
+      '/pages/seizureRecord/index',
392
+      '/pages/seizeStatistics/index',
393
+      '/pages/eikonStatistics/index',
394
+      '/pages/workProfile/index',
395
+      '/pages/voiceSubmissionDraft/index'
396
+    ],
397
+    zhijianke: [
398
+      '/pages/attendanceStatistics/index',
399
+      '/pages/inspectionChecklist/index',
400
+      '/pages/questionStatistics/index',
401
+      '/pages/inspectionStatistics/index',
402
+      '/pages/seizureRecord/index',
403
+      '/pages/seizeStatistics/index',
404
+      '/pages/eikonStatistics/index',
405
+      '/pages/workProfile/index',
406
+      '/pages/voiceSubmissionDraft/index'
407
+    ],
408
+  };
409
+
410
+  // 检查角色是否存在配置
411
+  if (!rolePermissions[role]) {
412
+    console.warn(`未找到角色 ${role} 的权限配置`);
413
+    return false;
414
+  }
415
+
416
+  // 检查路径是否在角色的权限列表中
417
+  return rolePermissions[role].includes(path);
418
+}
419
+

+ 13 - 0
src/utils/constant.js

@@ -0,0 +1,13 @@
1
+const constant = {
2
+  avatar: 'user_avatar',
3
+  id: 'user_id',
4
+  name: 'user_name',
5
+  roles: 'user_roles',
6
+  permissions: 'user_permissions',
7
+  nickName: 'nickName',
8
+  userInfo: 'userInfo',
9
+  eikonCurrentLevel: 'eikon_current_level',
10
+  eikonCurrentLevelId: 'eikon_current_level_id',
11
+ }
12
+
13
+ export default constant

+ 45 - 0
src/utils/dict.js

@@ -0,0 +1,45 @@
1
+// src/mixins/useDictMixin.js
2
+import { getDicts } from '@/api/system/dict/data';
3
+
4
+export default {
5
+  methods: {
6
+    async useDict(...args) {
7
+      const res = {};
8
+
9
+      for (const dictType of args) {
10
+        let dicts = this.getDictFromStore(dictType);
11
+
12
+        if (!dicts) {
13
+          try {
14
+            const response = await getDicts(dictType);
15
+            dicts = response.data.map(p => ({
16
+              label: p.dictLabel,
17
+              value: p.dictValue,
18
+              elTagType: p.listClass,
19
+              elTagClass: p.cssClass
20
+            }));
21
+            res[dictType] = dicts;
22
+            this.setDictToStore(dictType, dicts);
23
+          } catch (err) {
24
+            console.error(`获取字典 ${dictType} 失败`, err);
25
+            res[dictType] = [];
26
+          }
27
+        } else {
28
+          res[dictType] = dicts;
29
+        }
30
+      }
31
+
32
+      return res;
33
+    },
34
+
35
+    getDictFromStore(key) {
36
+      if (!key) return null;
37
+      const item = this.$store.state.dict.dict.find(item => item.key === key);
38
+      return item ? item.value : null;
39
+    },
40
+
41
+    setDictToStore(key, value) {
42
+      this.$store.dispatch('dict/setDict', { key, value }, { root: true });
43
+    }
44
+  }
45
+}

+ 13 - 0
src/utils/enums.js

@@ -0,0 +1,13 @@
1
+export const checkedLevelEnums = {
2
+    TEAM_LEVEL: 'TEAM_LEVEL',
3
+    PERSONNEL_LEVEL: 'PERSONNEL_LEVEL',
4
+    DEPARTMENT_LEVEL: 'DEPARTMENT_LEVEL',
5
+    STATION_LEVEL: 'STATION_LEVEL'
6
+}
7
+
8
+
9
+
10
+// 默认导出所有枚举
11
+export default {
12
+    checkedLevelEnums
13
+}

+ 6 - 0
src/utils/errorCode.js

@@ -0,0 +1,6 @@
1
+export default {
2
+  '401': '认证失败,无法访问系统资源',
3
+  '403': '当前操作没有权限',
4
+  '404': '访问资源不存在',
5
+  'default': '系统未知错误,请反馈给管理员'
6
+}

+ 321 - 0
src/utils/formatUtils.js

@@ -0,0 +1,321 @@
1
+/**
2
+ * 格式化方法
3
+ * @param {Date|string|number} input - 输入时间,可以是Date对象、时间戳或ISO格式字符串
4
+ * @param {string} [format='YYYY-MM-DD hh:mm:ss'] - 格式化字符串
5
+ * @param {Object} [options] - 配置选项
6
+ * @param {string} [options.timeZone='local'] - 时区,'local'或'UTC'
7
+ * @param {string} [options.locale='zh-CN'] - 区域设置
8
+ * @param {string} [options.defaultResult='-:-'] - 空值返回
9
+ * @returns {string} 格式化后的时间字符串
10
+ */
11
+export function formatTime(input, format = 'YYYY-MM-DD hh:mm:ss', options = {}) {
12
+  const {
13
+    timeZone = 'local',
14
+    locale = 'zh-CN',
15
+    defaultResult = '-:-'
16
+  } = options;
17
+
18
+  try {
19
+    // 1. 参数校验
20
+    if (input === null || input === undefined) {
21
+      return defaultResult;
22
+    }
23
+
24
+    // 2. 转换为Date对象
25
+    let date;
26
+    if (input instanceof Date) {
27
+      date = new Date(input);
28
+    } else if (typeof input === 'number') {
29
+      date = new Date(input);
30
+    } else if (typeof input === 'string') {
31
+      // 尝试解析ISO格式或常见字符串格式
32
+      date = new Date(input);
33
+      if (isNaN(date.getTime())) {
34
+        // 尝试处理不带时区的格式
35
+        const cleanedInput = input.replace(/-/g, '/').replace(/T/, ' ');
36
+        date = new Date(cleanedInput);
37
+        if (isNaN(date.getTime())) {
38
+          throw new Error(`Invalid date string: ${input}`);
39
+        }
40
+      }
41
+    } else {
42
+      return defaultResult;
43
+    }
44
+
45
+    // 检查日期是否有效
46
+    if (isNaN(date.getTime())) {
47
+      return defaultResult;
48
+    }
49
+
50
+    // 3. 提取日期时间组件(根据时区选择方法)
51
+    let year, month, day, hours, minutes, seconds, milliseconds, dayOfWeek;
52
+
53
+    if (timeZone === 'UTC') {
54
+      year = date.getUTCFullYear();
55
+      month = date.getUTCMonth() + 1;
56
+      day = date.getUTCDate();
57
+      hours = date.getUTCHours();
58
+      minutes = date.getUTCMinutes();
59
+      seconds = date.getUTCSeconds();
60
+      milliseconds = date.getUTCMilliseconds();
61
+      dayOfWeek = date.getUTCDay();
62
+    } else {
63
+      year = date.getFullYear();
64
+      month = date.getMonth() + 1;
65
+      day = date.getDate();
66
+      hours = date.getHours();
67
+      minutes = date.getMinutes();
68
+      seconds = date.getSeconds();
69
+      milliseconds = date.getMilliseconds();
70
+      dayOfWeek = date.getDay();
71
+    }
72
+
73
+    // 4. 定义替换规则
74
+    const replacements = {
75
+      YYYY: String(year).padStart(4, '0'),
76
+      YY: String(year).slice(-2),
77
+      MM: String(month).padStart(2, '0'),
78
+      M: String(month),
79
+      DD: String(day).padStart(2, '0'),
80
+      D: String(day),
81
+      hh: String(hours).padStart(2, '0'),
82
+      h: String(hours),
83
+      HH: String(hours % 12 || 12).padStart(2, '0'),
84
+      H: String(hours % 12 || 12),
85
+      mm: String(minutes).padStart(2, '0'),
86
+      m: String(minutes),
87
+      ss: String(seconds).padStart(2, '0'),
88
+      s: String(seconds),
89
+      SSS: String(milliseconds).padStart(3, '0'),
90
+      A: hours < 12 ? 'AM' : 'PM',
91
+      a: hours < 12 ? 'am' : 'pm',
92
+      ddd: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][dayOfWeek],
93
+      dddd: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][dayOfWeek],
94
+      Q: Math.ceil((month) / 3),
95
+      W: getWeekOfYear(date, timeZone),
96
+      Z: timeZone === 'UTC' ? '+00:00' : formatTimeZoneOffset(date.getTimezoneOffset())
97
+    };
98
+
99
+    // 5. 执行替换
100
+    let result = format;
101
+    for (const [token, value] of Object.entries(replacements)) {
102
+      result = result.replace(new RegExp(token, 'g'), value);
103
+    }
104
+
105
+    // 6. 处理本地化月份和星期名称
106
+    if (result.includes('MMMM') || result.includes('MMM')) {
107
+      const formatter = new Intl.DateTimeFormat(locale, {
108
+        month: 'long',
109
+        timeZone: timeZone === 'UTC' ? 'UTC' : undefined
110
+      });
111
+      const longMonth = formatter.format(date);
112
+      result = result.replace(/MMMM/g, longMonth);
113
+
114
+      const shortFormatter = new Intl.DateTimeFormat(locale, {
115
+        month: 'short',
116
+        timeZone: timeZone === 'UTC' ? 'UTC' : undefined
117
+      });
118
+      const shortMonth = shortFormatter.format(date);
119
+      result = result.replace(/MMM/g, shortMonth);
120
+    }
121
+
122
+    if (result.includes('dddd') || result.includes('ddd')) {
123
+      const formatter = new Intl.DateTimeFormat(locale, {
124
+        weekday: 'long',
125
+        timeZone: timeZone === 'UTC' ? 'UTC' : undefined
126
+      });
127
+      const longDay = formatter.format(date);
128
+      result = result.replace(/dddd/g, longDay);
129
+
130
+      const shortFormatter = new Intl.DateTimeFormat(locale, {
131
+        weekday: 'short',
132
+        timeZone: timeZone === 'UTC' ? 'UTC' : undefined
133
+      });
134
+      const shortDay = shortFormatter.format(date);
135
+      result = result.replace(/ddd/g, shortDay);
136
+    }
137
+
138
+    return result;
139
+  } catch (error) {
140
+    console.error('Error formatting time:', error);
141
+    return defaultResult;
142
+  }
143
+}
144
+
145
+// 辅助函数:获取一年中的第几周
146
+function getWeekOfYear(date, timeZone = 'local') {
147
+  const tempDate = new Date(date);
148
+  const firstDayOfYear = new Date(tempDate.getFullYear(), 0, 1);
149
+
150
+  if (timeZone === 'UTC') {
151
+    // 使用UTC方法计算
152
+    const utcDate = Date.UTC(tempDate.getUTCFullYear(), tempDate.getUTCMonth(), tempDate.getUTCDate());
153
+    const utcFirstDay = Date.UTC(firstDayOfYear.getUTCFullYear(), 0, 1);
154
+    const pastDaysOfYear = (utcDate - utcFirstDay) / 86400000;
155
+    return Math.ceil((pastDaysOfYear + firstDayOfYear.getUTCDay() + 1) / 7);
156
+  } else {
157
+    // 使用本地方法计算
158
+    const pastDaysOfYear = (tempDate - firstDayOfYear) / 86400000;
159
+    return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
160
+  }
161
+}
162
+
163
+// 辅助函数:格式化时区偏移
164
+function formatTimeZoneOffset(offset) {
165
+  const sign = offset <= 0 ? '+' : '-';
166
+  const absOffset = Math.abs(offset);
167
+  const hours = Math.floor(absOffset / 60);
168
+  const minutes = absOffset % 60;
169
+  return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
170
+}
171
+
172
+
173
+/*
174
+// 基本格式
175
+console.log(formatTime(now)); // "2023-05-15 14:30:45"
176
+console.log(formatTime(now, 'YYYY/MM/DD')); // "2023/05/15"
177
+console.log(formatTime(now, 'hh:mm:ss.SSS')); // "14:30:45.123"
178
+
179
+// 12小时制
180
+console.log(formatTime(now, 'HH:mm:ss A')); // "02:30:45 PM"
181
+
182
+// 季度和周数
183
+console.log(formatTime(now, 'YYYY-Q W')); // "2023-2 20"
184
+
185
+// 星期和月份名称
186
+console.log(formatTime(now, 'dddd, MMMM D, YYYY')); // "Monday, May 15, 2023"
187
+
188
+// 不同区域设置
189
+console.log(formatTime(now, 'dddd, MMMM D, YYYY', { locale: 'zh-CN' })); // "星期一, 五月 15, 2023"
190
+
191
+// UTC时间
192
+console.log(formatTime(now, 'YYYY-MM-DD HH:mm:ss Z', { timeZone: 'UTC' })); // "2023-05-15 06:30:45 +00:00"
193
+
194
+// 不同输入类型
195
+console.log(formatTime(1673789445123)); // 时间戳
196
+console.log(formatTime('2023-01-15T14:30:45.123Z')); // ISO字符串
197
+console.log(formatTime('2023/01/15 14:30:45')); // 普通字符串
198
+*/
199
+
200
+
201
+export function isAfterTodayStart(timeString) {
202
+  if (!timeString || typeof timeString !== 'string') {
203
+    return false;
204
+  }
205
+  // 尝试多种日期格式解析
206
+  let inputDate;
207
+
208
+  // 尝试标准格式
209
+  inputDate = new Date(timeString);
210
+
211
+  // 如果解析失败,尝试其他常见格式
212
+  if (isNaN(inputDate.getTime())) {
213
+    // 处理 YYYY-MM-DD 格式
214
+    if (/^\d{4}-\d{2}-\d{2}$/.test(timeString)) {
215
+      inputDate = new Date(timeString + 'T00:00:00');
216
+    }
217
+    // 处理 YYYY/MM/DD 格式
218
+    else if (/^\d{4}\/\d{2}\/\d{2}$/.test(timeString)) {
219
+      inputDate = new Date(timeString.replace(/\//g, '-') + 'T00:00:00');
220
+    }
221
+    // 处理时间戳
222
+    else if (/^\d+$/.test(timeString)) {
223
+      inputDate = new Date(parseInt(timeString));
224
+    }
225
+  }
226
+
227
+  // 如果还是无效日期,返回false
228
+  if (isNaN(inputDate.getTime())) {
229
+    console.warn('无效的时间格式:', timeString);
230
+    return false;
231
+  }
232
+
233
+  // 获取当前日期的零点时刻
234
+  const now = new Date();
235
+  const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
236
+
237
+  return inputDate.getTime() > todayStart.getTime();
238
+}
239
+
240
+/**
241
+ * 格式化人名
242
+ * @param {string} name - 输入的人名
243
+ * @returns {string} 格式化后的人名
244
+ */
245
+export function formatName(name) {
246
+  // 1. 输入验证
247
+  if (name == null) return ''; // 处理null或undefined
248
+  if (typeof name !== 'string') {
249
+    // 尝试转换非字符串输入
250
+    try {
251
+      name = String(name);
252
+    } catch (e) {
253
+      return '';
254
+    }
255
+  }
256
+
257
+  // 2. 去除首尾空白字符
258
+  name = name.trim();
259
+
260
+  // 3. 检查是否为空字符串
261
+  if (name.length === 0) return '';
262
+
263
+  // 4. 处理2个字符的情况(中文或英文)
264
+  if (name.length === 2) {
265
+    // 检查是否是有效字符(避免在符号或数字中间加空格)
266
+    const isAllValidChars = /^[\p{L}\p{M}]{2}$/u.test(name);
267
+    if (isAllValidChars) {
268
+      return `${name[0]} ${name[1]}`;
269
+    }
270
+  }
271
+
272
+  // 5. 返回其他情况的原名
273
+  return name;
274
+}
275
+
276
+// 计算时间范围
277
+export function calculateTimeRange(activeTimeRange, timeRange) {
278
+  const now = new Date();
279
+  let startDate = new Date();
280
+  let endDate = new Date();
281
+
282
+  switch (activeTimeRange) {
283
+    case 'week':
284
+      startDate.setDate(now.getDate() - 7);
285
+      break;
286
+    case 'month':
287
+      // 本月:当前月份的第一天到当前天
288
+      startDate = new Date(now.getFullYear(), now.getMonth(), 1);
289
+      endDate = new Date(now);
290
+      break;
291
+    case 'quarter':
292
+      startDate.setDate(now.getDate() - 90);
293
+      break;
294
+    case 'year':
295
+      // 本年:当前年份的第一天到当前天
296
+      startDate = new Date(now.getFullYear(), 0, 1);
297
+      endDate = new Date(now);
298
+      break;
299
+    case 'custom':
300
+      // 自定义时间范围:需要从组件中获取
301
+      // 假设组件有startDate和endDate属性
302
+      startDate = new Date(timeRange[0]);
303
+      endDate = new Date(timeRange[1]);
304
+      break;
305
+    default:
306
+      startDate.setDate(now.getDate() - 7); // 默认近一周
307
+  }
308
+
309
+  // 格式化日期为YYYY-MM-DD格式
310
+  const formatDate = (date) => {
311
+    const year = date.getFullYear();
312
+    const month = String(date.getMonth() + 1).padStart(2, '0');
313
+    const day = String(date.getDate()).padStart(2, '0');
314
+    return `${year}-${month}-${day}`;
315
+  };
316
+
317
+  return {
318
+    startDate: formatDate(startDate),
319
+    endDate: formatDate(endDate)
320
+  }
321
+}

+ 257 - 0
src/utils/handler.js

@@ -0,0 +1,257 @@
1
+/**
2
+ * 节流函数
3
+ * @param {Function} func 要执行的函数
4
+ * @param {number} delay 节流时间间隔(毫秒)
5
+ * @returns {Function} 经过节流处理的函数
6
+ */
7
+export function throttle (func, delay) {
8
+  let lastExecTime = 0;
9
+  let timerId = null;
10
+
11
+  return function (...args) {
12
+    const context = this;
13
+    const now = Date.now();
14
+    const timeSinceLastExec = now - lastExecTime;
15
+
16
+    // 如果距离上次执行时间已经超过delay,立即执行
17
+    if (timeSinceLastExec >= delay) {
18
+      lastExecTime = now;
19
+      func.apply(context, args);
20
+    }
21
+    // 否则设置定时器,在剩余时间后执行
22
+    else if (!timerId) {
23
+      timerId = setTimeout(() => {
24
+        lastExecTime = Date.now();
25
+        timerId = null;
26
+        func.apply(context, args);
27
+      }, delay - timeSinceLastExec);
28
+    }
29
+  };
30
+}
31
+
32
+/**
33
+ * 防抖函数
34
+ * @param {Function} func 要执行的函数
35
+ * @param {number} wait 等待时间(毫秒)
36
+ * @returns {Function} 经过防抖处理的函数
37
+ */
38
+export function debounce (func, wait) {
39
+  let timeoutId;
40
+
41
+  return function (...args) {
42
+    const context = this;
43
+
44
+    // 清除之前的定时器
45
+    clearTimeout(timeoutId);
46
+
47
+    // 设置新的定时器
48
+    timeoutId = setTimeout(() => {
49
+      func.apply(context, args);
50
+    }, wait);
51
+  };
52
+}
53
+
54
+export function generateRandomDigits (length = 6) {
55
+  let result = '';
56
+  for (let i = 0; i < length; i++) {
57
+    result += Math.floor(Math.random() * 10);
58
+  }
59
+  return result;
60
+}
61
+
62
+// 新增:生成头像渐变背景色
63
+export function getAvatarGradient (name) {
64
+  const colors = [
65
+    'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
66
+    'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
67
+    'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',
68
+    'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)',
69
+    'linear-gradient(135deg, #fa709a 0%, #fee140 100%)',
70
+    'linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)',
71
+    'linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%)',
72
+    'linear-gradient(135deg, #ff8a80 0%, #ff5722 100%)',
73
+    'linear-gradient(135deg, #84fab0 0%, #8fd3f4 100%)',
74
+    'linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%)'
75
+  ];
76
+
77
+  if (!name) return colors[ 0 ];
78
+  // 根据名字生成一个索引
79
+  let hash = 0;
80
+  for (let i = 0; i < name.length; i++) {
81
+    hash = name.charCodeAt(i) + ((hash << 5) - hash);
82
+  }
83
+  const index = Math.abs(hash) % colors.length;
84
+  return colors[ index ];
85
+}
86
+
87
+export const bufferConverter = {
88
+  // 字符串转ArrayBuffer(UTF-8编码)
89
+  stringToBase64(str) {
90
+    let length = 0;
91
+    for (let i = 0; i < str.length; i++) {
92
+      const code = str.charCodeAt(i);
93
+      if (code < 0x80) {
94
+        length += 1;
95
+      } else if (code < 0x800) {
96
+        length += 2;
97
+      } else {
98
+        length += 3;
99
+      }
100
+    }
101
+    
102
+    const buffer = new ArrayBuffer(length);
103
+    const view = new Uint8Array(buffer);
104
+    let offset = 0;
105
+    
106
+    for (let i = 0; i < str.length; i++) {
107
+      const code = str.charCodeAt(i);
108
+      
109
+      if (code < 0x80) {
110
+        view[offset++] = code;
111
+      } else if (code < 0x800) {
112
+        view[offset++] = 0xC0 | (code >> 6);
113
+        view[offset++] = 0x80 | (code & 0x3F);
114
+      } else {
115
+        view[offset++] = 0xE0 | (code >> 12);
116
+        view[offset++] = 0x80 | ((code >> 6) & 0x3F);
117
+        view[offset++] = 0x80 | (code & 0x3F);
118
+      }
119
+    }
120
+    
121
+    return uni.arrayBufferToBase64(buffer)
122
+  },
123
+
124
+  // ArrayBuffer转字符串(UTF-8解码)
125
+  base64ToString(base64) {
126
+    const buffer = uni.base64ToArrayBuffer(base64)
127
+    const bytes = new Uint8Array(buffer);
128
+    let result = '';
129
+    let i = 0;
130
+    
131
+    while (i < bytes.length) {
132
+      const byte1 = bytes[i++];
133
+      
134
+      if (byte1 < 0x80) {
135
+        result += String.fromCharCode(byte1);
136
+      } else if (byte1 < 0xE0) {
137
+        const byte2 = bytes[i++];
138
+        if (i > bytes.length) break;
139
+        result += String.fromCharCode(
140
+          ((byte1 & 0x1F) << 6) | (byte2 & 0x3F)
141
+        );
142
+      } else if (byte1 < 0xF0) {
143
+        const byte2 = bytes[i++];
144
+        const byte3 = bytes[i++];
145
+        if (i > bytes.length) break;
146
+        result += String.fromCharCode(
147
+          ((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F)
148
+        );
149
+      } else {
150
+        // 处理四字节UTF-8字符(代理对)
151
+        i += 3;
152
+        result += '�';
153
+      }
154
+    }
155
+    
156
+    return result;
157
+  }
158
+}
159
+
160
+
161
+export function isInRangeOptimized (current, center, radius) {
162
+  console.log(current, center);
163
+  
164
+  const R = 6371e3; // 地球半径 米
165
+  const radLat1 = (current.latitude * Math.PI) / 180;
166
+  const radLat2 = (center.latitude * Math.PI) / 180;
167
+  const deltaLat = ((center.latitude - current.latitude) * Math.PI) / 180;
168
+  const deltaLon = ((center.longitude - current.longitude) * Math.PI) / 180;
169
+
170
+  const a =
171
+    Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
172
+    Math.cos(radLat1) * Math.cos(radLat2) * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
173
+  
174
+  return a <= Math.pow(radius / R, 2);
175
+}
176
+
177
+/**
178
+ * WGS-84 转 GCJ-02(火星坐标系)
179
+ * @param {number} wgsLat - WGS-84 纬度
180
+ * @param {number} wgsLon - WGS-84 经度
181
+ * @returns {[number, number]} [gcjLat, gcjLon]
182
+ */
183
+export function wgs84ToGcj02(wgsLat, wgsLon) {
184
+  const a = 6378245.0; // 长半轴(GCJ-02 椭球参数)
185
+  const ee = 0.00669342162296594323; // 扁率(GCJ-02 椭球参数)
186
+
187
+  // 判断是否在国内(不在国内则直接返回原坐标)
188
+  if (outOfChina(wgsLat, wgsLon)) {
189
+    return [wgsLat, wgsLon];
190
+  }
191
+
192
+  // 计算偏移量
193
+  let dLat = transformLat(wgsLon - 105.0, wgsLat - 35.0);
194
+  let dLon = transformLon(wgsLon - 105.0, wgsLat - 35.0);
195
+  const radLat = (wgsLat * Math.PI) / 180.0;
196
+  let magic = Math.sin(radLat);
197
+  magic = 1 - ee * magic * magic;
198
+  const sqrtMagic = Math.sqrt(magic);
199
+  dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.PI);
200
+  dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI);
201
+
202
+  // 返回 GCJ-02 坐标
203
+  return [wgsLat + dLat, wgsLon + dLon];
204
+}
205
+
206
+// 辅助函数:判断坐标是否在国外(无需偏移)
207
+function outOfChina(lat, lon) {
208
+  return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271;
209
+}
210
+
211
+// 辅助函数:计算纬度偏移
212
+function transformLat(x, y) {
213
+  let ret =
214
+    -100.0 +
215
+    2.0 * x +
216
+    3.0 * y +
217
+    0.2 * y * y +
218
+    0.1 * x * y +
219
+    0.2 * Math.sqrt(Math.abs(x));
220
+  ret +=
221
+    ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) *
222
+      2.0) /
223
+    3.0;
224
+  ret +=
225
+    ((20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin((y / 3.0) * Math.PI)) *
226
+      2.0) /
227
+    3.0;
228
+  ret +=
229
+    ((160.0 * Math.sin((y / 12.0) * Math.PI) + 320 * Math.sin((y * Math.PI) / 30.0)) *
230
+      2.0) /
231
+    3.0;
232
+  return ret;
233
+}
234
+
235
+// 辅助函数:计算经度偏移
236
+function transformLon(x, y) {
237
+  let ret =
238
+    300.0 +
239
+    x +
240
+    2.0 * y +
241
+    0.1 * x * x +
242
+    0.1 * x * y +
243
+    0.1 * Math.sqrt(Math.abs(x));
244
+  ret +=
245
+    ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) *
246
+      2.0) /
247
+    3.0;
248
+  ret +=
249
+    ((20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin((x / 3.0) * Math.PI)) *
250
+      2.0) /
251
+    3.0;
252
+  ret +=
253
+    ((150.0 * Math.sin((x / 12.0) * Math.PI) + 300.0 * Math.sin((x / 30.0) * Math.PI)) *
254
+      2.0) /
255
+    3.0;
256
+  return ret;
257
+}

+ 51 - 0
src/utils/permission.js

@@ -0,0 +1,51 @@
1
+import store from '@/store'
2
+
3
+/**
4
+ * 字符权限校验
5
+ * @param {Array} value 校验值
6
+ * @returns {Boolean}
7
+ */
8
+export function checkPermi(value) {
9
+  if (value && value instanceof Array && value.length > 0) {
10
+    const permissions = store.getters && store.getters.permissions
11
+    const permissionDatas = value
12
+    const all_permission = "*:*:*"
13
+
14
+    const hasPermission = permissions.some(permission => {
15
+      return all_permission === permission || permissionDatas.includes(permission)
16
+    })
17
+
18
+    if (!hasPermission) {
19
+      return false
20
+    }
21
+    return true
22
+  } else {
23
+    console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
24
+    return false
25
+  }
26
+}
27
+
28
+/**
29
+ * 角色权限校验
30
+ * @param {Array} value 校验值
31
+ * @returns {Boolean}
32
+ */
33
+export function checkRole(value) {
34
+  if (value && value instanceof Array && value.length > 0) {
35
+    const roles = store.getters && store.getters.roles
36
+    const permissionRoles = value
37
+    const super_admin = "admin"
38
+
39
+    const hasRole = roles.some(role => {
40
+      return super_admin === role || permissionRoles.includes(role)
41
+    })
42
+
43
+    if (!hasRole) {
44
+      return false
45
+    }
46
+    return true
47
+  } else {
48
+    console.error(`need roles! Like checkRole="['admin','editor']"`)
49
+    return false
50
+  }
51
+}

+ 130 - 0
src/utils/request.js

@@ -0,0 +1,130 @@
1
+import store from '@/store'
2
+import config from '@/config'
3
+import { getToken } from '@/utils/auth'
4
+import errorCode from '@/utils/errorCode'
5
+import { toast, showConfirm, tansParams } from '@/utils/common'
6
+
7
+let timeout = 10000
8
+const baseUrl = config.baseUrl
9
+let isRelogin = false // 防止重复登录弹窗
10
+
11
+let silentLoginStatus = true // 暂时没有静默逻辑
12
+
13
+const request = config => {
14
+  // 是否需要设置 token
15
+  const isToken = (config.headers || {}).isToken === false
16
+  config.header = config.header || {}
17
+  
18
+  // 添加全局请求头
19
+  config.header['X-Request-Source'] = 'mobile'
20
+  
21
+  if (getToken() && !isToken) {
22
+    config.header['Authorization'] = 'Bearer ' + getToken()
23
+  }
24
+  // get请求映射params参数
25
+  if (config.params) {
26
+    let url = config.url + '?' + tansParams(config.params)
27
+    url = url.slice(0, -1)
28
+    config.url = url
29
+  }
30
+  return new Promise((resolve, reject) => {
31
+    uni.request({
32
+      method: config.method || 'get',
33
+      timeout: config.timeout || timeout,
34
+      url: config.baseUrl || baseUrl + config.url,
35
+      data: config.data,
36
+      header: config.header,
37
+      dataType: 'json'
38
+    }).then(response => {
39
+      let [error, res] = response
40
+      if (error) {
41
+        // toast('后端接口连接异常')
42
+        reject('后端接口连接异常')
43
+        return
44
+      }
45
+      const code = res.data.code || 200
46
+      const msg = errorCode[code] || res.data.msg || errorCode['default']
47
+      if (code === 401) {
48
+        // 静默登陆 等待后端实现 刷新token 或者 支持验证码后门才行
49
+        // 这里静默登陆 只保留一个静默登陆的请求;防止并发请求出现多次静默登陆
50
+        if (!silentLoginStatus) {
51
+          silentLoginStatus = uni.request({
52
+            method: config.method || 'get',
53
+            timeout: config.timeout || timeout,
54
+            url: config.baseUrl || baseUrl + config.url,
55
+            data: config.data,
56
+            header: config.header,
57
+            dataType: 'json'
58
+          })
59
+        }
60
+        // promise已经兑现 这里即使多次也只是单纯的获取以兑现的promise结果
61
+        silentLoginStatus && silentLoginStatus instanceof Promise && silentLoginStatus.then(res => {
62
+          // 重新请求一次将新的响应体返回给当前请求处理的resolve
63
+          request(config).then(resolve).catch(reject)
64
+        })
65
+        // 上面实现下面可以逻辑就可以删除了
66
+
67
+        // 获取当前页面信息
68
+        const pages = getCurrentPages()
69
+        const currentPage = pages[pages.length - 1]
70
+        const currentRoute = currentPage ? currentPage.route : ''
71
+
72
+        // 如果在登录页面,不显示弹窗,直接清除token
73
+        if (currentRoute === 'pages/login') {
74
+          const { removeToken } = require('@/utils/auth')
75
+          const storage = require('@/utils/storage').default
76
+          removeToken()
77
+          storage.clean()
78
+          store.commit('SET_TOKEN', '')
79
+          store.commit('SET_ROLES', [])
80
+          store.commit('SET_PERMISSIONS', [])
81
+          reject('无效的会话,或者会话已过期,请重新登录。')
82
+          return
83
+        }
84
+
85
+        if (!isRelogin) {
86
+          isRelogin = true
87
+          showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => {
88
+            if (res.confirm) {
89
+              // 直接清除本地数据,不调用logout API避免循环
90
+              store.commit('SET_TOKEN', '')
91
+              store.commit('SET_ROLES', [])
92
+              store.commit('SET_PERMISSIONS', [])
93
+              const { removeToken } = require('@/utils/auth')
94
+              const storage = require('@/utils/storage').default
95
+              removeToken()
96
+              storage.clean()
97
+              uni.reLaunch({ url: '/pages/login' })
98
+            }
99
+            isRelogin = false
100
+          }).catch(() => {
101
+            isRelogin = false
102
+          })
103
+        }
104
+        reject('无效的会话,或者会话已过期,请重新登录。')
105
+      } else if (code === 500) {
106
+        console.log("util=======", msg)
107
+        // toast(msg)
108
+        reject({ "code": code, "msg": msg })
109
+      } if (code !== 200) {
110
+        // toast(msg)
111
+        reject(code)
112
+      }
113
+      resolve(res.data)
114
+    })
115
+      .catch(error => {
116
+        let { message } = error
117
+        if (message === 'Network Error') {
118
+          message = '后端接口连接异常'
119
+        } else if (message.includes('timeout')) {
120
+          message = '系统接口请求超时'
121
+        } else if (message.includes('Request failed with status code')) {
122
+          message = '系统接口' + message.substr(message.length - 3) + '异常'
123
+        }
124
+        // toast(message)
125
+        reject(error)
126
+      })
127
+  })
128
+}
129
+
130
+export default request

+ 32 - 0
src/utils/storage.js

@@ -0,0 +1,32 @@
1
+import constant from './constant'
2
+
3
+// 存储变量名
4
+let storageKey = 'storage_data'
5
+
6
+// 存储节点变量名
7
+let storageNodeKeys = [constant.avatar, constant.userInfo, constant.id, constant.name, constant.roles, constant.permissions]
8
+
9
+const storage = {
10
+  set: function(key, value) {
11
+    if (storageNodeKeys.indexOf(key) != -1) {
12
+      let tmp = uni.getStorageSync(storageKey)
13
+      tmp = tmp ? tmp : {}
14
+      tmp[key] = value
15
+      uni.setStorageSync(storageKey, tmp)
16
+    }
17
+  },
18
+  get: function(key) {
19
+    let storageData = uni.getStorageSync(storageKey) || {}
20
+    return storageData[key] || ""
21
+  },
22
+  remove: function(key) {
23
+    let storageData = uni.getStorageSync(storageKey) || {}
24
+    delete storageData[key]
25
+    uni.setStorageSync(storageKey, storageData)
26
+  },
27
+  clean: function() {
28
+    uni.removeStorageSync(storageKey)
29
+  }
30
+}
31
+
32
+export default storage

+ 70 - 0
src/utils/upload.js

@@ -0,0 +1,70 @@
1
+import store from '@/store'
2
+import config from '@/config'
3
+import { getToken } from '@/utils/auth'
4
+import errorCode from '@/utils/errorCode'
5
+import { toast, showConfirm, tansParams } from '@/utils/common'
6
+
7
+let timeout = 10000
8
+const baseUrl = config.baseUrl
9
+
10
+const upload = config => {
11
+  // 是否需要设置 token
12
+  const isToken = (config.headers || {}).isToken === false
13
+  config.header = config.header || {}
14
+  if (getToken() && !isToken) {
15
+    config.header['Authorization'] = 'Bearer ' + getToken()
16
+  }
17
+  // get请求映射params参数
18
+  if (config.params) {
19
+    let url = config.url + '?' + tansParams(config.params)
20
+    url = url.slice(0, -1)
21
+    config.url = url
22
+  }
23
+  return new Promise((resolve, reject) => {
24
+      uni.uploadFile({
25
+        timeout: config.timeout || timeout,
26
+        url: baseUrl + config.url,
27
+        filePath: config.filePath,
28
+        name: config.name || 'file',
29
+        header: config.header,
30
+        formData: config.formData,
31
+        success: (res) => {
32
+          let result = JSON.parse(res.data)
33
+          const code = result.code || 200
34
+          const msg = errorCode[code] || result.msg || errorCode['default']
35
+          if (code === 200) {
36
+            resolve(result)
37
+          } else if (code == 401) {
38
+            showConfirm("登录状态已过期,您可以继续留在该页面,或者重新登录?").then(res => {
39
+              if (res.confirm) {
40
+                store.dispatch('LogOut').then(res => {
41
+                  uni.reLaunch({ url: '/pages/login/login' })
42
+                })
43
+              }
44
+            })
45
+            reject('无效的会话,或者会话已过期,请重新登录。')
46
+          } else if (code === 500) {
47
+            toast(msg)
48
+            reject('500')
49
+          } else if (code !== 200) {
50
+            toast(msg)
51
+            reject(code)
52
+          }
53
+        },
54
+        fail: (error) => {
55
+          let { message } = error
56
+          if (message == 'Network Error') {
57
+            message = '后端接口连接异常'
58
+          } else if (message.includes('timeout')) {
59
+            message = '系统接口请求超时'
60
+          } else if (message.includes('Request failed with status code')) {
61
+            message = '系统接口' + message.substr(message.length - 3) + '异常'
62
+          }
63
+          toast(message)
64
+          reject(error)
65
+        }
66
+      })
67
+  })
68
+}
69
+
70
+export default upload

+ 114 - 0
src/utils/validate.js

@@ -0,0 +1,114 @@
1
+/**
2
+ * 路径匹配器
3
+ * @param {string} pattern
4
+ * @param {string} path
5
+ * @returns {Boolean}
6
+ */
7
+export function isPathMatch(pattern, path) {
8
+  const regexPattern = pattern.replace(/\//g, '\\/').replace(/\*\*/g, '.*').replace(/\*/g, '[^\\/]*')
9
+  const regex = new RegExp(`^${regexPattern}$`)
10
+  return regex.test(path)
11
+}
12
+
13
+/**
14
+ * 判断value字符串是否为空 
15
+ * @param {string} value
16
+ * @returns {Boolean}
17
+ */
18
+export function isEmpty(value) {
19
+  if (value == null || value == "" || value == undefined || value == "undefined") {
20
+    return true
21
+  }
22
+  return false
23
+}
24
+
25
+/**
26
+ * 判断url是否是http或https 
27
+ * @param {string} url
28
+ * @returns {Boolean}
29
+ */
30
+export function isHttp(url) {
31
+  return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
32
+}
33
+
34
+/**
35
+ * 判断path是否为外链
36
+ * @param {string} path
37
+ * @returns {Boolean}
38
+ */
39
+export function isExternal(path) {
40
+  return /^(https?:|mailto:|tel:)/.test(path)
41
+}
42
+
43
+/**
44
+ * @param {string} str
45
+ * @returns {Boolean}
46
+ */
47
+export function validUsername(str) {
48
+  const valid_map = ['admin', 'editor']
49
+  return valid_map.indexOf(str.trim()) >= 0
50
+}
51
+
52
+/**
53
+ * @param {string} url
54
+ * @returns {Boolean}
55
+ */
56
+export function validURL(url) {
57
+  const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
58
+  return reg.test(url)
59
+}
60
+
61
+/**
62
+ * @param {string} str
63
+ * @returns {Boolean}
64
+ */
65
+export function validLowerCase(str) {
66
+  const reg = /^[a-z]+$/
67
+  return reg.test(str)
68
+}
69
+
70
+/**
71
+ * @param {string} str
72
+ * @returns {Boolean}
73
+ */
74
+export function validUpperCase(str) {
75
+  const reg = /^[A-Z]+$/
76
+  return reg.test(str)
77
+}
78
+
79
+/**
80
+ * @param {string} str
81
+ * @returns {Boolean}
82
+ */
83
+export function validAlphabets(str) {
84
+  const reg = /^[A-Za-z]+$/
85
+  return reg.test(str)
86
+}
87
+
88
+/**
89
+ * @param {string} email
90
+ * @returns {Boolean}
91
+ */
92
+export function validEmail(email) {
93
+  const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
94
+  return reg.test(email)
95
+}
96
+
97
+/**
98
+ * @param {string} str
99
+ * @returns {Boolean}
100
+ */
101
+export function isString(str) {
102
+  return typeof str === 'string' || str instanceof String
103
+}
104
+
105
+/**
106
+ * @param {Array} arg
107
+ * @returns {Boolean}
108
+ */
109
+export function isArray(arg) {
110
+  if (typeof Array.isArray === 'undefined') {
111
+    return Object.prototype.toString.call(arg) === '[object Array]'
112
+  }
113
+  return Array.isArray(arg)
114
+}