前段时间冲浪的时候发现了一个五笔字根练习的小项目(yhf7952/WubiGame),感觉还挺有意思,遂抄。它本身只是一个前端小项目,是原作者学习 Vue 的时候写的第一个项目。那我也就顺便用这个项目学习一点 Vue 的知识喽。
原项目的二级简码表有一些缺漏和错字,我进行了编辑修改。实际上由于不同输入法的实现和码表存在差异,因此二级简码有部分码字是不同的。一些表的部分二级简码对应优先对应词组,一些简码理论上能打出单字却没有对应码字(空码),因而我也无法给出完全正确的二级简码表。最后我综合几个码表,选择了空码最多(也即和其他码表定义冲突最小)的码表。
原项目显示字根采用了特殊字体。通过引用字体中字根对应的字符,即可实现字根显示(和图标字体显示应该是同样的原理)。但是字体中的字根也存在错误,需要修改。在线查看字体,可访问该网站,字体编辑可使用 FontCreator。
在原仓库的基础上
Vue2
升级到 Vue3
element
升级到 element-plus
。TypeScript
代替 js
Pug
代替 html
。tsimport { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import ElementPlus from 'unplugin-element-plus/vite'
// https://vite.dev/config/
export default defineConfig({
base: '/c/wubi/',
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
ElementPlus({}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
由于部署的生产环境使用的是子路径( https://vanblog.zerolacqua.top/c/wubi/ ),因此设置了 base
项。
unplugin-vue-components
和 unplugin-auto-import
等是用于自动按需导入 Element Plus 组件库的。
老项目运行 vue-cli-service serve
时报错
Error: error:0308010C:digital envelope routines::unsupported at new Hash (node:internal/crypto/hash:79:19) at Object.createHash (node:crypto:139:10) at module.exports
原因是项目依赖了过时的 SSL。
使用 pug
作为模板时,使用 <script setup>
检查不到 <templete lang="pug">
中使用的组件和变量,报 no-unused-vars
错误。
解决方式 1:切换到 html
模板
解决方式 2:眼不见心不烦,变量未使用不会有警告
js// eslint.config.js
export default [
{
rules: {
'@typescript-eslint/no-unused-vars': 'off',
},
},
]
json// tsconfig.json
{
"compilerOptions": {
"noUnusedLocals": true,
},
}
解决方案参见:https://github.com/vuejs/language-tools/issues/47
库的变化
要引入新的组件库,参考:https://element-plus.org/zh-CN/guide/quickstart.html
兄弟组件之间通信一般采用全局组件通信的方案,并不推荐借助父组件中转的通信。
常用的全局组件通信方法包括:
由于本项目是一个很小的 demo,我采用了 Reactive State 方案,简单易懂:
ts// src/store.ts
import { reactive } from 'vue'
export interface Store {
statData: StatData,
incrementRightTimes(): void,
incrementTotalTimes(): void,
setStatData(statData: StatData): void,
resetStatData(): void,
}
export interface StatData {
rightTimes: number,
totalTimes: number,
startDate: Date,
}
export const store: Store = reactive({
statData: {
rightTimes: 0,
totalTimes: 0,
startDate: new Date(),
},
incrementRightTimes() {
store.statData.rightTimes++
},
incrementTotalTimes() {
store.statData.totalTimes++
},
setStatData(statData: StatData) {
store.statData = statData
},
resetStatData() {
store.statData = {
rightTimes: 0,
totalTimes: 0,
startDate: new Date(),
}
},
})
在 src
目录下创建一个状态中心,设置若干需要使用的属性,并添加更新数据的方法。
在需要更改状态的组件中调用更新方法:
ts// GameComponent.vue
// input 事件
function onInput(str: string) {
const input = str.toLowerCase()
switch (props.gameMode) {
case GameMode.zigen:
case GameMode.yiji:
if (input === d1.value![0]) {
getData()
retryTimes.value = 0
isRight.value = true
store.incrementRightTimes()
} else {
isRight.value = false
retryTimes.value++
if (retryTimes.value >= 3) {
ElMessage({
message: `错了哦,正确答案是:${d1.value![0].toUpperCase()}`,
grouping: true,
type: 'error',
})
}
}
store.incrementTotalTimes()
intext.value = ''
return
case GameMode.erji:
if (input === d1.value![1]) {
getData()
store.incrementRightTimes()
isRight.value = true
} else {
isRight.value = false
}
store.incrementTotalTimes()
intext.value = ''
return
default:
throw new Error('GameMode error')
}
}
function reset() {
d1.value = undefined
d2.value = undefined
d3.value = undefined
d4.value = undefined
d5.value = undefined
getData()
store.resetStatData()
isRight.value = true
document.getElementById('intext')!.focus()
}
而后在需要获取状态的组件中进行读取:
ts// StatComponent.vue
const rightTimes = computed(() => store.statData.rightTimes)
const totalTimes = computed(() => store.statData.totalTimes)
const startDate = computed(() => store.statData.startDate)
不同的通信方案可参考:https://vue3.chengpeiquan.com/communication.html
使用基于断点的隐藏类,以及组件的响应式属性(xs、sm、md、lg 和 xl),当尺寸在不同大小时,展示不同的卡片数量:
pugdiv //- 根据大小屏幕显示不同的 gutter el-row( :class="{ zigen: gameMode == GameMode.zigen }" class="box" :gutter= 15 style="margin-top: 50px" ) el-col(:xs="3" :sm="3" :md="2" :lg="2" :xl="2") el-col(:xs="6" :sm="6" :md="4" :lg="4" :xl="4") el-card( shadow="always" :class="isRight ? 'boderNormal' : 'boderError'" ) | {{ d1[1] }} el-col(:xs="6" :sm="6" :md="4" :lg="4" :xl="4") el-card(shadow="always") | {{ d2[1] }} el-col(:xs="6" :sm="6" :md="4" :lg="4" :xl="4") el-card(shadow="always") | {{ d3[1] }} el-col.hidden-sm-and-down(:span="4" ) el-card(shadow="always") | {{ d4[1] }} el-col.hidden-sm-and-down(:span="4" ) el-card(shadow="always") | {{ d5[1] }} el-col(:xs="6" :sm="3" :md="2" :lg="2" :xl="2")
尽管采用了自动按需导入 Element Plus 的方案,但若想使用基于断点的隐藏类 hidden-sm-and-down
还是需要引入文件:
tsimport 'element-plus/theme-chalk/display.css'
使用媒体查询,当尺寸缩放过小时,调整字体卡片的大小:
css/* xs 以上 */
@media screen {
.box {
font-size: 1cm;
text-align: center;
line-height: 45px;
}
}
/* xs 以下 */
@media screen and (max-width: 300px) {
.box {
font-size: 0.6cm;
text-align: center;
line-height: 15px;
}
}
菜单栏对齐
删除菜单栏的 border-bottom
并调整颜色,同时需要注意 <style>
有不同的作用域。
页脚置底
el-container
的 display
是 flex
,故可以
css.el-container {
min-height: 100vh;
}
目前项目已归档,不会再做更改
草草写了一篇博文进度记录,细致度和上一篇差很多。中间拖了太久,甚至后面那个 next.js
项目也完成了,导致遗忘了不少细节,希望下次能边学习边记录吧。
本文作者:Zerol Acqua
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!