diff --git a/src/components/settings/StudentListCard.vue b/src/components/settings/StudentListCard.vue index 0485afc..8bd4797 100644 --- a/src/components/settings/StudentListCard.vue +++ b/src/components/settings/StudentListCard.vue @@ -142,7 +142,7 @@ class="text-body-1 flex-grow-1" @click="handleClick(index, student)" > - {{ student }} + {{ student.name }}
@@ -194,7 +194,7 @@ size="large" :loading="loading" :disabled="loading" - @click="$emit('save')" + @click="saveStudents" > 保存名单 @@ -205,7 +205,7 @@ size="large" :loading="loading" :disabled="loading" - @click="$emit('reload')" + @click="loadStudents" > 重载名单 @@ -219,6 +219,8 @@ import UnsavedWarning from "../common/UnsavedWarning.vue"; import "@/styles/warnings.scss"; import { pinyin } from "pinyin-pro"; +import dataProvider from "@/utils/dataProvider"; +import { getSetting } from "@/utils/settings"; export default { name: "StudentListCard", @@ -226,19 +228,7 @@ export default { UnsavedWarning, }, props: { - modelValue: { - type: Object, - required: true, - default: () => ({ - list: [], - text: "", - advanced: false, - }), - }, - loading: Boolean, - error: String, isMobile: Boolean, - unsavedChanges: Boolean, }, data() { @@ -248,101 +238,200 @@ export default { index: -1, name: "", }, + modelValue: { + list: [], + text: "", + advanced: false, + }, + loading: false, + error: null, + lastSavedData: null, + unsavedChanges: false, }; }, - emits: ["update:modelValue", "save", "reload"], - - computed: { - text: { - get() { - return this.modelValue.text; - }, - set(value) { - this.handleTextInput(value); + watch: { + modelValue: { + handler(newData) { + if (this.lastSavedData) { + this.unsavedChanges = JSON.stringify(newData.list) !== JSON.stringify(this.lastSavedData); + } + if (!this.modelValue.advanced) { + this.modelValue.text = newData.list + .slice() + .sort((a, b) => a.id - b.id) + .map(s => s.name) + .join("\n"); + } }, + deep: true, }, }, + mounted() { + this.loadStudents(); + }, + methods: { - // UI 相关方法 + async loadStudents() { + this.error = null; + try { + this.loading = true; + const classNum = getSetting("server.classNumber"); + + if (!classNum) { + throw new Error("请先设置班号"); + } + + try { + const response = await dataProvider.loadData("classworks-list-main"); + + if (response.success != false && Array.isArray(response)) { + this.modelValue.list = response.map((item, index) => { + if (typeof item === 'string') { + return { id: index + 1, name: item }; + } + return { + id: item.id || index + 1, + name: item.name || item.toString() + }; + }); + + this.modelValue.list.sort((a, b) => a.id - b.id); + this.modelValue.text = this.modelValue.list.map(s => s.name).join("\n"); + this.lastSavedData = JSON.parse(JSON.stringify(this.modelValue.list)); + this.unsavedChanges = false; + return; + } + } catch (error) { + console.warn( + "Failed to load student list from dedicated key, falling back to config", + error + ); + } + } catch (error) { + console.error("加载学生列表失败:", error); + this.error = error.message || "加载失败,请检查设置"; + this.$message?.error("加载失败", this.error); + } finally { + this.loading = false; + } + }, + + async saveStudents() { + try { + const classNum = getSetting("server.classNumber"); + + if (!classNum) { + throw new Error("请先设置班号"); + } + + const formattedList = this.modelValue.list + .slice() + .sort((a, b) => a.id - b.id) + .map((student, index) => ({ + id: index + 1, + name: student.name + })); + + const response = await dataProvider.saveData( + "classworks-list-main", + formattedList + ); + + if (response.success === false) { + throw new Error(response.error?.message || "保存失败"); + } + + this.modelValue.list = formattedList; + this.lastSavedData = JSON.parse(JSON.stringify(formattedList)); + this.unsavedChanges = false; + this.$message?.success("保存成功", "学生列表已更新"); + } catch (error) { + console.error("保存学生列表失败:", error); + this.$message?.error("保存失败", error.message || "请重试"); + } + }, + toggleAdvanced() { - const advanced = !this.modelValue.advanced; - this.updateModelValue({ - advanced, - text: advanced ? this.modelValue.list.join("\n") : this.modelValue.text, - list: this.modelValue.list, - }); + this.modelValue.advanced = !this.modelValue.advanced; }, - updateModelValue(newData) { - this.$emit("update:modelValue", { - ...this.modelValue, - ...newData, + handleTextInput(text) { + if (!this.modelValue.advanced) return; + + // Split the text into lines and filter out empty lines + const lines = text.split("\n").filter((line) => line.trim()); + + // Create a map of existing student names to their IDs + const currentIds = new Map(this.modelValue.list.map(s => [s.name, s.id])); + let maxId = Math.max(0, ...this.modelValue.list.map(s => s.id)); + + // Create new list preserving IDs for existing names and generating new IDs for new names + const newList = lines.map(name => { + name = name.trim(); + if (currentIds.has(name)) { + return { id: currentIds.get(name), name }; + } + return { id: ++maxId, name }; }); + + // Update the list + this.modelValue.list = newList; }, - // 基础编辑操作 addStudent() { const name = this.newStudentName.trim(); - if (!name || this.modelValue.list.includes(name)) return; - - const newList = [...this.modelValue.list, name]; - this.updateModelValue({ - list: newList, - text: newList.join("\n"), - }); - this.newStudentName = ""; - }, - - removeStudent(index) { - const newList = this.modelValue.list.filter((_, i) => i !== index); - this.updateModelValue({ - list: newList, - text: newList.join("\n"), - }); - }, - - moveStudent(index, direction) { - const newList = [...this.modelValue.list]; - let targetIndex; - - if (direction === "top") { - targetIndex = 0; - } else if (direction === "up") { - targetIndex = index - 1; - } else { - targetIndex = index + 1; - } - - if (targetIndex >= 0 && targetIndex < newList.length) { - const [student] = newList.splice(index, 1); - newList.splice(targetIndex, 0, student); - - this.updateModelValue({ - list: newList, - text: newList.join("\n"), - }); + if (name && !this.modelValue.list.some(s => s.name === name)) { + const maxId = Math.max(0, ...this.modelValue.list.map(s => s.id)); + this.modelValue.list.push({ id: maxId + 1, name }); + this.newStudentName = ""; } }, - // 文本编辑操作 - startEdit(index, name) { - this.editState = { index, name }; + startEdit(index, student) { + this.editState.index = index; + this.editState.name = student.name; }, saveEdit() { - const { index, name } = this.editState; - if (index === -1 || !name.trim()) return; + if (this.editState.index !== -1) { + const newName = this.editState.name.trim(); + if (newName && newName !== this.modelValue.list[this.editState.index].name) { + this.modelValue.list[this.editState.index].name = newName; + } + this.editState.index = -1; + this.editState.name = ""; + } + }, - const newList = [...this.modelValue.list]; - newList[index] = name.trim(); + removeStudent(index) { + if (index !== undefined) { + this.modelValue.list.splice(index, 1); + } + }, - this.updateModelValue({ - list: newList, - text: newList.join("\n"), - }); - this.editState = { index: -1, name: "" }; + moveStudent(index, direction) { + if (direction === "top") { + if (index > 0) { + const student = this.modelValue.list[index]; + this.modelValue.list.splice(index, 1); + this.modelValue.list.unshift(student); + this.modelValue.list.forEach((s, i) => s.id = i + 1); + } + } else { + const newIndex = direction === "up" ? index - 1 : index + 1; + if (newIndex >= 0 && newIndex < this.modelValue.list.length) { + [this.modelValue.list[index], this.modelValue.list[newIndex]] = [ + this.modelValue.list[newIndex], + this.modelValue.list[index], + ]; + [this.modelValue.list[index].id, this.modelValue.list[newIndex].id] = [ + this.modelValue.list[newIndex].id, + this.modelValue.list[index].id, + ]; + } + } }, handleClick(index, student) { @@ -351,35 +440,20 @@ export default { } }, - handleTextInput(value) { - const list = value - .split("\n") - .map((s) => s.trim()) - .filter((s) => s); - - this.updateModelValue({ - text: value, - list, - }); - }, - sortStudentsByPinyin() { - const newList = [...this.modelValue.list].sort((a, b) => { - const pinyinA = pinyin(a, { toneType: "none" ,mode: 'surname'}); - const pinyinB = pinyin(b, { toneType: "none",mode: 'surname' }); + const sorted = [...this.modelValue.list].sort((a, b) => { + const pinyinA = pinyin(a.name, { toneType: "none" }); + const pinyinB = pinyin(b.name, { toneType: "none" }); return pinyinA.localeCompare(pinyinB); }); - - this.updateModelValue({ - list: newList, - text: newList.join("\n"), - }); + sorted.forEach((s, i) => s.id = i + 1); + this.modelValue.list = sorted; }, }, }; - diff --git a/src/pages/settings.vue b/src/pages/settings.vue index 8ba2f6e..f2303cb 100644 --- a/src/pages/settings.vue +++ b/src/pages/settings.vue @@ -34,12 +34,13 @@ direction="vertical" > - + - Classworks 是开源免费的软件,官方没有提供任何形式的付费支持服务,源代码仓库地址在 - + { this.loadAllSettings(); }); - this.loadStudents(); }, beforeUnmount() { @@ -435,9 +429,7 @@ export default { }); }, - // 添加统一的设置处理方法 handleSettingsChange(newSettings) { - // 使用防抖来避免过多更新 if (this.settingsChangeTimeout) { clearTimeout(this.settingsChangeTimeout); } @@ -453,13 +445,12 @@ export default { this.showMessage("设置已更新", `${settingKey} 已保存`); } else { this.showError("保存失败", `${settingKey} 设置失败`); - // 回滚到原值 this.settings[section][key] = currentValue; } } }); }); - }, 100); // 添加100ms延迟 + }, 100); }, showMessage(title, content = "", type = "success") { @@ -470,89 +461,6 @@ export default { this.$message.error(title, content); }, - async loadStudents() { - this.studentsError = null; - try { - this.loading.students = true; - const classNum = getSetting("server.classNumber"); - - if (!classNum) { - throw new Error("请先设置班号"); - } - - try { - // Try to get student list from the dedicated key - const response = await dataProvider.loadData("classworks-list-main"); - - if (response.success != false && Array.isArray(response)) { - // Transform the data into a simple list of names - this.studentData.list = response.map((student) => student.name); - this.studentData.text = this.studentData.list.join("\n"); - this.lastSavedData = [...this.studentData.list]; - this.hasUnsavedChanges = false; - return; - } - } catch (error) { - console.warn( - "Failed to load student list from dedicated key, falling back to config", - error - ); - } - } catch (error) { - console.error("加载学生列表失败:", error); - this.studentsError = error.message || "加载失败,请检查设置"; - this.showError("加载失败", this.studentsError); - } finally { - this.loading.students = false; - } - }, - - async saveStudents() { - try { - const classNum = getSetting("server.classNumber"); - - if (!classNum) { - throw new Error("请先设置班号"); - } - - // Convert the list of names to the new format with IDs - const formattedStudentList = this.studentData.list.map( - (name, index) => ({ - id: index + 1, - name, - }) - ); - - // Save the student list to the dedicated key - const response = await dataProvider.saveData( - "classworks-list-main", - formattedStudentList - ); - - if (response.success == false) { - throw new Error(response.error?.message || "保存失败"); - } - - // 更新保存状态 - this.lastSavedData = [...this.studentData.list]; - this.hasUnsavedChanges = false; - this.showMessage("保存成功", "学生列表已更新"); - } catch (error) { - console.error("保存学生列表失败:", error); - this.showError("保存失败", error.message || "请重试"); - } - }, - - handleStudentDataChange(newData) { - // 仅在列表实际发生变化时更新 - if ( - JSON.stringify(newData.list) !== JSON.stringify(this.studentData.list) - ) { - this.studentData = { ...newData }; - this.hasUnsavedChanges = true; - } - }, - saveEdit() { if (this.editingIndex !== -1) { const newName = this.editingName.trim(); @@ -587,12 +495,6 @@ export default { } }, - setStudentNumber(index) { - this.studentToMove = index; - this.newPosition = String(index + 1); - this.numberDialog = true; - }, - applyNewPosition() { const newPos = parseInt(this.newPosition) - 1; if ( @@ -680,11 +582,9 @@ export default { onSettingsSaved() { this.showMessage("设置已更新", "您的设置已成功保存"); - // 如果需要,可以在这里重新加载相关数据 }, onSettingUpdate(key, value) { - // 处理设置更新 this.showMessage("设置已更新", `${key} 已保存为 ${value}`); }, },