diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index dbcb83a..2aac387 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -18,7 +18,8 @@
"Bash(\"D:\\Program Files\\mysql-8.0.32-winx64\\bin\\mysql.exe\" -h localhost -P 3306 -u root -p123456 -D martial_db -e \"SHOW TABLES LIKE ''%order%'';\")",
"Bash(\"D:\\Program Files\\mysql-8.0.32-winx64\\bin\\mysql.exe\" -h localhost -P 3306 -u root -p123456 -D martial_db -e \"SHOW TABLES;\")",
"Bash(\"D:\\Program Files\\mysql-8.0.32-winx64\\bin\\mysql.exe\" -h localhost -P 3306 -u root -p123456 -D martial_db -e \"DESCRIBE athlete;\")",
- "Bash(tree:*)"
+ "Bash(tree:*)",
+ "Bash(find:*)"
],
"deny": [],
"ask": []
diff --git a/api/schedule.js b/api/schedule.js
new file mode 100644
index 0000000..ff156af
--- /dev/null
+++ b/api/schedule.js
@@ -0,0 +1,103 @@
+/**
+ * 赛程编排相关API接口
+ */
+
+import request from '@/utils/request.js'
+
+/**
+ * 获取赛程编排结果
+ * @param {Number} competitionId - 赛事ID
+ */
+export function getScheduleResult(competitionId) {
+ return request.get('/martial/schedule/result', {
+ params: { competitionId }
+ })
+}
+
+/**
+ * 触发自动编排
+ * @param {Number} competitionId - 赛事ID
+ */
+export function triggerAutoArrange(competitionId) {
+ return request.post('/martial/schedule/auto-arrange', {
+ competitionId
+ })
+}
+
+/**
+ * 保存编排草稿
+ * @param {Object} data - 编排草稿数据
+ * @param {Number} data.competitionId - 赛事ID
+ * @param {Boolean} data.isDraft - 是否为草稿
+ * @param {Array} data.competitionGroups - 竞赛分组数据
+ */
+export function saveDraftSchedule(data) {
+ return request.post('/martial/schedule/save-draft', data)
+}
+
+/**
+ * 保存并锁定赛程编排
+ * @param {Number} competitionId - 赛事ID
+ */
+export function saveAndLockSchedule(competitionId) {
+ return request.post('/martial/schedule/save-and-lock', {
+ competitionId
+ })
+}
+
+/**
+ * 移动赛程分组到指定场地和时间段
+ * @param {Object} data - 移动请求数据
+ * @param {Number} data.groupId - 分组ID
+ * @param {Number} data.targetVenueId - 目标场地ID
+ * @param {Number} data.targetTimeSlotIndex - 目标时间段索引
+ */
+export function moveScheduleGroup(data) {
+ return request.post('/martial/schedule/move-group', data)
+}
+
+/**
+ * 获取调度数据
+ * @param {Object} params - 查询参数
+ * @param {Number} params.competitionId - 赛事ID
+ * @param {Number} params.venueId - 场地ID
+ * @param {Number} params.timeSlotIndex - 时间段索引
+ */
+export function getDispatchData(params) {
+ return request.get('/martial/schedule/dispatch-data', {
+ params
+ })
+}
+
+/**
+ * 调整出场顺序
+ * @param {Object} data - 调整请求数据
+ * @param {Number} data.detailId - 编排明细ID
+ * @param {Number} data.participantId - 参赛者记录ID
+ * @param {String} data.action - 调整动作(move_up/move_down/swap)
+ * @param {Number} data.targetOrder - 目标顺序(交换时使用)
+ */
+export function adjustOrder(data) {
+ return request.post('/martial/schedule/adjust-order', data)
+}
+
+/**
+ * 批量保存调度
+ * @param {Object} data - 保存调度数据
+ * @param {Number} data.competitionId - 赛事ID
+ * @param {Array} data.adjustments - 调整列表
+ */
+export function saveDispatch(data) {
+ return request.post('/martial/schedule/save-dispatch', data)
+}
+
+export default {
+ getScheduleResult,
+ triggerAutoArrange,
+ saveDraftSchedule,
+ saveAndLockSchedule,
+ moveScheduleGroup,
+ getDispatchData,
+ adjustOrder,
+ saveDispatch
+}
diff --git a/api/user.js b/api/user.js
index 24c09d2..5ab7acf 100644
--- a/api/user.js
+++ b/api/user.js
@@ -3,8 +3,90 @@
*/
import request from '@/utils/request.js'
+import md5 from '@/utils/md5.js'
+import { base64Encode } from '@/utils/base64.js'
+
+/**
+ * 用户登录
+ * 使用password模式(无需验证码)
+ */
+export function login(data) {
+ // 构建URL参数
+ const params = {
+ tenantId: '000000',
+ username: data.username,
+ password: md5(data.password),
+ grant_type: 'password', // 使用password模式
+ scope: 'all',
+ type: 'account'
+ }
+
+ // 转换为URL查询字符串
+ const queryString = Object.keys(params)
+ .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
+ .join('&')
+
+ // 使用saber3客户端凭证
+ const basicAuth = 'Basic ' + base64Encode('saber3:saber3_secret')
+
+ return request.post(`/blade-auth/oauth/token?${queryString}`, null, {
+ header: {
+ 'Authorization': basicAuth,
+ 'Tenant-Id': '000000'
+ }
+ })
+}
+
+/**
+ * 用户注册
+ */
+export function register(data) {
+ return request.post('/blade-system/user/register', {
+ tenantId: '000000',
+ userType: 2, // 2-app
+ account: data.account,
+ password: md5(data.password),
+ phone: data.phone,
+ realName: data.realName,
+ sex: data.sex,
+ code: data.code
+ })
+}
+
+/**
+ * 获取验证码
+ */
+export function getCaptcha(phone) {
+ return request.post('/blade-auth/captcha/send', {
+ phone: phone
+ })
+}
+
+/**
+ * 刷新Token
+ */
+export function refreshToken(refreshToken) {
+ return request.post('/blade-auth/oauth/token', {
+ tenantId: '000000',
+ refresh_token: refreshToken,
+ grant_type: 'refresh_token'
+ })
+}
+
+/**
+ * 退出登录
+ */
+export function logout() {
+ return request.post('/blade-auth/logout')
+}
export default {
+ login,
+ register,
+ getCaptcha,
+ refreshToken,
+ logout,
+
/**
* 获取用户信息
* @returns {Promise}
@@ -15,11 +97,15 @@ export default {
/**
* 修改密码
- * @param {Object} data { oldPassword, newPassword, confirmPassword }
+ * @param {Object} data { oldPassword, newPassword, newPassword1 }
* @returns {Promise}
*/
updatePassword(data) {
- return request.post('/blade-system/user/update-password', data)
+ return request.post('/blade-system/user/update-password', {
+ oldPassword: md5(data.oldPassword),
+ newPassword: md5(data.newPassword),
+ newPassword1: md5(data.newPassword1)
+ })
},
/**
@@ -28,6 +114,6 @@ export default {
* @returns {Promise}
*/
updateUserInfo(data) {
- return request.post('/blade-system/user/update-info', data)
+ return request.post('/blade-system/user/update', data)
}
}
diff --git a/config/api.config.js b/config/api.config.js
index 8c76468..7479a49 100644
--- a/config/api.config.js
+++ b/config/api.config.js
@@ -5,11 +5,9 @@
// 开发环境配置
const development = {
- // 使用代理,请求会被转发到 vue.config.js 中配置的目标地址
- baseURL: 'http://localhost:8123',
- timeout: 30000,
- // 如果需要代理,可以配置
- baseURL: '/api'
+ // 使用代理路径(vue.config.js会将/api代理到http://localhost:8123)
+ baseURL: '/api',
+ timeout: 30000
}
// 测试环境配置
diff --git a/pages.json b/pages.json
index 12746cb..2ef98ea 100644
--- a/pages.json
+++ b/pages.json
@@ -1,5 +1,19 @@
{
"pages": [
+ {
+ "path": "pages/login/login",
+ "style": {
+ "navigationBarTitleText": "登录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/register/register",
+ "style": {
+ "navigationBarTitleText": "注册",
+ "navigationStyle": "custom"
+ }
+ },
{
"path": "pages/home/home",
"style": {
diff --git a/pages/change-password/change-password.vue b/pages/change-password/change-password.vue
index cf2c6b3..e020d13 100644
--- a/pages/change-password/change-password.vue
+++ b/pages/change-password/change-password.vue
@@ -133,7 +133,7 @@ export default {
await userAPI.updatePassword({
oldPassword: this.formData.oldPassword,
newPassword: this.formData.newPassword,
- confirmPassword: this.formData.confirmPassword
+ newPassword1: this.formData.confirmPassword
})
uni.hideLoading()
diff --git a/pages/login/login.vue b/pages/login/login.vue
new file mode 100644
index 0000000..731c9ea
--- /dev/null
+++ b/pages/login/login.vue
@@ -0,0 +1,501 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 账号登录
+
+
+
+ 📱
+
+
+
+
+
+
+ 🔒
+
+
+ {{ showPassword ? '👁️' : '👁️🗨️' }}
+
+
+
+
+
+
+ 忘记密码?
+
+
+
+
+
+ 还没有账号?立即注册
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/profile/profile.vue b/pages/profile/profile.vue
index 3f36e49..a094cdb 100644
--- a/pages/profile/profile.vue
+++ b/pages/profile/profile.vue
@@ -49,6 +49,7 @@
+
+
diff --git a/pages/schedule/schedule-example.vue b/pages/schedule/schedule-example.vue
new file mode 100644
index 0000000..334b70b
--- /dev/null
+++ b/pages/schedule/schedule-example.vue
@@ -0,0 +1,448 @@
+
+
+
+
+
+
+
+ 编排状态:
+ {{ getStatusText() }}
+
+
+ 分组数:
+ {{ scheduleData.totalGroups || 0 }}
+
+
+ 参赛人数:
+ {{ scheduleData.totalParticipants || 0 }}
+
+
+ 最后编排时间:
+ {{ scheduleData.lastAutoScheduleTime }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编排结果
+
+
+
+ 场地:{{ group.venueName }}
+ 时间:{{ group.scheduleDate }} {{ group.scheduleTime }}
+
+
+
+
+
+
+ 暂无编排数据
+ 点击"自动编排"开始编排
+
+
+
+
+
+
+
diff --git a/utils/auth.js b/utils/auth.js
new file mode 100644
index 0000000..45f48fa
--- /dev/null
+++ b/utils/auth.js
@@ -0,0 +1,80 @@
+/**
+ * Token管理工具
+ */
+
+const TOKEN_KEY = 'access_token'
+const REFRESH_TOKEN_KEY = 'refresh_token'
+const USER_INFO_KEY = 'userInfo'
+
+/**
+ * 获取Token
+ */
+export function getToken() {
+ return uni.getStorageSync(TOKEN_KEY)
+}
+
+/**
+ * 设置Token
+ */
+export function setToken(token) {
+ return uni.setStorageSync(TOKEN_KEY, token)
+}
+
+/**
+ * 移除Token
+ */
+export function removeToken() {
+ return uni.removeStorageSync(TOKEN_KEY)
+}
+
+/**
+ * 获取RefreshToken
+ */
+export function getRefreshToken() {
+ return uni.getStorageSync(REFRESH_TOKEN_KEY)
+}
+
+/**
+ * 设置RefreshToken
+ */
+export function setRefreshToken(token) {
+ return uni.setStorageSync(REFRESH_TOKEN_KEY, token)
+}
+
+/**
+ * 获取用户信息
+ */
+export function getUserInfo() {
+ const userInfo = uni.getStorageSync(USER_INFO_KEY)
+ return userInfo ? JSON.parse(userInfo) : null
+}
+
+/**
+ * 设置用户信息
+ */
+export function setUserInfo(userInfo) {
+ return uni.setStorageSync(USER_INFO_KEY, JSON.stringify(userInfo))
+}
+
+/**
+ * 移除用户信息
+ */
+export function removeUserInfo() {
+ return uni.removeStorageSync(USER_INFO_KEY)
+}
+
+/**
+ * 检查是否登录
+ */
+export function isLogin() {
+ return !!(getToken() && getUserInfo())
+}
+
+/**
+ * 清除所有认证信息
+ */
+export function clearAuth() {
+ removeToken()
+ uni.removeStorageSync(REFRESH_TOKEN_KEY)
+ removeUserInfo()
+}
diff --git a/utils/base64.js b/utils/base64.js
new file mode 100644
index 0000000..538684f
--- /dev/null
+++ b/utils/base64.js
@@ -0,0 +1,40 @@
+/**
+ * Base64编码工具
+ */
+
+const base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+
+export function base64Encode(str) {
+ let out = ''
+ let i = 0
+ const len = str.length
+ let c1, c2, c3
+
+ while (i < len) {
+ c1 = str.charCodeAt(i++) & 0xff
+ if (i === len) {
+ out += base64EncodeChars.charAt(c1 >> 2)
+ out += base64EncodeChars.charAt((c1 & 0x3) << 4)
+ out += '=='
+ break
+ }
+ c2 = str.charCodeAt(i++)
+ if (i === len) {
+ out += base64EncodeChars.charAt(c1 >> 2)
+ out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4))
+ out += base64EncodeChars.charAt((c2 & 0xF) << 2)
+ out += '='
+ break
+ }
+ c3 = str.charCodeAt(i++)
+ out += base64EncodeChars.charAt(c1 >> 2)
+ out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4))
+ out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6))
+ out += base64EncodeChars.charAt(c3 & 0x3F)
+ }
+ return out
+}
+
+export default {
+ base64Encode
+}
diff --git a/utils/md5.js b/utils/md5.js
new file mode 100644
index 0000000..02787e5
--- /dev/null
+++ b/utils/md5.js
@@ -0,0 +1,212 @@
+/**
+ * MD5加密工具
+ * 用于密码加密
+ */
+
+function md5(string) {
+ function md5_RotateLeft(lValue, iShiftBits) {
+ return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits))
+ }
+
+ function md5_AddUnsigned(lX, lY) {
+ let lX4, lY4, lX8, lY8, lResult
+ lX8 = (lX & 0x80000000)
+ lY8 = (lY & 0x80000000)
+ lX4 = (lX & 0x40000000)
+ lY4 = (lY & 0x40000000)
+ lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF)
+ if (lX4 & lY4) {
+ return (lResult ^ 0x80000000 ^ lX8 ^ lY8)
+ }
+ if (lX4 | lY4) {
+ if (lResult & 0x40000000) {
+ return (lResult ^ 0xC0000000 ^ lX8 ^ lY8)
+ } else {
+ return (lResult ^ 0x40000000 ^ lX8 ^ lY8)
+ }
+ } else {
+ return (lResult ^ lX8 ^ lY8)
+ }
+ }
+
+ function md5_F(x, y, z) {
+ return (x & y) | ((~x) & z)
+ }
+
+ function md5_G(x, y, z) {
+ return (x & z) | (y & (~z))
+ }
+
+ function md5_H(x, y, z) {
+ return (x ^ y ^ z)
+ }
+
+ function md5_I(x, y, z) {
+ return (y ^ (x | (~z)))
+ }
+
+ function md5_FF(a, b, c, d, x, s, ac) {
+ a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_F(b, c, d), x), ac))
+ return md5_AddUnsigned(md5_RotateLeft(a, s), b)
+ }
+
+ function md5_GG(a, b, c, d, x, s, ac) {
+ a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_G(b, c, d), x), ac))
+ return md5_AddUnsigned(md5_RotateLeft(a, s), b)
+ }
+
+ function md5_HH(a, b, c, d, x, s, ac) {
+ a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_H(b, c, d), x), ac))
+ return md5_AddUnsigned(md5_RotateLeft(a, s), b)
+ }
+
+ function md5_II(a, b, c, d, x, s, ac) {
+ a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_I(b, c, d), x), ac))
+ return md5_AddUnsigned(md5_RotateLeft(a, s), b)
+ }
+
+ function md5_ConvertToWordArray(string) {
+ let lWordCount
+ const lMessageLength = string.length
+ const lNumberOfWords_temp1 = lMessageLength + 8
+ const lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64
+ const lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16
+ const lWordArray = Array(lNumberOfWords - 1)
+ let lBytePosition = 0
+ let lByteCount = 0
+ while (lByteCount < lMessageLength) {
+ lWordCount = (lByteCount - (lByteCount % 4)) / 4
+ lBytePosition = (lByteCount % 4) * 8
+ lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition))
+ lByteCount++
+ }
+ lWordCount = (lByteCount - (lByteCount % 4)) / 4
+ lBytePosition = (lByteCount % 4) * 8
+ lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition)
+ lWordArray[lNumberOfWords - 2] = lMessageLength << 3
+ lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29
+ return lWordArray
+ }
+
+ function md5_WordToHex(lValue) {
+ let WordToHexValue = '', WordToHexValue_temp = '', lByte, lCount
+ for (lCount = 0; lCount <= 3; lCount++) {
+ lByte = (lValue >>> (lCount * 8)) & 255
+ WordToHexValue_temp = '0' + lByte.toString(16)
+ WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2)
+ }
+ return WordToHexValue
+ }
+
+ function md5_Utf8Encode(string) {
+ string = string.replace(/\r\n/g, '\n')
+ let utftext = ''
+ for (let n = 0; n < string.length; n++) {
+ const c = string.charCodeAt(n)
+ if (c < 128) {
+ utftext += String.fromCharCode(c)
+ } else if ((c > 127) && (c < 2048)) {
+ utftext += String.fromCharCode((c >> 6) | 192)
+ utftext += String.fromCharCode((c & 63) | 128)
+ } else {
+ utftext += String.fromCharCode((c >> 12) | 224)
+ utftext += String.fromCharCode(((c >> 6) & 63) | 128)
+ utftext += String.fromCharCode((c & 63) | 128)
+ }
+ }
+ return utftext
+ }
+
+ let x = []
+ let k, AA, BB, CC, DD, a, b, c, d
+ const S11 = 7, S12 = 12, S13 = 17, S14 = 22
+ const S21 = 5, S22 = 9, S23 = 14, S24 = 20
+ const S31 = 4, S32 = 11, S33 = 16, S34 = 23
+ const S41 = 6, S42 = 10, S43 = 15, S44 = 21
+
+ string = md5_Utf8Encode(string)
+ x = md5_ConvertToWordArray(string)
+ a = 0x67452301
+ b = 0xEFCDAB89
+ c = 0x98BADCFE
+ d = 0x10325476
+
+ for (k = 0; k < x.length; k += 16) {
+ AA = a
+ BB = b
+ CC = c
+ DD = d
+ a = md5_FF(a, b, c, d, x[k + 0], S11, 0xD76AA478)
+ d = md5_FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756)
+ c = md5_FF(c, d, a, b, x[k + 2], S13, 0x242070DB)
+ b = md5_FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE)
+ a = md5_FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF)
+ d = md5_FF(d, a, b, c, x[k + 5], S12, 0x4787C62A)
+ c = md5_FF(c, d, a, b, x[k + 6], S13, 0xA8304613)
+ b = md5_FF(b, c, d, a, x[k + 7], S14, 0xFD469501)
+ a = md5_FF(a, b, c, d, x[k + 8], S11, 0x698098D8)
+ d = md5_FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF)
+ c = md5_FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1)
+ b = md5_FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE)
+ a = md5_FF(a, b, c, d, x[k + 12], S11, 0x6B901122)
+ d = md5_FF(d, a, b, c, x[k + 13], S12, 0xFD987193)
+ c = md5_FF(c, d, a, b, x[k + 14], S13, 0xA679438E)
+ b = md5_FF(b, c, d, a, x[k + 15], S14, 0x49B40821)
+ a = md5_GG(a, b, c, d, x[k + 1], S21, 0xF61E2562)
+ d = md5_GG(d, a, b, c, x[k + 6], S22, 0xC040B340)
+ c = md5_GG(c, d, a, b, x[k + 11], S23, 0x265E5A51)
+ b = md5_GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA)
+ a = md5_GG(a, b, c, d, x[k + 5], S21, 0xD62F105D)
+ d = md5_GG(d, a, b, c, x[k + 10], S22, 0x2441453)
+ c = md5_GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681)
+ b = md5_GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8)
+ a = md5_GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6)
+ d = md5_GG(d, a, b, c, x[k + 14], S22, 0xC33707D6)
+ c = md5_GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87)
+ b = md5_GG(b, c, d, a, x[k + 8], S24, 0x455A14ED)
+ a = md5_GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905)
+ d = md5_GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8)
+ c = md5_GG(c, d, a, b, x[k + 7], S23, 0x676F02D9)
+ b = md5_GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A)
+ a = md5_HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942)
+ d = md5_HH(d, a, b, c, x[k + 8], S32, 0x8771F681)
+ c = md5_HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122)
+ b = md5_HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C)
+ a = md5_HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44)
+ d = md5_HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9)
+ c = md5_HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60)
+ b = md5_HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70)
+ a = md5_HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6)
+ d = md5_HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA)
+ c = md5_HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085)
+ b = md5_HH(b, c, d, a, x[k + 6], S34, 0x4881D05)
+ a = md5_HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039)
+ d = md5_HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5)
+ c = md5_HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8)
+ b = md5_HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665)
+ a = md5_II(a, b, c, d, x[k + 0], S41, 0xF4292244)
+ d = md5_II(d, a, b, c, x[k + 7], S42, 0x432AFF97)
+ c = md5_II(c, d, a, b, x[k + 14], S43, 0xAB9423A7)
+ b = md5_II(b, c, d, a, x[k + 5], S44, 0xFC93A039)
+ a = md5_II(a, b, c, d, x[k + 12], S41, 0x655B59C3)
+ d = md5_II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92)
+ c = md5_II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D)
+ b = md5_II(b, c, d, a, x[k + 1], S44, 0x85845DD1)
+ a = md5_II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F)
+ d = md5_II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0)
+ c = md5_II(c, d, a, b, x[k + 6], S43, 0xA3014314)
+ b = md5_II(b, c, d, a, x[k + 13], S44, 0x4E0811A1)
+ a = md5_II(a, b, c, d, x[k + 4], S41, 0xF7537E82)
+ d = md5_II(d, a, b, c, x[k + 11], S42, 0xBD3AF235)
+ c = md5_II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB)
+ b = md5_II(b, c, d, a, x[k + 9], S44, 0xEB86D391)
+ a = md5_AddUnsigned(a, AA)
+ b = md5_AddUnsigned(b, BB)
+ c = md5_AddUnsigned(c, CC)
+ d = md5_AddUnsigned(d, DD)
+ }
+
+ return (md5_WordToHex(a) + md5_WordToHex(b) + md5_WordToHex(c) + md5_WordToHex(d)).toLowerCase()
+}
+
+export default md5
diff --git a/utils/request.js b/utils/request.js
index 9d8ddd7..861706b 100644
--- a/utils/request.js
+++ b/utils/request.js
@@ -9,6 +9,7 @@ class Request {
constructor() {
this.baseURL = config.baseURL
this.timeout = config.timeout
+ this.isRedirecting = false // 防止重复跳转登录页
}
/**
@@ -76,12 +77,13 @@ class Request {
* @returns {Object}
*/
getHeaders(customHeader = {}) {
- // 获取token
- const token = uni.getStorageSync('token') || ''
+ // 获取token - 使用access_token作为key
+ const token = uni.getStorageSync('access_token') || ''
return {
'Content-Type': 'application/json',
- 'Blade-Auth': token ? `Bearer ${token}` : '',
+ 'Blade-Auth': token ? `bearer ${token}` : '',
+ 'Tenant-Id': '000000',
...customHeader
}
}
@@ -95,24 +97,41 @@ class Request {
handleResponse(res, resolve, reject) {
const data = res.data
- // 判断HTTP状态码
- // 2xx 和 304 都是成功的状态码
+ // 判断HTTP状态码 - 特别处理401
+ if (res.statusCode === 401) {
+ this.handleTokenExpired()
+ reject({
+ statusCode: 401,
+ code: 401,
+ message: '未登录或登录已过期'
+ })
+ return
+ }
+
+ // 其他HTTP错误状态码
if (res.statusCode < 200 || (res.statusCode >= 300 && res.statusCode !== 304)) {
this.showError('网络请求失败')
reject({
+ statusCode: res.statusCode,
code: res.statusCode,
message: '网络请求失败'
})
return
}
+ // 特殊处理:OAuth2 token接口直接返回数据(有access_token字段)
+ if (data.access_token) {
+ resolve(data)
+ return
+ }
+
// 判断业务状态码
if (data.code === 200 || data.success === true) {
// 请求成功,返回数据
resolve(data.data)
} else {
// 业务错误处理
- const errorMsg = data.msg || data.message || '请求失败'
+ const errorMsg = data.msg || data.message || data.error_description || '请求失败'
// 特殊错误码处理
if (data.code === 401 || data.code === 403) {
@@ -122,6 +141,7 @@ class Request {
this.showError(errorMsg)
reject({
+ statusCode: res.statusCode,
code: data.code,
message: errorMsg,
data: data.data
@@ -174,23 +194,85 @@ class Request {
* 处理token过期
*/
handleTokenExpired() {
- // 清除token
- uni.removeStorageSync('token')
+ console.log('=== handleTokenExpired 被调用 ===')
+ console.log('isRedirecting:', this.isRedirecting)
+
+ // 防止重复跳转
+ if (this.isRedirecting) {
+ console.log('已经在跳转中,跳过')
+ return
+ }
+ this.isRedirecting = true
+
+ console.log('开始清除认证信息')
+ // 清除所有认证信息
+ uni.removeStorageSync('access_token')
+ uni.removeStorageSync('refresh_token')
uni.removeStorageSync('userInfo')
+ console.log('认证信息已清除')
// 提示用户
+ console.log('显示Toast提示')
uni.showToast({
title: '登录已过期,请重新登录',
icon: 'none',
- duration: 2000
+ duration: 1500
})
- // 跳转到登录页(如果有)
- setTimeout(() => {
- // uni.reLaunch({
- // url: '/pages/login/login'
- // })
- }, 2000)
+ // 立即跳转,不等待Toast
+ console.log('准备跳转到登录页')
+
+ // 获取当前页面路径
+ const pages = getCurrentPages()
+ console.log('当前页面栈:', pages)
+ const currentPage = pages[pages.length - 1]
+ const currentRoute = currentPage ? currentPage.route : ''
+
+ console.log('当前页面路由:', currentRoute)
+
+ // 如果当前不在登录页,才跳转
+ if (currentRoute !== 'pages/login/login') {
+ console.log('开始执行跳转...')
+
+ // 使用 setTimeout 确保在下一个事件循环执行
+ setTimeout(() => {
+ this.isRedirecting = false
+
+ uni.reLaunch({
+ url: '/pages/login/login',
+ success: () => {
+ console.log('✅ reLaunch 跳转成功')
+ },
+ fail: (err) => {
+ console.error('❌ reLaunch 失败:', err)
+ // 如果reLaunch失败,尝试使用redirectTo
+ uni.redirectTo({
+ url: '/pages/login/login',
+ success: () => {
+ console.log('✅ redirectTo 跳转成功')
+ },
+ fail: (err2) => {
+ console.error('❌ redirectTo 也失败:', err2)
+ // 最后尝试navigateTo
+ uni.navigateTo({
+ url: '/pages/login/login',
+ success: () => {
+ console.log('✅ navigateTo 跳转成功')
+ },
+ fail: (err3) => {
+ console.error('❌ navigateTo 也失败:', err3)
+ console.error('所有跳转方式都失败了!')
+ }
+ })
+ }
+ })
+ }
+ })
+ }, 100)
+ } else {
+ console.log('当前已在登录页,不需要跳转')
+ this.isRedirecting = false
+ }
}
/**
@@ -212,7 +294,7 @@ class Request {
/**
* POST请求
* @param {String} url 请求地址
- * @param {Object} data 请求参数
+ * @param {Object|String} data 请求参数(可以是对象或字符串)
* @param {Object} options 额外配置
* @returns {Promise}
*/