96 UI optimization (#122)

* fix(home): 🐛 解决多次切换路由后页面卡顿的问题 #96

卸载路由时清除requestAnimationFrame

* feat:  文件存储使用Blob格式

* style: 💄 修改部分类型any为具体类型

* feat:  界面设置中模块使用瀑布流布局 #96

* fix: 🐛 md文档更换文件夹解决控制台警告

* style: 💄 switch按钮改回使用daisyui组件

* refactor: ♻️ 所有人员列表提取tableColumn

* style: 💄 奖项列表中的图片类型修复
This commit is contained in:
LOG1997
2025-12-18 17:32:00 +08:00
committed by GitHub
parent 92254cb750
commit 5b8682bb7c
29 changed files with 351 additions and 146 deletions

View File

@@ -42,7 +42,7 @@ async function uploadFile(fileData: IFileData | null) {
function submitUpload() {
if (jsonFileData.value) {
// 把文件转化为json数据
const jsonData = jsonFileData.value.dataUrl
const jsonData = jsonFileData.value.data
console.log('jsonData', jsonData)
props.importAllConfigData(jsonData)
}

View File

@@ -1,19 +1,18 @@
<script setup lang='ts'>
import { useI18n } from 'vue-i18n'
import GridWaterfall from '@/components/Waterfall/index.vue'
import { DataSetting, LayoutSetting, PatternSetting, TextSetting, ThemeSetting } from './parts'
import { useViewModel } from './useViewModel'
const { t } = useI18n()
const { resetData, topTitleValue, languageValue, textSizeValue, currentFontValue, currentTitleFontValue, titleFontSyncGlobalValue, languageList, formErr, formData, cardSizeValue, isShowPrizeListValue, isShowAvatarValue, resetPersonLayout, isRowCountChange, themeValue, backgroundImageValue, cardColorValue, luckyCardColorValue, textColorValue, patternColorValue, imageList, rowCount, cardColor, patternColor, patternList, clearPattern, resetPattern, exportAllConfigData, importAllConfigData } = useViewModel()
</script>
<template>
<div class="flex flex-col gap-4">
<div class="w-4/5 flex flex-col gap-4">
<h2>{{ t('viewTitle.globalSetting') }}</h2>
<div class="flex flex-wrap h-auto w-full gap-6">
<!-- <div class="flex flex-wrap h-auto w-full gap-6"> -->
<GridWaterfall>
<!-- 数据操作 -->
<DataSetting :reset-data="resetData" :export-all-config-data="exportAllConfigData" :import-all-config-data="importAllConfigData" />
<!-- 文本设置主标题语言文字大小 -->
@@ -55,7 +54,8 @@ const { resetData, topTitleValue, languageValue, textSizeValue, currentFontValue
:clear-pattern="clearPattern"
:reset-pattern="resetPattern"
/>
</div>
</GridWaterfall>
<!-- </div> -->
</div>
</template>

View File

@@ -17,32 +17,32 @@ const uploadVisible = ref(false)
</script>
<template>
<dialog id="my_modal_1" ref="resetDataDialogRef" class="border-none modal">
<div class="modal-box">
<h3 class="text-lg font-bold">
{{ t('dialog.titleTip') }}
</h3>
<p class="py-4">
{{ t('dialog.dialogResetAllData') }}
</p>
<div class="modal-action">
<form method="dialog" class="flex gap-3">
<!-- if there is a button in form, it will close the modal -->
<button class="btn" @click="resetDataDialogRef.close()">
{{ t(`button.cancel`) }}
</button>
<button class="btn" @click="resetData">
{{ t('button.confirm') }}
</button>
</form>
</div>
</div>
</dialog>
<UploadJsonModal v-model:visible="uploadVisible" :import-all-config-data="importAllConfigData" />
<fieldset class="p-4 border text-setting fieldset bg-base-200 border-base-300 rounded-box w-xs pb-10">
<fieldset class="p-4 border text-setting fieldset bg-base-200 border-base-300 rounded-box w-xs pb-10">
<legend class="fieldset-legend">
数据操作
</legend>
<dialog id="my_modal_1" ref="resetDataDialogRef" class="border-none modal">
<div class="modal-box">
<h3 class="text-lg font-bold">
{{ t('dialog.titleTip') }}
</h3>
<p class="py-4">
{{ t('dialog.dialogResetAllData') }}
</p>
<div class="modal-action">
<form method="dialog" class="flex gap-3">
<!-- if there is a button in form, it will close the modal -->
<button class="btn" @click="resetDataDialogRef.close()">
{{ t(`button.cancel`) }}
</button>
<button class="btn" @click="resetData">
{{ t('button.confirm') }}
</button>
</form>
</div>
</div>
</dialog>
<UploadJsonModal v-model:visible="uploadVisible" :import-all-config-data="importAllConfigData" />
<label class="flex flex-row items-center form-control">
<div class="">
<div class="label flex flex-col justify-start items-start">

View File

@@ -16,7 +16,7 @@ const isShowAvatarValue = defineModel<boolean>('isShowAvatarValue', { required:
</script>
<template>
<fieldset class="p-4 border text-setting fieldset bg-base-200 border-base-300 rounded-box w-xs pb-10">
<fieldset class="p-4 border text-setting fieldset bg-base-200 border-base-300 rounded-box w-xs pb-10">
<legend class="fieldset-legend">
布局设置
</legend>

View File

@@ -15,7 +15,7 @@ const { t } = useI18n()
</script>
<template>
<fieldset class="p-4 border text-setting fieldset bg-base-200 border-base-300 rounded-box w-xs pb-10">
<fieldset class="p-4 border text-setting fieldset bg-base-200 border-base-300 rounded-box w-xs pb-10">
<legend class="fieldset-legend">
图案设置
</legend>

View File

@@ -1,4 +1,5 @@
<script setup lang='ts'>
import type { IImage } from '@/types/storeType'
import { reactive } from 'vue'
import { ColorPicker } from 'vue3-colorpicker'
import { useI18n } from 'vue-i18n'
@@ -7,7 +8,7 @@ import { daisyuiThemes } from '@/constant/theme'
import 'vue3-colorpicker/style.css'
interface Props {
imageList: Array<{ name: string, url: string, id: string }>
imageList: Array<IImage>
}
defineProps<Props>()
const themeList = reactive(daisyuiThemes)
@@ -23,7 +24,7 @@ const patternColorValue = defineModel<string>('patternColorValue')
</script>
<template>
<fieldset class="p-4 border text-setting fieldset bg-base-200 border-base-300 rounded-box w-xs pb-10">
<fieldset class="p-4 border text-setting fieldset bg-base-200 border-base-300 rounded-box w-xs pb-10">
<legend class="fieldset-legend">
主题设置
</legend>

View File

@@ -48,7 +48,7 @@ async function uploadFile(fileData: IFileData | null) {
async function getImageDbStore() {
const keys = await imageDbStore.keys()
if (keys.length > 0) {
imageDbStore.iterate((value: { fileName: string, dataUrl: string }, key: string) => {
imageDbStore.iterate((value: { fileName: string, data: Blob }, key: string) => {
globalConfig.addImage({
id: key,
name: value.fileName,
@@ -59,10 +59,10 @@ async function getImageDbStore() {
}
function submitUpload() {
if (imageData.value) {
const { dataUrl, fileName } = imageData.value
const { data, fileName } = imageData.value
const uniqueId = uuidv4()
imageDbStore.setItem(uniqueId, {
dataUrl,
data,
fileName,
})
.then(() => {

View File

@@ -52,7 +52,7 @@ async function uploadFile(fileData: IFileData | null) {
async function getAudioDbStore() {
const keys = await audioDbStore.keys()
if (keys.length > 0) {
audioDbStore.iterate((value: { fileName: string, dataUrl: string }, key: string) => {
audioDbStore.iterate((value: { fileName: string, data: Blob }, key: string) => {
globalConfig.addMusic({
id: key,
name: value.fileName,
@@ -63,10 +63,10 @@ async function getAudioDbStore() {
}
function submitUpload() {
if (audioData.value) {
const { dataUrl, fileName } = audioData.value
const { data, fileName } = audioData.value
const uniqueId = uuidv4()
audioDbStore.setItem(uniqueId, {
dataUrl,
data,
fileName,
})
.then(() => {

View File

@@ -0,0 +1,53 @@
import type { IPersonConfig } from '@/types/storeType'
import i18n from '@/locales/i18n'
interface IColumnsProps {
handleDeletePerson: (row: IPersonConfig) => void
}
export function tableColumns(props: IColumnsProps) {
return [
{
label: i18n.global.t('data.number'),
props: 'uid',
},
{
label: i18n.global.t('data.name'),
props: 'name',
},
{
label: i18n.global.t('data.department'),
props: 'department',
},
{
label: i18n.global.t('data.avatar'),
props: 'avatar',
formatValue(row: any) {
return row.avatar ? `<img src="${row.avatar}" alt="avatar" style="width: 50px; height: 50px;"/>` : '-'
},
},
{
label: i18n.global.t('data.identity'),
props: 'identity',
},
{
label: i18n.global.t('data.isWin'),
props: 'isWin',
formatValue(row: IPersonConfig) {
return row.isWin ? i18n.global.t('data.yes') : i18n.global.t('data.no')
},
},
{
label: i18n.global.t('data.operation'),
actions: [
{
label: i18n.global.t('data.delete'),
type: 'btn-error',
onClick: (row: IPersonConfig) => {
props.handleDeletePerson(row)
},
},
],
},
]
}

View File

@@ -10,7 +10,7 @@ import { useViewModel } from './useViewModel'
const resetDataDialogRef = ref()
const delAllDataDialogRef = ref()
const exportInputFileRef = ref()
const { resetData, deleteAll, handleFileChange, exportData, alreadyPersonList, allPersonList, tableColumns } = useViewModel({ exportInputFileRef })
const { resetData, deleteAll, handleFileChange, exportData, alreadyPersonList, allPersonList, tableColumnList } = useViewModel({ exportInputFileRef })
const { t } = useI18n()
const limitType = '.xlsx,.xls'
</script>
@@ -70,7 +70,7 @@ const limitType = '.xlsx,.xls'
</template>
</PageHeader>
<DaiysuiTable :table-columns="tableColumns" :data="allPersonList" />
<DaiysuiTable :table-columns="tableColumnList" :data="allPersonList" />
</div>
</template>

View File

@@ -8,6 +8,7 @@ import { loadingKey } from '@/components/Loading'
import i18n from '@/locales/i18n'
import useStore from '@/store'
import { readFileBinary, readLocalFileAsArraybuffer } from '@/utils/file'
import { tableColumns } from './columns'
import ImportExcelWorker from './importExcel.worker?worker'
export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<HTMLInputElement> }) {
@@ -15,52 +16,7 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<H
const loading = inject(loadingKey)
const personConfig = useStore().personConfig
const { getAllPersonList: allPersonList, getAlreadyPersonList: alreadyPersonList } = storeToRefs(personConfig)
const tableColumns = [
{
label: i18n.global.t('data.number'),
props: 'uid',
},
{
label: i18n.global.t('data.name'),
props: 'name',
},
{
label: i18n.global.t('data.department'),
props: 'department',
},
{
label: i18n.global.t('data.avatar'),
props: 'avatar',
formatValue(row: any) {
return row.avatar ? `<img src="${row.avatar}" alt="avatar" style="width: 50px; height: 50px;"/>` : '-'
},
},
{
label: i18n.global.t('data.identity'),
props: 'identity',
},
{
label: i18n.global.t('data.isWin'),
props: 'isWin',
formatValue(row: IPersonConfig) {
return row.isWin ? i18n.global.t('data.yes') : i18n.global.t('data.no')
},
},
{
label: i18n.global.t('data.operation'),
actions: [
{
label: i18n.global.t('data.delete'),
type: 'btn-error',
onClick: (row: IPersonConfig) => {
delPersonItem(row)
},
},
],
},
]
const tableColumnList = tableColumns({ handleDeletePerson: delPersonItem })
async function getExcelTemplateContent() {
const locale = i18n.global.locale.value
if (locale === 'zhCn') {
@@ -171,6 +127,6 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<H
exportData,
alreadyPersonList,
allPersonList,
tableColumns,
tableColumnList,
}
}

View File

@@ -25,7 +25,8 @@ const { alreadyPersonList, alreadyPersonDetail, isDetail, tableColumnsList, tabl
<label class="label flex items-center gap-2">
<p class="label-text">{{ t('table.detail') }}:</p>
<div class="flex items-center">
<Switch v-model="isDetail" class="cursor-pointer" />
<!-- <Switch v-model="isDetail" class="cursor-pointer" /> -->
<input v-model="isDetail" type="checkbox" :checked="isDetail" class="toggle toggle-primary">
</div>
</label>
</div>

View File

@@ -1,8 +1,8 @@
<script setup lang='ts'>
import markdownit from 'markdown-it'
import { onMounted, ref, watch } from 'vue'
import readmeEn from '@/../public/readme-en.md?raw'
import readmeZh from '@/../public/readme-zhCn.md?raw'
import readmeEn from '@/assets/md/readme-en.md?raw'
import readmeZh from '@/assets/md/readme-zhCn.md?raw'
import i18n from '@/locales/i18n'
const md = markdownit()