mirror of
https://github.com/ZeroCatDev/Classworks.git
synced 2025-07-03 01:39:22 +00:00
1
This commit is contained in:
parent
d214f172f6
commit
08e95a3efc
@ -1,11 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-card border>
|
<v-card
|
||||||
|
border
|
||||||
|
:color="hasChanges ? 'warning-subtle' : undefined"
|
||||||
|
:class="{ 'unsaved-changes': hasChanges }"
|
||||||
|
>
|
||||||
<v-card-item>
|
<v-card-item>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<v-icon icon="mdi-account-group" size="large" class="mr-2" />
|
<v-icon icon="mdi-account-group" size="large" class="mr-2" />
|
||||||
</template>
|
</template>
|
||||||
<v-card-title class="text-h6">学生列表</v-card-title>
|
<v-card-title class="text-h6">学生列表</v-card-title>
|
||||||
<template #append>
|
<template #append>
|
||||||
|
<v-chip
|
||||||
|
v-if="hasChanges"
|
||||||
|
color="warning"
|
||||||
|
size="small"
|
||||||
|
class="mr-2"
|
||||||
|
>
|
||||||
|
未保存
|
||||||
|
</v-chip>
|
||||||
<v-btn
|
<v-btn
|
||||||
:color="modelValue.advanced ? 'primary' : undefined"
|
:color="modelValue.advanced ? 'primary' : undefined"
|
||||||
variant="text"
|
variant="text"
|
||||||
@ -163,7 +175,7 @@
|
|||||||
<!-- 高级编辑模式 -->
|
<!-- 高级编辑模式 -->
|
||||||
<div v-else class="pt-2">
|
<div v-else class="pt-2">
|
||||||
<v-textarea
|
<v-textarea
|
||||||
v-model="modelValue.text"
|
v-model="text"
|
||||||
label="批量编辑学生列表"
|
label="批量编辑学生列表"
|
||||||
placeholder="每行输入一个学生姓名"
|
placeholder="每行输入一个学生姓名"
|
||||||
hint="使用文本编辑模式批量编辑学生名单,保存时会自动去除空行"
|
hint="使用文本编辑模式批量编辑学生名单,保存时会自动去除空行"
|
||||||
@ -182,8 +194,8 @@
|
|||||||
prepend-icon="mdi-content-save"
|
prepend-icon="mdi-content-save"
|
||||||
size="large"
|
size="large"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:disabled="loading"
|
:disabled="loading || !hasChanges"
|
||||||
@click="$emit('save')"
|
@click="handleSave"
|
||||||
>
|
>
|
||||||
保存名单
|
保存名单
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@ -193,7 +205,7 @@
|
|||||||
prepend-icon="mdi-refresh"
|
prepend-icon="mdi-refresh"
|
||||||
size="large"
|
size="large"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:disabled="loading"
|
:disabled="loading || !hasChanges"
|
||||||
@click="$emit('reload')"
|
@click="$emit('reload')"
|
||||||
>
|
>
|
||||||
重载名单
|
重载名单
|
||||||
@ -219,19 +231,60 @@ export default {
|
|||||||
},
|
},
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
error: String,
|
error: String,
|
||||||
isMobile: Boolean
|
isMobile: Boolean,
|
||||||
|
originalList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
newStudent: '',
|
newStudent: '',
|
||||||
editingIndex: -1,
|
editingIndex: -1,
|
||||||
editingName: ''
|
editingName: '',
|
||||||
|
internalOriginalList: [] // 添加内部状态
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
// 初始化内部状态
|
||||||
|
this.internalOriginalList = [...this.originalList];
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
originalList: {
|
||||||
|
handler(newList) {
|
||||||
|
this.internalOriginalList = [...newList];
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['update:modelValue', 'save', 'reload'],
|
emits: ['update:modelValue', 'save', 'reload'],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
text: {
|
||||||
|
get() {
|
||||||
|
return this.modelValue.text;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.handleTextInput(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasChanges() {
|
||||||
|
const currentList = this.modelValue.list;
|
||||||
|
const originalList = this.internalOriginalList;
|
||||||
|
|
||||||
|
if (currentList.length !== originalList.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 JSON 字符串比较来优化性能
|
||||||
|
return JSON.stringify(currentList) !== JSON.stringify(originalList);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
toggleAdvanced() {
|
toggleAdvanced() {
|
||||||
const advanced = !this.modelValue.advanced;
|
const advanced = !this.modelValue.advanced;
|
||||||
@ -321,6 +374,19 @@ export default {
|
|||||||
}
|
}
|
||||||
this.editingIndex = -1;
|
this.editingIndex = -1;
|
||||||
this.editingName = '';
|
this.editingName = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
handleClick(index, student) {
|
||||||
|
// 如果是移动端,点击即开始编辑
|
||||||
|
if (this.isMobile) {
|
||||||
|
this.startEdit(index, student);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleSave() {
|
||||||
|
await this.$emit('save');
|
||||||
|
// 保存成功后更新内部状态
|
||||||
|
this.internalOriginalList = [...this.modelValue.list];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,6 +402,17 @@ export default {
|
|||||||
transition: opacity 0.2s ease;
|
transition: opacity 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.unsaved-changes {
|
||||||
|
animation: subtle-pulse 2s infinite;
|
||||||
|
border: 2px solid rgb(var(--v-theme-warning));
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes subtle-pulse {
|
||||||
|
0% { border-color: rgba(var(--v-theme-warning), 1); }
|
||||||
|
50% { border-color: rgba(var(--v-theme-warning), 0.5); }
|
||||||
|
100% { border-color: rgba(var(--v-theme-warning), 1); }
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
@ -40,7 +40,7 @@ const providers = {
|
|||||||
return formatResponse(JSON.parse(rawData));
|
return formatResponse(JSON.parse(rawData));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
return formatError('读取本地数据失败');
|
return formatError('读取本地数据失败:'+error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ const providers = {
|
|||||||
localStorage.setItem(storageKey, JSON.stringify(data));
|
localStorage.setItem(storageKey, JSON.stringify(data));
|
||||||
return formatResponse(null, '保存成功');
|
return formatResponse(null, '保存成功');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return formatError('保存本地数据失败');
|
return formatError('保存本地数据失败:'+error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ const providers = {
|
|||||||
|
|
||||||
return formatResponse(JSON.parse(rawData));
|
return formatResponse(JSON.parse(rawData));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return formatError('读取本地配置失败');
|
return formatError('读取本地配置失败:'+error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ const providers = {
|
|||||||
localStorage.setItem(storageKey, JSON.stringify(config));
|
localStorage.setItem(storageKey, JSON.stringify(config));
|
||||||
return formatResponse(null, '保存成功');
|
return formatResponse(null, '保存成功');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return formatError('保存本地配置失败');
|
return formatError('保存本地配置失败:'+error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -145,7 +145,7 @@ const providers = {
|
|||||||
|
|
||||||
async saveConfig(key, config) {
|
async saveConfig(key, config) {
|
||||||
try {
|
try {
|
||||||
const res = await axios.post(`${key}/config`, config);
|
const res = await axios.put(`${key}/config`, config);
|
||||||
if (res.data?.status === false) {
|
if (res.data?.status === false) {
|
||||||
return formatError(res.data.msg || '保存失败', 'SAVE_ERROR');
|
return formatError(res.data.msg || '保存失败', 'SAVE_ERROR');
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user