diff --git a/frontend/ruoyi-ui/src/store/modules/baseData.js b/frontend/ruoyi-ui/src/store/modules/baseData.js new file mode 100644 index 0000000..a69a1ab --- /dev/null +++ b/frontend/ruoyi-ui/src/store/modules/baseData.js @@ -0,0 +1,200 @@ +/** + * 基础数据 Store + * 缓存年级、学科、区域、班级等基础数据 + * + * @author 湖北新华业务中台研发团队 + */ +import { defineStore } from 'pinia' +import request from '@/utils/request' + +const STORAGE_KEY = 'pguser:baseData' +const CACHE_EXPIRE = 24 * 60 * 60 * 1000 // 24小时 + +/** + * 从 localStorage 恢复数据 + */ +const loadFromStorage = () => { + try { + const data = localStorage.getItem(STORAGE_KEY) + if (data) { + const parsed = JSON.parse(data) + // 检查是否过期 + if (parsed.expire && Date.now() < parsed.expire) { + return parsed + } + } + } catch (e) { + console.error('加载缓存失败:', e) + } + return null +} + +/** + * 保存到 localStorage + */ +const saveToStorage = (state) => { + try { + const data = { + grades: state.grades, + subjects: state.subjects, + regionTree: state.regionTree, + classes: state.classes, + expire: Date.now() + CACHE_EXPIRE, + updateTime: new Date().toISOString() + } + localStorage.setItem(STORAGE_KEY, JSON.stringify(data)) + } catch (e) { + console.error('保存缓存失败:', e) + } +} + +const useBaseDataStore = defineStore('baseData', { + state: () => { + const cached = loadFromStorage() + return { + grades: cached?.grades || [], // 年级列表 + subjects: cached?.subjects || [], // 学科列表 + regionTree: cached?.regionTree || [], // 区域树 + classes: cached?.classes || [], // 班级列表 + loading: { + grades: false, + subjects: false, + regionTree: false, + classes: false + } + } + }, + + getters: { + // 年级选项(用于下拉) + gradeOptions: (state) => state.grades.map(g => ({ label: g.gradeName, value: g.gradeId })), + // 学科选项(用于下拉) + subjectOptions: (state) => state.subjects.map(s => ({ label: s.subjectName, value: s.subjectId })), + // 班级选项(用于下拉) + classOptions: (state) => state.classes.map(c => ({ label: c.className, value: c.classId })) + }, + + actions: { + // ==================== 年级 ==================== + + /** + * 获取年级列表(带缓存) + * @param {boolean} force 是否强制刷新 + */ + async fetchGrades(force = false) { + if (!force && this.grades.length > 0) { + return this.grades + } + + this.loading.grades = true + try { + const res = await request.get('/business/grade/listAll') + if (res.code === 200) { + this.grades = res.data || [] + saveToStorage(this) + } + return this.grades + } finally { + this.loading.grades = false + } + }, + + /** + * 清除年级缓存 + */ + clearGrades() { + this.grades = [] + saveToStorage(this) + }, + + // ==================== 学科 ==================== + + async fetchSubjects(force = false) { + if (!force && this.subjects.length > 0) { + return this.subjects + } + + this.loading.subjects = true + try { + const res = await request.get('/business/subject/listAll') + if (res.code === 200) { + this.subjects = res.data || [] + saveToStorage(this) + } + return this.subjects + } finally { + this.loading.subjects = false + } + }, + + clearSubjects() { + this.subjects = [] + saveToStorage(this) + }, + + // ==================== 区域 ==================== + + async fetchRegionTree(force = false) { + if (!force && this.regionTree.length > 0) { + return this.regionTree + } + + this.loading.regionTree = true + try { + const res = await request.get('/business/region/tree') + if (res.code === 200) { + this.regionTree = res.data || [] + saveToStorage(this) + } + return this.regionTree + } finally { + this.loading.regionTree = false + } + }, + + clearRegionTree() { + this.regionTree = [] + saveToStorage(this) + }, + + // ==================== 班级 ==================== + + async fetchClasses(force = false) { + if (!force && this.classes.length > 0) { + return this.classes + } + + this.loading.classes = true + try { + const res = await request.get('/business/class/listAll') + if (res.code === 200) { + this.classes = res.data || [] + saveToStorage(this) + } + return this.classes + } finally { + this.loading.classes = false + } + }, + + clearClasses() { + this.classes = [] + saveToStorage(this) + }, + + // ==================== 通用 ==================== + + /** + * 清除所有缓存 + */ + clearAll() { + this.grades = [] + this.subjects = [] + this.regionTree = [] + this.classes = [] + localStorage.removeItem(STORAGE_KEY) + } + } +}) + +export default useBaseDataStore diff --git a/frontend/ruoyi-ui/src/views/business/base/class/index.vue b/frontend/ruoyi-ui/src/views/business/base/class/index.vue index ff317a6..dc8640f 100644 --- a/frontend/ruoyi-ui/src/views/business/base/class/index.vue +++ b/frontend/ruoyi-ui/src/views/business/base/class/index.vue @@ -93,6 +93,9 @@ import { Delete, Edit, Plus, Refresh, Search } from '@element-plus/icons-vue' import { ElMessage, ElMessageBox } from 'element-plus' import { onMounted, ref } from 'vue' import request from '@/utils/request' +import useBaseDataStore from '@/store/modules/baseData' + +const baseDataStore = useBaseDataStore() const loading = ref(false) const tableData = ref([]) @@ -169,6 +172,7 @@ const handleSubmit = async () => { ? await request.put('/business/class', form.value) : await request.post('/business/class', form.value) if (res.code === 200) { + baseDataStore.clearClasses() // 清除班级缓存 ElMessage.success(isEdit ? '修改成功' : '新增成功') dialogVisible.value = false getList() @@ -184,6 +188,7 @@ const handleDelete = (row) => { }).then(async () => { const res = await request.delete(`/business/class/${row.classId}`) if (res.code === 200) { + baseDataStore.clearClasses() // 清除班级缓存 ElMessage.success('删除成功') getList() } diff --git a/frontend/ruoyi-ui/src/views/business/base/grade/index.vue b/frontend/ruoyi-ui/src/views/business/base/grade/index.vue index 9b0d12f..6b5bf3c 100644 --- a/frontend/ruoyi-ui/src/views/business/base/grade/index.vue +++ b/frontend/ruoyi-ui/src/views/business/base/grade/index.vue @@ -93,6 +93,9 @@ import { Delete, Edit, Plus, Refresh, Search } from '@element-plus/icons-vue' import { ElMessage, ElMessageBox } from 'element-plus' import { onMounted, ref } from 'vue' import request from '@/utils/request' +import useBaseDataStore from '@/store/modules/baseData' + +const baseDataStore = useBaseDataStore() const loading = ref(false) const tableData = ref([]) @@ -169,6 +172,7 @@ const handleSubmit = async () => { ? await request.put('/business/grade', form.value) : await request.post('/business/grade', form.value) if (res.code === 200) { + baseDataStore.clearGrades() // 清除年级缓存 ElMessage.success(isEdit ? '修改成功' : '新增成功') dialogVisible.value = false getList() @@ -184,6 +188,7 @@ const handleDelete = (row) => { }).then(async () => { const res = await request.delete(`/business/grade/${row.gradeId}`) if (res.code === 200) { + baseDataStore.clearGrades() // 清除年级缓存 ElMessage.success('删除成功') getList() } diff --git a/frontend/ruoyi-ui/src/views/business/base/region/index.vue b/frontend/ruoyi-ui/src/views/business/base/region/index.vue index 946eca8..f778e23 100644 --- a/frontend/ruoyi-ui/src/views/business/base/region/index.vue +++ b/frontend/ruoyi-ui/src/views/business/base/region/index.vue @@ -110,6 +110,9 @@ import { Delete, Edit, Plus, Refresh, Search, Sort } from '@element-plus/icons-v import { ElMessage, ElMessageBox } from 'element-plus' import { nextTick, onMounted, ref } from 'vue' import request from '@/utils/request' +import useBaseDataStore from '@/store/modules/baseData' + +const baseDataStore = useBaseDataStore() const loading = ref(false) const tableData = ref([]) @@ -220,6 +223,7 @@ const handleSubmit = async () => { ? await request.put('/business/region', form.value) : await request.post('/business/region', form.value) if (res.code === 200) { + baseDataStore.clearRegionTree() // 清除区域缓存 ElMessage.success(isEdit ? '修改成功' : '新增成功') dialogVisible.value = false getList() @@ -235,6 +239,7 @@ const handleDelete = (row) => { }).then(async () => { const res = await request.delete(`/business/region/${row.regionId}`) if (res.code === 200) { + baseDataStore.clearRegionTree() // 清除区域缓存 ElMessage.success('删除成功') getList() } diff --git a/frontend/ruoyi-ui/src/views/business/base/subject/index.vue b/frontend/ruoyi-ui/src/views/business/base/subject/index.vue index 831ebcc..7e5feac 100644 --- a/frontend/ruoyi-ui/src/views/business/base/subject/index.vue +++ b/frontend/ruoyi-ui/src/views/business/base/subject/index.vue @@ -93,6 +93,9 @@ import { Delete, Edit, Plus, Refresh, Search } from '@element-plus/icons-vue' import { ElMessage, ElMessageBox } from 'element-plus' import { onMounted, ref } from 'vue' import request from '@/utils/request' +import useBaseDataStore from '@/store/modules/baseData' + +const baseDataStore = useBaseDataStore() const loading = ref(false) const tableData = ref([]) @@ -169,6 +172,7 @@ const handleSubmit = async () => { ? await request.put('/business/subject', form.value) : await request.post('/business/subject', form.value) if (res.code === 200) { + baseDataStore.clearSubjects() // 清除学科缓存 ElMessage.success(isEdit ? '修改成功' : '新增成功') dialogVisible.value = false getList() @@ -184,6 +188,7 @@ const handleDelete = (row) => { }).then(async () => { const res = await request.delete(`/business/subject/${row.subjectId}`) if (res.code === 200) { + baseDataStore.clearSubjects() // 清除学科缓存 ElMessage.success('删除成功') getList() }