2026-02-28 17:51:15 +08:00
|
|
|
<template>
|
2026-03-03 13:59:02 +08:00
|
|
|
<div class="p-0 min-h-screen bg-gradient-to-b from-[#FFF8F0] to-white">
|
|
|
|
|
<div class="mb-6">
|
|
|
|
|
<h1 class="text-2xl font-semibold text-[#2D3436] m-0 mb-2 flex items-center gap-3"><SettingOutlined /> 系统设置</h1>
|
|
|
|
|
<p class="text-sm text-[#636E72] m-0">配置学校基本信息和通知偏好</p>
|
2026-02-28 17:51:15 +08:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<a-spin :spinning="loading">
|
2026-03-03 13:59:02 +08:00
|
|
|
<div class="flex flex-col gap-6">
|
2026-02-28 17:51:15 +08:00
|
|
|
<!-- 基本信息 -->
|
2026-03-03 13:59:02 +08:00
|
|
|
<div class="bg-white rounded-2xl overflow-hidden shadow-[0_4px_15px_rgba(0,0,0,0.05)]">
|
|
|
|
|
<div class="flex items-center gap-3 py-5 px-6 border-b border-[#F5F5F5] bg-[#FAFAFA]">
|
|
|
|
|
<span class="text-xl text-[#FF8C42] flex items-center"><HomeOutlined /></span>
|
|
|
|
|
<h3 class="m-0 text-base font-semibold text-[#2D3436]">基本信息</h3>
|
2026-02-28 17:51:15 +08:00
|
|
|
</div>
|
2026-03-03 13:59:02 +08:00
|
|
|
<div class="p-6 max-md:p-4">
|
2026-02-28 17:51:15 +08:00
|
|
|
<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">
|
2026-03-03 13:59:02 +08:00
|
|
|
<div class="flex items-center">
|
|
|
|
|
<div class="flex items-center gap-4" v-if="formData.schoolLogo">
|
|
|
|
|
<img :src="formData.schoolLogo" alt="Logo" class="w-20 h-20 object-contain rounded-lg border border-[#E0E0E0]" />
|
2026-02-28 17:51:15 +08:00
|
|
|
<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/*"
|
|
|
|
|
>
|
2026-03-03 13:59:02 +08:00
|
|
|
<div class="w-20 h-20 border-2 border-dashed border-[#D9D9D9] rounded-lg flex flex-col items-center justify-center cursor-pointer transition-all text-[#636E72] hover:border-[#FF8C42] hover:text-[#FF8C42]">
|
2026-02-28 17:51:15 +08:00
|
|
|
<PlusOutlined />
|
2026-03-03 13:59:02 +08:00
|
|
|
<span class="text-xs mt-1">上传Logo</span>
|
2026-02-28 17:51:15 +08:00
|
|
|
</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>
|
|
|
|
|
|
|
|
|
|
<!-- 通知设置 -->
|
2026-03-03 13:59:02 +08:00
|
|
|
<div class="bg-white rounded-2xl overflow-hidden shadow-[0_4px_15px_rgba(0,0,0,0.05)]">
|
|
|
|
|
<div class="flex items-center gap-3 py-5 px-6 border-b border-[#F5F5F5] bg-[#FAFAFA]">
|
|
|
|
|
<span class="text-xl text-[#FF8C42] flex items-center"><BellOutlined /></span>
|
|
|
|
|
<h3 class="m-0 text-base font-semibold text-[#2D3436]">通知设置</h3>
|
2026-02-28 17:51:15 +08:00
|
|
|
</div>
|
2026-03-03 13:59:02 +08:00
|
|
|
<div class="p-6 max-md:p-4">
|
2026-02-28 17:51:15 +08:00
|
|
|
<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="关"
|
|
|
|
|
/>
|
2026-03-03 13:59:02 +08:00
|
|
|
<span class="ml-3 max-md:ml-0 max-md:mt-1 max-md:block text-[#636E72] text-xs">当教师完成一次授课后发送通知</span>
|
2026-02-28 17:51:15 +08:00
|
|
|
</a-form-item>
|
|
|
|
|
<a-form-item label="任务提醒通知">
|
|
|
|
|
<a-switch
|
|
|
|
|
v-model:checked="formData.notifyOnTask"
|
|
|
|
|
checked-children="开"
|
|
|
|
|
un-checked-children="关"
|
|
|
|
|
/>
|
2026-03-03 13:59:02 +08:00
|
|
|
<span class="ml-3 max-md:ml-0 max-md:mt-1 max-md:block text-[#636E72] text-xs">当有新的阅读任务时发送通知</span>
|
2026-02-28 17:51:15 +08:00
|
|
|
</a-form-item>
|
|
|
|
|
<a-form-item label="成长档案通知">
|
|
|
|
|
<a-switch
|
|
|
|
|
v-model:checked="formData.notifyOnGrowth"
|
|
|
|
|
checked-children="开"
|
|
|
|
|
un-checked-children="关"
|
|
|
|
|
/>
|
2026-03-03 13:59:02 +08:00
|
|
|
<span class="ml-3 max-md:ml-0 max-md:mt-1 max-md:block text-[#636E72] text-xs">当更新学生成长档案时发送通知</span>
|
2026-02-28 17:51:15 +08:00
|
|
|
</a-form-item>
|
|
|
|
|
</a-form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 保存按钮 -->
|
2026-03-03 13:59:02 +08:00
|
|
|
<div class="flex justify-center p-6 bg-white rounded-2xl shadow-[0_4px_15px_rgba(0,0,0,0.05)]">
|
2026-02-28 17:51:15 +08:00
|
|
|
<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>
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
:deep(.ant-form-item-label) {
|
|
|
|
|
padding-bottom: 8px !important;
|
|
|
|
|
}
|
|
|
|
|
:deep(.ant-form-item) {
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|