diff --git a/student-vol/mock/actList.json b/student-vol/mock/actList.json index 98e7440..3c08925 100644 --- a/student-vol/mock/actList.json +++ b/student-vol/mock/actList.json @@ -32,9 +32,9 @@ "endTime": 1672099200000, "palce": "福州市养老院", "isYourSchool": false, - "canApply": true, + "canApply": false, "actPic": "./imgs/actImg.jpeg" } ] } -} +} \ No newline at end of file diff --git a/student-vol/mock/myApplyList.json b/student-vol/mock/myApplyList.json index ea9aced..6cdbaeb 100644 --- a/student-vol/mock/myApplyList.json +++ b/student-vol/mock/myApplyList.json @@ -5,6 +5,17 @@ "pageSize": 10, "pageCount": 1, "list": [ + { + "id": 3, + "title": "社区敬老院服务", + "startTime": 1672012800000, + "endTime": 1672099200000, + "palce": "福州市养老院", + "isYourSchool": false, + "canApply": true, + "actPic": "./imgs/actImg.jpeg", + "applyStatus": 1 + }, { "id": 1, "title": "运动会志愿者招募", @@ -29,4 +40,4 @@ } ] } -} +} \ No newline at end of file diff --git a/student-vol/mock/scoreOverview.json b/student-vol/mock/scoreOverview.json index a50e101..f01e9a6 100644 --- a/student-vol/mock/scoreOverview.json +++ b/student-vol/mock/scoreOverview.json @@ -2,7 +2,7 @@ "error": 0, "data": { "provinceRank": 156, - "totalScore": 128, + "totalScore": 132, "grandeRank": 8 } -} +} \ No newline at end of file diff --git a/student-vol/mock/serviceList.json b/student-vol/mock/serviceList.json index 25e52cc..ceb2241 100644 --- a/student-vol/mock/serviceList.json +++ b/student-vol/mock/serviceList.json @@ -5,6 +5,15 @@ "pageSize": 10, "pageCount": 1, "list": [ + { + "id": 4, + "pic": "./imgs/actImg.jpeg", + "content": "666", + "publisher": "未知来源", + "time": 1766717931972, + "status": 1, + "hour": 2 + }, { "id": 1, "pic": "./imgs/actImg.jpeg", @@ -31,4 +40,4 @@ } ] } -} +} \ No newline at end of file diff --git a/student-vol/mock/userInfo.json b/student-vol/mock/userInfo.json index 2b497ad..54ffa18 100644 --- a/student-vol/mock/userInfo.json +++ b/student-vol/mock/userInfo.json @@ -2,13 +2,13 @@ "error": 0, "data": { "avatar": "./imgs/avatar.png", - "name": "李华", - "code": 12343434, + "name": "666", + "code": 114514, "gender": 0, - "phoneNum": "18787698789", + "phoneNum": "1919810", "school": "福州大学至诚学院", - "profession": "软件工程", - "userType": 0, - "totalScore": 128 + "profession": "计算机", + "userType": 2, + "totalScore": 132 } -} +} \ No newline at end of file diff --git a/student-vol/mock/userScore.json b/student-vol/mock/userScore.json index e2d0c47..376b720 100644 --- a/student-vol/mock/userScore.json +++ b/student-vol/mock/userScore.json @@ -3,6 +3,6 @@ "data": { "avatar": "./imgs/avatar.png", "name": "李华", - "totalScore": 128 + "totalScore": 132 } -} +} \ No newline at end of file diff --git a/student-vol/mock/yearScore.json b/student-vol/mock/yearScore.json index 8469732..5f48266 100644 --- a/student-vol/mock/yearScore.json +++ b/student-vol/mock/yearScore.json @@ -1,8 +1,8 @@ { "error": 0, "data": { - "times": 12, - "duration": 48, - "score": 96 + "times": 13, + "duration": 50, + "score": 100 } -} +} \ No newline at end of file diff --git a/student-vol/src/views/UserCenter.vue b/student-vol/src/views/UserCenter.vue index c0def5a..63e5eb7 100644 --- a/student-vol/src/views/UserCenter.vue +++ b/student-vol/src/views/UserCenter.vue @@ -1,60 +1,58 @@ diff --git a/student-vol/vite.config.js b/student-vol/vite.config.js index 2b30e35..01cb8f5 100644 --- a/student-vol/vite.config.js +++ b/student-vol/vite.config.js @@ -5,137 +5,299 @@ import fs from "fs"; import path from "path"; function mockPlugin() { + const mockDir = path.resolve(process.cwd(), "mock"); + + const filePath = (file) => path.resolve(mockDir, file); + + const send = (res, obj, code = 200) => { + res.statusCode = code; + res.setHeader("Content-Type", "application/json; charset=utf-8"); + res.end(JSON.stringify(obj)); + }; + + const readJson = (file, fallback) => { + const p = filePath(file); + if (!fs.existsSync(p)) return fallback; + try { + return JSON.parse(fs.readFileSync(p, "utf-8") || "null") ?? fallback; + } catch { + return fallback; + } + }; + + const writeJson = (file, data) => { + const p = filePath(file); + fs.writeFileSync(p, JSON.stringify(data, null, 2), "utf-8"); + }; + + const readBodyJson = (req) => + new Promise((resolve, reject) => { + let body = ""; + req.on("data", (c) => (body += c)); + req.on("end", () => { + try { + resolve(JSON.parse(body || "{}")); + } catch (e) { + reject(e); + } + }); + }); + + const getPathname = (req) => { + try { + return new URL(req.url, "http://localhost").pathname; + } catch { + return req.url; + } + }; + + // 统一 success + const ok = (res) => send(res, { error: 0, msg: "success" }); + + // ===== 业务联动:积分更新规则 ===== + // yearScore: score=duration*2,看起来是 1小时=2分 + const POINTS_PER_HOUR = 2; + + const addScore = (deltaHour) => { + const deltaScore = (Number(deltaHour) || 0) * POINTS_PER_HOUR; + + // userInfo.json + const ui = readJson("userInfo.json", { error: 0, data: {} }); + ui.data = ui.data || {}; + ui.data.totalScore = (Number(ui.data.totalScore) || 0) + deltaScore; + writeJson("userInfo.json", ui); + + // userScore.json + const us = readJson("userScore.json", { error: 0, data: {} }); + us.data = us.data || {}; + us.data.totalScore = (Number(us.data.totalScore) || 0) + deltaScore; + writeJson("userScore.json", us); + + // scoreOverview.json + const ov = readJson("scoreOverview.json", { error: 0, data: {} }); + ov.data = ov.data || {}; + ov.data.totalScore = (Number(ov.data.totalScore) || 0) + deltaScore; + writeJson("scoreOverview.json", ov); + + // yearScore.json + const ys = readJson("yearScore.json", { error: 0, data: {} }); + ys.data = ys.data || {}; + ys.data.times = (Number(ys.data.times) || 0) + 1; + ys.data.duration = + (Number(ys.data.duration) || 0) + (Number(deltaHour) || 0); + ys.data.score = (Number(ys.data.score) || 0) + deltaScore; + writeJson("yearScore.json", ys); + }; + return { name: "simple-mock-plugin", configureServer(server) { - server.middlewares.use((req, res, next) => { + server.middlewares.use(async (req, res, next) => { + const pathname = getPathname(req); + + // ========== GET:仍然走你原来的静态文件 ========== const sendJSON = (file) => { - const filePath = path.resolve(process.cwd(), `mock/${file}`); - if (!fs.existsSync(filePath)) { - res.statusCode = 404; - res.setHeader("Content-Type", "application/json"); - return res.end( - JSON.stringify({ - error: `Mock file not found: ${file}`, - }), - ); + const p = filePath(file); + if (!fs.existsSync(p)) { + return send(res, { error: `Mock file not found: ${file}` }, 404); } - const json = fs.readFileSync(filePath, "utf-8"); - res.setHeader("Content-Type", "application/json"); - res.end(json); + res.setHeader("Content-Type", "application/json; charset=utf-8"); + res.end(fs.readFileSync(p, "utf-8")); }; - // 用户信息 - if (req.url === "/api/userInfo" && req.method === "GET") { + if (pathname === "/api/userInfo" && req.method === "GET") return sendJSON("userInfo.json"); - } - - // 提交用户信息 - if (req.url === "/api/userInfo/submit" && req.method === "POST") { - return sendJSON("success.json"); - } - - // 活动列表 - if (req.url.startsWith("/api/actList") && req.method === "GET") { + if (pathname.startsWith("/api/actList") && req.method === "GET") return sendJSON("actList.json"); - } - - // 积分概览 if ( - req.url.startsWith("/api/report/myOverview") && + pathname.startsWith("/api/report/myOverview") && req.method === "GET" - ) { + ) return sendJSON("scoreOverview.json"); - } - - // 排名列表 - if ( - req.url.startsWith("/api/report/rankList") && - req.method === "GET" - ) { + if (pathname.startsWith("/api/report/rankList") && req.method === "GET") return sendJSON("rank.json"); - } - - // 活动详情 if ( - req.url.startsWith("/api/actDetails/details") && + pathname.startsWith("/api/actDetails/details") && req.method === "GET" - ) { + ) return sendJSON("actDetails.json"); - } - - // 申请/撤销活动 - if ( - req.url.startsWith("/api/actDetails/apply") && - req.method === "POST" - ) { - return sendJSON("success.json"); - } - - // 我的报名列表 - if (req.url.startsWith("/api/myApplyList") && req.method === "GET") { + if (pathname.startsWith("/api/myApplyList") && req.method === "GET") return sendJSON("myApplyList.json"); - } - - // 活动来源列表 if ( - req.url.startsWith("/api/act/publisherList") && + pathname.startsWith("/api/act/publisherList") && req.method === "GET" - ) { + ) return sendJSON("publishers.json"); - } - - // 服务时长列表 if ( - req.url.startsWith("/api/act/durationsList") && + pathname.startsWith("/api/act/durationsList") && req.method === "GET" - ) { + ) return sendJSON("durationList.json"); + if ( + pathname.startsWith("/api/service/userScore") && + req.method === "GET" + ) + return sendJSON("userScore.json"); + if ( + pathname.startsWith("/api/service/yearList") && + req.method === "GET" + ) + return sendJSON("yearList.json"); + if ( + pathname.startsWith("/api/service/yearScore") && + req.method === "GET" + ) + return sendJSON("yearScore.json"); + if (pathname.startsWith("/api/service/list") && req.method === "GET") + return sendJSON("serviceList.json"); + if (pathname.startsWith("/api/service/details") && req.method === "GET") + return sendJSON("recordDetails.json"); + + // ========== POST:全部改成“落盘” ========== + // 1) 用户信息保存 + if (pathname === "/api/userInfo/submit" && req.method === "POST") { + try { + const incoming = await readBodyJson(req); // {name,code,gender,phoneNum,school,profession,userType...} + const old = readJson("userInfo.json", { error: 0, data: {} }); + const next = { + error: 0, + data: { ...(old.data || {}), ...incoming }, + }; + writeJson("userInfo.json", next); + return ok(res); + } catch { + return send(res, { error: 1, msg: "Invalid JSON body" }, 400); + } } - // 上传服务记录 + // 2) 申请/撤销活动:更新 myApplyList + actDetails(可选 actList) if ( - req.url.startsWith("/api/act/uploadService") && + pathname.startsWith("/api/actDetails/apply") && req.method === "POST" ) { - return sendJSON("success.json"); + try { + const incoming = await readBodyJson(req); + // 约定:至少要有 id;可选传 action: "apply" | "cancel" + const id = Number(incoming.id ?? incoming.actId); + const action = incoming.action || "apply"; + if (!id) return send(res, { error: 1, msg: "missing id" }, 400); + + // 读活动列表,用来补齐标题/时间/地点等字段 + const actList = readJson("actList.json", { + error: 0, + data: { list: [] }, + }); + const act = (actList.data?.list || []).find( + (x) => Number(x.id) === id, + ); + + // myApplyList.json + const applyJson = readJson("myApplyList.json", { + error: 0, + data: { current: 1, pageSize: 10, pageCount: 1, list: [] }, + }); + const list = applyJson.data?.list || []; + const existsIdx = list.findIndex((x) => Number(x.id) === id); + + if (action === "cancel") { + if (existsIdx >= 0) list.splice(existsIdx, 1); + } else { + // apply + if (existsIdx < 0) { + // 申请状态:你的 mock 里 1/2 出现过。这里用 1 表示已申请 + list.unshift({ ...(act || { id }), applyStatus: 1 }); + } else { + list[existsIdx].applyStatus = 1; + } + } + + applyJson.data = applyJson.data || {}; + applyJson.data.list = list; + applyJson.data.pageCount = 1; + writeJson("myApplyList.json", applyJson); + + // actDetails.json:让详情页刷新后看到申请状态变化 + const detailsJson = readJson("actDetails.json", { + error: 0, + data: { details: {} }, + }); + if ( + detailsJson.data?.details && + Number(detailsJson.data.details.id) === id + ) { + if (action === "cancel") { + detailsJson.data.details.applyStatus = 0; + detailsJson.data.details.canApply = true; + } else { + detailsJson.data.details.applyStatus = 1; + detailsJson.data.details.canApply = false; + } + writeJson("actDetails.json", detailsJson); + } + + // actList.json(可选):列表 canApply 同步变化 + if (act) { + const idx = (actList.data?.list || []).findIndex( + (x) => Number(x.id) === id, + ); + if (idx >= 0) { + actList.data.list[idx].canApply = action === "cancel"; + writeJson("actList.json", actList); + } + } + + return ok(res); + } catch { + return send(res, { error: 1, msg: "Invalid JSON body" }, 400); + } } - // 用户积分 + // 3) 上传服务记录:写 serviceList + 联动积分文件 if ( - req.url.startsWith("/api/service/userScore") && - req.method === "GET" + pathname.startsWith("/api/act/uploadService") && + req.method === "POST" ) { - return sendJSON("userScore.json"); - } + try { + const incoming = await readBodyJson(req); + // 约定:时长字段可能叫 hour / duration / value + const hour = + Number( + incoming.hour ?? incoming.duration ?? incoming.value ?? 0, + ) || 0; - // 年份列表 - if ( - req.url.startsWith("/api/service/yearList") && - req.method === "GET" - ) { - return sendJSON("yearList.json"); - } + // serviceList.json 结构:{data:{list:[{id,pic,content,publisher,time,status}]}} + const sl = readJson("serviceList.json", { + error: 0, + data: { current: 1, pageSize: 10, pageCount: 1, list: [] }, + }); + const list = sl.data?.list || []; - // 年度积分 - if ( - req.url.startsWith("/api/service/yearScore") && - req.method === "GET" - ) { - return sendJSON("yearScore.json"); - } + const nextId = + list.reduce((m, x) => Math.max(m, Number(x.id) || 0), 0) + 1; - // 服务记录列表 - if (req.url.startsWith("/api/service/list") && req.method === "GET") { - return sendJSON("serviceList.json"); - } + list.unshift({ + id: nextId, + pic: incoming.pic || "./imgs/actImg.jpeg", + content: incoming.content || "新增服务记录", + publisher: incoming.publisher || "未知来源", + time: incoming.time || Date.now(), + status: 1, // 1=通过?你 recordDetails.status=1,看起来是已审核/有效 + // 额外字段可保留(不影响页面) + hour, + }); - // 服务详情 - if ( - req.url.startsWith("/api/service/details") && - req.method === "GET" - ) { - return sendJSON("recordDetails.json"); + sl.data = sl.data || {}; + sl.data.list = list; + sl.data.pageCount = 1; + writeJson("serviceList.json", sl); + + // 同步更新积分(按 1小时=2分) + if (hour > 0) addScore(hour); + + return ok(res); + } catch { + return send(res, { error: 1, msg: "Invalid JSON body" }, 400); + } } next(); @@ -145,10 +307,6 @@ function mockPlugin() { } export default defineConfig({ - base: './', - plugins: [ - vue(), - mockPlugin(), - VueDevTools(), - ], -}) + base: "./", + plugins: [vue(), mockPlugin(), VueDevTools()], +});