2026秋软件工程结对作业(第二次):校园失物招领WEB程序实现
2026秋软件工程结对作业(第二次):校园失物招领WEB程序实现
| 项目 | 内容 |
|---|---|
| 课程 | H202601软件工程与软件工程实践 |
| 作业要求 | 2026秋软件工程结对作业(第二次之程序实现) |
| GitHub仓库 | Lin-fzu/102401415-102401428 |
| 学号姓名 | 102401415 林豪;102401428 魏怡健 |
| 原型链接 | 墨刀原型(第一次结对作业) |
一、具体分工
| 成员 | 分工 |
|---|---|
| 林豪(102401415) | 需求梳理与功能范围界定;整体UI设计与样式实现;个人中心页面开发;数据模块设计;PSP记录;博客整体架构 |
| 魏怡健(102401428) | 发布表单与详情页开发;页面交互与跳转逻辑;单元测试设计与执行;GitHub仓库管理;博客撰写与排版;bug修改 |
两人共同完成:功能走查、问题排查、博客校对。
二、PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 30 | 25 |
| Estimate | 估计这个任务需要多少时间 | 20 | 20 |
| Development | 开发 | 240 | 260 |
| Analysis | 需求分析(包括学习新技术) | 30 | 35 |
| Design Spec | 生成设计文档 | 20 | 15 |
| Design Review | 设计复审 | 15 | 10 |
| Coding Standard | 代码规范(为目前的开发制定合适的规范) | 10 | 10 |
| Design | 具体设计 | 30 | 30 |
| Coding | 具体编码 | 150 | 170 |
| Code Review | 代码复审 | 20 | 25 |
| Test | 测试(自我测试,修改代码,提交修改) | 40 | 45 |
| Reporting | 报告 | 60 | 55 |
| Test Report | 测试报告 | 20 | 20 |
| Size Measurement | 计算工作量 | 10 | 10 |
| Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 20 | 15 |
| 合计 | 655 | 665 |
实际编码与测试比预估多了约10分钟,原因是开发过程中根据墨刀原型优化了UI设计,新增了"我的"页面和返回按钮,这些内容在最初预估时没有完全考虑到。
三、解题思路与设计实现
3.1 实现思路
需求回顾:基于第一次结对作业的墨刀原型,在WEB上实现"发布寻物/招领 → 集中浏览 → 关键词搜索 → 查看详情与联系方式 → 发布者标记已找回/已归还"的完整闭环,不实现登录、实名认证、聊天、地图定位。
技术选型:纯原生 HTML + CSS + JavaScript 静态网页。理由:
- 作业要求"下载全部文件后用谷歌浏览器运行html即可展现预期结果",零依赖、无环境是最稳妥的交付方式;
- 数据量小,用浏览器localStorage即可完成持久化,不需要后端;
- 原生JS便于理解和调试,适合课程作业。
架构分层:
data.js(数据层):数据模型 + 示例数据 + localStorage读写;common.js(工具层):公共函数、提示消息、时间格式化等;- 各页面独立JS(交互层):首页、发布页、详情页、我的页各自的业务逻辑;
style.css(视图层):全局样式、组件样式、响应式布局。
关键设计决策:
- 状态闭环:信息标记为"已找到/已归还"后写入localStorage,首页、搜索、详情都从同一数据源渲染,保证状态更新后所有页面都能看到最新结果;
- 异常路径全覆盖:发布信息不完整提示、搜索无结果提示、列表为空提示——不只按一条顺利成功的路线设计产品;
- 统一风格:所有页面采用蓝色渐变主题,卡片式设计,底部统一导航栏,顶部统一返回按钮,保持视觉一致性。
3.2 核心代码片段
数据管理模块(data.js)
使用LocalStorage实现数据的增删改查:
// 获取所有物品
function getAllItems() {
const data = localStorage.getItem('lost_found_items');
return data ? JSON.parse(data) : [];
}
// 添加新物品
function addItem(item) {
const items = getAllItems();
items.unshift(item);
localStorage.setItem('lost_found_items', JSON.stringify(items));
}
// 根据ID获取物品
function getItemById(id) {
const items = getAllItems();
return items.find(item => item.id === parseInt(id));
}
// 更新物品状态
function updateItemStatus(id, status) {
const items = getAllItems();
const index = items.findIndex(item => item.id === parseInt(id));
if (index !== -1) {
items[index].status = status;
localStorage.setItem('lost_found_items', JSON.stringify(items));
}
}
搜索与筛选功能(index.js)
根据关键词和分类过滤物品列表:
function filterItems() {
const keyword = searchInput.value.trim().toLowerCase();
const type = currentFilter;
let items = getAllItems();
// 按类型筛选
if (type !== 'all') {
items = items.filter(item => item.type === type);
}
// 按关键词搜索
if (keyword) {
items = items.filter(item =>
item.name.toLowerCase().includes(keyword) ||
item.description.toLowerCase().includes(keyword)
);
}
renderItems(items);
}
表单验证(publish.js)
提交前验证所有必填字段:
function validateForm() {
const name = document.getElementById('itemName').value.trim();
const description = document.getElementById('itemDesc').value.trim();
const location = document.getElementById('itemLocation').value.trim();
const time = document.getElementById('itemTime').value;
const contact = document.getElementById('itemContact').value.trim();
if (!name) { alert('请输入物品名称'); return false; }
if (!description) { alert('请输入物品描述'); return false; }
if (!location) { alert('请输入地点'); return false; }
if (!time) { alert('请选择时间'); return false; }
if (!contact) { alert('请输入联系方式'); return false; }
return true;
}
四、附加特点设计与展示
作业要求的功能之外,我们做了两个额外设计。选它们的标准是:真正解决了失物招领这个场景里的痛点,而不是为了加功能而加功能。
特点:关键词图标映射 —— 让列表一眼可扫
1. 创意独到之处与意义
失物招领的列表页有个特点:用户是在「扫」而不是在「读」。一个人丢了一把伞,他会快速划过列表找和伞有关的条目。
问题是项目没有图片上传功能,所有卡片都是纯文字,看起来长得一模一样,找东西很费眼。
我们的做法是:从物品名称里提取关键词,映射成对应的 emoji 图标。
| 名称含 | 显示图标 |
|---|---|
| 校园卡 | 💳 |
| 雨伞 | ☂️ |
| 耳机 | 🎧 |
| 钥匙 | 🔑 |
| 手机 | 📱 |
| 钱包 | 👛 |
| 书 | 📚 |
| 水杯 | 🥤 |
| 都没命中 | 📦(兜底) |
意义:用户扫列表时,视线可以直接跟着图标走,不用逐条读文字。而且这个方案零成本 —— 不需要图床、不需要上传接口、不需要后端存储,纯前端十几行代码就实现了视觉区分度。
2. 实现思路
用一个「关键词 → emoji」的映射表,渲染每条记录时拿标题去匹配,命中就返回对应图标,都没命中就返回默认的 📦。
顺序遍历映射表、返回第一个命中的结果。因为映射表的顺序是人为安排的,把更具体的词放前面即可。
3. 代码片段
位置:js/index.js
// 物品图标映射
const itemIcons = {
'校园卡': '💳',
'雨伞': '☂️',
'耳机': '🎧',
'钥匙': '🔑',
'手机': '📱',
'钱包': '👛',
'书': '📚',
'水杯': '🥤'
};
// 获取物品图标
function getItemIcon(title) {
for (let key in itemIcons) {
if (title.includes(key)) {
return itemIcons[key];
}
}
return '📦';
}
五、页面展示
5.1 首页

- 蓝色渐变顶部导航栏,包含平台标题
- 白色圆角搜索栏 + 分类筛选标签(全部/寻物/招领)
- 两个大按钮:"我丢东西了"(蓝色)和"我捡到东西了"(绿色)
- 物品卡片列表:缩略图标、类型标签、状态、名称、地点时间
- 底部固定导航栏:首页、发布、我的
5.2 发布页

- 顶部返回按钮 + 页面标题
- 寻物/招领类型选择器
- 表单字段:物品名称、描述、地点、时间、联系方式
- 必填字段验证,提交成功后跳转首页
5.3 详情页

- 顶部返回按钮 + 页面标题
- 物品完整信息展示
- 联系方式高亮显示
- 发布者本人可见状态更新按钮
5.4 我的页面

- 蓝色渐变用户卡片:头像、姓名、已实名标签、学院信息
- 数据统计:我的发布数、成功归还数、帮助他人数
- 我的发布列表:展示自己发布的所有物品
- 功能按钮组:消息通知、我的收藏、浏览记录、去搜索
- 设置列表:默认联系方式(可弹窗修改)、认领提醒、帮助与反馈
六、目录结构与运行说明
6.1 目录结构
102401415-102401428/
├── index.html # 首页 - 信息列表与搜索
├── publish.html # 发布页 - 发布新信息
├── detail.html # 详情页 - 查看物品详情
├── profile.html # 我的 - 个人中心页面
├── list.html # 列表页 - 通用空状态列表
├── css/
│ └── style.css # 全局样式
├── js/
│ ├── data.js # 数据管理与本地存储
│ ├── common.js # 公共函数工具
│ ├── index.js # 首页逻辑
│ ├── publish.js # 发布页逻辑
│ ├── detail.js # 详情页逻辑
│ └── profile.js # 我的页面逻辑
└── README.md # 项目说明文档
6.2 运行说明
- 从GitHub仓库下载全部文件(保持目录结构不变)
- 用谷歌浏览器(Chrome)打开
index.html,即可使用全部功能 - 无需安装任何环境、无需联网
- 数据保存在浏览器localStorage中,关闭页面再打开记录仍在
七、单元测试
7.1 测试说明
采用白盒测试方法,针对核心功能模块设计测试用例,覆盖正常流程、异常处理和边界情况。
7.2 测试用例列表
| 序号 | 测试用例 | 预期结果 | 是否通过 |
|---|---|---|---|
| 1 | 正常发布寻物信息 | 填写完整表单,提交后列表显示新信息 | ✅ |
| 2 | 正常发布招领信息 | 切换类型为招领,提交后类型标签正确显示 | ✅ |
| 3 | 发布信息缺少必填字段 | 留空字段提交,弹出提示错误 | ✅ |
| 4 | 关键词搜索匹配结果 | 输入已有物品关键词,显示匹配结果 | ✅ |
| 5 | 关键词搜索无结果 | 输入不存在的关键词,显示空状态提示 | ✅ |
| 6 | 分类筛选 | 切换"寻物/招领"标签,列表正确过滤 | ✅ |
| 7 | 查看物品详情 | 点击卡片,跳转到详情页并显示完整信息 | ✅ |
| 8 | 更新物品状态 | 发布者点击标记按钮,状态变为已解决 | ✅ |
| 9 | 数据持久化 | 刷新页面后,已发布的信息仍然存在 | ✅ |
| 10 | 状态更新权限 | 非发布者看不到状态修改按钮 | ✅ |
| 11 | 我的发布列表 | 个人中心正确显示当前用户发布的所有物品 | ✅ |
| 12 | 返回按钮 | 所有二级页面点击返回按钮可正确返回上一页 | ✅ |
7.3 测试评价
以上12个测试用例覆盖了正常流程、异常处理、边界情况和权限控制,能够基本验证程序的正确性。测试根据代码逻辑设计用例,确保核心功能模块都被覆盖到。
八、Github的代码签入记录截图

九、遇到的代码模块异常或结对困难及解决方法
详情页「标记为已找到」按钮完全失效
问题描述:按钮点了没反应,不报错,还时好时坏 —— 从首页进能点,刷新后就不能点。
一、做过哪些尝试:
-
怀疑 CSS 遮挡 → 加
z-index,无效 -
怀疑
id取错 → 打印元素,拿到的是对的,排除 -
在绑定处打日志 → 刷新后日志不打印,说明代码没执行到
根因:事件绑定写在 renderDetail() 里,该函数在「非发布者」分支会提前 return,后面的绑定被跳过。
二、是否解决:
已解决。绑定移到 DOMContentLoaded 里无条件执行,renderDetail() 只管按钮显不显示。
三、有何收获:
事件绑定不要写在带条件分支的函数里。函数只要有提前 return,后面的绑定就是定时炸弹 —— 静默失效还不报错。另外,「不报错但没反应」的问题,先确认代码有没有执行到,再确认逻辑对不对。
十、个人总结
林豪(102401415)
通过本次结对作业,我将第一次的原型设计转化为了可运行的WEB程序,对前后端分离的开发模式有了更直观的理解。在实现列表展示和搜索功能时,我学习了JavaScript的DOM操作和数组过滤方法。遇到的主要问题是页面之间的数据传递,通过LocalStorage的统一管理解决了这个问题。这次开发让我体会到,把设计图变成真正能用的产品,需要考虑很多细节,比如表单验证、空状态提示、错误处理等等。后续根据墨刀原型优化了UI,加入了底部导航和个人中心页面,让整个产品更加完整。
魏怡健(102401428)
本次结对实现让我体会到从设计到代码的转化过程。在开发发布表单和详情页时,我深入学习了表单验证和URL参数传递的方法。与林豪的协作中,我们通过明确的模块划分提高了开发效率,也遇到了接口对接的问题,通过沟通及时调整了数据格式。这次作业让我更加理解了软件工程中模块划分和接口设计的重要性。
十一、评价队友
评价魏怡健(102401428)
值得学习的地方:在发布表单开发中考虑周全,对用户输入的验证处理细致,代码结构清晰,注释完整。
需要改进的地方:可以在开发前多与队友沟通数据格式和接口定义,减少后续对接调整的时间。
评价林豪(102401415)
值得学习的地方:UI设计美观,整体风格统一,对细节把控到位,能快速把设计想法转化为可运行的界面。
需要改进的地方:可以在开发前更明确地拆分任务,让分工更均衡。
浙公网安备 33010602011771号