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']
|
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
|
||||||
ImageSync: typeof import('./components/ImageSync/index.vue')['default']
|
ImageSync: typeof import('./components/ImageSync/index.vue')['default']
|
||||||
Loading: typeof import('./components/Loading/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']
|
PlayMusic: typeof import('./components/PlayMusic/index.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
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'>
|
<script setup lang='ts'>
|
||||||
import type { IImage } from '@/types/storeType'
|
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 localforage from 'localforage'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { onMounted, ref, watch } from 'vue'
|
import { onMounted, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
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 { t } = useI18n()
|
||||||
const globalConfig = useStore().globalConfig
|
const globalConfig = useStore().globalConfig
|
||||||
@@ -81,6 +82,8 @@ watch(() => imgUploadToast.value, (val) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
<PageHeader title="图片管理">
|
||||||
|
<template #buttons>
|
||||||
<div class="">
|
<div class="">
|
||||||
<label for="explore">
|
<label for="explore">
|
||||||
<input
|
<input
|
||||||
@@ -90,6 +93,9 @@ watch(() => imgUploadToast.value, (val) => {
|
|||||||
<span class="btn btn-primary btn-sm">{{ t('button.upload') }}</span>
|
<span class="btn btn-primary btn-sm">{{ t('button.upload') }}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</PageHeader>
|
||||||
|
|
||||||
<ul class="p-0">
|
<ul class="p-0">
|
||||||
<li v-for="item in localImageList" :key="item.id" class="mb-3">
|
<li v-for="item in localImageList" :key="item.id" class="mb-3">
|
||||||
<div class="flex items-center gap-8">
|
<div class="flex items-center gap-8">
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import type { IMusic } from '@/types/storeType'
|
import type { IMusic } from '@/types/storeType'
|
||||||
import useStore from '@/store'
|
|
||||||
import { readFileData } from '@/utils/file'
|
|
||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
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 { t } = useI18n()
|
||||||
const audioUploadToast = ref(0) // 0是不显示,1是成功,2是失败,3是不是图片
|
const audioUploadToast = ref(0) // 0是不显示,1是成功,2是失败,3是不是图片
|
||||||
@@ -74,6 +74,8 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<PageHeader title="音乐管理">
|
||||||
|
<template #buttons>
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<button class="btn btn-primary btn-sm" @click="resetMusic">
|
<button class="btn btn-primary btn-sm" @click="resetMusic">
|
||||||
{{ t('button.reset') }}
|
{{ t('button.reset') }}
|
||||||
@@ -89,6 +91,9 @@ onMounted(() => {
|
|||||||
{{ t('button.allDelete') }}
|
{{ t('button.allDelete') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</PageHeader>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ul class="p-0">
|
<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">
|
<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 { useI18n } from 'vue-i18n'
|
||||||
import DaiysuiTable from '@/components/DaiysuiTable/index.vue'
|
import DaiysuiTable from '@/components/DaiysuiTable/index.vue'
|
||||||
import CustomDialog from '@/components/Dialog/index.vue'
|
import CustomDialog from '@/components/Dialog/index.vue'
|
||||||
|
import PageHeader from '@/components/PageHeader/index.vue'
|
||||||
import { useViewModel } from './useViewModel'
|
import { useViewModel } from './useViewModel'
|
||||||
|
|
||||||
const resetDataDialogRef = ref()
|
const resetDataDialogRef = ref()
|
||||||
@@ -29,7 +30,8 @@ const limitType = '.xlsx,.xls'
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="min-w-1000px">
|
<div class="min-w-1000px">
|
||||||
<h2>{{ t('viewTitle.personManagement') }}</h2>
|
<PageHeader :title="t('viewTitle.personManagement')">
|
||||||
|
<template #buttons>
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<button class="btn btn-error btn-sm" @click="delAllDataDialogRef.showDialog()">
|
<button class="btn btn-error btn-sm" @click="delAllDataDialogRef.showDialog()">
|
||||||
{{ t('button.allDelete') }}
|
{{ t('button.allDelete') }}
|
||||||
@@ -42,7 +44,6 @@ const limitType = '.xlsx,.xls'
|
|||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
<label for="explore">
|
<label for="explore">
|
||||||
|
|
||||||
<div class="tooltip tooltip-bottom" :data-tip="t('tooltip.uploadExcelTip')">
|
<div class="tooltip tooltip-bottom" :data-tip="t('tooltip.uploadExcelTip')">
|
||||||
<input
|
<input
|
||||||
id="explore" ref="exportInputFileRef" type="file" class="" style="display: none"
|
id="explore" ref="exportInputFileRef" type="file" class="" style="display: none"
|
||||||
@@ -66,6 +67,9 @@ const limitType = '.xlsx,.xls'
|
|||||||
<span>{{ allPersonList.length }}</span>
|
<span>{{ allPersonList.length }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</PageHeader>
|
||||||
|
|
||||||
<DaiysuiTable :table-columns="tableColumns" :data="allPersonList" />
|
<DaiysuiTable :table-columns="tableColumns" :data="allPersonList" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
<!-- eslint-disable vue/no-parsing-error -->
|
<!-- eslint-disable vue/no-parsing-error -->
|
||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import type { IPersonConfig } from '@/types/storeType'
|
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 { storeToRefs } from 'pinia'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
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 { t } = useI18n()
|
||||||
const personConfig = useStore().personConfig
|
const personConfig = useStore().personConfig
|
||||||
@@ -39,8 +40,8 @@ const tableColumnsList = [
|
|||||||
label: i18n.global.t('data.avatar'),
|
label: i18n.global.t('data.avatar'),
|
||||||
props: 'avatar',
|
props: 'avatar',
|
||||||
formatValue(row: any) {
|
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'),
|
label: i18n.global.t('data.department'),
|
||||||
@@ -82,8 +83,8 @@ const tableColumnsDetail = [
|
|||||||
label: i18n.global.t('data.avatar'),
|
label: i18n.global.t('data.avatar'),
|
||||||
props: 'avatar',
|
props: 'avatar',
|
||||||
formatValue(row: any) {
|
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'),
|
label: i18n.global.t('data.department'),
|
||||||
@@ -121,7 +122,8 @@ const tableColumnsDetail = [
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="overflow-y-auto">
|
<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 class="flex items-center justify-start gap-10">
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('table.luckyPeopleNumber') }}:</span>
|
<span>{{ t('table.luckyPeopleNumber') }}:</span>
|
||||||
@@ -136,6 +138,9 @@ const tableColumnsDetail = [
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</PageHeader>
|
||||||
|
|
||||||
<DaiysuiTable v-if="!isDetail" :table-columns="tableColumnsList" :data="alreadyPersonList" />
|
<DaiysuiTable v-if="!isDetail" :table-columns="tableColumnsList" :data="alreadyPersonList" />
|
||||||
|
|
||||||
<DaiysuiTable v-if="isDetail" :table-columns="tableColumnsDetail" :data="alreadyPersonDetail" />
|
<DaiysuiTable v-if="isDetail" :table-columns="tableColumnsDetail" :data="alreadyPersonDetail" />
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import type { IPrizeConfig } from '@/types/storeType'
|
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 localforage from 'localforage'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { onMounted, ref, watch } from 'vue'
|
import { onMounted, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
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 { t } = useI18n()
|
||||||
const imageDbStore = localforage.createInstance({
|
const imageDbStore = localforage.createInstance({
|
||||||
@@ -149,7 +150,8 @@ watch(() => prizeList.value, (val: IPrizeConfig[]) => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h2>{{ t('viewTitle.prizeManagement') }}</h2>
|
<PageHeader :title="t('viewTitle.prizeManagement')">
|
||||||
|
<template #buttons>
|
||||||
<div class="flex w-full gap-3">
|
<div class="flex w-full gap-3">
|
||||||
<button class="btn btn-info btn-sm" @click="addPrize">
|
<button class="btn btn-info btn-sm" @click="addPrize">
|
||||||
{{ t('button.add') }}
|
{{ t('button.add') }}
|
||||||
@@ -161,6 +163,8 @@ watch(() => prizeList.value, (val: IPrizeConfig[]) => {
|
|||||||
{{ t('button.allDelete') }}
|
{{ t('button.allDelete') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #alerts>
|
||||||
<div role="alert" class="w-full my-4 alert alert-info">
|
<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">
|
<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
|
<path
|
||||||
@@ -170,6 +174,9 @@ watch(() => prizeList.value, (val: IPrizeConfig[]) => {
|
|||||||
</svg>
|
</svg>
|
||||||
<span>{{ t('dialog.tipResetPrize') }}</span>
|
<span>{{ t('dialog.tipResetPrize') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</PageHeader>
|
||||||
|
|
||||||
<ul class="p-0 m-0">
|
<ul class="p-0 m-0">
|
||||||
<li
|
<li
|
||||||
v-for="item in prizeList" :key="item.id" class="flex gap-10"
|
v-for="item in prizeList" :key="item.id" class="flex gap-10"
|
||||||
|
|||||||
Reference in New Issue
Block a user