pagetype详情页拆分工具栏为独立的子组件
This commit is contained in:
parent
aa06768238
commit
2760d25b70
@ -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) {
|
||||
|
||||
228
apps/jingrow/frontend/src/core/pagetype/default_toolbar.vue
Normal file
228
apps/jingrow/frontend/src/core/pagetype/default_toolbar.vue
Normal 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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user