diff --git a/.editorconfig b/.editorconfig
index 5760be5..e717f5e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,6 +4,7 @@ root = true
[*]
indent_style = space
indent_size = 2
+end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..42295cc
--- /dev/null
+++ b/.env.example
@@ -0,0 +1 @@
+TARO_APP_API_BASE_URL=
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..9d9ae43
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,8 @@
+* text=auto eol=lf
+
+# Keep binary assets untouched.
+*.png binary
+*.jpg binary
+*.jpeg binary
+*.gif binary
+*.ico binary
diff --git a/.gitignore b/.gitignore
index d1df1c0..0f9843e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,9 @@ npm-debug.log*
coverage
*.local
+.env
+.env.*
+!.env.example
# Editor directories and files
.vscode/*
diff --git a/README.md b/README.md
index 914aca8..b869d17 100644
--- a/README.md
+++ b/README.md
@@ -198,7 +198,7 @@ npm run build:weapp
## 🔧 配置说明
### 环境配置
-- 修改 `src/utils/request.js` 中的 `BASE_URL` 为实际API地址
+- 复制 `.env.example` 为 `.env.local`,并设置 `TARO_APP_API_BASE_URL`
- 调整 `src/utils/constants.js` 中的学期配置
## 贡献指南
diff --git a/config/index.js b/config/index.js
index 0cc607c..35c6d8b 100644
--- a/config/index.js
+++ b/config/index.js
@@ -1,10 +1,42 @@
import { defineConfig } from '@tarojs/cli'
+import { existsSync, readFileSync } from 'fs'
+import { resolve } from 'path'
import { Plugin } from 'vite'
import tailwindcss from 'tailwindcss'
import { UnifiedViteWeappTailwindcssPlugin as uvtw } from 'weapp-tailwindcss/vite'
import devConfig from './dev'
import prodConfig from './prod'
+const loadLocalEnv = () => {
+ const envFiles = ['.env.local', '.env']
+
+ envFiles.forEach((fileName) => {
+ const filePath = resolve(process.cwd(), fileName)
+ if (!existsSync(filePath)) return
+
+ readFileSync(filePath, 'utf8')
+ .split(/\r?\n/)
+ .forEach((line) => {
+ const trimmed = line.trim()
+ if (!trimmed || trimmed.startsWith('#')) return
+
+ const separatorIndex = trimmed.indexOf('=')
+ if (separatorIndex === -1) return
+
+ const key = trimmed.slice(0, separatorIndex).trim()
+ const value = trimmed.slice(separatorIndex + 1).trim().replace(/^['"]|['"]$/g, '')
+
+ if (key && process.env[key] === undefined) {
+ process.env[key] = value
+ }
+ })
+ })
+}
+
+loadLocalEnv()
+
+const API_BASE_URL = process.env.TARO_APP_API_BASE_URL || ''
+
// https://taro-docs.jd.com/docs/next/config#defineconfig-辅助函数
export default defineConfig(async (merge, { command, mode }) => {
const baseConfig = {
@@ -21,6 +53,7 @@ export default defineConfig(async (merge, { command, mode }) => {
outputRoot: 'dist',
plugins: [],
defineConstants: {
+ API_BASE_URL: JSON.stringify(API_BASE_URL)
},
copy: {
patterns: [
diff --git a/src/api/auth.js b/src/api/auth.js
index 164d899..ed425f5 100644
--- a/src/api/auth.js
+++ b/src/api/auth.js
@@ -1,8 +1,44 @@
-import { get, post, put } from '../utils/request'
+import { get, post, put, request } from '../utils/request'
export const authAPI = {
- wechatLogin(code) {
- return post('/api/v0/auth/wechat-login', { code })
+ wechatLogin(code, options = {}) {
+ return post('/api/v0/auth/wechat-login', { code }, {
+ skipAuthRefresh: true,
+ retryOnAuthFailure: false,
+ ...options
+ })
+ },
+
+ refresh(refreshToken, options = {}) {
+ return post('/api/v0/auth/refresh', {
+ refresh_token: refreshToken
+ }, {
+ skipAuthRefresh: true,
+ retryOnAuthFailure: false,
+ ...options
+ })
+ },
+
+ logout(options = {}) {
+ return request({
+ url: '/api/v0/auth/logout',
+ method: 'POST',
+ skipAuthRefresh: true,
+ retryOnAuthFailure: false,
+ handleAuthFailure: false,
+ ...options
+ })
+ },
+
+ logoutAll(options = {}) {
+ return request({
+ url: '/api/v0/auth/logout-all',
+ method: 'POST',
+ skipAuthRefresh: true,
+ retryOnAuthFailure: false,
+ handleAuthFailure: false,
+ ...options
+ })
}
}
@@ -13,5 +49,9 @@ export const userAPI = {
updateProfile(data) {
return put('/api/v0/user/profile', data)
+ },
+
+ getLoginDays() {
+ return get('/api/v0/user/login-days')
}
}
diff --git a/src/api/config.js b/src/api/config.js
index 0150b3b..9b826c0 100644
--- a/src/api/config.js
+++ b/src/api/config.js
@@ -1,24 +1,8 @@
-import { get, post, put, del } from '../utils/request'
+import { get } from '../utils/request'
export const configAPI = {
getConfig(key) {
if (!key) return Promise.reject(new Error('Config key is required'))
return get(`/api/v0/config/${key}`)
},
-
- searchConfig(params) {
- return get('/api/v0/config/search', params)
- },
-
- createConfig(data) {
- return post('/api/v0/config/', data)
- },
-
- updateConfig(key, data) {
- return put(`/api/v0/config/${key}`, data)
- },
-
- deleteConfig(key) {
- return del(`/api/v0/config/${key}`)
- }
}
diff --git a/src/api/contribution.js b/src/api/contribution.js
deleted file mode 100644
index b0739df..0000000
--- a/src/api/contribution.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { get, post } from '../utils/request'
-
-export const contributionAPI = {
- createContribution(data) {
- return post('/api/v0/contributions', data)
- },
-
- getContributions(params) {
- return get('/api/v0/contributions', params)
- },
-
- getContributionDetail(id) {
- return get(`/api/v0/contributions/${id}`)
- },
-
- reviewContribution(id, data) {
- return post(`/api/v0/contributions/${id}/review`, data)
- },
-
- getContributionStats() {
- return get('/api/v0/contributions/stats')
- },
-
- getContributionStatsAdmin() {
- return get('/api/v0/contributions/stats-admin')
- }
-}
diff --git a/src/api/courseTable.js b/src/api/courseTable.js
index 7998d05..50e7b81 100644
--- a/src/api/courseTable.js
+++ b/src/api/courseTable.js
@@ -1,10 +1,14 @@
-import { get, put, post } from '../utils/request'
+import { get, put, del } from '../utils/request'
export const courseTableAPI = {
getCourseTable(params) {
return get('/api/v0/coursetable', params)
},
+ getBindCount() {
+ return get('/api/v0/coursetable/bind-count')
+ },
+
searchClass(params) {
return get('/api/v0/coursetable/search', params)
},
@@ -17,8 +21,8 @@ export const courseTableAPI = {
return put('/api/v0/coursetable', data)
},
- resetBindCount(userId) {
- if (!userId) return Promise.reject(new Error('userId is required'))
- return post(`/api/v0/coursetable/reset/${userId}`)
+ deleteSchedule(semester) {
+ if (!semester) return Promise.reject(new Error('semester is required'))
+ return del(`/api/v0/coursetable/schedule?semester=${encodeURIComponent(semester)}`)
}
}
diff --git a/src/api/gpa.js b/src/api/gpa.js
new file mode 100644
index 0000000..83f8384
--- /dev/null
+++ b/src/api/gpa.js
@@ -0,0 +1,19 @@
+import { get, post, del } from '../utils/request'
+
+export const gpaAPI = {
+ getBackupList() {
+ return get('/api/v0/gpa/backup')
+ },
+
+ getBackupDetail(id) {
+ return get(`/api/v0/gpa/backup/${id}`)
+ },
+
+ createBackup(data) {
+ return post('/api/v0/gpa/backup', data)
+ },
+
+ deleteBackup(id) {
+ return del(`/api/v0/gpa/backup/${id}`)
+ }
+}
diff --git a/src/api/hero.js b/src/api/hero.js
index fdd0ec6..fb4dc6f 100644
--- a/src/api/hero.js
+++ b/src/api/hero.js
@@ -1,23 +1,7 @@
-import { get, post, put, del } from '../utils/request'
+import { get } from '../utils/request'
export const heroAPI = {
getHeroes() {
return get('/api/v0/heroes/')
},
-
- searchHeroes(params) {
- return get('/api/v0/heroes/search', params)
- },
-
- createHero(data) {
- return post('/api/v0/heroes/', data)
- },
-
- updateHero(id, data) {
- return put(`/api/v0/heroes/${id}`, data)
- },
-
- deleteHero(id) {
- return del(`/api/v0/heroes/${id}`)
- }
}
diff --git a/src/api/index.js b/src/api/index.js
index 4572a2d..01c028d 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -7,11 +7,12 @@ export { configAPI } from './config'
export { ossAPI } from './oss'
export { questionsAPI } from './questions'
export { notificationAPI } from './notification'
-export { contributionAPI } from './contribution'
export { materialAPI } from './material'
export { systemAPI, statAPI } from './system'
export { pointsAPI } from './points'
export { dictionaryAPI } from './dictionary'
-export { rbacAPI } from './rbac'
export { countdownAPI } from './countdown'
export { studyTaskAPI } from './studyTask'
+export { pomodoroAPI } from './pomodoro'
+export { gpaAPI } from './gpa'
+export { organizationAPI } from './organization'
diff --git a/src/api/material.js b/src/api/material.js
index 129838a..e2dd754 100644
--- a/src/api/material.js
+++ b/src/api/material.js
@@ -29,14 +29,6 @@ export const materialAPI = {
return post(`/api/v0/materials/${md5}/download`)
},
- deleteMaterial(md5) {
- return del(`/api/v0/admin/materials/${md5}`)
- },
-
- updateMaterialDesc(md5, data) {
- return put(`/api/v0/admin/material-desc/${md5}`, data)
- },
-
getCategories(params) {
return get('/api/v0/material-categories', params)
}
diff --git a/src/api/notification.js b/src/api/notification.js
index 0ba6378..b7f8093 100644
--- a/src/api/notification.js
+++ b/src/api/notification.js
@@ -1,4 +1,4 @@
-import { get, post, put, del } from '../utils/request'
+import { get } from '../utils/request'
export const notificationAPI = {
getNotifications(params) {
@@ -9,63 +9,8 @@ export const notificationAPI = {
return get(`/api/v0/notifications/${id}`)
},
- createNotification(data) {
- return post('/api/v0/admin/notifications', data)
- },
-
- updateNotification(id, data) {
- return put(`/api/v0/admin/notifications/${id}`, data)
- },
-
- publishNotification(id) {
- return post(`/api/v0/admin/notifications/${id}/publish`)
- },
-
- publishAdminNotification(id) {
- return post(`/api/v0/admin/notifications/${id}/publish-admin`)
- },
-
- deleteNotification(id) {
- return del(`/api/v0/admin/notifications/${id}`)
- },
-
- approveNotification(id, data) {
- return post(`/api/v0/admin/notifications/${id}/approve`, data)
- },
-
- getAdminNotifications(params) {
- return get('/api/v0/admin/notifications', params)
- },
-
- getAdminNotificationDetail(id) {
- return get(`/api/v0/admin/notifications/${id}`)
- },
-
getCategories() {
return get('/api/v0/categories')
},
- createCategory(data) {
- return post('/api/v0/admin/categories', data)
- },
-
- updateCategory(id, data) {
- return put(`/api/v0/admin/categories/${id}`, data)
- },
-
- convertToSchedule(id, data) {
- return post(`/api/v0/admin/notifications/${id}/schedule`, data)
- },
-
- getNotificationStats() {
- return get('/api/v0/admin/notifications/stats')
- },
-
- pinNotification(id) {
- return post(`/api/v0/admin/notifications/${id}/pin`)
- },
-
- unpinNotification(id) {
- return post(`/api/v0/admin/notifications/${id}/unpin`)
- }
}
diff --git a/src/api/organization.js b/src/api/organization.js
new file mode 100644
index 0000000..84494fc
--- /dev/null
+++ b/src/api/organization.js
@@ -0,0 +1,11 @@
+import { get } from '../utils/request'
+
+export const organizationAPI = {
+ getOrganizations(params) {
+ return get('/api/v0/organizations/', params)
+ },
+
+ getOrganizationDetail(id) {
+ return get(`/api/v0/organizations/${id}`)
+ },
+}
diff --git a/src/api/points.js b/src/api/points.js
index 5e81320..505e315 100644
--- a/src/api/points.js
+++ b/src/api/points.js
@@ -12,8 +12,4 @@ export const pointsAPI = {
getStats(params) {
return get('/api/v0/points/stats', params)
},
-
- grantPoints(data) {
- return post('/api/v0/points/grant', data)
- }
}
diff --git a/src/api/pomodoro.js b/src/api/pomodoro.js
new file mode 100644
index 0000000..6d0a476
--- /dev/null
+++ b/src/api/pomodoro.js
@@ -0,0 +1,15 @@
+import { get, post } from '../utils/request'
+
+export const pomodoroAPI = {
+ increment() {
+ return post('/api/v0/pomodoro/increment', {})
+ },
+
+ getCount() {
+ return get('/api/v0/pomodoro/count')
+ },
+
+ getRanking() {
+ return get('/api/v0/pomodoro/ranking')
+ }
+}
diff --git a/src/api/rbac.js b/src/api/rbac.js
deleted file mode 100644
index a262dd8..0000000
--- a/src/api/rbac.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { get, post } from '../utils/request'
-
-export const rbacAPI = {
- getRoles() {
- return get('/api/v0/admin/rbac/roles')
- },
-
- getPermissions() {
- return get('/api/v0/admin/rbac/permissions')
- },
-
- getRolesPermissions() {
- return get('/api/v0/admin/rbac/roles/permissions')
- },
-
- updateUserRoles(userId, data) {
- return post(`/api/v0/admin/rbac/users/${userId}/roles`, data)
- }
-}
diff --git a/src/api/review.js b/src/api/review.js
index 6b32cc2..eef3064 100644
--- a/src/api/review.js
+++ b/src/api/review.js
@@ -1,4 +1,4 @@
-import { get, post, del } from '../utils/request'
+import { get, post } from '../utils/request'
export const reviewAPI = {
getTeacherReviews(params) {
@@ -12,20 +12,4 @@ export const reviewAPI = {
getUserReviews(params) {
return get('/api/v0/reviews/user', params)
},
-
- getAllReviews(params) {
- return get('/api/v0/reviews/', params)
- },
-
- approveReview(id, data) {
- return post(`/api/v0/reviews/${id}/approve`, data)
- },
-
- rejectReview(id, data) {
- return post(`/api/v0/reviews/${id}/reject`, data)
- },
-
- deleteReview(id) {
- return del(`/api/v0/reviews/${id}`)
- }
}
diff --git a/src/api/system.js b/src/api/system.js
index 77f1cf1..e4b5241 100644
--- a/src/api/system.js
+++ b/src/api/system.js
@@ -1,21 +1,15 @@
-import Taro from '@tarojs/taro'
-import { get } from '../utils/request'
+import { get, request } from '../utils/request'
export const systemAPI = {
healthCheck() {
- return new Promise((resolve, reject) => {
- Taro.request({
- url: 'https://example.com/health',
- method: 'GET',
- success: (res) => {
- if (res.statusCode >= 200 && res.statusCode < 300) {
- resolve(res.data)
- } else {
- reject(new Error(`Health check failed: HTTP ${res.statusCode}`))
- }
- },
- fail: (err) => reject(new Error(err.errMsg || 'Health check failed'))
- })
+ return request({
+ url: '/health',
+ method: 'GET',
+ data: {},
+ silent: true,
+ skipAuthRefresh: true,
+ retryOnAuthFailure: false,
+ handleAuthFailure: false
})
}
}
diff --git a/src/app.config.js b/src/app.config.js
index 8e1e381..98c961a 100644
--- a/src/app.config.js
+++ b/src/app.config.js
@@ -12,11 +12,6 @@ export default {
'pages/graduation/index',
'pages/map/index',
'pages/teacher-reviews/index',
- 'pages/admin/teacher-reviews/index',
- 'pages/admin/heroes/index',
- 'pages/admin/config/index',
- 'pages/admin/rbac/index',
- 'pages/admin/points/index',
'pages/terms-of-service/index',
'pages/webview/index',
'pages/hero/index',
@@ -26,17 +21,15 @@ export default {
'pages/address/index',
'pages/final-review/index',
'pages/final-review/detail/index',
+ 'pages/major-transfer/index',
+ 'pages/competition/index',
+ 'pages/organization/index',
+ 'pages/organization/detail/index',
+ 'pages/qualification/index',
+ 'pages/exchange/index',
// 通知公告相关页面
'pages/notifications/index',
'pages/notifications/detail/index',
- 'pages/notifications/create/index',
- 'pages/notifications/manage/index',
- 'pages/notifications/categories/index',
- // 用户投稿相关页面
- 'pages/contributions/create/index',
- 'pages/contributions/mine/index',
- 'pages/contributions/detail/index',
- 'pages/contributions/review/index',
// 资料库相关页面
'pages/materials/index',
'pages/materials/detail/index',
diff --git a/src/pages/admin/config/index.config.js b/src/pages/admin/config/index.config.js
deleted file mode 100644
index 8545c26..0000000
--- a/src/pages/admin/config/index.config.js
+++ /dev/null
@@ -1,5 +0,0 @@
-export default {
- navigationBarTitleText: '配置管理'
-}
-
-
diff --git a/src/pages/admin/config/index.vue b/src/pages/admin/config/index.vue
deleted file mode 100644
index f1959cc..0000000
--- a/src/pages/admin/config/index.vue
+++ /dev/null
@@ -1,794 +0,0 @@
-
-
-
-
-
- 配置管理
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ filter.label }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ config.key }}
-
-
- {{ config.value_type }}
-
-
-
- {{ config.description }}
-
- 无描述
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 加载中...
-
-
-
-
-
-
-
- 加载更多中...
-
-
-
-
-
- 滚动加载更多
-
-
-
-
- 没有更多数据
-
-
-
-
-
- 暂无配置数据
-
- 点击添加第一个配置
-
-
-
-
-
-
-
-
-
-
-
- {{ isEditing ? "编辑配置" : "添加配置" }}
-
-
-
-
-
-
- 键名 *
-
-
-
- 键名不可修改
-
-
-
-
-
-
- 值类型 *
-
-
-
- {{ type.label }}
-
-
-
-
-
-
-
- 配置值 *
-
-
-
-
- {{ getValueHint(configForm.value_type) }}
-
-
-
-
-
-
- 描述
-
-
-
-
-
-
-
- 取消
-
-
- {{ saving ? "保存中..." : "保存" }}
-
-
-
-
-
-
-
-
-
-
-
- 删除配置
-
-
- 确定要删除配置 "{{ currentConfig?.key }}" 吗?此操作不可撤销。
-
-
-
-
- 取消
-
-
- {{ deleting ? "删除中..." : "确认删除" }}
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/admin/heroes/index.config.js b/src/pages/admin/heroes/index.config.js
deleted file mode 100644
index 63e6a00..0000000
--- a/src/pages/admin/heroes/index.config.js
+++ /dev/null
@@ -1,5 +0,0 @@
-export default {
- navigationBarTitleText: '英雄榜管理'
-}
-
-
diff --git a/src/pages/admin/heroes/index.vue b/src/pages/admin/heroes/index.vue
deleted file mode 100644
index 143e43e..0000000
--- a/src/pages/admin/heroes/index.vue
+++ /dev/null
@@ -1,675 +0,0 @@
-
-
-
-
-
- 英雄榜管理
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ filter.label }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ currentPage }}/{{ totalPages }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{
- hero.name
- }}
-
- {{ hero.is_show ? "显示" : "隐藏" }}
-
-
-
- 排序: {{ hero.sort }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 加载中...
-
-
-
-
-
-
- 暂无英雄榜数据
-
- 点击添加第一个英雄
-
-
-
-
-
-
-
-
-
-
-
- {{ isEditing ? "编辑英雄" : "添加英雄" }}
-
-
-
-
-
-
- 姓名 *
-
-
-
-
-
-
-
- 排序
-
-
- 数值越小排序越靠前
-
-
-
-
-
- 显示状态
-
-
-
- 显示
-
-
- 隐藏
-
-
-
-
-
-
-
- 取消
-
-
- {{ saving ? "保存中..." : "保存" }}
-
-
-
-
-
-
-
-
-
-
-
- 删除英雄
-
-
- 确定要删除英雄 "{{ currentHero?.name }}" 吗?
-
-
-
-
- 取消
-
-
- {{ deleting ? "删除中..." : "确认删除" }}
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/admin/points/index.config.js b/src/pages/admin/points/index.config.js
deleted file mode 100644
index 5d1a324..0000000
--- a/src/pages/admin/points/index.config.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export default {
- navigationBarTitleText: '积分管理'
-}
-
diff --git a/src/pages/admin/points/index.vue b/src/pages/admin/points/index.vue
deleted file mode 100644
index bd66f40..0000000
--- a/src/pages/admin/points/index.vue
+++ /dev/null
@@ -1,608 +0,0 @@
-
-
-
-
-
- 积分管理
-
-
-
-
-
-
-
-
-
-
-
-
-
- 赋予积分
-
-
-
- 查询用户
-
-
-
- 清除查询
-
-
-
-
-
-
-
-
- 当前查询用户ID:
- {{ selectedUserId }}
-
-
- {{ userInfo.nickname || '未知用户' }}
-
-
-
-
-
-
-
-
- {{ tab.label }}
-
-
-
-
-
-
-
-
-
-
-
-
- 加载中...
-
-
-
- 暂无交易记录
-
-
-
-
-
-
- {{ transaction.description }}
-
- {{ transaction.type === 1 ? '获得' : '消耗' }}
-
-
-
- 来源: {{ getSourceName(transaction.source) }}
-
-
- {{ formatDate(transaction.created_at) }}
-
-
-
-
- {{ transaction.type === 1 ? '+' : '-' }}{{ Math.abs(transaction.points) }}
-
-
-
-
-
-
-
-
-
-
-
-
- 加载中...
-
-
-
- 请先选择用户
-
-
-
-
-
- 当前积分
- {{ statsData.points || 0 }}
-
-
- 积分排名
- #{{ statsData.rank }}
-
-
-
-
-
- 来源统计
-
-
-
- {{ getSourceName(source) }}
-
-
- 获得
- +{{ stats.earned || 0 }}
-
-
- 消耗
- {{ stats.spent }}
-
-
-
-
-
- 暂无统计数据
-
-
-
-
-
-
-
-
-
-
-
-
- 赋予积分
-
-
-
-
-
-
- 用户ID *
-
-
-
-
-
-
-
- 积分数量 *
-
-
-
- 输入正数表示增加积分,负数表示扣除积分
-
-
-
-
-
-
- 操作描述 *
-
-
-
-
-
-
-
- 取消
-
-
- {{ granting ? "处理中..." : "确认" }}
-
-
-
-
-
-
-
-
-
-
-
- 查询用户
-
-
-
-
-
-
- 用户ID *
-
-
-
-
-
-
-
- 取消
-
-
- 查询
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/admin/rbac/index.config.js b/src/pages/admin/rbac/index.config.js
deleted file mode 100644
index dd1606c..0000000
--- a/src/pages/admin/rbac/index.config.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export default {
- navigationBarTitleText: '权限管理'
-}
-
diff --git a/src/pages/admin/rbac/index.vue b/src/pages/admin/rbac/index.vue
deleted file mode 100644
index d063ae8..0000000
--- a/src/pages/admin/rbac/index.vue
+++ /dev/null
@@ -1,566 +0,0 @@
-
-
-
-
-
- 权限管理
-
-
-
-
-
-
-
-
-
-
-
-
-
- 更新用户角色
-
-
-
-
-
-
-
-
- {{ tab.label }}
-
-
-
-
-
-
-
-
-
-
-
-
- 加载中...
-
-
-
- 暂无角色数据
-
-
-
-
-
-
- {{ role.name }}
-
- {{ role.role_tag }}
-
-
-
- {{ role.description }}
-
- 暂无描述
-
-
-
-
-
- {{ role.user_count }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- 加载中...
-
-
-
- 暂无权限数据
-
-
-
-
-
-
- {{ permission.name }}
-
- {{ permission.permission_tag }}
-
-
-
- {{ permission.description }}
-
- 暂无描述
-
-
-
-
-
-
-
-
-
-
-
- 加载中...
-
-
-
- 暂无角色权限数据
-
-
-
-
-
-
- {{ item.role.name }}
-
- {{ item.role.role_tag }}
-
-
-
- {{ item.role.description }}
-
-
-
-
-
- 拥有权限 ({{ item.permissions.length }}):
-
-
-
-
-
- {{ permission.name }}
-
- {{ permission.permission_tag }}
-
-
-
- {{ permission.description }}
-
-
-
-
-
-
-
- 该角色暂无权限
-
-
-
-
-
-
-
-
-
-
-
-
-
- 更新用户角色
-
-
-
-
-
-
- 用户ID *
-
-
-
-
-
-
-
- 选择角色 *
-
-
-
-
-
-
- {{ role.name }}
-
-
- {{ role.role_tag }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- 取消
-
-
- {{ updating ? "更新中..." : "更新" }}
-
-
-
-
-
-
-
-
-
-
-
-
- {{ currentRole?.name }}
-
-
-
- {{ currentRole?.role_tag }}
-
-
- 共 {{ currentRole?.user_count }} 个用户
-
-
-
-
-
-
- 用户ID列表
-
-
-
- {{ userId }}
-
-
-
-
- 该角色暂无用户
-
-
-
-
- 关闭
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/admin/teacher-reviews/index.config.js b/src/pages/admin/teacher-reviews/index.config.js
deleted file mode 100644
index 3e49ae8..0000000
--- a/src/pages/admin/teacher-reviews/index.config.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export default {
- navigationBarTitleText: '评价审核管理',
- backgroundColor: '#f5f5f5'
-}
diff --git a/src/pages/admin/teacher-reviews/index.vue b/src/pages/admin/teacher-reviews/index.vue
deleted file mode 100644
index 2ccd4ab..0000000
--- a/src/pages/admin/teacher-reviews/index.vue
+++ /dev/null
@@ -1,614 +0,0 @@
-
-
-
-
-
- 评价审核管理
-
-
-
-
-
-
-
-
-
-
- {{ filter.label }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{
- review.teacher_name
- }}
-
- {{ getStatusText(review.status) }}
-
-
-
- {{ review.course_name }}
- {{
- review.campus
- }}
-
- {{ getAttitudeText(review.attitude) }}
-
-
-
- {{
- formatDate(review.created_at)
- }}
-
-
-
-
-
- {{
- review.content
- }}
-
-
-
- 管理员备注:
- {{
- review.admin_note
- }}
-
-
-
-
-
-
-
- 拒绝
-
-
- 通过
-
-
- 删除
-
-
-
-
-
-
-
- 加载中...
-
-
-
-
- 没有更多评价了
-
-
-
-
-
- 暂无评价数据
-
-
-
-
-
-
-
-
-
- 审核通过
-
-
- {{ approveNote.length }}/200
-
-
-
-
- 取消
-
-
- {{ approving ? "处理中..." : "确认通过" }}
-
-
-
-
-
-
-
-
-
-
- 审核拒绝
-
-
- {{ rejectNote.length }}/200
-
-
-
-
- 取消
-
-
- {{ rejecting ? "处理中..." : "确认拒绝" }}
-
-
-
-
-
-
-
-
-
-
- 删除评价
-
-
-
- 取消
-
-
- {{ deleting ? "删除中..." : "确认删除" }}
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/competition/index.config.js b/src/pages/competition/index.config.js
new file mode 100644
index 0000000..0e494ad
--- /dev/null
+++ b/src/pages/competition/index.config.js
@@ -0,0 +1,6 @@
+export default {
+ navigationBarTitleText: '竞赛',
+ backgroundColor: '#f8fafc',
+ enableShareAppMessage: true,
+ enableShareTimeline: true,
+}
diff --git a/src/pages/competition/index.vue b/src/pages/competition/index.vue
new file mode 100644
index 0000000..b054cf7
--- /dev/null
+++ b/src/pages/competition/index.vue
@@ -0,0 +1,284 @@
+
+
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+
+
+
+ 正在加载竞赛数据...
+
+
+
+
+
+
+ 竞赛目录
+
+ {{ competitionCount }} 项
+
+
+ 数据来源:{{ competitionSource }}
+
+
+
+
+ {{ pageError }}
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+ 暂无竞赛数据
+
+
+
+
+
+ {{ section.category }}
+ {{ section.items.length }} 项
+
+
+
+ 暂无内容
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/contributions/create/index.config.js b/src/pages/contributions/create/index.config.js
deleted file mode 100644
index 9797d3d..0000000
--- a/src/pages/contributions/create/index.config.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default {
- navigationBarTitleText: '创建投稿'
-}
diff --git a/src/pages/contributions/create/index.vue b/src/pages/contributions/create/index.vue
deleted file mode 100644
index 6146549..0000000
--- a/src/pages/contributions/create/index.vue
+++ /dev/null
@@ -1,244 +0,0 @@
-
-
-
-
-
-
- 创建投稿
- 相信信息的力量,让更多人知道
-
-
-
-
-
-
- 投稿标题 *
-
-
-
- {{ errors.title }}
- {{ form.title.length }}/50
-
-
-
-
-
-
- 投稿内容 *
-
-
-
- {{ errors.content }}
- {{ form.content.length }}/1000
-
-
-
-
-
-
- 选择分类 *
-
-
-
-
-
-
-
- {{ category.name }}
-
-
-
- {{ errors.categories }}
-
-
-
-
-
-
-
-
-
-
-
-
- {{ isSubmitting ? '提交中...' : '提交投稿' }}
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/contributions/detail/index.config.js b/src/pages/contributions/detail/index.config.js
deleted file mode 100644
index 79e1ccb..0000000
--- a/src/pages/contributions/detail/index.config.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default {
- navigationBarTitleText: '投稿详情'
-}
diff --git a/src/pages/contributions/detail/index.vue b/src/pages/contributions/detail/index.vue
deleted file mode 100644
index 4b9f0a9..0000000
--- a/src/pages/contributions/detail/index.vue
+++ /dev/null
@@ -1,330 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ contribution.title }}
-
-
-
-
-
-
-
- {{ contribution.user.id }}
-
-
-
-
-
- {{ formatDateTime(contribution.created_at) }}
-
-
-
-
-
- {{ getStatusText(contribution.status) }}
-
-
-
-
-
-
-
- {{ category.name }}
-
-
-
-
-
-
-
-
-
-
- {{ contribution.content }}
-
-
-
-
-
-
-
-
-
- {{ contribution.status === 2 ? '审核通过' : contribution.status === 3 ? '审核未通过' : '等待审核' }}
-
-
-
-
-
- 审核意见:
-
-
- {{ contribution.review_note }}
-
-
-
-
-
-
- 审核人:{{ contribution.reviewer.nickname }}
-
-
-
-
-
- {{ formatDateTime(contribution.reviewed_at) }}
-
-
-
-
-
-
-
-
-
-
-
- 积分奖励
-
- 获得 {{ contribution.points_awarded }} 积分奖励
-
-
-
-
-
-
-
-
- 已发布信息
- 投稿已采纳,信息审核中
-
- {{ contribution.notification.title }}
-
-
- 浏览量:{{ contribution.notification.view_count || 0 }}
-
-
- 查看信息
-
-
-
-
-
-
-
-
-
-
-
-
- 投稿不存在或已被删除
-
- 返回
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/contributions/mine/index.vue b/src/pages/contributions/mine/index.vue
deleted file mode 100644
index a6c8107..0000000
--- a/src/pages/contributions/mine/index.vue
+++ /dev/null
@@ -1,401 +0,0 @@
-
-
-
-
-
- 状态筛选
-
- 清除筛选
-
-
-
-
-
- 全部
-
-
- 待审核
-
-
- 已采纳
-
-
- 已拒绝
-
-
-
-
-
-
-
-
-
-
-
- 加载中...
-
-
-
-
-
-
-
-
-
- 还没有投稿记录
-
- 创建投稿
-
-
-
-
-
-
-
-
-
-
-
- 投稿统计
-
- 刷新
-
-
-
-
-
-
- {{ contributionStats.total_count }}
- 总投稿
-
-
-
-
-
- {{ contributionStats.approved_count }}
- 已采纳
-
-
-
-
-
- {{ contributionStats.pending_count }}
- 待审核
-
-
-
-
-
- {{ contributionStats.total_points }}
- 总积分
-
-
-
-
-
-
-
-
-
-
- {{ contribution.title }}
-
-
-
-
-
-
- {{ getStatusText(contribution.status) }}
-
-
-
-
-
-
-
-
- {{ contribution.status === 2 ? "审核意见:" : "拒绝原因:" }}
-
- {{ contribution.review_note }}
-
-
-
-
-
-
-
-
-
- {{ category.name }}
-
-
-
-
-
-
- +{{ contribution.points_awarded }}积分
-
-
-
-
-
-
-
- {{ contribution.notification.view_count || 0 }}
-
-
-
-
- {{ formatDate(contribution.created_at) }}
-
-
-
-
-
-
-
-
- 加载中...
- 已加载全部
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/contributions/review/index.config.js b/src/pages/contributions/review/index.config.js
deleted file mode 100644
index 919b0a0..0000000
--- a/src/pages/contributions/review/index.config.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default {
- navigationBarTitleText: '投稿审核'
-}
diff --git a/src/pages/contributions/review/index.vue b/src/pages/contributions/review/index.vue
deleted file mode 100644
index 37985f1..0000000
--- a/src/pages/contributions/review/index.vue
+++ /dev/null
@@ -1,639 +0,0 @@
-
-
-
-
-
- 状态筛选
-
- 清除筛选
-
-
-
-
-
- 全部
-
-
- 待审核
-
-
- 已采纳
-
-
- 已拒绝
-
-
-
-
-
-
-
-
-
-
-
- 加载中...
-
-
-
-
-
-
-
-
-
- 没有找到相关投稿
-
-
-
-
-
-
-
-
-
- 投稿统计
-
- 刷新
-
-
-
-
-
- {{ stats.total }}
- 总数
-
-
- {{ stats.pending }}
- 待审核
-
-
- {{ stats.approved }}
- 已采纳
-
-
- {{ stats.rejected }}
- 已拒绝
-
-
- {{ stats.points }}
- 总积分
-
-
-
-
-
-
-
-
-
- {{ contribution.title }}
-
-
-
-
-
-
- {{ getStatusText(contribution.status) }}
-
-
-
-
-
-
-
- {{ contribution.content }}
-
-
-
-
-
-
-
-
-
- {{ contribution.user.id }}
-
-
-
-
-
- {{ category.name }}
-
-
-
-
-
-
-
-
- {{ contribution.points_awarded }}积分
-
-
-
-
- {{ formatDate(contribution.created_at) }}
-
-
-
-
-
-
-
-
- {{ contribution.status === 2 ? '审核意见:' : '拒绝原因:' }}
-
- {{ contribution.review_note }}
-
- 审核人:{{ contribution.reviewer.nickname }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 加载中...
- 已加载全部
-
-
-
-
-
-
-
-
-
- 审核投稿
-
-
-
-
-
-
-
- 审核结果
-
-
-
-
-
-
- 采纳投稿
- 将发布为新信息
-
-
-
-
-
-
-
-
- 拒绝投稿
- 需要填写拒绝原因
-
-
-
-
-
-
-
-
-
- 信息标题
-
-
-
-
-
- 信息内容
-
-
-
-
-
-
- 选择分类 *
-
-
-
-
-
-
-
- {{ category.name }}
-
-
-
-
-
-
-
- 积分奖励
-
-
-
-
-
-
-
- 审核意见 {{ reviewForm.status === 3 ? '(必填)' : '(可选)' }}
-
-
- {{ reviewForm.reviewNote.length }}/500
-
-
-
-
-
- 取消
-
-
-
- {{ reviewForm.status === 2 ? '采纳' : '拒绝' }}
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/discover/index.vue b/src/pages/discover/index.vue
index d191e61..0a55a25 100644
--- a/src/pages/discover/index.vue
+++ b/src/pages/discover/index.vue
@@ -264,21 +264,21 @@
-
+
- 大学生涯
+ 交换生
-
+
- 交换生
+ 大学生涯
{
});
};
+const goToMajorTransfer = () => {
+ Taro.navigateTo({ url: "/pages/major-transfer/index" });
+};
+
+const goToCompetition = () => {
+ Taro.navigateTo({ url: "/pages/competition/index" });
+};
+
+const goToOrganization = () => {
+ if (!authStore.requireAuth()) return;
+ Taro.navigateTo({ url: "/pages/organization/index" });
+};
+
+const goToQualification = () => {
+ Taro.navigateTo({ url: "/pages/qualification/index" });
+};
+
+const goToExchange = () => {
+ Taro.navigateTo({ url: "/pages/exchange/index" });
+};
+
+const goToCollegeJourney = () => {
+ Taro.navigateTo({ url: "/pages/college-journey/index" });
+};
+
const goToHero = () => {
Taro.navigateTo({ url: "/pages/hero/index" });
};
@@ -674,13 +699,6 @@ const goToGroupChat = () => {
Taro.navigateTo({ url: "/pages/groupchat/index" });
};
-const goToNoticeToZLK = () => {
- Taro.showToast({
- title: "请在资料库查找",
- icon: "success",
- });
-};
-
const goToJw = () => {
if (!authStore.requireAuth()) return;
Taro.navigateTo({ url: "/pages/gotojw/index" });
diff --git a/src/pages/exchange/index.config.js b/src/pages/exchange/index.config.js
new file mode 100644
index 0000000..f3c514c
--- /dev/null
+++ b/src/pages/exchange/index.config.js
@@ -0,0 +1,6 @@
+export default {
+ navigationBarTitleText: '交换生',
+ backgroundColor: '#f8fafc',
+ enableShareAppMessage: true,
+ enableShareTimeline: true,
+}
diff --git a/src/pages/exchange/index.vue b/src/pages/exchange/index.vue
new file mode 100644
index 0000000..f6d51f1
--- /dev/null
+++ b/src/pages/exchange/index.vue
@@ -0,0 +1,264 @@
+
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+
+
+ 正在加载交换生数据...
+
+
+
+
+
+
+ 遴选本科生赴国内高校交流培养的通知
+
+
+
+ 面向:大二下
+ 发布日期:{{ currentPublishDate || '待发布' }}
+
+
+
+
+ {{ loadError }}
+
+
+
+ 暂无交换生数据
+
+
+
+
+
+ {{ section.title || `说明 ${index + 1}` }}
+
+
+
+ {{ section.description }}
+
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+ {{ school.name || `学校 ${schoolIndex + 1}` }}
+ {{ school.quotaLabel }}
+
+ {{ school.remarks }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/failrate/index.vue b/src/pages/failrate/index.vue
index fc7c539..cb12a5d 100644
--- a/src/pages/failrate/index.vue
+++ b/src/pages/failrate/index.vue
@@ -147,10 +147,6 @@ const loadRandomData = async () => {
hasMore.value = false; // 随机数据不支持分页
} catch (error) {
console.error("获取随机挂科率数据失败:", error);
- Taro.showToast({
- title: "加载失败",
- icon: "error",
- });
} finally {
loading.value = false;
}
@@ -183,10 +179,7 @@ const searchFailRate = async (keyword, page = 1) => {
} catch (error) {
console.error("搜索挂科率数据失败:", error);
- Taro.showToast({
- title: "搜索失败",
- icon: "error",
- });
+
} finally {
loading.value = false;
loadingMore.value = false;
diff --git a/src/pages/final-review/detail/index.vue b/src/pages/final-review/detail/index.vue
index 86d60fe..eccc93c 100644
--- a/src/pages/final-review/detail/index.vue
+++ b/src/pages/final-review/detail/index.vue
@@ -805,7 +805,6 @@ const loadQuestionIds = async () => {
}
} catch (error) {
console.error("loadQuestionIds error", error);
- Taro.showToast({ title: "题目加载失败", icon: "none" });
} finally {
loadingList.value = false;
}
@@ -817,7 +816,6 @@ const extractQuestionIds = (res) => {
if (Array.isArray(res.question_ids)) return res.question_ids;
if (Array.isArray(res.data)) return res.data;
if (Array.isArray(res.list)) return res.list;
- if (res.Result && Array.isArray(res.Result.question_ids)) return res.Result.question_ids;
return [];
};
@@ -853,7 +851,6 @@ const loadQuestionDetail = async (index, shouldScroll = false) => {
}
} catch (error) {
console.error("loadQuestionDetail error", error);
- Taro.showToast({ title: "题目加载失败", icon: "none" });
} finally {
loadingQuestion.value = false;
}
@@ -1366,7 +1363,6 @@ const clearWrongBook = async () => {
}
} catch (error) {
console.error("clear wrong book failed", error);
- Taro.showToast({ title: "清空失败", icon: "none" });
}
};
@@ -1621,7 +1617,6 @@ const deleteProgress = (type, isOldVersion = false) => {
Taro.showToast({ title: "已删除", icon: "success", duration: 1000 });
} catch (error) {
console.error("delete old progress failed", error);
- Taro.showToast({ title: "删除失败", icon: "none" });
}
} else {
clearProgress(type);
diff --git a/src/pages/final-review/index.vue b/src/pages/final-review/index.vue
index 66ca645..a574a85 100644
--- a/src/pages/final-review/index.vue
+++ b/src/pages/final-review/index.vue
@@ -138,7 +138,7 @@ const fetchCategories = async () => {
categoryData = JSON.parse(res);
} else if (res && typeof res === 'object') {
// 如果返回的是对象,尝试从value字段获取
- categoryData = res.value || res.data || res;
+ categoryData = Object.prototype.hasOwnProperty.call(res, 'value') ? res.value : res;
if (typeof categoryData === 'string') {
categoryData = JSON.parse(categoryData);
}
diff --git a/src/pages/gpa-calculator/index.vue b/src/pages/gpa-calculator/index.vue
index c9f58e7..b21541f 100644
--- a/src/pages/gpa-calculator/index.vue
+++ b/src/pages/gpa-calculator/index.vue
@@ -39,7 +39,7 @@
-
+
添加课程
+
+
+
+
+
+ 备份恢复
+
+
+
+
+
+
+
+ {{ activeBackupTitle || `云端存档 ${activeBackupId}` }}
+
+ 当前操作不会影响本地数据
+
+
+ 切回本地
+
@@ -79,7 +108,14 @@
- 已添加课程
+
+
+ {{ isLocalDataView ? '已添加课程' : '备份预览课程' }}
+
+
+ 更新时间:{{ activeBackupUpdatedAt }}
+
+
@@ -132,13 +168,19 @@
@@ -153,7 +195,9 @@
暂无课程数据
- 请在上方添加课程信息
+
+ {{ isLocalDataView ? '请在上方添加课程信息' : '当前备份中没有可恢复的课程' }}
+
@@ -161,7 +205,7 @@
- 您的数据不会被上传
+ 默认仅保存在本地,点击“备份恢复”后才会上传到服务器
@@ -341,6 +385,109 @@
+
+
+
+
+
+ 备份恢复
+ 最多可上传 6 份数据
+
+
+ 刷新
+
+
+
+
+
+ 上传本地数据
+ 当前 {{ localCoursesSnapshot.length }} 门课程
+
+
+
+ 切回本地
+ 恢复本地展示
+
+
+
+
+ 云端备份列表
+ 加载中...
+
+
+
+
+
+
+
+
+ {{ backup.title || `云端存档 ${backup.id}` }}
+
+
+ 更新时间:{{ formatBackupTime(backup.updated_at || backup.created_at) }}
+
+
+ 课程数:{{ getBackupCourseCount(backup) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ isBackupListLoadFailed ? '备份加载失败' : '暂无云端备份' }}
+
+ 请点击右上角刷新重试
+
+
+
+
+ 关闭
+
+
+
+
@@ -410,19 +557,82 @@
-
diff --git a/src/pages/groupchat/index.vue b/src/pages/groupchat/index.vue
index a515445..26bb4c2 100644
--- a/src/pages/groupchat/index.vue
+++ b/src/pages/groupchat/index.vue
@@ -57,7 +57,7 @@
暂无群聊推荐
- 请联系管理员添加群聊配置
+ 群聊配置暂未开放
diff --git a/src/pages/hero/index.vue b/src/pages/hero/index.vue
index 5f2f163..ed43248 100644
--- a/src/pages/hero/index.vue
+++ b/src/pages/hero/index.vue
@@ -73,11 +73,7 @@ const fetchHeroes = async () => {
}
} catch (error) {
console.error('获取英雄名单失败:', error)
- Taro.showToast({
- title: '获取英雄名单失败',
- icon: 'error',
- duration: 2000
- })
+
heroes.value = []
} finally {
loading.value = false
diff --git a/src/pages/home/components/CountdownCard.vue b/src/pages/home/components/CountdownCard.vue
index 124b5a1..7f9a763 100644
--- a/src/pages/home/components/CountdownCard.vue
+++ b/src/pages/home/components/CountdownCard.vue
@@ -473,10 +473,7 @@ const submitCountdown = async () => {
hideAddModal()
} catch (error) {
- Taro.showToast({
- title: error.message || '添加失败',
- icon: 'error'
- })
+
} finally {
submitting.value = false
}
@@ -497,10 +494,7 @@ const updateCountdown = async () => {
hideEditModal()
} catch (error) {
- Taro.showToast({
- title: error.message || '更新失败',
- icon: 'error'
- })
+
} finally {
submitting.value = false
}
@@ -528,10 +522,7 @@ const deleteCountdown = async () => {
hideEditModal()
} catch (error) {
- Taro.showToast({
- title: error.message || '删除失败',
- icon: 'error'
- })
+
}
}
}
diff --git a/src/pages/home/components/PomodoroCard.vue b/src/pages/home/components/PomodoroCard.vue
new file mode 100644
index 0000000..8ca708a
--- /dev/null
+++ b/src/pages/home/components/PomodoroCard.vue
@@ -0,0 +1,654 @@
+
+
+
+ 番茄钟
+
+
+
+
+
+
+
+
+
+ {{ preset.label }}
+ {{ preset.shortLabel }}
+
+
+
+
+
+
+ {{ helperText }}
+ {{ formattedTime }}
+
+
+ {{ isRunning ? '进行中' : '待开始' }}
+
+
+
+
+
+
+
+
+ {{ progressText }}
+ {{ currentPreset.shortLabel }}
+
+
+
+
+
+
+
+ {{ isRunning ? '暂停' : '开始' }}
+
+
+
+
+
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+ 专注排行榜
+
+
+
+
+
+
+ 我的累计完成
+ {{ completedCount }} 次
+
+
+
+
+ 登录后查看排行榜与同步进度
+ 当前仍可使用本地番茄钟
+
+
+
+ 加载中...
+
+
+
+
+ 还没有排行数据
+ 完成一个番茄后会自动同步
+
+
+
+
+
+ 昵称
+ 专注次数
+
+
+
+
+
+ {{ index + 1 }}
+
+
+
+ 番茄{{ item.nickname }}
+
+ {{ item.pomodoro_count }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/home/components/StudyTaskCard.vue b/src/pages/home/components/StudyTaskCard.vue
index cfe3c08..4b928c4 100644
--- a/src/pages/home/components/StudyTaskCard.vue
+++ b/src/pages/home/components/StudyTaskCard.vue
@@ -41,40 +41,40 @@
-
已完成({{ stats.completed_count }})
-
待完成({{ stats.pending_count }})
-
+
-
劳逸结合 · 爱自己
-
+
-
还没有完成的任务
-
+
+ class="relative p-3 border border-gray-200 rounded-lg bg-white" @tap="showEditTaskModal(task)">
@@ -162,8 +162,8 @@
-
-
+
+
{{
formatCompletedTime(
task.completed_at || task.updated_at
@@ -500,12 +500,6 @@ const getPriorityColorClass = (priority) => {
};
const getTaskBorderClass = (task) => {
- const daysLeft = studyTaskStore.calculateDaysLeft(task.due_date);
-
- if (task.status === 2) return "border-green-200";
- if (daysLeft < 0) return "border-red-200"; // 过期
- if (daysLeft === 0) return "border-orange-300"; // 今天
- if (daysLeft <= 3) return "border-amber-200"; // 紧急
return "border-gray-200";
};
@@ -728,10 +722,7 @@ const submitTask = async () => {
hideAddModal();
} catch (error) {
- Taro.showToast({
- title: error.message || "添加失败",
- icon: "error",
- });
+
} finally {
submitting.value = false;
}
@@ -768,10 +759,7 @@ const updateTask = async () => {
hideEditModal();
} catch (error) {
- Taro.showToast({
- title: error.message || "更新失败",
- icon: "error",
- });
+
} finally {
submitting.value = false;
}
@@ -799,10 +787,7 @@ const deleteTask = async () => {
hideEditModal();
} catch (error) {
- Taro.showToast({
- title: error.message || "删除失败",
- icon: "error",
- });
+
}
}
};
@@ -833,10 +818,7 @@ const toggleTaskStatus = async (taskId) => {
duration: 1000,
});
} catch (error) {
- Taro.showToast({
- title: error.message || "更新失败",
- icon: "error",
- });
+
}
};
diff --git a/src/pages/home/index.vue b/src/pages/home/index.vue
index 427afc7..c87cec6 100644
--- a/src/pages/home/index.vue
+++ b/src/pages/home/index.vue
@@ -1,6 +1,9 @@
-
+
@@ -27,8 +30,13 @@
-
-
+
+
@@ -37,6 +45,11 @@
+
+ 左右滑动切换单词
+ {{ isRefreshingWord ? '加载新词中...' : '' }}
+
+
@@ -176,6 +189,7 @@ import TodayCourse from './components/TodayCourse.vue'
import CountdownCard from './components/CountdownCard.vue'
import StudyTaskCard from './components/StudyTaskCard.vue'
import NotificationCard from './components/NotificationCard.vue'
+import PomodoroCard from './components/PomodoroCard.vue'
import { useAuthStore } from '../../stores/auth'
import { useShareAppMessage, useShareTimeline } from '@tarojs/taro'
import { dictionaryAPI } from '../../api'
@@ -185,14 +199,26 @@ const authStore = useAuthStore()
const updateManager = Taro.getUpdateManager()
// 每日一词
-const dailyWord = ref(null)
+const WORD_BUFFER_SIZE = 5
+const wordBuffer = ref([])
+const currentWordIndex = ref(-1)
+const dailyWord = computed(() => {
+ return wordBuffer.value[currentWordIndex.value] || null
+})
const isWordDetailVisible = ref(false)
+const isRefreshingWord = ref(false)
+const wordTouchState = ref({
+ startX: 0,
+ startY: 0,
+ startTime: 0
+})
// 默认卡片配置
const defaultCards = [
{ name: 'TodayCourse', label: '今日课程', iconClass: 'i-lucide-book-open text-blue-500', component: TodayCourse },
{ name: 'NotificationCard', label: '通知公告', iconClass: 'i-lucide-bell text-orange-500', component: NotificationCard },
{ name: 'StudyTaskCard', label: '学习任务', iconClass: 'i-lucide-clipboard-check text-green-500', component: StudyTaskCard },
+ { name: 'PomodoroCard', label: '番茄钟', iconClass: 'i-lucide-timer-reset text-red-500', component: PomodoroCard },
{ name: 'CountdownCard', label: '倒数日', iconClass: 'i-lucide-timer text-purple-500', component: CountdownCard }
]
@@ -208,15 +234,27 @@ const orderedCards = computed(() => {
).filter(Boolean)
})
+const normalizeCardOrder = (savedOrder) => {
+ if (!Array.isArray(savedOrder) || savedOrder.length === 0) {
+ return defaultCards.map(card => card.name)
+ }
+
+ const validCardNames = new Set(defaultCards.map(card => card.name))
+ const normalizedOrder = savedOrder.filter(name => validCardNames.has(name))
+ const missingCards = defaultCards
+ .map(card => card.name)
+ .filter(name => !normalizedOrder.includes(name))
+
+ return [...normalizedOrder, ...missingCards]
+}
+
// 初始化卡片顺序
const initCardOrder = () => {
try {
const savedOrder = Taro.getStorageSync('homeCardOrder')
- if (savedOrder && Array.isArray(savedOrder) && savedOrder.length === defaultCards.length) {
- cardOrder.value = savedOrder
- } else {
- cardOrder.value = defaultCards.map(card => card.name)
- }
+ const normalizedOrder = normalizeCardOrder(savedOrder)
+ cardOrder.value = normalizedOrder
+ Taro.setStorageSync('homeCardOrder', normalizedOrder)
} catch (error) {
console.error('加载卡片顺序失败:', error)
cardOrder.value = defaultCards.map(card => card.name)
@@ -291,18 +329,32 @@ const resetOrder = () => {
}
// 获取每日一词
-const fetchDailyWord = async () => {
+const fetchDailyWord = async (force = false) => {
// 只在登录状态下请求每日一词
if (!authStore.isLoggedIn) {
return
}
- if(dailyWord.value) {
+ if (!force && dailyWord.value) {
+ return
+ }
+ if (isRefreshingWord.value) {
return
}
+
+ isRefreshingWord.value = true
try {
- dailyWord.value = await dictionaryAPI.getRandomWord()
+ const nextWord = await dictionaryAPI.getRandomWord()
+ pushWordToBuffer(nextWord)
} catch (error) {
console.error('获取每日一词失败:', error)
+ if (force) {
+ Taro.showToast({
+ title: '刷新失败',
+ icon: 'error'
+ })
+ }
+ } finally {
+ isRefreshingWord.value = false
}
}
@@ -316,6 +368,90 @@ const showWordDetail = () => {
// 隐藏单词详情
const hideWordDetail = () => {
isWordDetailVisible.value = false
+ resetWordTouchState()
+}
+
+const resetWordTouchState = () => {
+ wordTouchState.value = {
+ startX: 0,
+ startY: 0,
+ startTime: 0
+ }
+}
+
+const pushWordToBuffer = (word) => {
+ if (!word) return
+
+ const nextBuffer = [...wordBuffer.value, word]
+ if (nextBuffer.length > WORD_BUFFER_SIZE) {
+ nextBuffer.shift()
+ }
+
+ wordBuffer.value = nextBuffer
+ currentWordIndex.value = nextBuffer.length - 1
+}
+
+const showPreviousWord = () => {
+ if (currentWordIndex.value <= 0) {
+ return false
+ }
+
+ currentWordIndex.value -= 1
+ return true
+}
+
+const showNextWord = async () => {
+ if (currentWordIndex.value < wordBuffer.value.length - 1) {
+ currentWordIndex.value += 1
+ return
+ }
+
+ await fetchDailyWord(true)
+}
+
+const handleWordTouchStart = (e) => {
+ const touch = e.touches?.[0]
+ if (!touch) return
+
+ wordTouchState.value = {
+ startX: touch.clientX,
+ startY: touch.clientY,
+ startTime: Date.now()
+ }
+}
+
+const handleWordTouchMove = () => {}
+
+const handleWordTouchEnd = async (e) => {
+ if (!isWordDetailVisible.value || isRefreshingWord.value) {
+ resetWordTouchState()
+ return
+ }
+
+ const touch = e.changedTouches?.[0]
+ if (!touch) {
+ resetWordTouchState()
+ return
+ }
+
+ const deltaX = touch.clientX - wordTouchState.value.startX
+ const deltaY = touch.clientY - wordTouchState.value.startY
+ const deltaTime = Date.now() - wordTouchState.value.startTime
+ const isHorizontalSwipe = Math.abs(deltaX) > 50 &&
+ Math.abs(deltaX) > Math.abs(deltaY) &&
+ deltaTime < 500
+ const isValidLeftSwipe = deltaX < -50 &&
+ isHorizontalSwipe
+ const isValidRightSwipe = deltaX > 50 &&
+ isHorizontalSwipe
+
+ if (isValidLeftSwipe) {
+ await showNextWord()
+ } else if (isValidRightSwipe) {
+ showPreviousWord()
+ }
+
+ resetWordTouchState()
}
onMounted(() => {
diff --git a/src/pages/contributions/mine/index.config.js b/src/pages/major-transfer/index.config.js
similarity index 53%
rename from src/pages/contributions/mine/index.config.js
rename to src/pages/major-transfer/index.config.js
index e3b9a31..851025a 100644
--- a/src/pages/contributions/mine/index.config.js
+++ b/src/pages/major-transfer/index.config.js
@@ -1,5 +1,6 @@
export default {
- navigationBarTitleText: '我的投稿',
+ navigationBarTitleText: '转专业',
+ backgroundColor: '#f8fafc',
enableShareAppMessage: true,
enableShareTimeline: true
}
diff --git a/src/pages/major-transfer/index.vue b/src/pages/major-transfer/index.vue
new file mode 100644
index 0000000..d425e64
--- /dev/null
+++ b/src/pages/major-transfer/index.vue
@@ -0,0 +1,1217 @@
+
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+
+
+ 正在加载转专业数据...
+
+
+
+
+
+
+ 通知文件
+
+ 按年份和学期查看,点击通知展开附件列表
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+ {{ sectionErrors.notice }}
+
+
+
+
+
+ {{ season.label }}
+
+
+
+
+ 暂无通知
+
+
+
+
+
+
+
+
+ {{ notice.name || '未命名通知' }}
+
+ {{ Number(notice.status) === 1 ? '已发布' : '未发布' }}
+
+
+
+ {{ notice.publishDate || '发布时间待定' }}
+
+
+
+
+
+
+
+
+
+ {{ attachment.name }}
+
+
+
+ 附件 {{ getNoticeAttachmentCount(notice.attachments) }} 个
+
+ {{ Number(notice.status) === 1 ? '复制下载链接' : '暂未发布' }}
+
+
+
+
+
+
+
+
+
+
+
+ 情况说明
+ 常规机会、特殊情形和禁止转专业情况
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+ {{ sectionErrors.explain }}
+
+
+
+
+
+
+ 常规机会
+
+ 暂无说明
+
+
+
+ {{ chance.name || `机会 ${index + 1}` }}
+ 常规批次
+
+
+ 咨询时间:{{ chance.consultTime || '未说明' }}
+ 申请时间:{{ chance.applyTime || '未说明' }}
+ 考核时间:{{ chance.assessmentTime || '未说明' }}
+ 转入时间:{{ chance.transferTime || '未说明' }}
+ 审批时间:{{ chance.approvalTime }}
+
+ {{ chance.principle || '暂无原则说明' }}
+
+
+
+
+
+
+
+ 特殊情况
+
+ 暂无特殊情况说明
+
+
+ {{ item.title || `特殊情况 ${index + 1}` }}
+ {{ item.content || '暂无说明' }}
+
+
+
+
+
+
+
+ 禁止转专业情形
+
+ 暂无限制说明
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+
+ 学院专业
+ 查看可选择的学院、专业
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+ {{ sectionErrors.majorList }}
+
+
+
+ 无
+
+
+
+
+ 学院
+ 专业
+ 备注
+
+
+
+ {{ group.college }}
+
+
+
+ {{ row.name }}
+ {{ row.note || '-' }}
+
+
+
+
+
+
+
+
+
+
+ 数据分析
+ 桑基图展示学院流向,点击可高亮
+
+ {{ majorDataSourceYear }}级数据
+
+
+ 总人数 {{ totalFlowCount }}
+ 流向数 {{ currentFlowData.length }}
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+ {{ sectionErrors.majorData }}
+
+
+
+ 暂无数据
+
+
+
+
+
+ 学院流向桑基图
+ 左侧来源,右侧目标
+
+
+
+
+
+
+
+ {{ selectionTitle }}
+ {{ selectionDescription }}
+
+
+
+
+ 主要流向
+ 点击高亮
+
+
+
+ {{ flow.source }} → {{ flow.target }}
+ {{ flow.count }}人
+
+
+
+
+
+
+
+
+
+ 常见问题
+ {{ majorQA.length }} 问
+
+ 点击问题展开答案
+
+
+
+ {{ sectionErrors.majorQA }}
+
+
+
+ 暂无常见问题
+
+
+
+
+
+ {{ item.id || '?' }}
+ {{ item.title || '未命名问题' }}
+
+
+
+ {{ item.content || '内容待补充' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/materials/detail/index.vue b/src/pages/materials/detail/index.vue
index c96e799..c1a857f 100644
--- a/src/pages/materials/detail/index.vue
+++ b/src/pages/materials/detail/index.vue
@@ -131,25 +131,6 @@
-
-
-
- 管理员操作
-
-
- 编辑资料描述
-
-
- 删除资料
-
-
-
@@ -162,65 +143,6 @@
下载资料
-
-
-
-
- 编辑资料描述
-
-
- 标签(逗号分隔)
-
-
-
-
- 资料描述
-
-
-
-
- 外部链接
-
-
-
-
-
-
-
-
-
- 取消
-
-
- 保存
-
-
-
-
@@ -270,7 +192,6 @@ const loadMaterialDetail = async () => {
userRating.value = res?.user_rating || 0
} catch (error) {
console.error('加载资料详情失败:', error)
- Taro.showToast({ title: '加载失败', icon: 'error' })
} finally {
loading.value = false
}
@@ -325,11 +246,6 @@ const rateMaterial = async (rating) => {
} catch (error) {
// 评分失败,恢复之前的评分
userRating.value = oldRating
-
- Taro.showToast({
- title: '评分失败',
- icon: 'none'
- })
console.error('评分失败:', error)
}
}
@@ -352,10 +268,6 @@ const handleDownload = async () => {
}
})
} catch (error) {
- Taro.showToast({
- title: '操作失败',
- icon: 'none'
- })
console.error('下载记录失败:', error)
}
}
@@ -375,60 +287,6 @@ const openExternalLink = () => {
})
}
-// 编辑资料
-const editMaterial = () => {
- editForm.value = {
- tags: material.value.tags || '',
- description: material.value.description || '',
- external_link: material.value.external_link || '',
- is_recommended: material.value.is_recommended || false
- }
- showEditDialog.value = true
-}
-
-// 提交编辑
-const submitEdit = async () => {
- try {
- await materialAPI.updateMaterialDesc(md5.value, editForm.value)
-
- Taro.showToast({
- title: '更新成功',
- icon: 'success'
- })
-
- showEditDialog.value = false
-
- // 重新获取数据,只更新编辑的字段
- refreshMaterialData(['tags', 'description', 'external_link', 'is_recommended'])
- } catch (error) {
- Taro.showToast({
- title: '更新失败',
- icon: 'none'
- })
- console.error('更新失败:', error)
- }
-}
-
-// 删除资料
-const deleteMaterial = () => {
- Taro.showModal({
- title: '确认删除',
- content: '确定要删除这份资料吗?此操作不可撤销。',
- success: async (res) => {
- if (res.confirm) {
- const result = await materialAPI.deleteMaterial(md5.value)
- Taro.showToast({
- title: result.message,
- icon: 'success'
- })
- setTimeout(() => {
- Taro.navigateBack()
- }, 1500)
- }
- }
- })
-}
-
// 格式化文件大小
const formatFileSize = (bytes) => {
if (!bytes) return '未知'
diff --git a/src/pages/materials/index.vue b/src/pages/materials/index.vue
index 0740d87..2becca9 100644
--- a/src/pages/materials/index.vue
+++ b/src/pages/materials/index.vue
@@ -69,26 +69,9 @@
-
-
-
- 一起来学热搜
- 热门搜索
-
-
-
- {{ word.keywords }}
-
-
-
-
+
-
-
-
- {{ index + 1 }}
-
-
- {{
- item.file_name
- }}
-
-
- 热度 {{ hotRankType === 0 ? item.total_hotness : item.period_hotness || 0 }}
- ·
- {{ item.download_count || 0 }} 下载
+
+
+
+
+ {{ index + 1 }}
-
-
- {{ tag.trim() }}
-
+
+ {{
+ item.file_name
+ }}
+
+
+ 热度 {{ hotRankType === 0 ? item.total_hotness : item.period_hotness || 0 }}
+ ·
+ {{ item.download_count || 0 }} 下载
+
+
+
+ {{ tag.trim() }}
+
+
+
+ 暂无热榜数据
+
diff --git a/src/pages/notifications/categories/index.config.js b/src/pages/notifications/categories/index.config.js
deleted file mode 100644
index 5b601d0..0000000
--- a/src/pages/notifications/categories/index.config.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default {
- navigationBarTitleText: '信息管理'
-}
diff --git a/src/pages/notifications/categories/index.vue b/src/pages/notifications/categories/index.vue
deleted file mode 100644
index e9c1474..0000000
--- a/src/pages/notifications/categories/index.vue
+++ /dev/null
@@ -1,493 +0,0 @@
-
-
-
-
-
-
-
-
- 加载中...
-
-
-
-
-
-
-
-
-
- 还没有分类
-
- 创建分类
-
-
-
-
-
-
-
-
-
-
-
-
- {{ category.name }}
-
-
-
- {{ category.is_active ? '启用' : '禁用' }}
-
-
-
-
-
- 排序: {{ category.sort }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ isEditing ? '编辑分类' : '创建分类' }}
-
-
-
-
-
-
-
-
-
- 分类名称 *
-
-
-
- {{ errors.name }}
- {{ categoryForm.name.length }}/20
-
-
-
-
-
- 排序值
-
- 排序值越小,在列表中显示越靠前
-
-
-
-
- 状态设置
-
-
-
-
-
-
- 启用
- 分类将在前端显示
-
-
-
-
-
-
-
-
- 禁用
- 分类将不在前端显示
-
-
-
-
-
-
-
-
- 取消
-
-
-
- {{ isSubmitting ? '提交中...' : (isEditing ? '保存修改' : '创建分类') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/notifications/create/index.config.js b/src/pages/notifications/create/index.config.js
deleted file mode 100644
index 5b601d0..0000000
--- a/src/pages/notifications/create/index.config.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default {
- navigationBarTitleText: '信息管理'
-}
diff --git a/src/pages/notifications/create/index.vue b/src/pages/notifications/create/index.vue
deleted file mode 100644
index e31ae1b..0000000
--- a/src/pages/notifications/create/index.vue
+++ /dev/null
@@ -1,435 +0,0 @@
-
-
-
-
-
-
- {{ isEditing ? '编辑信息' : '创建信息' }}
-
- {{ isEditing ? '修改信息内容' : '发布重要信息,让更多同学看到' }}
-
-
-
-
-
-
-
- 信息标题 *
-
-
-
- {{ errors.title }}
- {{ form.title.length }}/50
-
-
-
-
-
-
- 信息内容 *
-
-
-
- {{ errors.content }}
- {{ form.content.length }}/10000
-
-
-
-
-
-
- 选择分类 *
-
-
-
-
-
-
-
- {{ category.name }}
-
-
-
- {{ errors.categories }}
-
-
-
-
-
-
-
-
-
-
-
-
- {{ isSubmitting ? '提交中...' : getSubmitButtonText() }}
-
-
-
-
-
-
-
-
- 预览效果
-
-
-
-
-
-
-
-
-
-
-
- 信息预览
-
-
-
-
-
-
-
-
- {{ form.title || '信息标题预览' }}
-
-
-
-
-
-
- {{ authStore.userInfo?.nickname || '发布者' }}
-
-
-
- {{ getCurrentDateTime() }}
-
-
-
-
-
-
- {{ getCategoryName(categoryId) }}
-
-
-
-
-
-
-
-
- {{ form.content || '信息内容预览...' }}
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/notifications/detail/index.vue b/src/pages/notifications/detail/index.vue
index 6d149e4..898d72a 100644
--- a/src/pages/notifications/detail/index.vue
+++ b/src/pages/notifications/detail/index.vue
@@ -1,3 +1,4 @@
+
@@ -62,8 +63,8 @@
-
- {{ notification.content }}
+
+ {{ notification.content }}
@@ -152,181 +153,6 @@
-
-
-
- 添加日程
-
-
-
-
-
-
-
-
- 添加到日程
-
-
-
-
-
-
-
- 日程标题
-
-
-
-
-
- 日程描述
-
-
-
-
-
-
- 时间段设置
-
-
- 添加时间段
-
-
-
-
-
-
-
-
- 时间段 {{ index + 1 }}
-
-
-
-
-
-
-
-
- 时间段名称(选填)
-
-
-
-
-
- 开始
- onTimeSlotDateChange(index, 'startDate', e)"
- class="flex-1"
- >
-
- {{ timeSlot.startDate || '选择日期' }}
-
-
- onTimeSlotTimeChange(index, 'startTime', e)"
- class="flex-1"
- >
-
- {{ timeSlot.startTime || '选择时间' }}
-
-
-
-
-
-
- 结束
- onTimeSlotDateChange(index, 'endDate', e)"
- class="flex-1"
- >
-
- {{ timeSlot.endDate || '选择日期' }}
-
-
- onTimeSlotTimeChange(index, 'endTime', e)"
- class="flex-1"
- >
-
- {{ timeSlot.endTime || '选择时间' }}
-
-
-
-
-
- toggleAllDay(index)">
-
-
-
- 全天
-
-
-
-
-
-
-
-
- 取消
-
-
- 确认添加
-
-
-
-
-
@@ -334,41 +160,19 @@
import { ref, computed, onMounted } from 'vue'
import Taro from '@tarojs/taro'
import { useNotificationStore } from '../../../stores/notifications'
-import { useAuthStore } from '../../../stores/auth'
import SkeletonDetail from '../../../components/SkeletonDetail.vue'
const notificationStore = useNotificationStore()
-const authStore = useAuthStore()
// 页面参数
const notificationId = ref('')
const isLoading = ref(true)
const preloadData = ref(null)
-const showScheduleModal = ref(false)
-const scheduleForm = ref({
- title: '',
- description: '',
- timeSlots: [{
- name: '',
- startDate: '',
- startTime: '',
- endDate: '',
- endTime: '',
- isAllDay: false
- }]
-})
// 计算属性
const notification = computed(() => {
- // 优先使用预加载数据,然后使用store中的详情数据
- const storeData = (isAdmin.value || isOperator.value)
- ? notificationStore.adminNotificationDetail
- : notificationStore.notificationDetail
-
- return storeData || preloadData.value
+ return notificationStore.notificationDetail || preloadData.value
})
-const isAdmin = computed(() => authStore.isAdmin)
-const isOperator = computed(() => authStore.userInfo?.role === 3)
// 页面生命周期
onMounted(() => {
@@ -399,100 +203,20 @@ const fetchNotificationDetail = async () => {
// 如果没有预加载数据,清理之前的详情数据
if (!preloadData.value) {
notificationStore.notificationDetail = null
- notificationStore.adminNotificationDetail = null
}
- // 根据用户权限调用不同的API
- if (isAdmin.value || isOperator.value) {
- await notificationStore.fetchAdminNotificationDetail(notificationId.value)
- } else {
- await notificationStore.fetchNotificationDetail(notificationId.value)
- }
+ await notificationStore.fetchNotificationDetail(notificationId.value)
// 数据加载完成后,清除预加载数据
preloadData.value = null
} catch (error) {
console.error('获取信息详情失败:', error)
- Taro.showToast({
- title: '加载失败',
- icon: 'error'
- })
+
} finally {
isLoading.value = false
}
}
-// 日程相关方法
-const openScheduleModal = () => {
- // 重置表单数据
- scheduleForm.value = {
- title: '',
- description: '',
- timeSlots: [{
- name: '',
- startDate: '',
- startTime: '',
- endDate: '',
- endTime: '',
- isAllDay: false
- }]
- }
-
- // 如果已经有日程数据,填充到编辑表单
- if (notification.value?.schedule) {
- scheduleForm.value.title = notification.value.schedule.title || ''
- scheduleForm.value.description = notification.value.schedule.description || ''
-
- // 将后端的time_slots数据转换为前端表单格式
- if (notification.value.schedule.time_slots && Array.isArray(notification.value.schedule.time_slots)) {
- scheduleForm.value.timeSlots = notification.value.schedule.time_slots.map(slot => ({
- name: slot.name || '',
- startDate: slot.start_date || '',
- startTime: slot.start_time || '',
- endDate: slot.end_date || '',
- endTime: slot.end_time || '',
- isAllDay: slot.is_all_day || false
- }))
-
- } else {
- }
- } else {
- }
-
- // 打开模态框
- showScheduleModal.value = true
-}
-
-const addTimeSlot = () => {
- scheduleForm.value.timeSlots.push({
- name: '',
- startDate: '',
- startTime: '',
- endDate: '',
- endTime: '',
- isAllDay: false
- })
-}
-
-const removeTimeSlot = (index) => {
- if (scheduleForm.value.timeSlots.length > 1) {
- scheduleForm.value.timeSlots.splice(index, 1)
- }
-}
-
-const onTimeSlotDateChange = (index, field, e) => {
- scheduleForm.value.timeSlots[index][field] = e.detail.value
-}
-
-const onTimeSlotTimeChange = (index, field, e) => {
- scheduleForm.value.timeSlots[index][field] = e.detail.value
-}
-
-// 切换全天状态
-const toggleAllDay = (index) => {
- scheduleForm.value.timeSlots[index].isAllDay = !scheduleForm.value.timeSlots[index].isAllDay
-}
-
// 获取日程时间段列表(支持多种数据结构)
const getScheduleTimeSlots = () => {
if (!notification.value) return []
@@ -547,86 +271,11 @@ const isTimeSlotActive = (timeSlot) => {
}
}
-const convertToSchedule = async () => {
- if (!scheduleForm.value.title.trim()) {
- Taro.showToast({
- title: '请输入日程标题',
- icon: 'error'
- })
- return
- }
-
- // 验证至少有一个有效的时间段
- const validTimeSlots = scheduleForm.value.timeSlots.filter(slot => slot.startDate)
- if (validTimeSlots.length === 0) {
- Taro.showToast({
- title: '请至少添加一个时间段',
- icon: 'error'
- })
- return
- }
-
- // 验证每个时间段的完整性
- for (let i = 0; i < validTimeSlots.length; i++) {
- const slot = validTimeSlots[i]
- if (!slot.startDate) {
- Taro.showToast({
- title: `请设置时间段${i + 1}的开始日期`,
- icon: 'error'
- })
- return
- }
- if (!slot.isAllDay && !slot.startTime) {
- Taro.showToast({
- title: `请设置时间段${i + 1}的开始时间`,
- icon: 'error'
- })
- return
- }
- }
-
- try {
- // 构建多个时间段的数据
- const timeSlots = validTimeSlots.map((slot, index) => ({
- name: slot.name || `${scheduleForm.value.title} - 时间段${index + 1}`,
- start_date: slot.startDate,
- end_date: slot.endDate || slot.startDate,
- start_time: slot.isAllDay ? '00:00' : slot.startTime || '09:00',
- end_time: slot.isAllDay ? '23:59' : slot.endTime || '17:00',
- is_all_day: slot.isAllDay
- }))
-
- const data = {
- title: scheduleForm.value.title,
- description: scheduleForm.value.description,
- time_slots: timeSlots
- }
-
- await notificationStore.convertToSchedule(notificationId.value, data)
-
- Taro.showToast({
- title: `添加成功,共${timeSlots.length}个时间段`,
- icon: 'success'
- })
-
- showScheduleModal.value = false
-
- // 重新获取通知详情以显示最新的日程信息
- await fetchNotificationDetail()
- } catch (error) {
- console.error('转换为日程失败:', error)
- Taro.showToast({
- title: '添加失败',
- icon: 'error'
- })
- }
-}
-
// 工具函数
const getStatusClass = (status) => {
switch (status) {
case 1: return 'bg-gray-100 text-gray-600' // 草稿
- case 2: return 'bg-yellow-100 text-yellow-700' // 待审核
+ case 2: return 'bg-yellow-100 text-yellow-700' // 处理中
case 3: return 'bg-green-100 text-green-700' // 已发布
case 4: return 'bg-red-100 text-red-700' // 已删除
default: return 'bg-gray-100 text-gray-600'
@@ -636,7 +285,7 @@ const getStatusClass = (status) => {
const getStatusText = (status) => {
switch (status) {
case 1: return '草稿'
- case 2: return '待审核'
+ case 2: return '处理中'
case 3: return '已发布'
case 4: return '已删除'
default: return '未知'
diff --git a/src/pages/notifications/index.vue b/src/pages/notifications/index.vue
index 6f6ab18..571e865 100644
--- a/src/pages/notifications/index.vue
+++ b/src/pages/notifications/index.vue
@@ -1,3 +1,4 @@
+
@@ -41,59 +42,10 @@
-
-
-
-
-
-
-
- 创建
-
-
-
-
-
-
-
- 管理
-
-
-
-
-
-
-
- 审核
-
-
-
- {{ pendingContributionsCount }}
-
-
-
-
-
-
-
-
- 分类
-
-
-
-
-
-
+
加载中...
@@ -102,7 +54,7 @@
-
+
@@ -113,49 +65,8 @@
-
-
-
- 贡献信息差
-
- 查看全部
-
-
-
-
-
-
-
- {{
- contributionStats.total_count
- }}
- 总投稿数
-
-
-
-
-
- {{
- contributionStats.approved_count
- }}
- 已采纳
-
-
-
-
-
-
-
- 投稿
-
-
-
-
-
-
@@ -213,9 +124,9 @@
-
- 加载中...
-
+ 加载中...
+ 已加载全部
@@ -224,14 +135,14 @@
-
-
diff --git a/src/pages/organization/detail/index.config.js b/src/pages/organization/detail/index.config.js
new file mode 100644
index 0000000..409dc6d
--- /dev/null
+++ b/src/pages/organization/detail/index.config.js
@@ -0,0 +1,6 @@
+export default {
+ navigationBarTitleText: '组织详情',
+ backgroundColor: '#f8fafc',
+ enableShareAppMessage: true,
+ enableShareTimeline: true,
+}
diff --git a/src/pages/organization/detail/index.vue b/src/pages/organization/detail/index.vue
new file mode 100644
index 0000000..0ce7556
--- /dev/null
+++ b/src/pages/organization/detail/index.vue
@@ -0,0 +1,171 @@
+
+
+
+ 加载中...
+
+
+
+ {{ pageError }}
+
+
+ 重新加载
+
+
+ 返回
+
+
+
+
+
+
+
+ {{ organization.name }}
+
+
+
+ {{ organization.organization_type || '组织' }}
+
+
+
+
+
+
+ 所属单位
+ {{ organization.affiliation || '未填写' }}
+
+
+
+ 所属校区
+ {{ organization.campus || '未填写' }}
+
+
+
+ 联系方式
+
+ {{ organization.contact || '未填写' }}
+
+ 复制
+
+
+
+
+
+ 组织介绍
+
+ {{ organization.introduction || '该组织暂未补充介绍。' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/organization/index.config.js b/src/pages/organization/index.config.js
new file mode 100644
index 0000000..bb31023
--- /dev/null
+++ b/src/pages/organization/index.config.js
@@ -0,0 +1,6 @@
+export default {
+ navigationBarTitleText: '组织',
+ backgroundColor: '#f8fafc',
+ enableShareAppMessage: true,
+ enableShareTimeline: true,
+}
diff --git a/src/pages/organization/index.vue b/src/pages/organization/index.vue
new file mode 100644
index 0000000..fdecd7f
--- /dev/null
+++ b/src/pages/organization/index.vue
@@ -0,0 +1,409 @@
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+ {{ pageError }}
+
+ 重新加载
+
+
+
+
+
+
+
+
+
+ 类型
+ {{ draftFilters.organization_type || '无' }}
+
+
+
+
+
+ 所属
+ {{ draftFilters.affiliation || '无' }}
+
+
+
+
+
+ 校区
+ {{ draftFilters.campus || '无' }}
+
+
+
+
+
+ {{ listHint }}
+
+
+ 已筛选
+
+ {{ totalLabel }}
+
+
+
+
+
+ 暂无符合条件的组织
+
+
+
+
+
+
+ {{ item.name }}
+
+
+ {{ item.organization_type || '未分类' }}
+
+
+
+
+
+
+ {{ item.affiliation || '未知所属' }}
+
+
+
+ {{ item.campus || '未知校区' }}
+
+
+
+
+ {{ getOrganizationDescription(item) }}
+
+
+
+
+
+ 加载更多...
+
+
+
+
+ 已加载全部
+ 如需录入请联系客服
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/points/index.vue b/src/pages/points/index.vue
index 668d7de..37a9ab0 100644
--- a/src/pages/points/index.vue
+++ b/src/pages/points/index.vue
@@ -252,10 +252,6 @@ const initPage = async () => {
pointsStats.value = statsRes
} catch (error) {
console.error('初始化页面失败:', error)
- Taro.showToast({
- title: '加载失败',
- icon: 'error'
- })
} finally {
isLoading.value = false
}
@@ -327,7 +323,7 @@ const getSourceName = (source) => {
'review': '发布评价',
'contribution': '投稿信息',
'redeem': '兑换奖品',
- 'admin_grant': '管理员赋予'
+ 'admin_grant': '系统发放'
}
return sourceNames[source] || source
}
diff --git a/src/pages/profile/index.vue b/src/pages/profile/index.vue
index 743b28e..6cf4b22 100644
--- a/src/pages/profile/index.vue
+++ b/src/pages/profile/index.vue
@@ -1,3 +1,4 @@
+
@@ -22,7 +23,7 @@
账号
- {{ roleTagText }}
+ {{ roleTagText }}
{{ userInfo?.id || "无" }}
@@ -79,63 +80,6 @@
-
-
-
-
-
- 评价管理
-
-
-
-
- 英雄管理
-
-
-
-
- 配置管理
-
-
-
-
- 权限管理
-
-
-
-
-
- 重置绑定
-
-
-
-
- 积分管理
-
-
-
-
+
+
+
+
+
+ 用户等级
+
+
+
+
+
+ 登录天数
+ {{ loginDaysData.loginDays }}/25
+
+
+
+
+
+
+
+
+
+ 当前等级
+ {{ roleTagText }}
+
+
+ 下一等级
+ {{ roleTagMap.user_active.text }}
+
+
+ 达成条件
+ 过去 {{ loginDaysData.pastDays }} 天内登录 25 天
+
+
+
+
+
+ 知道了
+
+
+
@@ -175,12 +167,14 @@
import { ref, computed } from "vue";
import { useAuthStore } from "../../stores/auth";
import Taro from "@tarojs/taro";
-import { courseTableAPI, pointsAPI } from "../../api/index";
+import { courseTableAPI, pointsAPI, userAPI } from "../../api/index";
const authStore = useAuthStore();
const userPoints = ref(0);
const pointsLoaded = ref(false);
const pointsLoadFailed = ref(false);
+const loginDaysModal = ref(false);
+const loginDaysData = ref({ loginDays: 0, pastDays: 100 });
// 计算属性
const userInfo = computed(() => authStore.userInfo);
@@ -216,6 +210,11 @@ const roleTagClass = computed(() => {
return roleTagMap.user_basic.class;
});
+const isAtLeastActive = computed(() => {
+ const roleTags = userInfo.value?.role_tags || [];
+ return ['admin', 'operator', 'user_verified', 'user_active'].some(tag => roleTags.includes(tag));
+});
+
// 方法
const goToLogin = () => {
Taro.navigateTo({ url: "/pages/login/index" });
@@ -225,68 +224,6 @@ const goToMyPoints = () => {
Taro.navigateTo({ url: "/pages/points/index" });
};
-const goToReviewManagement = () => {
- Taro.navigateTo({ url: "/pages/admin/teacher-reviews/index" });
-};
-
-const goToHeroManagement = () => {
- Taro.navigateTo({ url: "/pages/admin/heroes/index" });
-};
-
-const goToConfigManagement = () => {
- Taro.navigateTo({ url: "/pages/admin/config/index" });
-};
-
-const goToRbacManagement = () => {
- Taro.navigateTo({ url: "/pages/admin/rbac/index" });
-};
-
-const goToPointsManagement = () => {
- Taro.navigateTo({ url: "/pages/admin/points/index" });
-};
-
-const goToResetBindCount = () => {
- Taro.showModal({
- title: "重置绑定",
- editable: true,
- placeholderText: "用户ID",
- success: (res) => {
- if (res.confirm && res.content && res.content.trim() !== "") {
- const userId = res.content.trim();
- // 延迟执行,避免与modal关闭冲突
- setTimeout(async () => {
- // 显示加载中
- Taro.showLoading({
- title: "处理中...",
- mask: true
- });
-
- try {
- await courseTableAPI.resetBindCount(userId);
- Taro.hideLoading();
- // 使用modal显示结果,避免toast冲突
- Taro.showModal({
- title: "操作成功",
- content: "重置绑定成功!",
- showCancel: false,
- confirmText: "确定"
- });
- } catch (error) {
- Taro.hideLoading();
- // 使用modal显示错误,避免toast冲突
- Taro.showModal({
- title: "操作失败",
- content: error.message || "重置绑定失败,请重试",
- showCancel: false,
- confirmText: "确定"
- });
- }
- }, 300);
- }
- },
- });
-};
-
const handleLogout = () => {
Taro.showModal({
title: "提示",
@@ -303,6 +240,21 @@ const goToTermsOfService = () => {
Taro.navigateTo({ url: "/pages/terms-of-service/index" });
};
+// 显示登录活跃度进度
+const showLoginDaysProgress = async () => {
+ try {
+ const res = await userAPI.getLoginDays()
+
+ loginDaysData.value = {
+ loginDays: res.login_days || 0,
+ pastDays: res.past_days || 100
+ }
+ loginDaysModal.value = true
+ } catch (error) {
+
+ }
+};
+
// 获取用户积分
const fetchUserPoints = async () => {
diff --git a/src/pages/qualification/index.config.js b/src/pages/qualification/index.config.js
new file mode 100644
index 0000000..f8c92c9
--- /dev/null
+++ b/src/pages/qualification/index.config.js
@@ -0,0 +1,6 @@
+export default {
+ navigationBarTitleText: '考级考证',
+ backgroundColor: '#f8fafc',
+ enableShareAppMessage: true,
+ enableShareTimeline: true,
+}
diff --git a/src/pages/qualification/index.vue b/src/pages/qualification/index.vue
new file mode 100644
index 0000000..b2795fe
--- /dev/null
+++ b/src/pages/qualification/index.vue
@@ -0,0 +1,330 @@
+
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+
+
+ 正在加载考级考证数据...
+
+
+
+
+
+
+ {{ activeTitle }}
+ {{ activeDescription }}
+
+ {{ activeCount }} 项
+
+
+
+
+ {{ activeError }}
+
+
+
+ 暂无考级考试数据
+
+
+
+ 暂无职业资格数据
+
+
+
+
+
+ {{ item.name }}
+
+
+
+ {{ item.displayDate }}
+
+ {{ item.intro || '暂无介绍' }}
+
+
+
+
+
+
+ {{ item.name }}
+ {{ item.displayDate || '时间待更新' }}
+
+
+
+
+
+ {{ child.name }}
+ {{ child.displayDate || '时间待更新' }}
+
+
+
+
+
+ {{ item.category || '职业资格' }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/schedule/index.vue b/src/pages/schedule/index.vue
index bd78f42..2482027 100644
--- a/src/pages/schedule/index.vue
+++ b/src/pages/schedule/index.vue
@@ -439,10 +439,6 @@ const handleDeleteCourse = async (data) => {
})
} catch (error) {
console.error('删除课程失败:', error)
- Taro.showToast({
- title: '删除失败',
- icon: 'error'
- })
}
}
@@ -475,10 +471,7 @@ const handleSubmitCourse = async (submitData) => {
icon: 'success'
})
} catch (error) {
- Taro.showToast({
- title: '保存失败',
- icon: 'error'
- })
+
}
}
@@ -620,11 +613,6 @@ const loadCourseData = async () => {
if (error.message && error.message.includes('未设置班级')) {
return
}
-
- Taro.showToast({
- title: '获取课程表失败',
- icon: 'error'
- })
}
}
}
diff --git a/src/pages/schedule/schedule-bind/index.vue b/src/pages/schedule/schedule-bind/index.vue
index 16cc4c8..52d8ac2 100644
--- a/src/pages/schedule/schedule-bind/index.vue
+++ b/src/pages/schedule/schedule-bind/index.vue
@@ -4,17 +4,11 @@
-
+ @input="handleSearch" />
-
+
搜索
@@ -27,12 +21,8 @@
-
+
{{ classItem.class_id }}
{{ classItem.semester }}
@@ -44,23 +34,16 @@
-
+
上一页
{{ currentPage }} / {{ totalPages }}
-
+ :class="{ 'opacity-50': currentPage >= totalPages }">
下一页
@@ -68,7 +51,8 @@
-
+
没有找到相关班级
请检查班级名称是否正确
@@ -76,9 +60,15 @@
- 仅有 2 次绑定机会
+ 已绑定 {{ bindCount }}/2 次
请输入班级名称进行搜索
如:25软件1班
+
+
+
+ 重置课表数据
+
@@ -90,17 +80,11 @@
-
+
取消
-
+
{{ isBinding ? '绑定中...' : '确认绑定' }}
@@ -114,6 +98,7 @@ import { ref, computed, onBeforeUnmount } from 'vue'
import Taro from '@tarojs/taro'
import { useScheduleStore } from '../../../stores/schedule'
import { useAuthStore } from '../../../stores/auth'
+import { courseTableAPI } from '../../../api/index'
defineOptions({
name: 'ScheduleBindPage'
@@ -122,6 +107,22 @@ defineOptions({
const scheduleStore = useScheduleStore()
const authStore = useAuthStore()
+// 绑定次数
+const bindCount = ref(0)
+const bindCountLoaded = ref(false)
+
+const fetchBindCount = async () => {
+ try {
+ const res = await courseTableAPI.getBindCount()
+ bindCount.value = res.bind_count || 0
+ bindCountLoaded.value = true
+ } catch (error) {
+ console.error('获取绑定次数失败:', error)
+ }
+}
+
+fetchBindCount()
+
// 计算属性:判断用户是否只有 user_basic 角色标签
const isOnlyUserBasic = computed(() => {
const roleTags = authStore.userInfo?.role_tags || []
@@ -186,10 +187,6 @@ const search = async () => {
} catch (error) {
console.error('搜索班级失败:', error)
- Taro.showToast({
- title: '搜索失败',
- icon: 'error'
- })
} finally {
isLoading.value = false
}
@@ -222,10 +219,6 @@ const loadPage = async () => {
} catch (error) {
console.error('加载页面失败:', error)
- Taro.showToast({
- title: '加载失败,请重试',
- icon: 'error'
- })
} finally {
isLoading.value = false
}
@@ -270,15 +263,43 @@ const confirmBind = async () => {
} catch (error) {
console.error('绑定班级失败:', error)
- Taro.showToast({
- title: '请联系客服',
- icon: 'error'
- })
} finally {
isBinding.value = false
selectedClass.value = null
}
}
+
+// 重置个人课表
+const handleResetSchedule = () => {
+ const currentSemester = scheduleStore.semester
+
+ if (!currentSemester) {
+ Taro.showToast({ title: '无学期信息', icon: 'error' })
+ return
+ }
+
+ Taro.showModal({
+ title: '重置个人课表',
+ content: `确定要重置「${currentSemester}」的课表数据吗?重置后课表将恢复为班级初始数据。`,
+ confirmColor: '#ef4444',
+ success: async (res) => {
+ if (res.confirm) {
+ try {
+ await courseTableAPI.deleteSchedule(currentSemester)
+ scheduleStore.fetchCourseTable(currentSemester, true)
+ Taro.showToast({ title: '重置成功', icon: 'success' })
+ setTimeout(() => {
+ // 传递消息通知课表页刷新
+ Taro.eventCenter.trigger('reloadSchedule')
+ Taro.navigateBack()
+ }, 1000)
+
+ } catch (error) {
+ }
+ }
+ }
+ })
+}