From c477090aea37318be8b083ccb0018720a9bd3bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E5=B2=A9?= Date: Fri, 20 Jun 2025 18:28:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=AE=8C=E9=A6=96=E9=A1=B5=E5=92=8C?= =?UTF-8?q?=E6=88=91=E7=9A=84=E9=A1=B5=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 12 +- src/api/index.js | 86 +++++++- src/api/login.js | 19 +- src/api/request.js | 38 +++- src/api/socket.js | 214 +++++++++++++++++++ src/api/user.js | 15 +- src/common/common.js | 5 +- src/pages.json | 6 - src/pages/index/index.vue | 180 +++++++++------- src/pages/login/login.vue | 2 +- src/pages/me/index.vue | 140 +++++++++++- src/pages/study/index.vue | 52 ++++- src/pagesA.json | 3 +- src/static/images/me/default_user_avatar.png | Bin 0 -> 2793 bytes 14 files changed, 648 insertions(+), 124 deletions(-) create mode 100644 src/api/socket.js create mode 100644 src/static/images/me/default_user_avatar.png diff --git a/.env.development b/.env.development index 7afecec6..09db5e1c 100644 --- a/.env.development +++ b/.env.development @@ -3,6 +3,14 @@ ENV = 'development' # 'development' # base api -#VITE_APP_BASE_API_Url = 'http://25.18.122.66:9786' + # 苗扬 -VITE_APP_BASE_API_Url = 'http://25.64.16.133:9786' +#VITE_APP_BASE_API_Url = 'http://25.64.16.133:9786' + + +#VITE_APP_BASE_API_Url = 'http://aitstest.jlbank.com.cn:7002' + + +# app入口 +VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001' +#VITE_APP_BASE_API_Url = 'http://25.18.122.66:9786' diff --git a/src/api/index.js b/src/api/index.js index 5b9738f1..dd40aa06 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,12 +1,92 @@ import request from '@/api/request' -// 获取首页统计数据方法 +// 获取首页统计数据方法(查询个人学习统计信息 ) export const getStatisticsData = (data) => { + /** + userId 学员ID String + stdtTmLen 本年学习时长 double + accmPracticeCnt 累计训练次数 int + accmExamCnt 累计考试次数 int + **/ return request({ - url: '/plappro/process/queryProcessList', + url: '/traapp/traStdyInfo/queryTraStdyStatisticsPerson', method: 'post', data, - suppressErrors:true + suppressErrors: true + }); +}; + +// 最新课程查询接口 +export const queryCrsInfoByLastnew = (data) => { + /** + crsNum 课程编号 String + crsName 课程名称 String + tagId 课程分类 String + prdLineId 生产线分类 String + issuTm 发布日期 String + imgId 课程封面ID String + accmStdyCnt 累计学习次数 int + evalGrade 课程评分 Bigdecimal + tagCataList 课程标签集 List + tagId 标签ID String + tagName 标签名称 String + **/ + return request({ + url: '/traapp/mycrs/queryCrsInfoByLastnew', + method: 'post', + data, + suppressErrors: true + }); +}; + + +// 热门课程查询接口 +export const queryCrsInfoByPopular = (data) => { + /** + tagId 标签ID(可选填),为空时查全部 必输项:false 类型:String + 出参: + tagId 标签ID(可选填),为空时查全部 必输项:false 类型:String + 出参: + crsId 课程ID StringcrsNum 课程编号 String + crsName 课程名称 String + tagId 课程分类 String + prdLineId 生产线分类 String + issuTm 发布日期 String + imgId 课程封面ID String + accmStdyCnt 累计学习次数 int + evalGrade 课程评分 Bigdecimal + tagCataList 课程标签集 List + tagId 标签ID String + tagName 标签名称 String + **/ + return request({ + url: '/traapp/mycrs/queryCrsInfoByPopular', + method: 'post', + data, + suppressErrors: true + }); +}; + +// 推荐课程查询接口 +export const queryCrsInfoByRecmd = (data) => { + /** + crsNum 课程编号 String + crsName 课程名称 String + tagId 课程分类 String + prdLineId 生产线分类 String + issuTm 发布日期 String + imgId 课程封面ID String + accmStdyCnt 累计学习次数 int + evalGrade 课程评分 Bigdecimal + tagCataList 课程标签集 List + tagId 标签ID String + tagName 标签名称 String + **/ + return request({ + url: '/traapp/mycrs/queryCrsInfoByRecmd', + method: 'post', + data, + suppressErrors: true }); }; \ No newline at end of file diff --git a/src/api/login.js b/src/api/login.js index 8f3389d1..5777e4ab 100644 --- a/src/api/login.js +++ b/src/api/login.js @@ -25,9 +25,19 @@ export const useAccountLoginApp = (data) => request({ return user_data.body } return Promise.reject({ rtnCode, body }) +}).catch(res => { + console.log('报错s'); + console.log(res); }) -// 获取个人信息 +/** + * 获取个人信息 + * 入参 + gender F是女 M男 + imageAddr: 图片 + loginName ID + userName 姓名 + */ export const queryCurrentUser = (data) => request({ url: '/traapp/access/queryCurrentUser', method: 'post', @@ -46,4 +56,11 @@ export const getLoginAppCheckCode = (data) => request({ url: '/traapp/access/checkCode', method: 'get', data +}) + +// 退出登录 +export const loginOut = (data) => request({ + url: '/traapp/access/loginOut', + method: 'post', + data }) \ No newline at end of file diff --git a/src/api/request.js b/src/api/request.js index 987b4efe..cee9f5af 100644 --- a/src/api/request.js +++ b/src/api/request.js @@ -15,6 +15,26 @@ export const get_base_url = url => { } } +// 请求错误码 +export const REQUES_ERROR_CODES = { + NO_LOGIN: ['QQ0005', '0005'] //登录 +}; + +// 去登录弹窗状态 +let no_login_show_modal_state = false +// 去登录方法 +export const goto_login_fun = () => { + // if (no_login_show_modal_state) return; + no_login_show_modal_state = true + common.hideLoading() + common.show('未登录','现在去登录').then(() => { + common.navigateTo('/pages/login/login') + }).finally(() => { + no_login_show_modal_state = false + }) +} + + export default function request({ url, method, @@ -46,16 +66,13 @@ export default function request({ if (isStream) { // 流式传输,直接返回 return resolve(response); } - - // 0005 QQ0005 登录已过期 - // suppressErrors // 模拟拦截器功能 const res = response.data if (res?.rtnCode === '0000'){ return resolve(res) } - //到这里全是异常情况 + //到这里下面全是异常的处理 if(suppressErrors){ // 静默的接口,报错也不处理,因为下面有公共处理 return reject() } @@ -63,16 +80,15 @@ export default function request({ common.msg("系统异常.") return reject() } - if (res.rtnCode === '0005' || res.rtnCode === 'QQ0005'){ - common.hideLoading() - common.show('未登录','现在去登录').then(() => { - common.navigateTo('/pages/login/login') - }) + // 0005 QQ0005 登录已过期 + if(REQUES_ERROR_CODES['NO_LOGIN'].includes(res.rtnCode)) { + goto_login_fun() } + return reject(res) }, - fail() { - console.log('131231'); + fail(res) { + console.log('131231', res, url); } }); }) diff --git a/src/api/socket.js b/src/api/socket.js new file mode 100644 index 00000000..0343f476 --- /dev/null +++ b/src/api/socket.js @@ -0,0 +1,214 @@ +export default class WebSocketUtil { + /** + * WebSocket工具类,用于管理WebSocket连接 + * @param {string} url - WebSocket服务器地址 + * @param {Object} options - 配置选项 + * @param {number} [options.maxReconnectCount=5] - 最大重连次数 + * @param {number} [options.reconnectInterval=3000] - 重连间隔时间(ms) + * @param {number} [options.heartbeatInterval=30000] - 心跳间隔时间(ms) + * @param {string|Object} [options.heartbeatMsg='ping'] - 心跳消息 + */ + constructor(url, options = {}) { + this.url = url; + this.options = options; + this.socketTask = null; // WebSocket任务实例 + this.reconnectTimer = null; // 重连计时器 + this.reconnectCount = 0; // 当前重连次数 + this.maxReconnectCount = options.maxReconnectCount || 5; // 最大重连次数 + this.reconnectInterval = options.reconnectInterval || 3000; // 重连间隔(ms) + this.heartbeatTimer = null; // 心跳计时器 + this.heartbeatInterval = options.heartbeatInterval || 30000; // 心跳间隔(ms) + this.heartbeatMsg = options.heartbeatMsg || 'ping'; // 心跳消息 + this.callbacks = { + open: [], // 连接打开回调 + message: [], // 消息接收回调 + close: [], // 连接关闭回调 + error: [] // 错误处理回调 + }; + this.init(); // 初始化WebSocket连接 + } + + /** + * 初始化WebSocket连接 + */ + init() { + // 创建WebSocket连接 + this.socketTask = uni.connectSocket({ + url: this.url, + header: this.options.header || {}, + protocols: this.options.protocols || [], + success: () => { + console.log('WebSocket连接创建成功'); + }, + fail: (err) => { + console.error('WebSocket连接创建失败', err); + this.triggerEvent('error', err); + this.tryReconnect(); // 连接失败时尝试重连 + } + }); + + // 监听WebSocket连接打开事件 + this.socketTask.onOpen(() => { + console.log('WebSocket连接已打开'); + this.reconnectCount = 0; // 重置重连计数 + clearInterval(this.reconnectTimer); // 清除重连计时器 + this.startHeartbeat(); // 启动心跳机制 + this.triggerEvent('open'); // 触发连接打开事件 + }); + + // 监听WebSocket消息接收事件 + this.socketTask.onMessage((res) => { + console.log('收到WebSocket消息', res); + this.triggerEvent('message', res); // 触发消息接收事件 + }); + + // 监听WebSocket连接关闭事件 + this.socketTask.onClose((res) => { + console.log('WebSocket连接已关闭', res); + clearInterval(this.heartbeatTimer); // 清除心跳计时器 + this.triggerEvent('close', res); // 触发连接关闭事件 + this.tryReconnect(); // 尝试重连 + }); + + // 监听WebSocket错误事件 + this.socketTask.onError((err) => { + console.error('WebSocket发生错误', err); + clearInterval(this.heartbeatTimer); // 清除心跳计时器 + this.triggerEvent('error', err); // 触发错误事件 + this.tryReconnect(); // 尝试重连 + }); + } + + /** + * 注册事件回调 + * @param {string} event - 事件名称(open/message/close/error) + * @param {Function} callback - 回调函数 + * @returns {WebSocketUtil} - 返回当前实例,支持链式调用 + */ + on(event, callback) { + if (this.callbacks[event]) { + this.callbacks[event].push(callback); + } + return this; + } + + /** + * 移除事件回调 + * @param {string} event - 事件名称(open/message/close/error) + * @param {Function} callback - 要移除的回调函数 + * @returns {WebSocketUtil} - 返回当前实例,支持链式调用 + */ + off(event, callback) { + if (this.callbacks[event]) { + this.callbacks[event] = this.callbacks[event].filter(cb => cb !== callback); + } + return this; + } + + /** + * 触发事件回调 + * @param {string} event - 事件名称 + * @param {any} [data] - 传递给回调函数的数据 + */ + triggerEvent(event, data) { + if (this.callbacks[event]) { + this.callbacks[event].forEach(callback => callback(data)); + } + } + + /** + * 通过WebSocket发送消息 + * @param {string|Object} message - 要发送的消息,可以是字符串或对象 + * @returns {WebSocketUtil} - 返回当前实例,支持链式调用 + */ + send(message) { + if (this.socketTask && this.getStatus() === 1) { + // 发送消息,对象会自动转换为JSON字符串 + this.socketTask.send({ + data: typeof message === 'string' ? message : JSON.stringify(message), + success: () => { + console.log('WebSocket消息发送成功'); + }, + fail: (err) => { + console.error('WebSocket消息发送失败', err); + this.triggerEvent('error', err); + } + }); + } else { + console.error('WebSocket连接未打开,无法发送消息'); + this.triggerEvent('error', new Error('WebSocket连接未打开')); + } + return this; + } + + /** + * 关闭WebSocket连接 + * @param {number} [code=1000] - 关闭码 + * @param {string} [reason=''] - 关闭原因 + * @returns {WebSocketUtil} - 返回当前实例,支持链式调用 + */ + close(code = 1000, reason = '') { + if (this.socketTask) { + clearInterval(this.heartbeatTimer); // 清除心跳计时器 + clearInterval(this.reconnectTimer); // 清除重连计时器 + // 关闭WebSocket连接 + this.socketTask.close({ + code, + reason, + success: () => { + console.log('WebSocket连接正在关闭'); + }, + fail: (err) => { + console.error('WebSocket关闭失败', err); + this.triggerEvent('error', err); + } + }); + } + return this; + } + + /** + * 获取WebSocket连接状态 + * @returns {number} - 连接状态:0-连接中,1-已连接,2-连接关闭中,3-已关闭,-1-未知 + */ + getStatus() { + if (this.socketTask) { + try { + return this.socketTask.readyState; + } catch (e) { + console.error('获取WebSocket状态失败', e); + return -1; + } + } + return -1; + } + + /** + * 尝试重新连接WebSocket + */ + tryReconnect() { + if (this.reconnectCount < this.maxReconnectCount) { + clearInterval(this.reconnectTimer); + this.reconnectTimer = setTimeout(() => { + this.reconnectCount++; + console.log(`尝试重新连接WebSocket (${this.reconnectCount}/${this.maxReconnectCount})`); + this.init(); // 重新初始化WebSocket连接 + }, this.reconnectInterval); + } else { + console.error('达到最大重连次数,停止尝试'); + this.triggerEvent('error', new Error('达到最大重连次数')); + } + } + + /** + * 启动心跳机制 + */ + startHeartbeat() { + clearInterval(this.heartbeatTimer); + this.heartbeatTimer = setInterval(() => { + if (this.getStatus() === 1) { + this.send(this.heartbeatMsg); // 发送心跳消息 + } + }, this.heartbeatInterval); + } +} \ No newline at end of file diff --git a/src/api/user.js b/src/api/user.js index 6441cf86..61674f89 100644 --- a/src/api/user.js +++ b/src/api/user.js @@ -1,11 +1,18 @@ import request from '@/api/request' -// 获取列表接口 -export const process_listPage = (data) => { +// 我的-统计信息 +export const queryTraStdyStatisticsSelf = (data) => { + /** + userId 学员ID String + stdtTmLen 累计学习时长 double + accmTaskCnt 累计完成任务 int + accmPoint 累计获得积分 int + **/ return request({ - url: '/plappro/process/queryProcessList', + url: '/traapp/traStdyInfo/queryTraStdyStatisticsSelf', method: 'post', - data + data, + suppressErrors: true }); }; \ No newline at end of file diff --git a/src/common/common.js b/src/common/common.js index 557a6ed5..d01c5286 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -9,7 +9,7 @@ function msg(title, duration = 1000) { } function show(title, msg, showCancel = true) { - let res = new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { uni.showModal({ title: title, content: msg, @@ -18,11 +18,10 @@ function show(title, msg, showCancel = true) { resolve(res.confirm) }, fail: function(res) { - reject(false) + reject(res) }, }); }) - return res; } const loading = (title = '加载中', mask = true) => uni.showLoading({ diff --git a/src/pages.json b/src/pages.json index a1259033..d3155b84 100644 --- a/src/pages.json +++ b/src/pages.json @@ -10,7 +10,6 @@ } }, { -<<<<<<< HEAD "path": "pages/courseDetail/index", "style": { "navigationBarTitleText": "课程详情", @@ -20,8 +19,6 @@ } }, { -======= ->>>>>>> f0015d5ea46c4904ce35c5ec4a03028d9ec6d5b4 "path": "pages/study/index", "style": { "navigationBarTitleText": "学习", @@ -93,8 +90,6 @@ "titleNView": false } } -<<<<<<< HEAD -======= }, { "path": "pages/course/index", @@ -104,7 +99,6 @@ "titleNView": false } } ->>>>>>> f0015d5ea46c4904ce35c5ec4a03028d9ec6d5b4 } ], "tabBar": { diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue index 93264ae0..b6d84005 100644 --- a/src/pages/index/index.vue +++ b/src/pages/index/index.vue @@ -27,8 +27,9 @@ - + @@ -52,20 +53,22 @@ - 年度学习时长 + 本年学习时长 - h + h - 累计训练时长 + 累计训练次数 - h + 累计考试次数 - + @@ -121,15 +124,15 @@ - - + + - + - {{item.title}} + {{item.crsName}} - 发布:{{item.date}} 丨已学:{{item.num}} + 发布:{{item.issuTm.substr(0, 10)}} 丨已学:{{item.accmStdyCnt}} 去学习 @@ -152,17 +155,17 @@ - + - - + + - + - {{item.title}} + {{item.crsName}} - 发布:{{item.date}} 丨已学:{{item.num}} + 发布:{{item.issuTm.substr(0, 10)}} 丨已学:{{item.accmStdyCnt}} 去学习 @@ -184,17 +187,17 @@ - + - - + + - + - {{item.title}} + {{item.crsName}} - 发布:{{item.date}} 丨已学:{{item.num}} + 发布:{{item.issuTm.substr(0, 10)}} 丨已学:{{item.accmStdyCnt}} 去学习 @@ -210,15 +213,17 @@ - + -