Extreme Programming
搜索算法(基于performSearch函数)选择localStorage。SheetJS库的实现方法。
·
文章目录
| 本次作业所属课程 | EE308FZ(Software Engineering) |
|---|---|
| 团队名称 | 兑怼对队 |
| 作业要求 | Extreme Programming |
| 本次作业目标 | 基础要求博客格式,使用Markdown格式;正确提供Github仓库链接和代码规范链接。功能实现思路说明、程序截图、成员分工说明。代码实现 页面样式;收藏联系人功能;添加多种联系方式功能;导入与导出功能 |
一、链接
Github仓库链接:https://github.com/duiduiduidui-team/duiduiduidui/tree/master
代码规范链接:https://github.com/2625690482/Code-Standards/tree/master
云服务器链接:https://brandy666666.github.io/address-book-system/
二、团队成员信息
| 姓名 | 李奕霏 | 杨璐 |
|---|---|---|
| FZU学号 | 832301118 | 832301123 |
| MU学号 | 23125535 | 23132604 |
| 分工 | HTML/CSS界面实现、收藏功能、多联系方式管理、搜索筛选;撰写博客整理程序截图和视频、制作个人PSP表 | Excel导入导出功能、模态框交互、云服务器部署、项目测试;整理Git提交记录、部署文档、制作个人PSP表 |
| 贡献比例 | 50% | 50% |
三、Github提交日志

| 姓名 | 李奕霏 | 杨璐 |
|---|---|---|
| 提交次数 | 3 | 2 |
四、功能实现思路说明
4.1 整体架构设计
系统采用MVVM(Model-View-ViewModel)轻量级架构,通过清晰的三层分离实现:
-
数据层(Model)
- 联系人数据存储在
localStorage中 - 使用
contacts数组作为内存缓存 - 数据格式为标准的JSON对象
- 联系人数据存储在
-
视图层(View)
index.html:页面结构style.css:样式表现- DOM元素构成用户界面组件
-
控制层(Controller)
script.js:连接数据和视图- 通过事件监听实现用户交互
- 数据变更自动同步到视图
4.2 模块架构设计
系统模块划分为:
核心模块架构:
├── 初始化模块(DOMContentLoaded)
│ ├── 加载联系人数据
│ ├── 设置事件监听器
│ └── 更新统计信息
├── 数据管理模块
│ ├── loadContacts() - 数据加载
│ ├── saveContacts() - 数据保存
│ └── updateStats() - 统计更新
├── 联系人操作模块
│ ├── 增:saveContact()
│ ├── 删:deleteContact()
│ ├── 改:editContact()
│ └── 查:renderContacts()
├── 搜索筛选模块
│ ├── performSearch() - 搜索功能
│ └── 筛选按钮事件 - 筛选功能
└── 导入导出模块
├── exportToExcel() - 导出Excel
└── importFromExcel() - 导入Excel
4.3 Excel处理技术
SheetJS库的实现方法
// Excel导出实现流程(从exportToExcel函数提取)
1. 数据验证:检查contacts数组是否为空
2. 数据转换:contacts对象 → 二维数组worksheetData
3. 创建列头:定义Excel表头结构
4. 填充数据:遍历contacts,提取各字段数据
5. 格式设置:设置列宽(colWidths)
6. 文件生成:使用XLSX库生成Excel文件
7. 触发下载:创建虚拟标签触发下载
关键转换逻辑
// 数据转换逻辑
contacts.forEach(contact => {
// 初始化联系方式映射
const methods = {
'手机': '', '电话': '', '邮箱': '',
'微信': '', 'QQ': '', '其他': ''
};
// 填充联系方式(扁平化处理)
contact.methods.forEach(method => {
if (methods.hasOwnProperty(method.type)) {
methods[method.type] = method.value;
}
});
// 构建Excel行数据
const row = [
contact.name || '',
contact.company || '',
contact.position || '',
methods['手机'],
methods['电话'],
methods['邮箱'],
methods['微信'],
methods['QQ'],
methods['其他'],
contact.notes || '',
contact.isFavorite ? '是' : '否'
];
});
4.4 搜索功能实现
搜索算法(基于performSearch函数)
// 多字段模糊搜索实现
function performSearch() {
const searchTerm = searchInput.value.trim().toLowerCase();
// 1. 空搜索词处理
if (!searchTerm) {
renderContacts();
return;
}
// 2. 多字段搜索逻辑
const searchResults = contacts.filter(contact =>
// 姓名搜索(必填字段)
contact.name.toLowerCase().includes(searchTerm) ||
// 可选字段搜索(使用可选链操作符)
contact.company?.toLowerCase().includes(searchTerm) ||
contact.position?.toLowerCase().includes(searchTerm) ||
contact.notes?.toLowerCase().includes(searchTerm) ||
// 联系方式搜索(遍历数组)
contact.methods.some(method =>
method.value.toLowerCase().includes(searchTerm)
)
);
// 3. 搜索结果渲染
if (searchResults.length === 0) {
// 空状态提示
contactList.innerHTML = `...`;
} else {
// 渲染搜索结果
contactList.innerHTML = '';
searchResults.forEach(contact => {
// 使用相同的renderContacts逻辑但只渲染搜索结果
});
}
}
算法特点
- 大小写不敏感:统一转为小写比较
- 模糊匹配:使用includes而非===精确匹配
- 多字段搜索:覆盖所有可能的信息字段
- 实时性:即时搜索,无延迟
4.5 数据存储方案选择
选择localStorage
// 1. 零配置
let contacts = JSON.parse(localStorage.getItem('addressBookContacts')) || [];
// 2. 同步API,操作简便
function saveContacts() {
localStorage.setItem('addressBookContacts', JSON.stringify(contacts));
updateStats();
}
// 3. 足够容量:每个联系人约200-500字节,5MB localStorage可存储约10000-25000个联系人
具体实现方法
// 数据加载 - 带容错处理
function loadContacts() {
const saved = localStorage.getItem('addressBookContacts');
if (saved) {
// 防止JSON解析失败导致程序崩溃
try {
contacts = JSON.parse(saved);
} catch (e) {
console.error('数据解析失败,使用默认值');
contacts = [];
}
}
renderContacts();
}
// 数据保存 - 自动序列化
function saveContacts() {
localStorage.setItem('addressBookContacts', JSON.stringify(contacts));
updateStats(); // 保存后自动更新统计
}
4.6联系人保存流程
function saveContact(e) {
e.preventDefault();
// 1. 收集表单数据
const id = document.getElementById('contactId').value || generateId();
const name = document.getElementById('name').value.trim();
// 2. 表单验证
if (!name) {
alert('请输入姓名');
return;
}
// 3. 收集联系方式(动态表单处理)
const methods = [];
const methodRows = contactMethodsContainer.querySelectorAll('.method-row');
methodRows.forEach(row => {
const type = row.querySelector('.method-type').value;
const value = row.querySelector('.method-value').value.trim();
if (value) {
methods.push({ type, value });
}
});
// 4. 构建完整联系人对象
const contactData = {
id,
name,
company: document.getElementById('company').value.trim(),
position: document.getElementById('position').value.trim(),
notes: document.getElementById('notes').value.trim(),
methods,
isFavorite: document.getElementById('isFavorite').checked,
createdAt: new Date().toISOString()
};
// 5. 区分新增和编辑
const index = contacts.findIndex(c => c.id === id);
if (index > -1) {
contacts[index] = contactData; // 编辑
} else {
contacts.push(contactData); // 新增
}
// 6. 保存并更新UI
saveContacts();
renderContacts();
contactModal.style.display = 'none';
}
4.7 交互设计实现
// 1. 即时反馈机制
// 搜索框Enter键支持
searchInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') performSearch();
});
// 2. 模态框管理
// 点击外部关闭模态框
window.addEventListener('click', (e) => {
if (e.target === contactModal) contactModal.style.display = 'none';
if (e.target === confirmModal) confirmModal.style.display = 'none';
});
// 3. 动态表单交互
// 联系方式动态添加/删除
addMethodBtn.addEventListener('click', addContactMethod);
function addContactMethod(type = '', value = '') {
// 动态创建表单行
const methodRow = document.createElement('div');
methodRow.className = 'method-row';
methodRow.innerHTML = `
<select class="method-type">...</select>
<input type="text" class="method-value" value="${value}">
<button type="button" class="remove-method">X</button>
`;
// 添加删除功能
const removeBtn = methodRow.querySelector('.remove-method');
removeBtn.addEventListener('click', () => {
methodRow.remove();
});
}
4.8 错误处理和用户引导
// 1. 删除确认(防止误操作)
function showDeleteConfirm(id) {
contactToDelete = id;
confirmModal.style.display = 'flex'; // 显示确认对话框
}
// 2. 导入文件验证
function importFromExcel(event) {
const file = event.target.files[0];
// 文件类型验证
if (!file.name.match(/\.(xlsx|xls)$/i)) {
alert('请选择Excel文件 (.xlsx 或 .xls)');
return;
}
// 文件读取和错误处理
const reader = new FileReader();
reader.onload = function(e) {
try {
// 数据处理逻辑...
} catch (error) {
console.error('导入失败:', error);
alert('导入失败,请检查Excel文件格式是否正确');
}
};
}
// 3. 空状态提示
function renderContacts() {
if (filteredContacts.length === 0) {
contactList.innerHTML = `
<div class="empty-state">
<i class="fas fa-address-book fa-3x"></i>
<p>暂无联系人,点击"添加联系人"开始</p>
</div>
`;
return;
}
}
五、程序运行展示
5.1收藏联系人


5.2添加多种联系方式

5.3导入与导出


5.4搜索联系人

5.5演示视频
六、合作中遇到的困难及解决方案
- 功能需求理解不一致
初期对"多种联系方式"的具体实现方式理解不同
解决方案:沟通明确详细的功能说明,确保理解一致 - 代码风格不统一
变量命名、注释格式、文件结构等存在差异
解决方案:制定了团队代码规范文档,并随时沟通理解对方内容 - 分工覆盖不足
某些边界情况未充分明确分工,导致合并运行出现bug
解决方案:检查出错问题,明确责任边界 - 项目进度把控困难
某些任务耗时超出预期,影响整体进度
解决方案:设定明确的里程碑和检查点
七、PSP表格
- 李奕霏
| PSP阶段 | 任务内容 | 预估时间(分钟) | 实际时间(分钟) |
|---|---|---|---|
| 计划 | 需求分析与功能规划 | 10 | 7 |
| 界面设计草稿 | 20 | 23 | |
| 技术方案选择 | 20 | 18 | |
| 设计 | HTML结构设计 | 20 | 45 |
| CSS样式方案设计 | 20 | 14 | |
| 响应式布局规划 | 20 | 19 | |
| 组件交互设计 | 20 | 20 | |
| 开发 | HTML基础框架搭建 | 60 | 73 |
| CSS样式编写 | 120 | 107 | |
| 响应式设计实现 | 90 | 68 | |
| 收藏功能开发 | 60 | 74 | |
| 多联系方式管理 | 90 | 88 | |
| 搜索筛选功能 | 90 | 59 | |
| 界面交互优化 | 60 | 68 | |
| 文档 | 程序截图制作 | 30 | 27 |
| 操作视频录制 | 10 | 23 | |
| PSP表整理 | 20 | 21 | |
| 博客撰写整理 | 60 | 132 | |
| 总计 | 820 | 886 |
- 杨璐
| PSP阶段 | 任务内容 | 预估时间(分钟) | 实际时间(分钟) |
|---|---|---|---|
| 计划 | 功能需求分析 | 20 | 10 |
| 接口设计 | 20 | 24 | |
| 设计 | 数据导入导出方案设计 | 20 | 12 |
| 模态框交互流程设计 | 20 | 18 | |
| 数据库结构设计 | 20 | 27 | |
| 开发 | Excel导入功能开发 | 120 | 113 |
| Excel导出功能开发 | 120 | 94 | |
| 模态框交互逻辑 | 90 | 102 | |
| 表单验证与提交处理 | 60 | 77 | |
| 数据存储管理 | 60 | 54 | |
| 测试及调整 | 90 | 124 | |
| 部署 | 90 | 48 | |
| 文档 | Git提交记录整理 | 10 | 7 |
| PSP表整理 | 20 | 26 | |
| 总计 | 760 | 736 |
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)