pagetype详情页拆分工具栏为独立的子组件

This commit is contained in:
jingrow 2025-11-02 19:09:25 +08:00
parent aa06768238
commit 2760d25b70
2 changed files with 257 additions and 169 deletions

View File

@ -7,83 +7,34 @@
<h1 class="page-title">{{ pageTitle }}</h1>
<p class="page-description">{{ pageDescription }}</p>
</div>
<n-space align="center">
<!-- 侧边栏位置切换按钮 -->
<n-button
type="default"
size="medium"
@click="toggleSidebarPosition"
:title="sidebarPosition === 'left' ? '切换到右侧' : '切换到左侧'"
class="header-action-btn"
>
<template #icon>
<n-icon>
<Icon :icon="sidebarPosition === 'left' ? 'fluent:panel-right-16-regular' : 'fluent:panel-left-16-regular'" />
</n-icon>
</template>
</n-button>
<!-- 刷新按钮 -->
<n-button
type="default"
size="medium"
@click="handleRefresh"
:disabled="loading || isNew"
:title="t('Refresh')"
class="header-action-btn"
>
<template #icon>
<n-icon>
<Icon icon="tabler:refresh" />
</n-icon>
</template>
</n-button>
<!-- 删除按钮 -->
<n-button
type="default"
size="medium"
@click="handleDelete"
:disabled="loading || isNew"
:title="t('Delete')"
class="header-action-btn delete-btn"
>
<template #icon>
<n-icon>
<Icon icon="tabler:trash" />
</n-icon>
</template>
</n-button>
<component
v-if="toolbarComponent"
:is="toolbarComponent"
:context="{
entity,
id,
record,
canEdit,
loading,
save: handleSave,
router,
t
}"
/>
<n-button type="default" size="medium" @click="goBack" :disabled="loading">
<template #icon>
<n-icon><Icon icon="tabler:arrow-left" /></n-icon>
</template>
{{ t('Back') }}
</n-button>
<n-button type="primary" size="medium" :disabled="loading" @click="handleSave" v-if="canEdit" class="save-btn-brand">
<template #icon>
<n-icon>
<Icon icon="tabler:check" />
</n-icon>
</template>
{{ t('Save') }}
</n-button>
</n-space>
<component
v-if="toolbarComponent"
:is="toolbarComponent"
:context="{
entity,
id,
record,
canEdit,
loading,
save: handleSave,
router,
t
}"
/>
<DefaultToolbar
v-else
:entity="entity"
:id="id"
:record="record"
:can-edit="canEdit"
:loading="loading"
:sidebar-position="sidebarPosition"
@toggle-sidebar-position="toggleSidebarPosition"
@refresh="handleRefresh"
@delete="handleDelete"
@go-back="goBack"
@save="handleSave"
/>
</n-space>
</div>
@ -321,6 +272,7 @@ import { updateRecord, getRecord, getRecordAttachments, deleteAttachment, upload
import { downloadImageToLocal } from '@/shared/api/common'
import { usePageTypeSlug } from '@/shared/utils/slug'
import { resolvePagetypeToolbarOverride } from '@/core/registry/pagetypeOverride'
import DefaultToolbar from './default_toolbar.vue'
const route = useRoute()
const router = useRouter()
@ -1618,98 +1570,6 @@ watch(() => route.params.entity, async (newEntity, oldEntity) => {
background: #d1d5db !important;
}
/* 头部操作按钮统一样式 */
.header-action-btn {
display: inline-flex;
align-items: center;
justify-content: center;
}
/* 删除按钮悬浮时使用红色 */
.header-action-btn.delete-btn:hover:not(:disabled) {
background: #ef4444 !important;
border-color: #ef4444 !important;
color: white !important;
}
.header-action-btn.delete-btn:hover:not(:disabled) :deep(.n-button__border),
.header-action-btn.delete-btn:hover:not(:disabled) :deep(.n-button__state-border) {
border-color: #ef4444 !important;
}
/* 保存按钮 - 使用柔和的品牌色系,与列表页创建按钮一致 */
.save-btn-brand {
background: #e6f8f0 !important;
border: 1px solid #1fc76f !important;
color: #0d684b !important;
}
.save-btn-brand :deep(.n-button__border) {
border: none !important;
border-color: transparent !important;
}
.save-btn-brand :deep(.n-button__state-border) {
border: none !important;
border-color: transparent !important;
}
.save-btn-brand:hover {
background: #dcfce7 !important;
border-color: #1fc76f !important;
border: 1px solid #1fc76f !important;
color: #166534 !important;
box-shadow: 0 2px 8px rgba(31, 199, 111, 0.15) !important;
}
.save-btn-brand:hover :deep(.n-button__border),
.save-btn-brand:hover :deep(.n-button__state-border) {
border: none !important;
border-color: transparent !important;
}
.save-btn-brand:focus {
background: #dcfce7 !important;
border-color: #1fc76f !important;
border: 1px solid #1fc76f !important;
color: #166534 !important;
box-shadow: 0 0 0 2px rgba(31, 199, 111, 0.2) !important;
}
.save-btn-brand:focus :deep(.n-button__border),
.save-btn-brand:focus :deep(.n-button__state-border) {
border: none !important;
border-color: transparent !important;
}
.save-btn-brand:active {
background: #1fc76f !important;
border-color: #1fc76f !important;
border: 1px solid #1fc76f !important;
color: white !important;
box-shadow: 0 1px 4px rgba(31, 199, 111, 0.2) !important;
}
.save-btn-brand:active :deep(.n-button__border),
.save-btn-brand:active :deep(.n-button__state-border) {
border: none !important;
border-color: transparent !important;
}
.save-btn-brand:disabled {
background: #f1f5f9 !important;
border: 1px solid #e2e8f0 !important;
border-color: #e2e8f0 !important;
color: #94a3b8 !important;
opacity: 0.6 !important;
cursor: not-allowed !important;
}
.save-btn-brand:disabled :deep(.n-button__border),
.save-btn-brand:disabled :deep(.n-button__state-border) {
border: none !important;
border-color: transparent !important;
}
/* 响应式设计 - Naive UI Layout 自动处理 */
@media (max-width: 768px) {

View File

@ -0,0 +1,228 @@
<template>
<n-space align="center">
<!-- 侧边栏位置切换按钮 -->
<n-button
type="default"
size="medium"
@click="$emit('toggle-sidebar-position')"
:title="sidebarPosition === 'left' ? '切换到右侧' : '切换到左侧'"
class="header-action-btn"
>
<template #icon>
<n-icon>
<Icon :icon="sidebarPosition === 'left' ? 'fluent:panel-right-16-regular' : 'fluent:panel-left-16-regular'" />
</n-icon>
</template>
</n-button>
<!-- 刷新按钮 -->
<n-button
type="default"
size="medium"
@click="$emit('refresh')"
:disabled="loading || isNew"
:title="t('Refresh')"
class="header-action-btn"
>
<template #icon>
<n-icon>
<Icon icon="tabler:refresh" />
</n-icon>
</template>
</n-button>
<!-- 删除按钮 -->
<n-button
type="default"
size="medium"
@click="$emit('delete')"
:disabled="loading || isNew"
:title="t('Delete')"
class="header-action-btn delete-btn"
>
<template #icon>
<n-icon>
<Icon icon="tabler:trash" />
</n-icon>
</template>
</n-button>
<!-- 返回按钮 -->
<n-button type="default" size="medium" @click="$emit('go-back')" :disabled="loading">
<template #icon>
<n-icon><Icon icon="tabler:arrow-left" /></n-icon>
</template>
{{ t('Back') }}
</n-button>
<!-- 保存按钮 -->
<n-button
type="primary"
size="medium"
:disabled="loading"
@click="$emit('save')"
v-if="canEdit"
class="save-btn-brand"
>
<template #icon>
<n-icon>
<Icon icon="tabler:check" />
</n-icon>
</template>
{{ t('Save') }}
</n-button>
</n-space>
</template>
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
import { NButton, NSpace, NIcon } from 'naive-ui'
import { Icon } from '@iconify/vue'
import { t } from '@/shared/i18n'
interface Props {
entity: string
id: string
record?: any
canEdit: boolean
loading: boolean
sidebarPosition?: 'left' | 'right'
}
interface Emits {
(e: 'toggle-sidebar-position'): void
(e: 'refresh'): void
(e: 'delete'): void
(e: 'go-back'): void
(e: 'save'): void
}
const props = withDefaults(defineProps<Props>(), {
sidebarPosition: 'left',
record: () => ({})
})
defineEmits<Emits>()
//
const isNew = computed(() => {
const idValue = props.id
return idValue === 'new' || idValue.startsWith('new-')
})
// localStorage
const sidebarPosition = ref<'left' | 'right'>(props.sidebarPosition || 'left')
// props
watch(() => props.sidebarPosition, (newPos) => {
if (newPos) {
sidebarPosition.value = newPos
}
}, { immediate: true })
onMounted(() => {
// sidebarPosition localStorage
if (!props.sidebarPosition) {
const savedPosition = localStorage.getItem('jingrow-sidebar-position')
if (savedPosition === 'left' || savedPosition === 'right') {
sidebarPosition.value = savedPosition
}
}
})
</script>
<style scoped>
/* 头部操作按钮统一样式 */
.header-action-btn {
display: inline-flex;
align-items: center;
justify-content: center;
}
/* 删除按钮悬浮时使用红色 */
.header-action-btn.delete-btn:hover:not(:disabled) {
background: #ef4444 !important;
border-color: #ef4444 !important;
color: white !important;
}
.header-action-btn.delete-btn:hover:not(:disabled) :deep(.n-button__border),
.header-action-btn.delete-btn:hover:not(:disabled) :deep(.n-button__state-border) {
border-color: #ef4444 !important;
}
/* 保存按钮 - 使用柔和的品牌色系,与列表页创建按钮一致 */
.save-btn-brand {
background: #e6f8f0 !important;
border: 1px solid #1fc76f !important;
color: #0d684b !important;
}
.save-btn-brand :deep(.n-button__border) {
border: none !important;
border-color: transparent !important;
}
.save-btn-brand :deep(.n-button__state-border) {
border: none !important;
border-color: transparent !important;
}
.save-btn-brand:hover {
background: #dcfce7 !important;
border-color: #1fc76f !important;
border: 1px solid #1fc76f !important;
color: #166534 !important;
box-shadow: 0 2px 8px rgba(31, 199, 111, 0.15) !important;
}
.save-btn-brand:hover :deep(.n-button__border),
.save-btn-brand:hover :deep(.n-button__state-border) {
border: none !important;
border-color: transparent !important;
}
.save-btn-brand:focus {
background: #dcfce7 !important;
border-color: #1fc76f !important;
border: 1px solid #1fc76f !important;
color: #166534 !important;
box-shadow: 0 0 0 2px rgba(31, 199, 111, 0.2) !important;
}
.save-btn-brand:focus :deep(.n-button__border),
.save-btn-brand:focus :deep(.n-button__state-border) {
border: none !important;
border-color: transparent !important;
}
.save-btn-brand:active {
background: #1fc76f !important;
border-color: #1fc76f !important;
border: 1px solid #1fc76f !important;
color: white !important;
box-shadow: 0 1px 4px rgba(31, 199, 111, 0.2) !important;
}
.save-btn-brand:active :deep(.n-button__border),
.save-btn-brand:active :deep(.n-button__state-border) {
border: none !important;
border-color: transparent !important;
}
.save-btn-brand:disabled {
background: #f1f5f9 !important;
border: 1px solid #e2e8f0 !important;
border-color: #e2e8f0 !important;
color: #94a3b8 !important;
opacity: 0.6 !important;
cursor: not-allowed !important;
}
.save-btn-brand:disabled :deep(.n-button__border),
.save-btn-brand:disabled :deep(.n-button__state-border) {
border: none !important;
border-color: transparent !important;
}
</style>