新增后台管理系统(答辩用

This commit is contained in:
2026-06-24 05:07:32 +08:00
parent 2251f31524
commit 1f209e6761
98 changed files with 25099 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
/**
* 工具函数
*/
/**
* 格式化日期时间
*/
export function formatDateTime(dateStr, showTime = true) {
if (!dateStr) return '--'
const d = new Date(dateStr)
const pad = (n) => String(n).padStart(2, '0')
const date = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`
if (!showTime) return date
return `${date} ${pad(d.getHours())}:${pad(d.getMinutes())}`
}
/**
* 格式化日期
*/
export function formatDate(dateStr) {
return formatDateTime(dateStr, false)
}
/**
* 格式化时间
*/
export function formatTime(dateStr) {
if (!dateStr) return '--'
const d = new Date(dateStr)
const pad = (n) => String(n).padStart(2, '0')
return `${pad(d.getHours())}:${pad(d.getMinutes())}`
}
/**
* 格式化时间(短格式:MM/DD HH:mm
*/
export function formatTimeShort(dateStr) {
if (!dateStr) return '--'
const d = new Date(dateStr)
const pad = (n) => String(n).padStart(2, '0')
return `${d.getMonth() + 1}/${d.getDate()} ${pad(d.getHours())}:${pad(d.getMinutes())}`
}
/**
* 课程状态(由后端 GET /api/groupCourse/statuses 统一提供,避免前后端各自硬编码)
*/
let _statusMap = null
let _statusClassMap = null
const DEFAULT_STATUS_MAP = { '0': '正常', '1': '已取消', '2': '已结束', '3': '进行中', '4': '超时' }
const DEFAULT_STATUS_CLASS = { '0': 'green', '1': 'orange', '2': 'gray', '3': 'blue', '4': 'red' }
/**
* 从后端加载课程状态映射,返回 Promise<boolean>
*/
export async function loadCourseStatuses() {
try {
const res = await uni.request({
url: `${getBaseUrl()}/api/groupCourse/statuses`,
method: 'GET'
})
if (res.statusCode === 200 && res.data?.success && Array.isArray(res.data.data)) {
const map = {}
const cls = {}
res.data.data.forEach(item => {
map[String(item.dbValue)] = item.label
cls[String(item.dbValue)] = item.cssClass
})
_statusMap = map
_statusClassMap = cls
return true
}
} catch (e) {
console.warn('加载课程状态映射失败,使用默认映射:', e)
}
return false
}
function getBaseUrl() {
// #ifdef H5
return '/gym-api'
// #endif
// #ifndef H5
return 'http://localhost:8084'
// #endif
}
function getStatusMap() {
return _statusMap || DEFAULT_STATUS_MAP
}
function getStatusClassMap() {
return _statusClassMap || DEFAULT_STATUS_CLASS
}
export function getCourseStatusLabel(status, deletedAt) {
if (deletedAt) return '已删除'
return getStatusMap()[String(status)] || '未知'
}
export function getCourseStatusClass(status, deletedAt) {
if (deletedAt) return 'tag-gray'
return 'tag-' + (getStatusClassMap()[String(status)] || 'gray')
}
/**
* 判断课程是否有实际时间记录
*/
export function hasActualTime(course) {
return !!(course && (course.actualStartTime || course.actualEndTime))
}
/**
* Toast 提示
*/
export function showToast(title, icon = 'none') {
uni.showToast({ title, icon, duration: 2000 })
}
/**
* 确认弹窗
*/
export function showConfirm(content, title = '提示') {
return new Promise((resolve) => {
uni.showModal({
title,
content,
success: (res) => {
resolve(res.confirm)
}
})
})
}
+122
View File
@@ -0,0 +1,122 @@
/**
* HTTP请求封装 - 基于 uni.request
* 风格参考 gym-manage-cuit 的 request.ts
*/
// 后端API基础地址
// H5端: 使用 /gym-api 前缀走vite代理(避免与源码 api/ 目录冲突),代理重写为 /api
// App端: 直接请求后端域名
// #ifdef H5
const BASE_URL = '/gym-api'
// #endif
// #ifndef H5
const BASE_URL = 'http://localhost:8084'
// #endif
// token存储key
const TOKEN_KEY = 'coach_access_token'
const REFRESH_TOKEN_KEY = 'coach_refresh_token'
const USER_INFO_KEY = 'coach_user_info'
function getToken() {
return uni.getStorageSync(TOKEN_KEY) || ''
}
function getRefreshToken() {
return uni.getStorageSync(REFRESH_TOKEN_KEY) || ''
}
function setToken(token) {
uni.setStorageSync(TOKEN_KEY, token)
}
function setRefreshToken(token) {
uni.setStorageSync(REFRESH_TOKEN_KEY, token)
}
function clearAuth() {
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(REFRESH_TOKEN_KEY)
uni.removeStorageSync(USER_INFO_KEY)
}
/**
* 发起请求
*/
function request(options) {
return new Promise((resolve, reject) => {
const token = getToken()
const header = {
'Content-Type': 'application/json',
...(options.header || {})
}
if (token) {
header['Authorization'] = `Bearer ${token}`
}
uni.request({
url: BASE_URL + options.url,
method: options.method || 'GET',
data: options.data || {},
header: header,
timeout: options.timeout || 15000,
success: (res) => {
const statusCode = res.statusCode
if (statusCode >= 200 && statusCode < 300) {
resolve(res.data)
} else if (statusCode === 401) {
// token过期,清除登录状态
const isLoginRequest = options.url?.includes('/auth/login')
if (!isLoginRequest) {
clearAuth()
uni.reLaunch({ url: '/pages/login/login' })
}
reject(res)
} else {
reject(res)
}
},
fail: (err) => {
uni.showToast({
title: '网络请求失败',
icon: 'none',
duration: 2000
})
reject(err)
}
})
})
}
// 导出便捷方法
request.get = function(url, params = {}) {
let query = ''
if (Object.keys(params).length > 0) {
const parts = []
for (const key in params) {
if (params[key] !== undefined && params[key] !== null && params[key] !== '') {
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
}
}
if (parts.length > 0) {
query = '?' + parts.join('&')
}
}
return request({ url: url + query, method: 'GET' })
}
request.post = function(url, data = {}) {
return request({ url, method: 'POST', data })
}
request.put = function(url, data = {}) {
return request({ url, method: 'PUT', data })
}
request.delete = function(url, data = {}) {
return request({ url, method: 'DELETE', data })
}
export { request, getToken, setToken, setRefreshToken, clearAuth, TOKEN_KEY, USER_INFO_KEY }
export default request