feat: 扩展动画学科类型、添加Ctrl+K搜索快捷键、无障碍优化
- 前端动画类型选择扩展至12个学科(数学/物理/化学/生物/地理/语文/英语/历史/政治/美术/音乐/通用) - 后端schema验证同步更新支持所有新学科类型 - 扩展inferAnimationType自动推断函数支持更多学科关键词 - 添加Ctrl+K键盘快捷键聚焦全局搜索框 - 搜索框添加键盘快捷键提示徽章 - 添加focus-visible焦点样式和prefers-reduced-motion动画减弱 - 添加text-ellipsis/clamp-2/clamp-3工具类 - 添加smooth scroll平滑滚动
This commit is contained in:
@@ -5,7 +5,7 @@ from typing import Optional, Any
|
||||
|
||||
class AnimationGenerate(BaseModel):
|
||||
prompt: str = Field(..., min_length=1, description="教学动画描述")
|
||||
anim_type: str = Field(default="general", pattern=r"^(math|physics|chemistry|biology|geography|general)$")
|
||||
anim_type: str = Field(default="general", pattern=r"^(math|physics|chemistry|biology|geography|chinese|english|history|politics|art|music|general)$")
|
||||
subject: str = Field(default="", max_length=50)
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
|
||||
<form class="header-center" @submit.prevent="submitSearch">
|
||||
<el-icon><Search /></el-icon>
|
||||
<input v-model="searchText" placeholder="搜索资源、模板或输入知识点快速创作" />
|
||||
<input ref="searchInputRef" v-model="searchText" placeholder="搜索资源、模板或输入知识点快速创作" />
|
||||
<kbd class="kbd-hint">Ctrl K</kbd>
|
||||
<button v-if="searchText" type="button" title="清空搜索" @click="searchText = ''">×</button>
|
||||
</form>
|
||||
|
||||
@@ -78,7 +79,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import { Bell } from '@element-plus/icons-vue'
|
||||
import { getNotifications, getUnreadCount, markRead, markAllRead, type NotificationItem } from '@/api/notification'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
@@ -138,7 +139,17 @@ function formatTime(ts: string | null): string {
|
||||
}
|
||||
const searchText = ref('')
|
||||
|
||||
const searchInputRef = ref<HTMLInputElement>()
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||
e.preventDefault()
|
||||
searchInputRef.value?.focus()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', handleKeydown)
|
||||
fetchUnread()
|
||||
setInterval(fetchUnread, 60000)
|
||||
|
||||
@@ -173,6 +184,11 @@ function submitSearch() {
|
||||
router.push('/search')
|
||||
}
|
||||
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('keydown', handleKeydown)
|
||||
})
|
||||
|
||||
watch(() => route.query.keyword, (keyword) => {
|
||||
searchText.value = typeof keyword === 'string' ? keyword : ''
|
||||
}, { immediate: true })
|
||||
@@ -272,6 +288,19 @@ watch(() => route.query.keyword, (keyword) => {
|
||||
color: #00543d;
|
||||
}
|
||||
|
||||
.kbd-hint {
|
||||
font-size: 10px;
|
||||
font-family: monospace;
|
||||
color: #9aa7a0;
|
||||
background: #fff;
|
||||
border: 1px solid #d6e4d8;
|
||||
border-radius: 4px;
|
||||
padding: 1px 5px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.header-center span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@@ -402,6 +402,57 @@ html, body, #app {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* Focus visible for accessibility */
|
||||
*:focus-visible {
|
||||
outline: 2px solid var(--primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Smooth scroll */
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* Reduce motion for accessibility */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Better link defaults */
|
||||
a {
|
||||
color: var(--primary);
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
a:hover {
|
||||
color: var(--primary-dark);
|
||||
}
|
||||
|
||||
/* Text truncation utility */
|
||||
.text-ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Line clamp utilities */
|
||||
.clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
.clamp-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ===== Print Styles ===== */
|
||||
@media print {
|
||||
.zj-sidebar,
|
||||
|
||||
@@ -53,6 +53,14 @@
|
||||
<el-option label="数学" value="math" />
|
||||
<el-option label="物理" value="physics" />
|
||||
<el-option label="化学" value="chemistry" />
|
||||
<el-option label="生物" value="biology" />
|
||||
<el-option label="地理" value="geography" />
|
||||
<el-option label="语文" value="chinese" />
|
||||
<el-option label="英语" value="english" />
|
||||
<el-option label="历史" value="history" />
|
||||
<el-option label="政治" value="politics" />
|
||||
<el-option label="美术" value="art" />
|
||||
<el-option label="音乐" value="music" />
|
||||
<el-option label="通用" value="general" />
|
||||
</el-select>
|
||||
</div>
|
||||
@@ -547,6 +555,9 @@ function typeLabel(type: string) {
|
||||
chinese: '语文',
|
||||
english: '英语',
|
||||
history: '历史',
|
||||
politics: '政治',
|
||||
art: '美术',
|
||||
music: '音乐',
|
||||
general: '通用',
|
||||
}
|
||||
return map[type] || '通用'
|
||||
@@ -613,9 +624,17 @@ function inferSubject(text: string) {
|
||||
}
|
||||
|
||||
function inferAnimationType(text: string) {
|
||||
if (/数学|函数|几何|圆柱|公式/.test(text)) return 'math'
|
||||
if (/物理|力|电|光/.test(text)) return 'physics'
|
||||
if (/化学|实验|反应/.test(text)) return 'chemistry'
|
||||
if (/数学|几何|方程|圆周|公式|分数|小数/.test(text)) return 'math'
|
||||
if (/物理|力学|电流|光学|磁场|运动/.test(text)) return 'physics'
|
||||
if (/化学|实验|反应|元素|分子/.test(text)) return 'chemistry'
|
||||
if (/生物|细胞|遗传|生态|植物/.test(text)) return 'biology'
|
||||
if (/地理|气候|地形|地图|经度/.test(text)) return 'geography'
|
||||
if (/语文|古诗|文言文|阅读|写作/.test(text)) return 'chinese'
|
||||
if (/英语|单词|语法|English/.test(text)) return 'english'
|
||||
if (/历史|朝代|战争|文物|古代/.test(text)) return 'history'
|
||||
if (/政治|法律|宪法|经济/.test(text)) return 'politics'
|
||||
if (/美术|绘画|色彩/.test(text)) return 'art'
|
||||
if (/音乐|乐器|歌曲|节奏/.test(text)) return 'music'
|
||||
return 'general'
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user