diff --git a/package.json b/package.json index bfe3a57..62f93fe 100644 --- a/package.json +++ b/package.json @@ -52,9 +52,12 @@ "@dcloudio/uni-quickapp-webview": "3.0.0-4030620241128001", "@dcloudio/uni-ui": "^1.5.7", "@hyoga/uni-socket.io": "^3.0.4", + "axios": "^1.9.0", + "axios-adapter-uniapp": "^0.1.4", "dayjs": "^1.11.13", "moment": "^2.30.1", "pinia": "^3.0.2", + "uuid": "^11.1.0", "vue": "^3.4.21", "vue-i18n": "^9.1.9", "vue-router": "^4.5.1" diff --git a/src/pages/checkin/checkin.vue b/src/pages/checkin/checkin.vue index f5e86d3..bd55dd1 100644 --- a/src/pages/checkin/checkin.vue +++ b/src/pages/checkin/checkin.vue @@ -1,10 +1,438 @@ - - - 签到 + + + + {{qdttitle}} + + + + 累计时间 + {{ formattedTime }} + + + + + + 立即签到 + + + + + + 立即签到 + + + + + 请输入签到密码 + + 完成密码签到 + + {{ passwordCheckInResult }} + + + + + + 请扫描二维码进行签到 + 开始扫码 + + + + + + + 不可签到 + + + + + + {{ checkInResult }} + + + 请诚信学习 + + + {{qidndaotime}} + + + + + \ No newline at end of file +.checkin-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100vh; /* 使容器高度为视口高度 */ + padding: 20px; + background-color: #f9f9f9; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +.time-display { + text-align: center; + margin-bottom: 20px; + font-size: 14px; /* 缩小“累计时间”文本的字体大小 */ + color: #333; +} + +.time-display span:first-child { + display: block; + font-size: 14px; + color: #666; + margin-bottom: 5px; +} + +.time-display span:last-child { + font-size: 36px; + color: #333; +} + +.checkin-button { + background-color: #ff4d4f; + color: white; + border: none; + border-radius: 50%; + width: 150px; /* 进一步增大按钮尺寸 */ + height: 150px; + font-size: 24px; /* 增大按钮内文字的字体大小 */ + transition: background-color 0.3s; + display: flex; + align-items: center; + justify-content: center; + margin: 20px 0; /* 调整按钮与上下元素的间距 */ +} + +.checkin-button:hover { + background-color: #e53935; +} + +.checkin-result { + margin-top: 20px; + font-size: 18px; + color: green; + font-weight: bold; +} + +.password-checkin { + text-align: center; + margin-bottom: 20px; +} + +.password-checkin p { + font-size: 18px; + color: #333; + margin-bottom: 10px; +} + +.password-checkin input { + padding: 10px; + margin-bottom: 10px; + border: 1px solid #ccc; + border-radius: 4px; + width: 200px; + text-align: center; +} + +.password-checkin button { + background-color: #ff4d4f; + color: white; + border: none; + border-radius: 50px; + width: 150px; + height: 40px; + font-size: 16px; + transition: background-color 0.3s; +} + +.password-checkin button:hover { + background-color: #e53935; +} + +.checkin-result { + margin-top: 20px; + font-size: 18px; + margin-left: 10px; + font-weight: bold; +} + +.checkin-result.success { + color: green; +} +.checkin-result-time { + margin-top: 20px; + + font-size: 12px; + font-weight: bold; + color: #7f8fa6; +} + +.checkin-result-time.success { + color: green; +} +.checkin-result.error { + color: red; +} + +.scan-checkin { + text-align: center; + margin-bottom: 20px; +} + +.scan-checkin p { + font-size: 18px; + color: #333; + margin-bottom: 10px; +} +.scan-button { + background-color: #4CAF50; /* 绿色背景 */ + color: white; + border: none; + border-radius: 50px; /* 圆角按钮 */ + padding: 12px 24px; + font-size: 16px; + font-weight: bold; + width: 80%; + max-width: 300px; + margin: 10px auto; + display: block; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影增加质感 */ + transition: background-color 0.3s ease, transform 0.2s ease; +} + +.scan-button:hover { + background-color: #45a049; + transform: translateY(-2px); /* 微动效果 */ +} + +.scan-button:active { + transform: scale(0.98); /* 按下缩小一点 */ +} + diff --git a/src/pages/discussion/discussion.vue b/src/pages/discussion/discussion.vue index 9543a98..f7aac4b 100644 --- a/src/pages/discussion/discussion.vue +++ b/src/pages/discussion/discussion.vue @@ -1,10 +1,318 @@ - - - 讨论 + + + + + + + + + {{ item.username }} + {{ item.timestamp }} 回复:{{ reply }} 阅读:{{ reading }} + + + + + + {{ item.title }} + + + + + + + + + + + + + + {{item_xiang.wb}} + + + + + + + + + + + + + + + + + + {{ comment.author }} + {{ moment(comment.fabutime).format("MM-DD HH:mm") }} + {{ comment.neirong }} + + + + + + + + + + 发表 + + + + \ No newline at end of file +.discussion { + padding: 20px; +} + +.user-info { + display: flex; + align-items: center; + margin-bottom: 10px; +} + +.avatar { + width: 35px; + height: 35px; + border-radius: 50%; + margin-right: 10rpx; +} + +.user-details .username { + font-weight: bold; +} + +.user-details .timestamp { + color: #888; + font-size: 12px; +} + +.title { + font-size: 21px; + margin: 20px 0; +} + +.content { + margin-bottom: 20px; + padding-bottom: 30rpx; + border-bottom: #d0cece solid; +} + +.paragraph { + display: block; + text-indent: 2em; +} + +.comments { + margin-top: 20px; +} + +.comment { + display: flex; + align-items: flex-start; + margin-bottom: 10px; + padding-bottom: 10px; + border-bottom: #eeeeee solid; +} + +.comment-details { + flex: 1; + margin-left: 10px; +} + +.comment-details .username { + font-weight: bold; +} + +.comment-details .timestamp { + color: #888; + font-size: 12px; +} + +.comment-content { + margin-top: 5px; +} + +.comment-input { + position: fixed;/* 固定定位 */ + bottom: 0; + left: 0; + right: 0; + display: flex; + align-items: center; + padding: 10px; + background-color: #f9f9f9; + border-top: 1rpx solid #ddd; + z-index: 10; +} + +.add-comment-btn { + background-color: transparent; + border: none; + font-size: 20px; + color: #007AFF; + margin-right: 10px; +} + +.input-box { + flex: 1; + height: 30px; + padding: 0 10px; + font-size: 14px; + border: 1px solid #ccc; + border-radius: 6px; +} + +.submit-button { + background-color: #007AFF; + color: white; + font-size: 14px; + padding: 5px 15px; + border-radius: 5px; + margin-left: 10px; +} + diff --git a/src/pages/shangke/shangke.vue b/src/pages/shangke/shangke.vue index 18b52cc..f2d8c59 100644 --- a/src/pages/shangke/shangke.vue +++ b/src/pages/shangke/shangke.vue @@ -16,8 +16,8 @@ - - + enterinclassactive(item)"> + {{ item.showname }} {{ item.title }} @@ -25,7 +25,7 @@ {{ moment(item.time).fromNow() }} - {{ finish.includes(item.id) ? '已完成' : '' }} + {{ item.finish==="finish" ? item.finishwb : '' }} @@ -65,7 +65,7 @@ import uvDivider from '@/pages/shangke/fgx.vue'; const kctivityAndPPT = ref([ { id: 0, lx: 'notice', wb: '上课了' }, { lx: 'ppt', id: 1, name: 'PPT展示1', src: '/static/course-image/ppt1.png', time: '2025-05-06T19:54:43+08:00' }, -{ lx: 'activity', id: 2, name: '课堂活动1', lxA: '讨论', color: 'green', showname: '课程讨论', title: '历史为什么选择了中国共产党', time: '2025-05-06T19:54:43+08:00' } +{ lx: 'activity', id: '181346ab-e4ca-4110-b92e-cca0fbea99c1', name: '课堂活动1', lxA: '讨论', color: 'green', showname: '课程讨论', title: '历史为什么选择了中国共产党', time: '2025-05-06T19:54:43+08:00' } ]) const finish = ref([1]) const currentPage = ref(1) @@ -74,12 +74,31 @@ const currentPage = ref(1) let socket moment.locale('zh-cn'); // 初始化连接 +const handleRefresh = (newId) => { + let index=kctivityAndPPT.value.findIndex(item => item.id === newId); + if(index!==-1){ + kctivityAndPPT.value[index].finish="finish" + } +} onLoad((options) => { console.log('load', options) console.log(kctivityAndPPT.value) socket = null; if (options) { + uni.request({ + url: 'http://localhost:3400/apistu/getskxiang', + method: 'GET', + data: { // 注意:GET 请求的参数需放在 `data` 而非 `params` 字段:ml-citation{ref="8" data="citationList"} + teacherid:'202413501', + kch:'n8z1A1', + xuehao:'202413501062' + }, + success: (res) => { /* ... */ + kctivityAndPPT.value = res.data + console.log(res.data) + } + }); socket = io('ws://localhost:3400', { transports: ['websocket'], auth: { @@ -103,6 +122,7 @@ onLoad((options) => { }) } // 监听事件 + uni.$on('finish', handleRefresh) socket.on('connect', () => console.log('Socket 已连接')) socket.on('server', (data) => { console.log(data) @@ -127,6 +147,17 @@ const handleMessage = (data) => { console.log('处理消息:', data) } +const enterinclassactive=(item)=>{ + if(item.lxA == '讨论'){ + uni.navigateTo({ + url: '/pages/discussion/discussion?uuid=' +item.id + }); + }else if(item.lxA=='签到'){ + uni.navigateTo({ + url: '/pages/checkin/checkin?uuid=' +item.id + }); + } +} // 发送数据的方法 const sendData = () => { if (socket) { diff --git a/src/pages/shangke/shangke.vueback b/src/pages/shangke/shangke.vueback new file mode 100644 index 0000000..375b50a --- /dev/null +++ b/src/pages/shangke/shangke.vueback @@ -0,0 +1,201 @@ + + + + + 上课 + + + 课堂活动与 PPT 列表 + + + 展示:课程活动 + + + + + + {{moment(item.time).fromNow()}} + 已完成 + + + + + + {{ item.showname }} + {{ item.title }} + + + + + {{moment(item.time).fromNow()}} + {{finish.includes(item.id)?'已完成':''}} + + + + + + + {{item.wb}} + + + + + + + 暂无课堂活动或 PPT 数据 + + + + + + + + diff --git a/src/utile/uuid/regex.ts b/src/utile/uuid/regex.ts new file mode 100644 index 0000000..92f79a1 --- /dev/null +++ b/src/utile/uuid/regex.ts @@ -0,0 +1 @@ +export default /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i; diff --git a/src/utile/uuid/rng.ts b/src/utile/uuid/rng.ts new file mode 100644 index 0000000..23604ce --- /dev/null +++ b/src/utile/uuid/rng.ts @@ -0,0 +1,14 @@ + +export default function rng(): Promise { + return new Promise((resolve) => { + wx.getRandomValues({ + length: 16, + success: res => { + resolve(new Uint8Array(res.randomValues)); + }, + fail: () => { + throw new Error("Failed to get random values"); + } + }) + }); +} diff --git a/src/utile/uuid/stringify.ts b/src/utile/uuid/stringify.ts new file mode 100644 index 0000000..946d1c5 --- /dev/null +++ b/src/utile/uuid/stringify.ts @@ -0,0 +1,57 @@ +import validate from './validate.js'; + +/** + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + */ +const byteToHex: string[] = []; + +for (let i = 0; i < 256; ++i) { + byteToHex.push((i + 0x100).toString(16).slice(1)); +} + +export function unsafeStringify(arr: Uint8Array, offset: number = 0) { + // Note: Be careful editing this code! It's been tuned for performance + // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 + // + // Note to future-self: No, you can't remove the `toLowerCase()` call. + // REF: https://github.com/uuidjs/uuid/pull/677#issuecomment-1757351351 + return ( + byteToHex[arr[offset + 0]] + + byteToHex[arr[offset + 1]] + + byteToHex[arr[offset + 2]] + + byteToHex[arr[offset + 3]] + + '-' + + byteToHex[arr[offset + 4]] + + byteToHex[arr[offset + 5]] + + '-' + + byteToHex[arr[offset + 6]] + + byteToHex[arr[offset + 7]] + + '-' + + byteToHex[arr[offset + 8]] + + byteToHex[arr[offset + 9]] + + '-' + + byteToHex[arr[offset + 10]] + + byteToHex[arr[offset + 11]] + + byteToHex[arr[offset + 12]] + + byteToHex[arr[offset + 13]] + + byteToHex[arr[offset + 14]] + + byteToHex[arr[offset + 15]] + ).toLowerCase(); +} + +function stringify(arr: Uint8Array, offset: number = 0) { + const uuid = unsafeStringify(arr, offset); + // Consistency check for valid UUID. If this throws, it's likely due to one + // of the following: + // - One or more input array values don't map to a hex octet (leading to + // "undefined" in the uuid) + // - Invalid input values for the RFC `version` or `variant` fields + if (!validate(uuid)) { + throw TypeError('Stringified UUID is invalid'); + } + + return uuid; +} + +export default stringify; diff --git a/src/utile/uuid/uuidv4.ts b/src/utile/uuid/uuidv4.ts new file mode 100644 index 0000000..0deafcf --- /dev/null +++ b/src/utile/uuid/uuidv4.ts @@ -0,0 +1,28 @@ +import rng from './rng.js'; +import { unsafeStringify } from './stringify.js'; + +async function uuidv4(random: undefined | Uint8Array = undefined, + buf: undefined | Uint8Array = undefined, + offset: number = 0): Promise { + + const rnds = random || await rng(); + + // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` + rnds[6] = (rnds[6] & 0x0f) | 0x40; + rnds[8] = (rnds[8] & 0x3f) | 0x80; + + // Copy bytes to buffer, if provided + if (buf) { + offset = offset || 0; + + for (let i = 0; i < 16; ++i) { + buf[offset + i] = rnds[i]; + } + + return buf; + } + + return unsafeStringify(rnds); +} + +export default uuidv4; diff --git a/src/utile/uuid/validate.ts b/src/utile/uuid/validate.ts new file mode 100644 index 0000000..860e851 --- /dev/null +++ b/src/utile/uuid/validate.ts @@ -0,0 +1,7 @@ +import REGEX from './regex.js'; + +function validate(uuid: string) { + return typeof uuid === 'string' && REGEX.test(uuid); +} + +export default validate;
请输入签到密码
请扫描二维码进行签到
暂无课堂活动或 PPT 数据