kindergarten/reading-platform-frontend/src/views/school/settings/SettingsView.vue

330 lines
7.7 KiB
Vue
Raw Normal View History

<template>
<div class="settings-view">
<div class="page-header">
<h1><SettingOutlined /> 系统设置</h1>
<p>配置学校基本信息和通知偏好</p>
</div>
<a-spin :spinning="loading">
<div class="settings-content">
<!-- 基本信息 -->
<div class="settings-card">
<div class="card-header">
<span class="card-icon"><HomeOutlined /></span>
<h3>基本信息</h3>
</div>
<div class="card-body">
<a-form
:model="formData"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 16 }"
>
<a-form-item label="学校名称">
<a-input v-model:value="formData.schoolName" placeholder="请输入学校名称" />
</a-form-item>
<a-form-item label="学校Logo">
<div class="logo-upload">
<div class="logo-preview" v-if="formData.schoolLogo">
<img :src="formData.schoolLogo" alt="Logo" />
<div class="logo-actions">
<a-button type="link" size="small" @click="formData.schoolLogo = ''">
<DeleteOutlined /> 删除
</a-button>
</div>
</div>
<a-upload
v-else
:show-upload-list="false"
:before-upload="handleLogoUpload"
accept="image/*"
>
<div class="upload-placeholder">
<PlusOutlined />
<span>上传Logo</span>
</div>
</a-upload>
</div>
</a-form-item>
<a-form-item label="学校地址">
<a-textarea
v-model:value="formData.address"
placeholder="请输入学校地址"
:rows="2"
/>
</a-form-item>
</a-form>
</div>
</div>
<!-- 通知设置 -->
<div class="settings-card">
<div class="card-header">
<span class="card-icon"><BellOutlined /></span>
<h3>通知设置</h3>
</div>
<div class="card-body">
<a-form
:label-col="{ span: 8 }"
:wrapper-col="{ span: 12 }"
>
<a-form-item label="课程完成通知">
<a-switch
v-model:checked="formData.notifyOnLesson"
checked-children="开"
un-checked-children="关"
/>
<span class="switch-hint">当教师完成一次授课后发送通知</span>
</a-form-item>
<a-form-item label="任务提醒通知">
<a-switch
v-model:checked="formData.notifyOnTask"
checked-children="开"
un-checked-children="关"
/>
<span class="switch-hint">当有新的阅读任务时发送通知</span>
</a-form-item>
<a-form-item label="成长档案通知">
<a-switch
v-model:checked="formData.notifyOnGrowth"
checked-children="开"
un-checked-children="关"
/>
<span class="switch-hint">当更新学生成长档案时发送通知</span>
</a-form-item>
</a-form>
</div>
</div>
<!-- 保存按钮 -->
<div class="action-bar">
<a-button type="primary" size="large" :loading="saving" @click="handleSave">
<SaveOutlined /> 保存设置
</a-button>
</div>
</div>
</a-spin>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import {
SettingOutlined,
HomeOutlined,
BellOutlined,
SaveOutlined,
PlusOutlined,
DeleteOutlined,
} from '@ant-design/icons-vue';
import { getSettings, updateSettings, type SystemSettings, type UpdateSettingsDto } from '@/api/school';
import { fileApi } from '@/api/file';
const loading = ref(false);
const saving = ref(false);
const formData = ref<UpdateSettingsDto>({
schoolName: '',
schoolLogo: '',
address: '',
notifyOnLesson: true,
notifyOnTask: true,
notifyOnGrowth: false,
});
const loadSettings = async () => {
loading.value = true;
try {
const data = await getSettings();
formData.value = {
schoolName: data.schoolName || '',
schoolLogo: data.schoolLogo || '',
address: data.address || '',
notifyOnLesson: data.notifyOnLesson,
notifyOnTask: data.notifyOnTask,
notifyOnGrowth: data.notifyOnGrowth,
};
} catch (error) {
console.error('Failed to load settings:', error);
message.error('加载设置失败');
} finally {
loading.value = false;
}
};
const handleLogoUpload = async (file: File) => {
try {
const result = await fileApi.uploadFile(file, 'cover');
formData.value.schoolLogo = result.filePath;
message.success('Logo上传成功');
} catch (error) {
console.error('Failed to upload logo:', error);
message.error('Logo上传失败');
}
return false;
};
const handleSave = async () => {
saving.value = true;
try {
await updateSettings(formData.value);
message.success('设置保存成功');
} catch (error) {
console.error('Failed to save settings:', error);
message.error('保存设置失败');
} finally {
saving.value = false;
}
};
onMounted(() => {
loadSettings();
});
</script>
<style scoped>
.settings-view {
padding: 0;
min-height: 100vh;
background: linear-gradient(180deg, #FFF8F0 0%, #FFFFFF 100%);
}
.page-header {
margin-bottom: 24px;
}
.page-header h1 {
font-size: 24px;
font-weight: 600;
color: #2D3436;
margin: 0 0 8px 0;
display: flex;
align-items: center;
gap: 12px;
}
.page-header p {
font-size: 14px;
color: #636E72;
margin: 0;
}
.settings-content {
display: flex;
flex-direction: column;
gap: 24px;
}
.settings-card {
background: white;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
}
.card-header {
display: flex;
align-items: center;
gap: 12px;
padding: 20px 24px;
border-bottom: 1px solid #F5F5F5;
background: #FAFAFA;
}
.card-icon {
font-size: 20px;
color: #FF8C42;
display: flex;
align-items: center;
}
.card-header h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #2D3436;
}
.card-body {
padding: 24px;
}
.logo-upload {
display: flex;
align-items: center;
}
.logo-preview {
display: flex;
align-items: center;
gap: 16px;
}
.logo-preview img {
width: 80px;
height: 80px;
object-fit: contain;
border-radius: 8px;
border: 1px solid #E0E0E0;
}
.upload-placeholder {
width: 80px;
height: 80px;
border: 2px dashed #D9D9D9;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s;
color: #636E72;
}
.upload-placeholder:hover {
border-color: #FF8C42;
color: #FF8C42;
}
.upload-placeholder span {
font-size: 12px;
margin-top: 4px;
}
.switch-hint {
margin-left: 12px;
color: #636E72;
font-size: 12px;
}
.action-bar {
display: flex;
justify-content: center;
padding: 24px;
background: white;
border-radius: 16px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
}
@media (max-width: 768px) {
.card-body {
padding: 16px;
}
:deep(.ant-form-item-label) {
padding-bottom: 8px !important;
}
:deep(.ant-form-item) {
margin-bottom: 16px;
}
.switch-hint {
display: block;
margin-left: 0;
margin-top: 4px;
}
}
</style>