2026-05-09 11:46:24 +08:00

1127 lines
31 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts" setup>
import wordTableApi from '@/api/subject/wordTable'
import type { Word, Data, BaseSelect, NavListType, TextBook, TextBookData } from './interface'
import AllBooksSeleton from './components/AllBookSeleton.vue'
import useReadLoading from './hooks/useReadLoading'
import { nextTick, watch } from 'vue'
import Select from './components/Select.vue'
import Dropdown from './components/Dropdown.vue'
import WordPopup from './components/WordPopup.vue'
import AdaptiveText from './test/components/AdaptiveText.vue'
import { storeToRefs } from 'pinia'
import { useLearnWordStore } from '@/stores/learnWord/useLearnWordStore'
import useExam from './test/hooks/useExam'
import { useRoute, useRouter } from 'vue-router'
import { userStore } from '@/stores'
import hud from '@/utils/hud'
import { familiarity_know_well, familiarity_master } from '.'
import dict from '@/utils/dict'
defineOptions({ name: 'WordTable' })
const OSS_URL = import.meta.env.VITE_OSS_URL
const empty_gril = `${OSS_URL}/english_client/normalMode/empty_gril.png`
const nav_bg = `url(${OSS_URL}/english_client/normalMode/nav_bg.png)`
const back_btn = `url(${OSS_URL}/english_client/normalMode/back_btn.png)`
const { userInfo } = storeToRefs(userStore())
// 参数
const route = useRoute()
const { stageId, versionId, bookId, familiarity } = route.query
const router = useRouter()
const state = useLearnWordStore()
const isLoading = ref(false) // 书籍加载
const isLoading2 = ref(false) // 触底加载延时
const readLoading = useReadLoading() // 分级阅读加载
// 全部图书骨架屏
const allBooksSkeleton = ref({
allLoading: true,
})
const showSeletonTimer = ref() // 骨架屏定时器
const showDetail = ref(false) // 弹框
const pupData = ref<Word>()
const cardListRef = ref()
const scrollBoxRef = ref()
/* 阶段列表 */
const stageList = ref([] as Data[])
const stageActive = ref('' as any)
// 是否处于掌握的界面
const isMaster = computed(
() =>
(activeNav.value.type === nav_stage && familiarityActive.value === familiarity_master) ||
(activeNav.value.type === nav_book && textBookFamiliarityActive.value === familiarity_master),
)
const familiarityList = ref([
{
key: '',
value: '全部',
count: '',
config: 'wordCount',
},
{
key: '0',
value: '未解锁',
count: '',
config: 'wordUnlockCount',
},
{
key: '1',
value: '陌生',
count: '',
config: 'wordStrangerCount',
},
{
key: '2',
value: '认识',
count: '',
config: 'wordKnowCount',
},
{
key: familiarity_know_well,
value: '熟悉',
count: '',
config: 'wordKnowWellCount',
},
{
key: familiarity_master,
value: '掌握',
count: '',
config: 'wordGraspCount',
},
] as Data[])
const familiarityActive = ref('' as any)
/* 教材---阶段列表 */
const textBookStageList = ref([] as Data[])
const textBookStageActive = ref('' as any)
function isSameValue(a: any, b: any) {
if ((a === undefined || a === null || a === '') && (b === undefined || b === null || b === '')) {
return true
}
return String(a) === String(b)
}
async function loadVersionsByStage(stageId: any) {
if (!stageId) return
const currentStageId = stageId
const courses = await wordTableApi.getXkwCourseList(stageId)
if (!isSameValue(textBookStageActive.value, currentStageId)) return
const englishCourse =
courses.find((item: any) => item.shortName === '英语' || item.name.includes('英语')) ||
courses[0]
if (!englishCourse) return
const versions = await wordTableApi.getXkwTextbookVersionList(englishCourse.id)
if (!isSameValue(textBookStageActive.value, currentStageId)) return
versionList.value = versions.map((item: BaseSelect) => ({
key: item.id,
value: item.name,
})) as any
}
watch(() => textBookStageActive.value, loadVersionsByStage)
/* 教材---版本列表 */
const versionList = ref([] as Data[])
const versionActive = ref('' as any)
async function loadTextbooksByVersion(versionId: any) {
if (!versionId) return
const currentVersionId = versionId
const textbooks = await wordTableApi.getXkwTextbookList(versionId)
if (!isSameValue(versionActive.value, currentVersionId)) return
textBookList.value = textbooks.map((item: any) => ({
key: item.id,
value: item.volume,
})) as any
}
watch(() => versionActive.value, loadTextbooksByVersion)
/* 教材列表 */
const textBookList = ref([] as TextBook[])
const textBookActive = ref('' as any)
watch(
() => textBookActive.value,
nv => {
const textBook = textBookList.value.find((item: TextBook) => item.key === nv)
wordsInfo.all = textBook?.singleWordCount || 0
wordsInfo.learned = textBook?.learnWordCount || 0
},
)
const textBookFamiliarityActive = ref('' as any)
// 章节
const chapterList = ref([] as any[])
const chapterActive = ref('' as any)
watch(
() => textBookActive.value,
async nv => {
if (!nv) return
const currentTextbookId = nv
const list = await wordTableApi.getXkwTextbookCatalogList(nv)
if (!isSameValue(textBookActive.value, currentTextbookId)) return
chapterList.value = [
{ key: undefined, value: '全部' },
...list.map((item: any) => ({ key: item.id, value: item.name })),
]
},
)
// 模块
const nav_stage = 1
const nav_book = 2
const navList = [
{
name: '阶段',
type: nav_stage,
filterParams: reactive({
vocabularyId: stageActive,
grasp: familiarityActive,
}),
getData: wordTableApi.getWordTableStageList,
},
{
name: '教材',
type: nav_book,
filterParams: reactive({
chapterId: chapterActive,
textbookId: textBookActive,
grasp: textBookFamiliarityActive,
}),
getData: wordTableApi.getTextbookWord,
},
]
const activeNav = ref(navList[0] as NavListType)
const wordList = ref<Word[]>([])
const filterReady = ref(false)
const allBookPage = ref({
current: 1,
size: 30,
total: 0,
totalPage: 0,
})
const scrollHeight = ref(0) // 书籍滚动高度
const wordsInfo = reactive({
learned: 0,
all: 0,
})
const exam = useExam()
// 返回首页
function tapBackBtn() {
/* 关卡列表可能已经变成教材页的列表,需重新刷新 */
state.refreshLevelList = true
state.isBook = false
// uni.navigateBack();
router.back()
}
// 模块切换
function tapSwitchNav(i: NavListType) {
if (fixedBook.value || fixedStage.value) {
showFixedTips()
return
}
activeNav.value = i
saveUserFilter()
}
function jump() {
const bookMode = activeNav.value.type === nav_book
const params = {
size: 20,
testType: bookMode ? 3 : 4,
wordGraspFlag: bookMode ? textBookFamiliarityActive.value : familiarityActive.value,
textbookId: bookMode ? textBookActive.value : null,
chapterId: bookMode ? chapterActive.value : null,
vocabularyId: bookMode ? null : stageActive.value,
}
let subtitle = ''
const titleSep = '·'
if (bookMode) {
// 初中
const stage = getNameTool(textBookStageList.value, textBookStageActive.value)
// 版本
const version = getNameTool(versionList.value, versionActive.value) || '全部'
// 年级
const grade = getNameTool(textBookList.value, textBookActive.value).replace(
/\\d+\/\d+\/,
'',
)
// 单元
const chapter = getNameTool(chapterList.value, chapterActive.value)
// 熟悉度
const familiarity = getNameTool(familiarityList.value, textBookFamiliarityActive.value)
subtitle = [stage, version, grade, chapter, familiarity].join(titleSep)
} else {
// 阶段
const stage = getNameTool(stageList.value, stageActive.value)
// 熟悉度
const familiarity = getNameTool(familiarityList.value, familiarityActive.value)
subtitle = [stage, familiarity].join(titleSep)
}
const path = `/subject/learnWord/word-table-test`
dict.set(path, {
params,
subtitle,
})
router.push(path)
// exam.wordGraspFlag.value = Number(familiarityActive.value)
// exam.mode.value = activeNav.value.type === 2 ? 3 : 4 // 0: 复习模式, 1随机模式 3: 从教材进去
// exam.wordGrade.value = stageActive.value
// if (activeNav.value.type === 2) {
// exam.teachData.value.textBookFamiliarityActive = textBookFamiliarityActive.value
// exam.teachData.value.textBookId = textBookActive.value
// }
// hud.loading('加载中')
// getWordTestWords({
// size: exam.numOfQuestions.value,
// testType: exam.mode.value,
// wordGraspFlag:
// activeNav.value.type === 2 ? textBookFamiliarityActive.value : familiarityActive.value,
// startTime: new Date().getTime(),
// textbookId: activeNav.value.type === 2 ? textBookActive.value : null,
// chapterId: activeNav.value.type === 2 ? chapterActive.value : null,
// vocabularyId: stageActive.value,
// })
// .then(res => {
// if (!res.chooseTitle && !res.spellTitle) {
// return XMessage.error('暂无单词')
// }
// exam.questionsChooseTitleLib.value = res.chooseTitle
// exam.questionSpellTitleLib.value = res.spellTitle
// exam.questions.total = res.chooseTitle.length + res.spellTitle.length
// let title = ''
// const titleSep = ' · '
// if (activeNav.value.type === 2) {
// // 初中
// const stage = getNameTool(textBookStageList.value, textBookStageActive.value)
// // 版本
// const version = getNameTool(versionList.value, versionActive.value) || '全部'
// // 年级
// const grade = getNameTool(textBookList.value, textBookActive.value).replace(
// /\\d+\/\d+\/,
// '',
// )
// // 单元
// const chapter = getNameTool(chapterList.value, chapterActive.value)
// // 熟悉度
// const familiarity = getNameTool(familiarityList.value, textBookFamiliarityActive.value)
// title = [stage, version, grade, chapter, familiarity].join(titleSep)
// } else {
// // 阶段
// const stage = getNameTool(stageList.value, stageActive.value)
// // 熟悉度
// const familiarity = getNameTool(familiarityList.value, familiarityActive.value)
// title = [stage, familiarity].join(titleSep)
// }
// router.push(`/subject/learnWord/word-table-test?title=${title}`)
// })
// .catch(() => {
// hud.error('网络异常,请稍后重试')
// })
// .finally(() => {
// hud.hideLoading()
// })
}
function getNameTool(list: anyObj[], id: any | number) {
return list.find(item => item.key === id)?.value
}
// 触底滚动加载
function onScrollToLower(e: anyObj) {
if (isLoading.value || isLoading2.value) return
if (allBookPage.value.totalPage >= allBookPage.value.current) {
fetchList()
}
}
// 获取图书筛选条件
async function fetchFilterList() {
try {
/* 阶段---年级 */
const res = await wordTableApi.getWordTableStage()
if (res.data.code === 200) {
let list = res.data.data.map((item: BaseSelect) => ({
key: item.id,
value: item.name,
}))
list.unshift({
key: '',
value: '全部',
})
stageList.value = list
}
/* 教材---阶段 */
const stageRes = await wordTableApi.getXkwStageList()
textBookStageList.value = stageRes.map((item: BaseSelect) => ({
key: item.id,
value: item.name,
}))
// 如果没有已选阶段,默认选第一项并主动触发级联
if (!textBookStageActive.value && textBookStageList.value.length) {
textBookStageActive.value = textBookStageList.value[0].key
}
setTimeout(() => {
allBooksSkeleton.value.allLoading = false
}, 100)
} catch (err) {
console.log(err)
}
}
// 弹出框
function showPup(item: Word) {
if (!item.grasp) return
showDetail.value = true
pupData.value = { ...item }
}
watch(
() => activeNav.value.filterParams,
() => {
if (!filterReady.value) return
resetAndFetchList()
},
{
deep: true,
flush: 'post',
},
)
function resetAndFetchList(needLoading = true) {
wordList.value = []
allBookPage.value.current = 1
fetchList(needLoading)
}
// 获取全部图书
async function fetchList(needLoading = false) {
isLoading2.value = true
if (needLoading) {
isLoading.value = true
}
try {
const res = await activeNav.value.getData({
...allBookPage.value,
...activeNav.value.filterParams,
})
if (res.data.code === 200) {
allBookPage.value.total = res.data.data.totalRows
allBookPage.value.totalPage = res.data.data.totalPage
familiarityList.value.forEach(item => {
item.count = item.config ? res.data.data[item.config] : ''
})
wordList.value.push(...res.data.data.rows)
allBookPage.value.current++
allBooksSkeleton.value.allLoading = false
}
} catch (err) {
console.log(err)
} finally {
isLoading.value = false
isLoading2.value = false
}
}
// async function updata() {
// const pageNum = allBookPage.value.current
// await nextTick()
// wordList.value = []
// allBookPage.value.current = 1
// for (let i = 1; i < pageNum; i++) {
// await fetchList()
// }
// }
function saveUserFilter() {
const saveData = {
activeNavType: activeNav.value.type,
familiarityActive: familiarityActive.value,
stageActive: stageActive.value,
textBookStageActive: textBookStageActive.value,
versionActive: versionActive.value,
textBookActive: textBookActive.value,
chapterActive: chapterActive.value,
textBookFamiliarityActive: textBookFamiliarityActive.value,
}
wordTableApi.saveUserWordFilterRecord({
userId: userInfo.value.userId,
wordTestConfig: JSON.stringify(saveData),
})
}
async function initUserFilter() {
// 获取用户筛选条件记录
return wordTableApi
.getUserWordFilterRecord({
userId: userInfo.value.userId,
})
.then((res: any) => {
const config = res.data.data.wordTestConfig
if (config) {
const data = JSON.parse(config)
if (data.activeNavType === nav_book) {
activeNav.value = navList[1]
} else if (data.activeNavType === nav_stage) {
activeNav.value = navList[0]
}
familiarityActive.value = data.familiarityActive
stageActive.value = data.stageActive
textBookStageActive.value = data.textBookStageActive
versionActive.value = data.versionActive
textBookActive.value = data.textBookActive
chapterActive.value = data.chapterActive
textBookFamiliarityActive.value = data.textBookFamiliarityActive
}
})
.catch((err: any) => {
console.log(err)
})
}
// 是否固定教材,不让修改
const fixedBook = ref(false)
// 是否固定阶段,不让修改
const fixedStage = ref(false)
function showFixedTips() {
hud.error('请您专心完成导学任务哦,加油!')
}
onMounted(async () => {
if (bookId) {
// 通过路由参数来初始化
activeNav.value = navList[1]
textBookStageActive.value = stageId
versionActive.value = versionId
textBookActive.value = bookId
textBookFamiliarityActive.value = familiarity
fixedBook.value = true
saveUserFilter()
} else if (familiarity) {
familiarityActive.value = familiarity
if (stageId) {
stageActive.value = stageId
fixedStage.value = true
}
} else {
await initUserFilter()
}
await fetchFilterList()
await nextTick()
filterReady.value = true
resetAndFetchList()
// await Promise.all([initUserFilter(), fetchFilterList()]);
// fetchList()
// updata();
})
onUnmounted(() => {
clearTimeout(showSeletonTimer.value)
readLoading.clearLoadingTimer()
})
</script>
<template>
<div class="page__container">
<div id="page_box" class="page__box">
<div id="navigationBox" class="navigation">
<div
v-for="i in navList"
:key="i.type"
:class="{ inactivity: true, active: i.type === activeNav.type }"
@click="tapSwitchNav(i)"
>
<span class="name">{{ i.name }}</span>
</div>
</div>
<div
class="back-btn"
:style="{ position: activeNav.type === nav_stage ? 'fixed' : 'absolute' }"
@click="tapBackBtn"
></div>
<div id="books" class="all_books">
<template v-if="allBooksSkeleton.allLoading">
<!-- 骨架屏 -->
<AllBooksSeleton />
</template>
<template v-else>
<div id="filterBox" class="filter_box">
<div class="filter_list">
<!-- 阶段筛选项 -->
<div v-show="activeNav.type === nav_stage" class="filter_bar">
<div class="filter_item">
<div class="label">年级</div>
<Select
v-model="stageActive"
:disabled="fixedStage"
:list="stageList"
@onChange="saveUserFilter"
@disabled-click="showFixedTips"
/>
</div>
<div class="filter_item">
<div class="label">熟悉度</div>
<Select
v-model="familiarityActive"
:list="familiarityList"
is-colored
@onChange="saveUserFilter"
/>
</div>
</div>
<!-- 教材筛选项 -->
<div v-show="activeNav.type === nav_book" class="filter_bar filter_bar_textbook">
<div class="books">
<Dropdown
v-model="textBookStageActive"
:menu="textBookStageList"
:disabled="fixedBook"
@changeValue="saveUserFilter"
@disabled-click="showFixedTips"
/>
<Dropdown
v-model="versionActive"
:menu="versionList"
:disabled="fixedBook"
@changeValue="saveUserFilter"
@disabled-click="showFixedTips"
/>
<Dropdown
v-model="textBookActive"
:menu="textBookList"
:disabled="fixedBook"
@changeValue="saveUserFilter"
@disabled-click="showFixedTips"
/>
<Dropdown
v-model="chapterActive"
:menu="chapterList"
:disabled="fixedBook"
@changeValue="saveUserFilter"
@disabled-click="showFixedTips"
/>
</div>
<div class="filter_item">
<!-- <div class="label">熟悉度</div> -->
<Select
v-model="textBookFamiliarityActive"
:list="familiarityList"
is-colored
@onChange="saveUserFilter"
/>
</div>
</div>
<!-- <div class="books_num">数量{{ allBookPage.total }}</div> -->
</div>
</div>
<div class="books_box">
<div v-if="isLoading" class="shelf">
<div class="loading">
<img :src="readLoading.getLoading()" />
<text>正在加载中...</text>
</div>
</div>
<van-list
v-else
ref="scrollBoxRef"
class="scroll_box hidden_scroll"
offset="50"
@load="onScrollToLower"
>
<!-- 加载动效 -->
<!-- 空状态 -->
<template v-if="allBookPage.total === 0">
<div class="flex-col-center empty">
<img :src="empty_gril" />
<span>暂无单词</span>
</div>
</template>
<!-- 非空状态 -->
<template v-else>
<div ref="cardListRef" class="card_list">
<!-- 单词卡片 -->
<div
v-for="item in wordList"
:key="item.wordId"
:class="['card', `familiarity_${item.grasp + 1}`]"
@click="showPup(item)"
>
<div class="word">{{ item.spell }}</div>
<div class="chinese">
<AbbrImg :abbr="item.typeAbbr" gap="8"></AbbrImg>
<AdaptiveText
:text="item.simpleDesc"
:defaultFontSize="20"
font-family="PingFang SC"
wrap
:lineWidth="218"
:maxNumRows="3"
/>
</div>
<div v-if="isMaster" class="review-count">
<span class="review-success">{{ item.reviewSuccessful }}</span>
<span class="review-failure">{{ item.reviewFailed }}</span>
</div>
</div>
</div>
<span
v-if="scrollHeight"
class="t-icon t-icon-huidaodingbu scroll_top_icon"
@click="() => (scrollHeight = 0)"
></span>
</template>
</van-list>
<div v-show="wordList.length" class="btn pointer" @click="jump">开始学习</div>
</div>
</template>
</div>
</div>
<WordPopup v-model="showDetail" :pupData="pupData || {}"></WordPopup>
</div>
</template>
<style scoped lang="scss">
@use './style/familiarityColor.scss' as *;
* {
box-sizing: border-box;
}
.flex-col-center {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.page__container {
width: 100vw;
height: 100%;
z-index: 1;
background-color: #dfdaf2;
.over_filter {
position: fixed;
top: 0;
z-index: 99;
left: 50%;
transform: translate(-25%);
width: 100%;
.condition {
padding: 1.33vh 76px;
margin-right: 160px;
white-space: nowrap;
border: 0.33vh solid #fffef2;
background-color: #fff01f;
border-radius: 0 0 1.67vh 1.67vh;
font-size: 2.67vh;
line-height: 4vh;
color: rgba(0, 0, 0, 0.75);
&:last-child {
margin-right: 0;
}
}
}
.page__box {
padding-top: 3.33vh;
.back-btn {
left: 30px;
z-index: 999;
cursor: pointer;
width: 10vh;
height: 10vh;
background-image: v-bind(back_btn);
background-repeat: no-repeat;
background-size: cover;
}
.btn__rank {
position: fixed;
left: 32px;
top: 21.67vh;
z-index: 2;
.trophy__box {
width: 120px;
height: 10vh;
display: flex;
align-items: center;
justify-content: center;
border: 0.33vh solid #fffef5;
background: rgba(0, 0, 0, 0.35);
backdrop-filter: blur(3px);
border-radius: 50%;
.trophy {
width: 80px;
height: 6.67vh;
}
}
.btn__rank__text {
box-sizing: content-box;
position: relative;
width: 120px;
text-align: center;
margin-top: -1.83vh;
font-size: 0;
z-index: 1;
span {
color: #fffef5;
text-align: center;
font-family: $font-text;
font-size: 2.33vh;
text-shadow:
-2px -2px 0 #6a3906,
2px -2px 0 #6a3906,
-2px 2px 0 #6a3906,
2px 2px 0 #6a3906;
/* 设置描边效果 */
}
img {
width: 82px;
height: 2.08vh;
}
}
}
.navigation {
display: flex;
position: absolute;
left: 50%;
transform: translate(-50%);
z-index: 99;
.inactivity {
display: flex;
justify-content: center;
padding: 1.33vh 0 0.67vh 0;
margin-right: 56px;
width: 212px;
height: 6vh;
line-height: 4vh;
font-size: 2.33vh;
font-family: $font-special;
background-color: #fff78c;
border: 0.33vh solid #d9d163;
border-radius: 1.67vh 1.67vh 0 0;
box-sizing: border-box;
color: rgba(0, 0, 0, 0.45);
cursor: pointer;
}
.active {
background-color: #fffef2;
border-bottom: 0.33vh solid #fffef2;
color: rgba(0, 0, 0, 0.75);
}
}
.all_books {
position: relative;
width: 1920px;
margin: 0 auto;
padding: 4.83vh 30px 0 30px;
height: calc(100vh - 58px);
.filter_box {
position: relative;
z-index: 2;
padding: 0.83vh;
&::before {
content: '';
position: absolute;
top: 0.5vh;
right: -50px;
display: block;
width: 1860px;
height: 7.83vh;
background-image: v-bind(nav_bg);
background-size: 100% 100%;
background-repeat: no-repeat;
z-index: -1;
}
&::after {
content: '';
position: absolute;
top: 0.5vh;
left: -60px;
display: block;
width: 1860px;
height: 7.83vh;
background-image: v-bind(nav_bg);
background-size: 100% 100%;
background-repeat: no-repeat;
z-index: -1;
}
.filter_list {
position: relative;
padding: 1.83vh 20px 2.5vh;
margin: 0 10px;
// margin-left: 13px;
// width: 1814px;
background-color: #fffef5;
border-radius: 3.33vh;
border: 0.33vh solid #d9d163;
box-sizing: border-box;
.filter_bar {
display: flex;
justify-content: space-around;
align-items: center;
}
.filter_bar_textbook {
padding: 0 22px;
gap: 20px;
.books {
flex: 1;
display: flex;
gap: 20px;
min-width: 0;
:deep(.dropdown__container) {
flex: 1;
min-width: 0;
}
:deep(.dropdown__container:first-child) {
flex: none;
.btn {
width: 150px;
}
}
}
.filter_item {
:deep(.select_item) {
width: 100px;
.name,
.count {
width: 100%;
}
}
}
}
.filter_item {
display: flex;
align-items: center;
color: #6a3906;
font-family: $font-special;
font-size: 2vh;
}
.books_num {
position: absolute;
right: 32px;
bottom: 0;
text-align: right;
font-size: 1.67vh;
color: #736d20;
}
}
}
.books_box {
position: relative;
width: 1756px;
margin: -6.83vh auto 0;
padding: 8.17vh 0 1.33vh;
border-radius: 3.33vh;
background-color: #fffef5;
border: 0.33vh solid #d9d163;
.scroll_box {
height: 76.5vh;
overflow: auto;
}
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
display: none;
}
.scroll_top_icon {
position: fixed;
bottom: 6.67vh;
right: 32px;
width: 96px;
height: 8vh;
cursor: pointer;
background-repeat: no-repeat;
}
.empty {
height: 100%;
color: rgba(0, 0, 0, 0.45);
text-align: center;
font-family: 'PingFang SC';
font-size: 1.67vh;
text-align: center;
img {
width: 240px;
height: 20vh;
margin-bottom: 1.67vh;
}
}
.shelf {
position: relative;
// height: 100%;
height: calc(100vh - 282px);
.loading {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-top: 8.33vh;
img {
width: 240px;
height: 23.33vh;
}
span {
font-size: 2vh;
color: #666;
font-family: $font-text;
}
}
}
.card_list {
margin: 0 41px;
display: flex;
flex-wrap: wrap;
padding-bottom: 11.67vh;
.card {
position: relative;
z-index: 0;
width: 264px;
height: 13.33vh;
padding: 1.67vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow: hidden;
border-radius: 1.67vh;
border: 0.33vh solid $color-familiarity-1;
cursor: pointer;
.review-count {
position: absolute;
top: 0;
left: 0;
right: 0;
display: flex;
justify-content: space-between;
text-align: center;
.review-success {
width: 60px;
color: #fff;
background-color: rgba(105, 191, 59, 0.8);
border-bottom-right-radius: 20px;
}
.review-failure {
width: 60px;
color: #fff;
background-color: rgba(187, 47, 38, 0.8);
border-bottom-left-radius: 20px;
}
}
&:not(:nth-child(6n)) {
margin-right: 16px;
}
&:nth-child(n + 7) {
margin-top: 1.33vh;
}
&.familiarity_1 {
border-color: $color-familiarity-1;
background-color: $bg-familiarity-1;
}
&.familiarity_2 {
border-color: $color-familiarity-2;
background-color: $bg-familiarity-2;
}
&.familiarity_3 {
border-color: $color-familiarity-3;
background-color: $bg-familiarity-3;
}
&.familiarity_4 {
border-color: $color-familiarity-4;
background-color: $bg-familiarity-4;
}
&.familiarity_5 {
border-color: $color-familiarity-5;
background-color: $bg-familiarity-5;
}
.word {
color: $font-color;
text-align: center;
font-family: $font-text;
font-size: 2vh;
font-weight: 900;
}
.chinese {
color: $font-color;
text-align: center;
font-family: $font-text;
font-size: 1.67vh;
font-weight: 600;
:deep(.abbr-box) {
margin: 0.5vh 0 0.83vh;
img {
height: 2.67vh;
vertical-align: middle;
}
}
}
}
}
.btn {
position: absolute;
left: 50%;
bottom: 3.33vh;
transform: translateX(-50%);
border-radius: 4.67vh;
border: 0.33vh solid #6a3906;
background: #fff01f;
padding: 0.83vh 90px;
color: #ffa51f;
text-align: center;
font-family: $font-special;
font-size: 4vh;
}
}
}
}
}
</style>