Skip to content

Commit

Permalink
feat: dozens of things
Browse files Browse the repository at this point in the history
  • Loading branch information
yb6b committed Mar 19, 2024
1 parent 551dc52 commit 4d0eded
Show file tree
Hide file tree
Showing 14 changed files with 704 additions and 44 deletions.
6 changes: 4 additions & 2 deletions components/search/FetchSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ const p = defineProps<{
compFont?: string
/** 方案的ID */
id: string
/** 自定义字根转编码时的规则 */
dasm?: (comp: string[], compDict: Record<string, string>) => string
}>()
provide('font', p.compFont)
// 一丨丿丶乙
provide('high', p.id.includes('easy')?'':'⼀⼂⺂⼁⼃')
provide('high', p.id.includes('easy') ? '' : '⼀⼂⺂⼁⼃')
const schemaData = shallowRef<{
compDict: Record<string, string>,
hanziDict: Record<string, string | Array<string>>
Expand All @@ -42,5 +44,5 @@ onMounted(async () => {

<template>
<div class="text-gray-600" v-if="!schemaData">正在加载拆分数据……</div>
<Search v-else :compFont :hanziDict="schemaData.hanziDict" :compDict="schemaData.compDict" />
<Search v-else :hanziDict="schemaData.hanziDict" :compDict="schemaData.compDict" />
</template>
4 changes: 3 additions & 1 deletion components/search/Search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const p = defineProps<{
hanziDict: Record<string, string | Array<string>>
/** 字根到按键的映射 */
compDict?: Record<string, string>
/** 自定义字根转编码时的规则 */
dasm?: (comp: string[], compDict: Record<string, string>) => string
}>()
const urlSearchParams = useUrlSearchParams()
Expand All @@ -37,7 +39,7 @@ const textToResult = (text: string): Result => [...text]
.filter(z => z in p.hanziDict)
.map(zi => {
const comps = [...p.hanziDict[zi]]
const keys = comps.map(c => p.compDict[c])
const keys = p.dasm ? [...p.dasm(comps, p.compDict)] : comps.map(c => p.compDict[c])
return [zi, comps, keys] as const
})
Expand Down
70 changes: 70 additions & 0 deletions components/train/HanziAnki.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<script setup lang="ts">
/** 单字练习 */
import { shallowRef, onMounted, provide } from "vue";
import { ChaiCard, ZigenCard, cache, fetchJsonWithCache } from "./share";
import Train from "./anki/TrainAnki.vue";
const p = defineProps<{
/** 卡片组的名字 */
name: string,
/** 单字练习的数据的JSON文件的路径,要加 / */
chaiJson: string
/** 练习的范围,从第几条到第几条,不填则是全部 */
range?: [start: number, end: number]
/** 字根练习的数据JSON文件的路径,如果chaiJson里只有拆分没有编码,则必须提供字根数据 */
zigenJson?: string,
/** 字根的字体CSS名称 */
fontClass?: string
}>()
provide("font", p.fontClass)
let cardsName = p.name + '_single'
const range = p.range
if (range) {
cardsName += `_${range[0]}_${range[1]}`
}
const cards = shallowRef<ChaiCard[]>(cache[cardsName])
onMounted(async () => {
/** 初始化时候,要处理请求json数据、截断数据、补齐编码字段 */
if (cards.value) return;
let chaifenCards: ChaiCard[] = await fetchJsonWithCache(p.chaiJson)
if (range) {
chaifenCards = chaifenCards.slice(range[0], range[1])
}
// 填上编码信息
if (p.zigenJson) {
const zigenCard = await fetchJsonWithCache(p.zigenJson) as ZigenCard[]
const zigenKeyMap = new Map(zigenCard.map(v => [v.name, v.key]))
for (const e of chaifenCards) {
if (e.key) continue
if (!e.comp) {
const msg = `${p.zigenJson} 文件里 ${e} 没有comp字段也没有key字段`
alert(msg)
throw new Error(msg)
}
e.key = [...e.comp].map(gen => zigenKeyMap.get(gen) || '').join('')
}
}
cache[cardsName] = chaifenCards
return cards.value = chaifenCards
})
</script>

<template>
<Train v-if="cards" :name="cardsName" :cards />
<h2 class="text-gray-700" v-else>
下载数据中……
</h2>
</template>./TrainAnki2.vue
68 changes: 34 additions & 34 deletions components/train/TrainCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,48 +86,48 @@ watch(progress, async (newV, oldV) => {
<progress class="progress w-full" :value="progress" :max="cards.length" />
</div>
<template v-if="!showConfetti">
<div class="flex justify-around mb-8">

<div class="w-40 flex justify-center h-28 p-5 overflow-hidden">
<Transition enterActiveClass="animate__animated animate__bounceInLeft"
leaveActiveClass="animate__animated animate__bounceOutRight" mode="out-in">
<div :key="card.name"
:class="['text-6xl animate__animated', 'kaiti-font', fontClass, { 'text-red-400': !isCorrect, 'animate__headShake': !isCorrect }]">
{{ card.name }}</div>
</Transition>
</div>
<slot :card>
<div class="flex justify-around mb-8">

<div class="flex flex-col" v-if="'rel' in card || 'kind' in card">
<div :key="card.name"
:class="['text-6xl animate__animated', 'kaiti-font', fontClass, { 'text-red-400': !isCorrect, 'animate__headShake': !isCorrect }]">
{{ card.name }}</div>

<div class="flex tracking-widest flex-col opacity-70" v-if="'rel' in card">
<div class="flex flex-col" v-if="'rel' in card || 'kind' in card">

<div class="text-gray-500 text-sm">
相关的字:
</div>
<div class="flex tracking-widest flex-col opacity-70" v-if="'rel' in card">

<div>
{{ card.rel }}
<div class="text-gray-500 text-sm">
相关的字:
</div>

<div>
{{ card.rel }}
</div>
</div>
</div>

<div class=" tracking-widest pt-6 text-blue-600 dark:text-blue-300"
v-if="'kind' in card && card.kind == 'b'">
五个基础笔画
</div>
<div class=" tracking-widest pt-6 text-blue-600 dark:text-blue-300"
v-if="'kind' in card && card.kind == 'eb'">
25个二笔小码
<div class=" tracking-widest pt-6 text-blue-600 dark:text-blue-300"
v-if="'kind' in card && card.kind == 'b'">
五个基础笔画
</div>
<div class=" tracking-widest pt-6 text-blue-600 dark:text-blue-300"
v-if="'kind' in card && card.kind == 'eb'">
25个二笔小码
</div>
</div>
</div>
</div>
<div class="flex justify-center p-5">
<input id="input_el" type="text" placeholder="输入字根编码" v-model="userKeys"
:class="['input w-half max-w-xs input-bordered text-center input-sm dark:bg-slate-800 bg-white', { 'input-error': !isCorrect }]" />
</div>
<div :class="['text-center', { 'opacity-0': !isFirstLearn }]">答案是 <b class="font-mono">{{ card.key }}</b>
<span :class="[fontClass]" v-if="'comp' in card">({{ card.comp }})</span>
</div>
<div class="flex justify-center p-5">
<input id="input_el" type="text" placeholder="输入编码" v-model="userKeys"
:class="['input w-half max-w-xs input-bordered text-center input-sm dark:bg-slate-800 bg-white', { 'input-error': !isCorrect }]" />
</div>
<div :class="['text-center', { 'opacity-0': !isFirstLearn }]">答案是 <b class="font-mono">{{ card.key
}}</b>
<span :class="[fontClass]" v-if="'comp' in card">({{ card.comp }})</span>
</div>
</slot>
</template>


<template v-else>
<div class="p-10 text-6xl text-center font-bold text-orange-800 font-sans tracking-widest -rotate-6">
🎉恭喜完成练习!!</div>
Expand All @@ -140,6 +140,6 @@ watch(progress, async (newV, oldV) => {

<div class="text-gray-500 flex justify-between">
<div>训练进度:{{ progress }} / {{ cards.length }}</div>
<button class="btn btn-ghost btn-sm" @click="_ => restart()">restart</button>
<button class="btn btn-ghost btn-sm font-light" @click="_ => restart()">restart</button>
</div>
</template>
102 changes: 102 additions & 0 deletions components/train/anki/TrainAnki.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<script setup lang="ts">
import { shallowRef, watch, onMounted, inject, nextTick } from "vue";
import { useAnki } from "./useAnki";
import { Card, startConfette } from "../share";
const p = defineProps<{
/** 复习卡片的数据 */
cards: Card[]
/** 复习卡片的名字 */
name: string
}>()
const fontClass = inject('font')
const {
progress,
card,
answer,
restart,
} = useAnki(p.cards, p.name)
const isCorrect = shallowRef(true)
const userKeys = shallowRef('')
// 聚焦输入框
onMounted(() => {
const element = document.getElementById('input_el')
element?.focus()
})
watch(userKeys, (newKeys) => {
// 多个编码没有打完就不提示错误
if (newKeys.length < card.value!.key.length)
return
// 检查回答
if (newKeys === card.value.key) {
answer(isCorrect.value)
isCorrect.value = true
} else {
isCorrect.value = false
}
// 清空输入
userKeys.value = ''
})
// 烟花效果
const showConfetti = shallowRef(false)
watch(progress, async (newV, oldV) => {
const newLearn = newV.meet
const oldLearn = oldV.meet
if (newLearn === p.cards.length && newLearn > oldLearn) {
showConfetti.value = true
await nextTick()
startConfette()
}
})
</script>

<template>
<div
:class="['md:w-2/3 w-full shadow-sm my-12 pb-24 bg-opacity-10 transition-color rounded-md', { 'bg-red-700': !isCorrect, 'bg-slate-500': isCorrect }]">
<div class="flex justify-center mb-24">
<progress class="progress w-full" :value="progress.familiar" :max="cards.length" />
</div>
<template v-if="!showConfetti">
<div class="flex justify-around mb-8">

<div :key="card.name"
:class="['text-6xl animate__animated', 'kaiti-font', { 'text-red-400': !isCorrect, 'animate__headShake': !isCorrect }]">
{{ card.name }}</div>


</div>
<div class="flex justify-center p-5">
<input id="input_el" type="text" placeholder="输入编码" v-model="userKeys"
:class="['input w-half max-w-xs input-bordered text-center input-sm dark:bg-slate-800 bg-white', { 'input-error': !isCorrect }]" />
</div>
<div :class="['text-center', { 'opacity-0': isCorrect }]">答案是 <b class="font-mono">{{ card.key }}</b>
<span :class="[fontClass]" v-if="'comp' in card">({{ card.comp }})</span>
</div>
</template>

<template v-else>
<div class="p-10 text-6xl text-center font-bold text-orange-800 font-sans tracking-widest -rotate-6">
🎉恭喜过关!!</div>
<div class="flex justify-center mt-10">
<button class="btn btn-success" @click="_ => showConfetti = false">继续练习</button>
</div>
</template>

</div>

<div class="text-gray-500 flex justify-between">
<div class="text-gray-500">训练进度: {{ progress.meet }} / {{ cards.length }}</div>
<button class="btn btn-ghost btn-sm font-light" @click="_ => restart()">restart</button>
</div>
</template>
Loading

0 comments on commit 4d0eded

Please sign in to comment.