mirror of
https://github.com/handsomezhuzhu/QQuiz.git
synced 2026-04-18 22:42:53 +00:00
🎉 Initial commit: QQuiz - 智能刷题与题库管理平台
## 功能特性 ✅ **核心功能** - 多文件上传与智能去重(基于 content_hash) - 异步文档解析(支持 TXT/PDF/DOCX/XLSX) - AI 智能题目提取与评分(OpenAI/Anthropic/Qwen) - 断点续做与进度管理 - 自动错题本收集 ✅ **技术栈** - Backend: FastAPI + SQLAlchemy 2.0 + PostgreSQL - Frontend: React 18 + Vite + Tailwind CSS - Deployment: Docker Compose ✅ **项目结构** - 53 个文件 - 完整的前后端分离架构 - Docker/源码双模部署支持 🚀 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
117
frontend/src/utils/helpers.js
Normal file
117
frontend/src/utils/helpers.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Utility Helper Functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Format date to readable string
|
||||
*/
|
||||
export const formatDate = (dateString) => {
|
||||
const date = new Date(dateString)
|
||||
return new Intl.DateTimeFormat('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
}).format(date)
|
||||
}
|
||||
|
||||
/**
|
||||
* Format relative time (e.g., "2 days ago")
|
||||
*/
|
||||
export const formatRelativeTime = (dateString) => {
|
||||
const date = new Date(dateString)
|
||||
const now = new Date()
|
||||
const diff = now - date
|
||||
const seconds = Math.floor(diff / 1000)
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const hours = Math.floor(minutes / 60)
|
||||
const days = Math.floor(hours / 24)
|
||||
|
||||
if (days > 7) {
|
||||
return formatDate(dateString)
|
||||
} else if (days > 0) {
|
||||
return `${days} 天前`
|
||||
} else if (hours > 0) {
|
||||
return `${hours} 小时前`
|
||||
} else if (minutes > 0) {
|
||||
return `${minutes} 分钟前`
|
||||
} else {
|
||||
return '刚刚'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exam status badge color
|
||||
*/
|
||||
export const getStatusColor = (status) => {
|
||||
const colors = {
|
||||
pending: 'bg-gray-100 text-gray-800',
|
||||
processing: 'bg-blue-100 text-blue-800',
|
||||
ready: 'bg-green-100 text-green-800',
|
||||
failed: 'bg-red-100 text-red-800'
|
||||
}
|
||||
return colors[status] || 'bg-gray-100 text-gray-800'
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exam status text
|
||||
*/
|
||||
export const getStatusText = (status) => {
|
||||
const texts = {
|
||||
pending: '等待中',
|
||||
processing: '处理中',
|
||||
ready: '就绪',
|
||||
failed: '失败'
|
||||
}
|
||||
return texts[status] || status
|
||||
}
|
||||
|
||||
/**
|
||||
* Get question type text
|
||||
*/
|
||||
export const getQuestionTypeText = (type) => {
|
||||
const texts = {
|
||||
single: '单选题',
|
||||
multiple: '多选题',
|
||||
judge: '判断题',
|
||||
short: '简答题'
|
||||
}
|
||||
return texts[type] || type
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate progress percentage
|
||||
*/
|
||||
export const calculateProgress = (current, total) => {
|
||||
if (total === 0) return 0
|
||||
return Math.round((current / total) * 100)
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate file type
|
||||
*/
|
||||
export const isValidFileType = (filename) => {
|
||||
const allowedExtensions = ['txt', 'pdf', 'doc', 'docx', 'xlsx', 'xls']
|
||||
const extension = filename.split('.').pop().toLowerCase()
|
||||
return allowedExtensions.includes(extension)
|
||||
}
|
||||
|
||||
/**
|
||||
* Format file size
|
||||
*/
|
||||
export const formatFileSize = (bytes) => {
|
||||
if (bytes === 0) return '0 Bytes'
|
||||
const k = 1024
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate text
|
||||
*/
|
||||
export const truncateText = (text, maxLength = 100) => {
|
||||
if (text.length <= maxLength) return text
|
||||
return text.substring(0, maxLength) + '...'
|
||||
}
|
||||
Reference in New Issue
Block a user