feat(PageHeader): 新增 PageHeader 组件并应用于多个配置页面
新增了 PageHeader 通用组件,用于统一页面标题和操作按钮区域的布局。 该组件包含 title 属性以及 buttons 和 alerts 两个具名插槽,便于复用和维护。 已在以下页面中集成使用: - 图片管理页(ImageConfig.vue) - 音乐管理页(MusicConfig.vue) - 人员管理页(PersonAll/index.vue) - 中奖者管理页(PersonAlready.vue) - 奖品管理页(PrizeConfig.vue)
This commit is contained in:
1
src/components.d.ts
vendored
1
src/components.d.ts
vendored
@@ -14,6 +14,7 @@ declare module 'vue' {
|
||||
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
|
||||
ImageSync: typeof import('./components/ImageSync/index.vue')['default']
|
||||
Loading: typeof import('./components/Loading/index.vue')['default']
|
||||
PageHeader: typeof import('./components/PageHeader/index.vue')['default']
|
||||
PlayMusic: typeof import('./components/PlayMusic/index.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
|
||||
24
src/components/PageHeader/index.vue
Normal file
24
src/components/PageHeader/index.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup lang='ts'>
|
||||
const props = defineProps<{
|
||||
title: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<h1 class="text-lg leading-10">
|
||||
{{ props.title }}
|
||||
</h1>
|
||||
<div class="button-group my-4">
|
||||
<slot name="buttons" />
|
||||
</div>
|
||||
<div class="divider mt-0" />
|
||||
<div>
|
||||
<slot name="alerts" />
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,12 +1,13 @@
|
||||
<script setup lang='ts'>
|
||||
import type { IImage } from '@/types/storeType'
|
||||
import ImageSync from '@/components/ImageSync/index.vue'
|
||||
import useStore from '@/store'
|
||||
import { readFileData } from '@/utils/file'
|
||||
import localforage from 'localforage'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import ImageSync from '@/components/ImageSync/index.vue'
|
||||
import PageHeader from '@/components/PageHeader/index.vue'
|
||||
import useStore from '@/store'
|
||||
import { readFileData } from '@/utils/file'
|
||||
|
||||
const { t } = useI18n()
|
||||
const globalConfig = useStore().globalConfig
|
||||
@@ -81,6 +82,8 @@ watch(() => imgUploadToast.value, (val) => {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<PageHeader title="图片管理">
|
||||
<template #buttons>
|
||||
<div class="">
|
||||
<label for="explore">
|
||||
<input
|
||||
@@ -90,6 +93,9 @@ watch(() => imgUploadToast.value, (val) => {
|
||||
<span class="btn btn-primary btn-sm">{{ t('button.upload') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<ul class="p-0">
|
||||
<li v-for="item in localImageList" :key="item.id" class="mb-3">
|
||||
<div class="flex items-center gap-8">
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script setup lang='ts'>
|
||||
import type { IMusic } from '@/types/storeType'
|
||||
import useStore from '@/store'
|
||||
import { readFileData } from '@/utils/file'
|
||||
import localforage from 'localforage'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import PageHeader from '@/components/PageHeader/index.vue'
|
||||
import useStore from '@/store'
|
||||
import { readFileData } from '@/utils/file'
|
||||
|
||||
const { t } = useI18n()
|
||||
const audioUploadToast = ref(0) // 0是不显示,1是成功,2是失败,3是不是图片
|
||||
@@ -74,6 +74,8 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<PageHeader title="音乐管理">
|
||||
<template #buttons>
|
||||
<div class="flex gap-3">
|
||||
<button class="btn btn-primary btn-sm" @click="resetMusic">
|
||||
{{ t('button.reset') }}
|
||||
@@ -89,6 +91,9 @@ onMounted(() => {
|
||||
{{ t('button.allDelete') }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<div>
|
||||
<ul class="p-0">
|
||||
<li v-for="item in localMusicListValue" :key="item.id" class="flex items-center gap-6 pb-2 mb-3 divide-y">
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import DaiysuiTable from '@/components/DaiysuiTable/index.vue'
|
||||
import CustomDialog from '@/components/Dialog/index.vue'
|
||||
import PageHeader from '@/components/PageHeader/index.vue'
|
||||
import { useViewModel } from './useViewModel'
|
||||
|
||||
const resetDataDialogRef = ref()
|
||||
@@ -29,7 +30,8 @@ const limitType = '.xlsx,.xls'
|
||||
/>
|
||||
|
||||
<div class="min-w-1000px">
|
||||
<h2>{{ t('viewTitle.personManagement') }}</h2>
|
||||
<PageHeader :title="t('viewTitle.personManagement')">
|
||||
<template #buttons>
|
||||
<div class="flex gap-3">
|
||||
<button class="btn btn-error btn-sm" @click="delAllDataDialogRef.showDialog()">
|
||||
{{ t('button.allDelete') }}
|
||||
@@ -42,7 +44,6 @@ const limitType = '.xlsx,.xls'
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="explore">
|
||||
|
||||
<div class="tooltip tooltip-bottom" :data-tip="t('tooltip.uploadExcelTip')">
|
||||
<input
|
||||
id="explore" ref="exportInputFileRef" type="file" class="" style="display: none"
|
||||
@@ -66,6 +67,9 @@ const limitType = '.xlsx,.xls'
|
||||
<span>{{ allPersonList.length }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<DaiysuiTable :table-columns="tableColumns" :data="allPersonList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<!-- eslint-disable vue/no-parsing-error -->
|
||||
<script setup lang='ts'>
|
||||
import type { IPersonConfig } from '@/types/storeType'
|
||||
import DaiysuiTable from '@/components/DaiysuiTable/index.vue'
|
||||
import i18n from '@/locales/i18n'
|
||||
import useStore from '@/store'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import DaiysuiTable from '@/components/DaiysuiTable/index.vue'
|
||||
import PageHeader from '@/components/PageHeader/index.vue'
|
||||
import i18n from '@/locales/i18n'
|
||||
import useStore from '@/store'
|
||||
|
||||
const { t } = useI18n()
|
||||
const personConfig = useStore().personConfig
|
||||
@@ -39,8 +40,8 @@ const tableColumnsList = [
|
||||
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;"/>` : '-';
|
||||
}
|
||||
return row.avatar ? `<img src="${row.avatar}" alt="avatar" style="width: 50px; height: 50px;"/>` : '-'
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('data.department'),
|
||||
@@ -82,8 +83,8 @@ const tableColumnsDetail = [
|
||||
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;"/>` : '-';
|
||||
}
|
||||
return row.avatar ? `<img src="${row.avatar}" alt="avatar" style="width: 50px; height: 50px;"/>` : '-'
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('data.department'),
|
||||
@@ -121,7 +122,8 @@ const tableColumnsDetail = [
|
||||
|
||||
<template>
|
||||
<div class="overflow-y-auto">
|
||||
<h2>{{ t('viewTitle.winnerManagement') }}</h2>
|
||||
<PageHeader :title="t('viewTitle.winnerManagement')">
|
||||
<template #buttons>
|
||||
<div class="flex items-center justify-start gap-10">
|
||||
<div>
|
||||
<span>{{ t('table.luckyPeopleNumber') }}:</span>
|
||||
@@ -136,6 +138,9 @@ const tableColumnsDetail = [
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<DaiysuiTable v-if="!isDetail" :table-columns="tableColumnsList" :data="alreadyPersonList" />
|
||||
|
||||
<DaiysuiTable v-if="isDetail" :table-columns="tableColumnsDetail" :data="alreadyPersonDetail" />
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script setup lang='ts'>
|
||||
import type { IPrizeConfig } from '@/types/storeType'
|
||||
import EditSeparateDialog from '@/components/NumberSeparate/EditSeparateDialog.vue'
|
||||
import i18n from '@/locales/i18n'
|
||||
import useStore from '@/store'
|
||||
import localforage from 'localforage'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import EditSeparateDialog from '@/components/NumberSeparate/EditSeparateDialog.vue'
|
||||
import PageHeader from '@/components/PageHeader/index.vue'
|
||||
import i18n from '@/locales/i18n'
|
||||
import useStore from '@/store'
|
||||
|
||||
const { t } = useI18n()
|
||||
const imageDbStore = localforage.createInstance({
|
||||
@@ -149,7 +150,8 @@ watch(() => prizeList.value, (val: IPrizeConfig[]) => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h2>{{ t('viewTitle.prizeManagement') }}</h2>
|
||||
<PageHeader :title="t('viewTitle.prizeManagement')">
|
||||
<template #buttons>
|
||||
<div class="flex w-full gap-3">
|
||||
<button class="btn btn-info btn-sm" @click="addPrize">
|
||||
{{ t('button.add') }}
|
||||
@@ -161,6 +163,8 @@ watch(() => prizeList.value, (val: IPrizeConfig[]) => {
|
||||
{{ t('button.allDelete') }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template #alerts>
|
||||
<div role="alert" class="w-full my-4 alert alert-info">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-6 h-6 stroke-current shrink-0">
|
||||
<path
|
||||
@@ -170,6 +174,9 @@ watch(() => prizeList.value, (val: IPrizeConfig[]) => {
|
||||
</svg>
|
||||
<span>{{ t('dialog.tipResetPrize') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<ul class="p-0 m-0">
|
||||
<li
|
||||
v-for="item in prizeList" :key="item.id" class="flex gap-10"
|
||||
|
||||
Reference in New Issue
Block a user