170
社区成员
Course for This Assignment | 2401_MU_SE_EE308 |
---|---|
Student ID and Name | 832201128_Xiong ZhiXue & 832201121_Gao Yu |
Assignment Requirements | Develop a web-based contact management system with bookmarking, multiple contact methods, and import/export features. |
Objectives of This Assignment | Practice modular design and collaborative software engineering techniques and achieve functional and collaborative milestones |
https://github.com/xzx904/Contact-book.git
This image shows an intuitive interface to an electronic address book, clearly listing the details of many contacts. Each contact is equipped with their name, phone number, email address, and a series of useful action buttons such as "Delete," "Edit," and "Top." This information is carefully organized in a tabular format, allowing users to view and manage their contacts at a glance.
In addition, the address book interface also provides the "Export Excel" function, which allows users to export contact information to an Excel table with one click for further data processing or backup. In one corner of the interface, there is also an option to "select File" to import an Excel table with contact information into the address book.
This GIF demonstrates the Bookmark Contacts feature of the Contact book application. Users can move a specific contact to the top of the list by clicking the "top" button, and the font of all the information of the top contact will be bolded and blackened, making it easy to access important contacts. In addition, the app also supports the "untop" function, allowing users to untop at any time.
Code Reference:
// 渲染联系人列表
function renderContacts() {
// 将联系人按置顶顺序排序
contacts.sort((a, b) => {
if (a.isTop && b.isTop) {
return a.topOrder - b.topOrder; // 如果都置顶,按置顶顺序排列
}
return b.isTop - a.isTop; // 置顶的联系人排在最前面
});
contactTable.innerHTML = "";
contacts.forEach((contact) => {
const row = document.createElement("tr");
const rowClass = contact.isTop ? "highlight" : ""; // 如果置顶,添加加粗样式
const phoneList = contact.phone
.map((p, index) => `${p} <button class="delete-phone" data-id="${contact.id}" data-phone-index="${index}">删除</button>`)
.join("<br>");
const emailList = contact.email.join("<br>");
row.innerHTML = `
<td class="${rowClass}">${contact.name}</td>
<td class="${rowClass}">${phoneList || "无"}</td>
<td class="${rowClass}">${emailList || "无"}</td>
<td>
<button class="edit" data-id="${contact.id}">编辑</button>
<button class="delete" data-id="${contact.id}">删除</button>
<button class="top ${contact.isTop ? 'top-button' : ''}" data-id="${contact.id}">
${contact.isTop ? '取消置顶' : '置顶'}
</button>
</td>
`;
contactTable.appendChild(row);
});
}
// 置顶按钮点击事件
contactTable.addEventListener("click", (e) => {
if (e.target.classList.contains("top")) {
const contactId = e.target.dataset.id;
const contact = contacts.find((c) => c.id === contactId);
if (contact) {
if (!contact.isTop) {
// 置顶
contact.isTop = true;
contact.topOrder = Date.now(); // 用时间戳记录置顶顺序
} else {
// 取消置顶
contact.isTop = false;
contact.topOrder = null; // 取消置顶时清除顺序
}
renderContacts(); // 重新渲染
}
}
if (e.target.classList.contains("edit")) {
const contactId = e.target.dataset.id;
const contact = contacts.find((c) => c.id === contactId);
if (contact) {
document.getElementById("contactId").value = contact.id;
document.getElementById("name").value = contact.name;
document.getElementById("phone").value = contact.phone.join(", ");
document.getElementById("email").value = contact.email.join(", ");
}
}
if (e.target.classList.contains("delete")) {
const contactId = e.target.dataset.id;
contacts = contacts.filter((c) => c.id !== contactId);
renderContacts();
}
if (e.target.classList.contains("delete-phone")) {
const contactId = e.target.dataset.id;
const phoneIndex = e.target.dataset.phoneIndex;
const contact = contacts.find((c) => c.id === contactId);
if (contact) {
contact.phone.splice(phoneIndex, 1);
renderContacts();
}
}
});
This GIF shows the ability to add additional information about a contact in your address book. Users can re-edit the contact information by clicking the "Edit" button, then change the phone or email, and then click the "Add/Modify" button, you can reflect a variety of phones and email in the contact information, and equipped with a "delete" button for deleting redundant phones.
Code Reference:
// 添加或修改联系人
contactForm.addEventListener("submit", (e) => {
e.preventDefault();
const id = document.getElementById("contactId").value; // 隐藏字段
const name = document.getElementById("name").value.trim();
const phone = document.getElementById("phone").value.trim();
const email = document.getElementById("email").value.trim();
if (id) {
// 编辑联系人
const contact = contacts.find((c) => c.id === id);
if (contact) {
contact.name = name;
if (phone && !contact.phone.includes(phone)) {
contact.phone.push(phone);
}
if (email && !contact.email.includes(email)) {
contact.email.push(email);
}
}
} else {
// 添加新联系人
let existingContact = contacts.find((c) => c.name === name);
if (existingContact) {
if (phone && !existingContact.phone.includes(phone)) {
existingContact.phone.push(phone);
}
if (email && !existingContact.email.includes(email)) {
existingContact.email.push(email);
}
} else {
contacts.push({
id: Date.now().toString(),
name,
phone: phone ? [phone] : [],
email: email ? [email] : [],
});
}
}
renderContacts();
contactForm.reset();
document.getElementById("contactId").value = ""; // 重置隐藏字段
});
// 编辑联系人信息
contactTable.addEventListener("click", (e) => {
const target = e.target;
const id = target.dataset.id;
if (target.classList.contains("edit")) {
const contact = contacts.find((c) => c.id === id);
if (contact) {
document.getElementById("contactId").value = contact.id;
document.getElementById("name").value = contact.name;
document.getElementById("phone").value = "";
document.getElementById("email").value = "";
}
}
if (target.classList.contains("delete")) {
contacts = contacts.filter((c) => c.id !== id);
renderContacts();
}
if (target.classList.contains("delete-phone")) {
const phoneIndex = target.dataset.phoneIndex;
const contact = contacts.find((c) => c.id === id);
if (contact) {
contact.phone.splice(phoneIndex, 1);
}
renderContacts();
}
});
This GIF illustrates the Import and Export Contacts feature of the contact book application.
Export Function: Allows users to export all contact information from the address book into an Excel file, with each row representing a contact and each column corresponding to a specific contact information field (e.g., name, phone number, backup phone number, email,).
Import function: After clicking the "Select File" button in the address book, the user can choose to upload an Excel file with contact information. The system will automatically parse the content of the file and import the contact information strictly according to the order of name, phone number, and email address. After the "Select File" button, the file name of the imported Excel table will be displayed. If no file is imported, "No file selected" will be displayed.
Import function implementation:
Export function implementation:
// 导出为 Excel
exportButton.addEventListener("click", () => {
const data = contacts.map((contact) => ({
姓名: contact.name,
电话: contact.phone.join(", "),
邮箱: contact.email.join(", "),
}));
const ws = XLSX.utils.json_to_sheet(data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "通讯录");
XLSX.writeFile(wb, "通讯录.xlsx");
});
// 导入 Excel
importFile.addEventListener("change", (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = new Uint8Array(e.target.result);
const workbook = XLSX.read(data, { type: "array" });
const sheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[sheetName];
const importedData = XLSX.utils.sheet_to_json(worksheet);
if (!Array.isArray(importedData) || importedData.length === 0) {
alert("文件内容为空或格式不正确!");
return;
}
importedData.forEach((row) => {
const name = (row["姓名"] || "").toString().trim();
const phones = (row["电话"] || "")
.toString()
.split(",")
.map((p) => p.trim());
const emails = (row["邮箱"] || "")
.toString()
.split(",")
.map((e) => e.trim());
if (!name) return;
let existingContact = contacts.find((c) => c.name === name);
if (existingContact) {
phones.forEach((p) => {
if (p && !existingContact.phone.includes(p)) {
existingContact.phone.push(p);
}
});
emails.forEach((e) => {
if (e && !existingContact.email.includes(e)) {
existingContact.email.push(e);
}
});
} else {
contacts.push({
id: Date.now().toString(),
name,
phone: phones.filter(Boolean),
email: emails.filter(Boolean),
});
}
});
renderContacts();
alert("导入成功!");
} catch (err) {
alert("文件解析失败,请检查文件格式!");
console.error(err);
}
};
reader.readAsArrayBuffer(file);
});
Team Member | Role | Responsibilities | Contribution (%) |
---|---|---|---|
Gao Yu | Frontend Developer | UI layout creation, implementing forms, integrating APIs, participating in tech stack selection, planning and design, testing, and project evaluation | 50% |
Xiong ZhiXue | Backend Developer | Server and database setup, API development, system architecture design, deployment (server hosting and configuration), and back-end testing for robustness and functionality | 50% |
Challenge 1: When developing features to add additional information to contacts, we often encounter the problem that the back end frequently receives empty JSON objects. This phenomenon causes errors in data validation, which in turn affects the normal operation of the function.
Challenge 2: The deployment of multiple projects on low-cost micro-servers significantly increased the server burden, resulting in reduced processing power, especially during peak user hours, and significantly reduced system efficiency.
Challenge 3: In the process of building the Excel file import tool, we noticed that users often submitted incorrectly formatted files, which led to data processing failures and database data inconsistencies.
Challenge 4: In team collaboration, poor communication occasionally leads to unclear priorities, which leads to duplication of effort or delays in integrating back-end functionality with the front end.
PSP Table for Member Gao Yu
Modules | Task Description | Estimated Time | Actual Time |
---|---|---|---|
Group Discussion and Demand Analysis | Determine the required functions of the web page | 1 hour | 1 hour |
Knowledge Learning | Learning about Excel Import and Export | 1 hour | 2 hour |
Frontend Development | Create a Vue project | 1 hour | 2 hour |
Frontend Test and Debug | Test the web page functions | 2 hour | 3 hour |
Deploy | Deploy the application to the server (Huawei Cloud) | 1 hour | 2 hour |
Total | 6 hour | 10 hour |
PSP Table for Member Xiong ZhiXue
Modules | Task Description | Estimated Time | Actual Time |
---|---|---|---|
Group Discussion and Demand Analysis | Determine the required functions of the web page | 1 hour | 1 hour |
Knowledge Learning | Learning about Excel Import and Export | 1 hour | 2 hour |
Backend Development | Create a SpringBoot project | 3 hour | 5 hour |
Backend Test and Debug | Test the web page functions | 1 hour | 2 hour |
Deploy | Deploy the application to the server | 1 hour | 2 hour |
Total | 7 hour | 12 hour |