refactor: adapt page header styles for responsive design (#7544)

#### What type of PR is this?

/area ui
/kind improvement
/milestone 2.21.x

#### What this PR does / why we need it:

Adapt page header styles for responsive design

before:

<img width="415" alt="image" src="https://github.com/user-attachments/assets/feee6f09-9a77-4bbf-94c2-77725c9ce76b" />

after:

<img width="416" alt="image" src="https://github.com/user-attachments/assets/60bb07a3-5d34-4cf6-b103-5d1f2206d62d" />

#### Which issue(s) this PR fixes:

Fixes https://github.com/halo-dev/halo/issues/6425

#### Special notes for your reviewer:

#### Does this PR introduce a user-facing change?

```release-note
优化移动端下文章编辑页面的样式
```
pull/7549/head
Ryan Wang 2025-06-13 14:06:42 +08:00 committed by GitHub
parent 016d4cd94b
commit d39a571e46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 392 additions and 388 deletions

View File

@ -250,31 +250,29 @@ watch(
/> />
<VPageHeader :title="$t('core.attachment.title')"> <VPageHeader :title="$t('core.attachment.title')">
<template #icon> <template #icon>
<IconFolder class="mr-2 self-center" /> <IconFolder />
</template> </template>
<template #actions> <template #actions>
<VSpace> <VButton
<VButton v-permission="['system:attachments:manage']"
v-permission="['system:attachments:manage']" size="sm"
size="sm" @click="policyVisible = true"
@click="policyVisible = true" >
> <template #icon>
<template #icon> <IconDatabase2Line />
<IconDatabase2Line /> </template>
</template> {{ $t("core.attachment.actions.storage_policies") }}
{{ $t("core.attachment.actions.storage_policies") }} </VButton>
</VButton> <VButton
<VButton v-permission="['system:attachments:manage']"
v-permission="['system:attachments:manage']" type="secondary"
type="secondary" @click="uploadVisible = true"
@click="uploadVisible = true" >
> <template #icon>
<template #icon> <IconUpload />
<IconUpload /> </template>
</template> {{ $t("core.common.buttons.upload") }}
{{ $t("core.common.buttons.upload") }} </VButton>
</VButton>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -243,7 +243,7 @@ const handleApproveInBatch = async () => {
<template> <template>
<VPageHeader :title="$t('core.comment.title')"> <VPageHeader :title="$t('core.comment.title')">
<template #icon> <template #icon>
<IconMessage class="mr-2 self-center" /> <IconMessage />
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -204,24 +204,22 @@ watch(
<template> <template>
<VPageHeader :title="$t('core.deleted_page.title')"> <VPageHeader :title="$t('core.deleted_page.title')">
<template #icon> <template #icon>
<IconDeleteBin class="mr-2 self-center text-green-600" /> <IconDeleteBin class="text-green-600" />
</template> </template>
<template #actions> <template #actions>
<VSpace> <VButton :route="{ name: 'SinglePages' }" size="sm">
<VButton :route="{ name: 'SinglePages' }" size="sm"> {{ $t("core.common.buttons.back") }}
{{ $t("core.common.buttons.back") }} </VButton>
</VButton> <VButton
<VButton v-permission="['system:singlepages:manage']"
v-permission="['system:singlepages:manage']" :route="{ name: 'SinglePageEditor' }"
:route="{ name: 'SinglePageEditor' }" type="secondary"
type="secondary" >
> <template #icon>
<template #icon> <IconAddCircle />
<IconAddCircle /> </template>
</template> {{ $t("core.common.buttons.new") }}
{{ $t("core.common.buttons.new") }} </VButton>
</VButton>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>
<div class="m-0 md:m-4"> <div class="m-0 md:m-4">

View File

@ -27,7 +27,6 @@ import {
Toast, Toast,
VButton, VButton,
VPageHeader, VPageHeader,
VSpace,
} from "@halo-dev/components"; } from "@halo-dev/components";
import type { EditorProvider } from "@halo-dev/console-shared"; import type { EditorProvider } from "@halo-dev/console-shared";
import { useLocalStorage } from "@vueuse/core"; import { useLocalStorage } from "@vueuse/core";
@ -464,71 +463,69 @@ async function handleUploadImage(file: File, options?: AxiosRequestConfig) {
<VPageHeader :title="$t('core.page.title')"> <VPageHeader :title="$t('core.page.title')">
<template #icon> <template #icon>
<IconPages class="mr-2 self-center" /> <IconPages />
</template> </template>
<template #actions> <template #actions>
<VSpace> <EditorProviderSelector
<EditorProviderSelector v-if="editorProviders.length > 1"
v-if="editorProviders.length > 1" :provider="currentEditorProvider"
:provider="currentEditorProvider" :allow-forced-select="!isUpdateMode"
:allow-forced-select="!isUpdateMode" @select="handleChangeEditorProvider"
@select="handleChangeEditorProvider" />
/> <VButton
<VButton v-if="isUpdateMode"
v-if="isUpdateMode" size="sm"
size="sm" type="default"
type="default" @click="
@click=" $router.push({
$router.push({ name: 'SinglePageSnapshots',
name: 'SinglePageSnapshots', query: { name: routeQueryName },
query: { name: routeQueryName }, })
}) "
" >
> <template #icon>
<template #icon> <IconHistoryLine />
<IconHistoryLine /> </template>
</template> {{ $t("core.page_editor.actions.snapshots") }}
{{ $t("core.page_editor.actions.snapshots") }} </VButton>
</VButton> <VButton
<VButton size="sm"
size="sm" type="default"
type="default" :loading="previewPending"
:loading="previewPending" @click="handlePreview"
@click="handlePreview" >
> <template #icon>
<template #icon> <IconEye />
<IconEye /> </template>
</template> {{ $t("core.common.buttons.preview") }}
{{ $t("core.common.buttons.preview") }} </VButton>
</VButton> <VButton :loading="saving" size="sm" type="default" @click="handleSave">
<VButton :loading="saving" size="sm" type="default" @click="handleSave"> <template #icon>
<template #icon> <IconSave />
<IconSave /> </template>
</template> {{ $t("core.common.buttons.save") }}
{{ $t("core.common.buttons.save") }} </VButton>
</VButton> <VButton
<VButton v-if="isUpdateMode"
v-if="isUpdateMode" size="sm"
size="sm" type="default"
type="default" @click="handleOpenSettingModal"
@click="handleOpenSettingModal" >
> <template #icon>
<template #icon> <IconSettings />
<IconSettings /> </template>
</template> {{ $t("core.common.buttons.setting") }}
{{ $t("core.common.buttons.setting") }} </VButton>
</VButton> <VButton
<VButton type="secondary"
type="secondary" :loading="publishing"
:loading="publishing" @click="handlePublishClick"
@click="handlePublishClick" >
> <template #icon>
<template #icon> <IconSendPlaneFill />
<IconSendPlaneFill /> </template>
</template> {{ $t("core.common.buttons.publish") }}
{{ $t("core.common.buttons.publish") }} </VButton>
</VButton>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>
<div class="editor border-t" style="height: calc(100vh - 3.5rem)"> <div class="editor border-t" style="height: calc(100vh - 3.5rem)">

View File

@ -281,28 +281,26 @@ watch(selectedPageNames, (newValue) => {
<VPageHeader :title="$t('core.page.title')"> <VPageHeader :title="$t('core.page.title')">
<template #icon> <template #icon>
<IconPages class="mr-2 self-center" /> <IconPages />
</template> </template>
<template #actions> <template #actions>
<VSpace> <VButton
<VButton v-permission="['system:singlepages:view']"
v-permission="['system:singlepages:view']" :route="{ name: 'DeletedSinglePages' }"
:route="{ name: 'DeletedSinglePages' }" size="sm"
size="sm" >
> {{ $t("core.page.actions.recycle_bin") }}
{{ $t("core.page.actions.recycle_bin") }} </VButton>
</VButton> <VButton
<VButton v-permission="['system:singlepages:manage']"
v-permission="['system:singlepages:manage']" :route="{ name: 'SinglePageEditor' }"
:route="{ name: 'SinglePageEditor' }" type="secondary"
type="secondary" >
> <template #icon>
<template #icon> <IconAddCircle />
<IconAddCircle /> </template>
</template> {{ $t("core.common.buttons.new") }}
{{ $t("core.common.buttons.new") }} </VButton>
</VButton>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -8,7 +8,6 @@ import {
VCard, VCard,
VLoading, VLoading,
VPageHeader, VPageHeader,
VSpace,
} from "@halo-dev/components"; } from "@halo-dev/components";
import { useQuery, useQueryClient } from "@tanstack/vue-query"; import { useQuery, useQueryClient } from "@tanstack/vue-query";
import { useRouteQuery } from "@vueuse/router"; import { useRouteQuery } from "@vueuse/router";
@ -118,17 +117,15 @@ function handleCleanup() {
<template> <template>
<VPageHeader :title="singlePage?.spec.title"> <VPageHeader :title="singlePage?.spec.title">
<template #icon> <template #icon>
<IconHistoryLine class="mr-2 self-center" /> <IconHistoryLine />
</template> </template>
<template #actions> <template #actions>
<VSpace> <VButton size="sm" @click="$router.back()">
<VButton size="sm" @click="$router.back()"> {{ $t("core.common.buttons.back") }}
{{ $t("core.common.buttons.back") }} </VButton>
</VButton> <VButton size="sm" type="danger" @click="handleCleanup">
<VButton size="sm" type="danger" @click="handleCleanup"> {{ $t("core.page_snapshots.operations.cleanup.button") }}
{{ $t("core.page_snapshots.operations.cleanup.button") }} </VButton>
</VButton>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -214,24 +214,22 @@ watch(
<template> <template>
<VPageHeader :title="$t('core.deleted_post.title')"> <VPageHeader :title="$t('core.deleted_post.title')">
<template #icon> <template #icon>
<IconDeleteBin class="mr-2 self-center text-green-600" /> <IconDeleteBin class="text-green-600" />
</template> </template>
<template #actions> <template #actions>
<VSpace> <VButton :route="{ name: 'Posts' }" size="sm">
<VButton :route="{ name: 'Posts' }" size="sm"> {{ $t("core.common.buttons.back") }}
{{ $t("core.common.buttons.back") }} </VButton>
</VButton> <VButton
<VButton v-permission="['system:posts:manage']"
v-permission="['system:posts:manage']" :route="{ name: 'PostEditor' }"
:route="{ name: 'PostEditor' }" type="secondary"
type="secondary" >
> <template #icon>
<template #icon> <IconAddCircle />
<IconAddCircle /> </template>
</template> {{ $t("core.common.buttons.new") }}
{{ $t("core.common.buttons.new") }} </VButton>
</VButton>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -29,7 +29,6 @@ import {
Toast, Toast,
VButton, VButton,
VPageHeader, VPageHeader,
VSpace,
} from "@halo-dev/components"; } from "@halo-dev/components";
import type { EditorProvider } from "@halo-dev/console-shared"; import type { EditorProvider } from "@halo-dev/console-shared";
import { useLocalStorage } from "@vueuse/core"; import { useLocalStorage } from "@vueuse/core";
@ -503,68 +502,64 @@ useSlugify(
<VPageHeader :title="$t('core.post.title')"> <VPageHeader :title="$t('core.post.title')">
<template #icon> <template #icon>
<IconBookRead class="mr-2 self-center" /> <IconBookRead />
</template> </template>
<template #actions> <template #actions>
<VSpace> <EditorProviderSelector
<EditorProviderSelector v-if="editorProviders.length > 1"
v-if="editorProviders.length > 1" :provider="currentEditorProvider"
:provider="currentEditorProvider" :allow-forced-select="!isUpdateMode"
:allow-forced-select="!isUpdateMode" @select="handleChangeEditorProvider"
@select="handleChangeEditorProvider" />
/> <VButton
<VButton v-if="isUpdateMode"
v-if="isUpdateMode" size="sm"
size="sm" type="default"
type="default" @click="$router.push({ name: 'PostSnapshots', query: { name: name } })"
@click=" >
$router.push({ name: 'PostSnapshots', query: { name: name } }) <template #icon>
" <IconHistoryLine />
> </template>
<template #icon> {{ $t("core.post_editor.actions.snapshots") }}
<IconHistoryLine /> </VButton>
</template> <VButton
{{ $t("core.post_editor.actions.snapshots") }} size="sm"
</VButton> type="default"
<VButton :loading="previewPending"
size="sm" @click="handlePreview"
type="default" >
:loading="previewPending" <template #icon>
@click="handlePreview" <IconEye />
> </template>
<template #icon> {{ $t("core.common.buttons.preview") }}
<IconEye /> </VButton>
</template> <VButton :loading="saving" size="sm" type="default" @click="handleSave">
{{ $t("core.common.buttons.preview") }} <template #icon>
</VButton> <IconSave />
<VButton :loading="saving" size="sm" type="default" @click="handleSave"> </template>
<template #icon> {{ $t("core.common.buttons.save") }}
<IconSave /> </VButton>
</template> <VButton
{{ $t("core.common.buttons.save") }} v-if="isUpdateMode"
</VButton> size="sm"
<VButton type="default"
v-if="isUpdateMode" @click="handleOpenSettingModal"
size="sm" >
type="default" <template #icon>
@click="handleOpenSettingModal" <IconSettings />
> </template>
<template #icon> {{ $t("core.common.buttons.setting") }}
<IconSettings /> </VButton>
</template> <VButton
{{ $t("core.common.buttons.setting") }} type="secondary"
</VButton> :loading="publishing"
<VButton @click="handlePublishClick"
type="secondary" >
:loading="publishing" <template #icon>
@click="handlePublishClick" <IconSendPlaneFill />
> </template>
<template #icon> {{ $t("core.common.buttons.publish") }}
<IconSendPlaneFill /> </VButton>
</template>
{{ $t("core.common.buttons.publish") }}
</VButton>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>
<div class="editor border-t" style="height: calc(100vh - 3.5rem)"> <div class="editor border-t" style="height: calc(100vh - 3.5rem)">

View File

@ -384,31 +384,29 @@ watch(
/> />
<VPageHeader :title="$t('core.post.title')"> <VPageHeader :title="$t('core.post.title')">
<template #icon> <template #icon>
<IconBookRead class="mr-2 self-center" /> <IconBookRead />
</template> </template>
<template #actions> <template #actions>
<VSpace> <VButton :route="{ name: 'Categories' }" size="sm">
<VButton :route="{ name: 'Categories' }" size="sm"> {{ $t("core.post.actions.categories") }}
{{ $t("core.post.actions.categories") }} </VButton>
</VButton> <VButton :route="{ name: 'Tags' }" size="sm">
<VButton :route="{ name: 'Tags' }" size="sm"> {{ $t("core.post.actions.tags") }}
{{ $t("core.post.actions.tags") }} </VButton>
</VButton> <VButton :route="{ name: 'DeletedPosts' }" size="sm">
<VButton :route="{ name: 'DeletedPosts' }" size="sm"> {{ $t("core.post.actions.recycle_bin") }}
{{ $t("core.post.actions.recycle_bin") }} </VButton>
</VButton>
<VButton <VButton
v-permission="['system:posts:manage']" v-permission="['system:posts:manage']"
:route="{ name: 'PostEditor' }" :route="{ name: 'PostEditor' }"
type="secondary" type="secondary"
> >
<template #icon> <template #icon>
<IconAddCircle /> <IconAddCircle />
</template> </template>
{{ $t("core.common.buttons.new") }} {{ $t("core.common.buttons.new") }}
</VButton> </VButton>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -10,7 +10,6 @@ import {
VCard, VCard,
VLoading, VLoading,
VPageHeader, VPageHeader,
VSpace,
} from "@halo-dev/components"; } from "@halo-dev/components";
import { useQuery, useQueryClient } from "@tanstack/vue-query"; import { useQuery, useQueryClient } from "@tanstack/vue-query";
import { useRouteQuery } from "@vueuse/router"; import { useRouteQuery } from "@vueuse/router";
@ -117,17 +116,15 @@ function handleCleanup() {
<template> <template>
<VPageHeader :title="post?.spec.title"> <VPageHeader :title="post?.spec.title">
<template #icon> <template #icon>
<IconHistoryLine class="mr-2 self-center" /> <IconHistoryLine />
</template> </template>
<template #actions> <template #actions>
<VSpace> <VButton size="sm" @click="$router.back()">
<VButton size="sm" @click="$router.back()"> {{ $t("core.common.buttons.back") }}
{{ $t("core.common.buttons.back") }} </VButton>
</VButton> <VButton size="sm" type="danger" @click="handleCleanup">
<VButton size="sm" type="danger" @click="handleCleanup"> {{ $t("core.post_snapshots.operations.cleanup.button") }}
{{ $t("core.post_snapshots.operations.cleanup.button") }} </VButton>
</VButton>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -64,7 +64,7 @@ async function handleUpdateInBatch() {
<CategoryEditingModal v-if="creationModal" @close="creationModal = false" /> <CategoryEditingModal v-if="creationModal" @close="creationModal = false" />
<VPageHeader :title="$t('core.post_category.title')"> <VPageHeader :title="$t('core.post_category.title')">
<template #icon> <template #icon>
<IconBookRead class="mr-2 self-center" /> <IconBookRead />
</template> </template>
<template #actions> <template #actions>

View File

@ -164,7 +164,7 @@ watch(selectedTagNames, (newVal) => {
/> />
<VPageHeader :title="$t('core.post_tag.title')"> <VPageHeader :title="$t('core.post_tag.title')">
<template #icon> <template #icon>
<IconBookRead class="mr-2 self-center" /> <IconBookRead />
</template> </template>
<template #actions> <template #actions>
<VButton <VButton

View File

@ -35,7 +35,7 @@ provide<ComputedRef<DashboardWidgetDefinition[]>>(
<template> <template>
<VPageHeader :title="$t('core.dashboard.title')"> <VPageHeader :title="$t('core.dashboard.title')">
<template #icon> <template #icon>
<IconDashboard class="mr-2 self-center" /> <IconDashboard />
</template> </template>
<template #actions> <template #actions>
<VButton <VButton

View File

@ -13,7 +13,6 @@ import {
VButton, VButton,
VDropdown, VDropdown,
VDropdownItem, VDropdownItem,
VSpace,
VTabbar, VTabbar,
} from "@halo-dev/components"; } from "@halo-dev/components";
import type { import type {
@ -321,12 +320,12 @@ function handleCopyFromLayout(breakpoint: string) {
} }
</script> </script>
<template> <template>
<div <div class="page-header py-1.5">
class="flex items-center justify-between bg-white px-4 py-1.5 gap-5 flex-wrap sticky top-0 z-10" <h2 class="page-header__title">
>
<h2 class="flex items-center truncate text-xl font-bold text-gray-800">
<IconDashboard class="mr-2 self-center" /> <IconDashboard class="mr-2 self-center" />
<span>{{ $t("core.dashboard_designer.title") }}</span> <span class="page-header__title-text">
{{ $t("core.dashboard_designer.title") }}
</span>
</h2> </h2>
<div <div
class="hidden sm:block" class="hidden sm:block"
@ -339,7 +338,7 @@ function handleCopyFromLayout(breakpoint: string) {
@change="handleBreakpointChange" @change="handleBreakpointChange"
></VTabbar> ></VTabbar>
</div> </div>
<VSpace> <div class="page-header__actions">
<VButton ghost @click="handleBack"> <VButton ghost @click="handleBack">
{{ $t("core.common.buttons.back") }} {{ $t("core.common.buttons.back") }}
</VButton> </VButton>
@ -398,7 +397,7 @@ function handleCopyFromLayout(breakpoint: string) {
</template> </template>
{{ $t("core.common.buttons.save") }} {{ $t("core.common.buttons.save") }}
</VButton> </VButton>
</VSpace> </div>
</div> </div>
<div class="dashboard m-4 transition-all" :style="designContainerStyles"> <div class="dashboard m-4 transition-all" :style="designContainerStyles">

View File

@ -246,7 +246,7 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
/> />
<VPageHeader :title="$t('core.menu.title')"> <VPageHeader :title="$t('core.menu.title')">
<template #icon> <template #icon>
<IconListSettings class="mr-2 self-center" /> <IconListSettings />
</template> </template>
</VPageHeader> </VPageHeader>
<div class="m-0 md:m-4"> <div class="m-0 md:m-4">

View File

@ -195,32 +195,30 @@ onMounted(() => {
<BasicLayout> <BasicLayout>
<VPageHeader :title="selectedTheme?.spec.displayName"> <VPageHeader :title="selectedTheme?.spec.displayName">
<template #icon> <template #icon>
<IconPalette class="mr-2 self-center" /> <IconPalette />
</template> </template>
<template #actions> <template #actions>
<VSpace> <VButton
<VButton v-show="!isActivated"
v-show="!isActivated" v-permission="['system:themes:manage']"
v-permission="['system:themes:manage']" size="sm"
size="sm" type="primary"
type="primary" @click="handleActiveTheme()"
@click="handleActiveTheme()" >
> {{ $t("core.common.buttons.activate") }}
{{ $t("core.common.buttons.activate") }} </VButton>
</VButton> <VButton type="default" size="sm" @click="previewModal = true">
<VButton type="default" size="sm" @click="previewModal = true"> <template #icon>
<template #icon> <IconEye />
<IconEye /> </template>
</template> {{ $t("core.common.buttons.preview") }}
{{ $t("core.common.buttons.preview") }} </VButton>
</VButton> <VButton type="secondary" @click="themesModal = true">
<VButton type="secondary" @click="themesModal = true"> <template #icon>
<template #icon> <IconListSettings />
<IconListSettings /> </template>
</template> {{ $t("core.theme.actions.management") }}
{{ $t("core.theme.actions.management") }} </VButton>
</VButton>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -166,7 +166,6 @@ const description = computed(() => {
<VAvatar <VAvatar
:src="authProvider?.spec.logo" :src="authProvider?.spec.logo"
:alt="authProvider?.spec.displayName" :alt="authProvider?.spec.displayName"
class="mr-2"
size="sm" size="sm"
/> />
</template> </template>

View File

@ -71,7 +71,7 @@ async function onSortUpdate() {
<template> <template>
<VPageHeader :title="$t('core.identity_authentication.title')"> <VPageHeader :title="$t('core.identity_authentication.title')">
<template #icon> <template #icon>
<IconLockPasswordLine class="mr-2 self-center" /> <IconLockPasswordLine />
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -63,7 +63,7 @@ onMounted(async () => {
<template> <template>
<VPageHeader :title="$t('core.backup.title')"> <VPageHeader :title="$t('core.backup.title')">
<template #icon> <template #icon>
<IconServerLine class="mr-2 self-center" /> <IconServerLine />
</template> </template>
<template #actions> <template #actions>
<VButton type="secondary" @click="handleCreate"> <VButton type="secondary" @click="handleCreate">

View File

@ -195,7 +195,7 @@ const handleDownloadLogfile = () => {
<template> <template>
<VPageHeader :title="$t('core.overview.title')"> <VPageHeader :title="$t('core.overview.title')">
<template #icon> <template #icon>
<IconTerminalBoxLine class="mr-2 self-center" /> <IconTerminalBoxLine />
</template> </template>
<template #actions> <template #actions>
<VButton size="sm" @click="handleCopy"> <VButton size="sm" @click="handleCopy">

View File

@ -26,7 +26,6 @@ provide<Ref<Setting | undefined>>("setting", setting);
v-if="plugin" v-if="plugin"
:src="plugin.status?.logo" :src="plugin.status?.logo"
:alt="plugin.spec.displayName" :alt="plugin.spec.displayName"
class="mr-2"
size="sm" size="sm"
/> />
</template> </template>

View File

@ -48,7 +48,7 @@ watch(
<template> <template>
<VPageHeader :title="$t('core.plugin.extension-settings.title')"> <VPageHeader :title="$t('core.plugin.extension-settings.title')">
<template #icon> <template #icon>
<IconSettings class="mr-2 self-center" /> <IconSettings />
</template> </template>
<template #actions> <template #actions>
<VButton size="sm" @click="$router.back()"> <VButton size="sm" @click="$router.back()">

View File

@ -159,33 +159,31 @@ onMounted(() => {
<VPageHeader :title="$t('core.plugin.title')"> <VPageHeader :title="$t('core.plugin.title')">
<template #icon> <template #icon>
<IconPlug class="mr-2 self-center" /> <IconPlug />
</template> </template>
<template #actions> <template #actions>
<VSpace> <HasPermission :permissions="['*']">
<HasPermission :permissions="['*']">
<VButton
size="sm"
@click="$router.push({ name: 'PluginExtensionPointSettings' })"
>
<template #icon>
<IconSettings />
</template>
{{ $t("core.plugin.actions.extension-point-settings") }}
</VButton>
</HasPermission>
<VButton <VButton
v-permission="['system:plugins:manage']" size="sm"
type="secondary" @click="$router.push({ name: 'PluginExtensionPointSettings' })"
@click="pluginInstallationModalVisible = true"
> >
<template #icon> <template #icon>
<IconAddCircle /> <IconSettings />
</template> </template>
{{ $t("core.common.buttons.install") }} {{ $t("core.plugin.actions.extension-point-settings") }}
</VButton> </VButton>
</VSpace> </HasPermission>
<VButton
v-permission="['system:plugins:manage']"
type="secondary"
@click="pluginInstallationModalVisible = true"
>
<template #icon>
<IconAddCircle />
</template>
{{ $t("core.common.buttons.install") }}
</VButton>
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -104,7 +104,7 @@ const handleUpdateRole = async () => {
<template> <template>
<VPageHeader :title="$t('core.role.detail.title')"> <VPageHeader :title="$t('core.role.detail.title')">
<template #icon> <template #icon>
<IconShieldUser class="mr-2 self-center" /> <IconShieldUser />
</template> </template>
</VPageHeader> </VPageHeader>
<div class="m-0 md:m-4"> <div class="m-0 md:m-4">

View File

@ -207,7 +207,7 @@ const handleDelete = async (role: Role) => {
<VPageHeader :title="$t('core.role.title')"> <VPageHeader :title="$t('core.role.title')">
<template #icon> <template #icon>
<IconShieldUser class="mr-2 self-center" /> <IconShieldUser />
</template> </template>
<template #actions> <template #actions>
<VButton <VButton

View File

@ -80,7 +80,7 @@ provide<Ref<Setting | undefined>>("setting", setting);
<template> <template>
<VPageHeader :title="$t('core.setting.title')"> <VPageHeader :title="$t('core.setting.title')">
<template #icon> <template #icon>
<IconSettings class="mr-2 self-center" /> <IconSettings />
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -43,7 +43,7 @@ const routes = computed(() => {
<template> <template>
<VPageHeader :title="$t('core.tool.title')"> <VPageHeader :title="$t('core.tool.title')">
<template #icon> <template #icon>
<IconToolsFill class="mr-2 self-center" /> <IconToolsFill />
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -187,40 +187,38 @@ function onCreationModalClose() {
<VPageHeader :title="$t('core.user.title')"> <VPageHeader :title="$t('core.user.title')">
<template #icon> <template #icon>
<IconUserSettings class="mr-2 self-center" /> <IconUserSettings />
</template> </template>
<template #actions> <template #actions>
<VSpace> <VButton
<VButton v-permission="['system:roles:view']"
v-permission="['system:roles:view']" :route="{ name: 'Roles' }"
:route="{ name: 'Roles' }" size="sm"
size="sm" type="default"
type="default" >
> <template #icon>
<IconShieldUser />
</template>
{{ $t("core.user.actions.roles") }}
</VButton>
<HasPermission :permissions="['*']">
<VButton :route="{ name: 'AuthProviders' }" size="sm" type="default">
<template #icon> <template #icon>
<IconShieldUser /> <IconLockPasswordLine />
</template> </template>
{{ $t("core.user.actions.roles") }} {{ $t("core.user.actions.identity_authentication") }}
</VButton> </VButton>
<HasPermission :permissions="['*']"> </HasPermission>
<VButton :route="{ name: 'AuthProviders' }" size="sm" type="default"> <VButton
<template #icon> v-permission="['system:users:manage']"
<IconLockPasswordLine /> type="secondary"
</template> @click="creationModal = true"
{{ $t("core.user.actions.identity_authentication") }} >
</VButton> <template #icon>
</HasPermission> <IconAddCircle />
<VButton </template>
v-permission="['system:users:manage']" {{ $t("core.common.buttons.new") }}
type="secondary" </VButton>
@click="creationModal = true"
>
<template #icon>
<IconAddCircle />
</template>
{{ $t("core.common.buttons.new") }}
</VButton>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>

View File

@ -4,15 +4,53 @@ defineProps<{
}>(); }>();
</script> </script>
<template> <template>
<div class="flex items-center justify-between bg-white p-4 h-14"> <div class="page-header">
<div class="min-w-0 flex-1 self-center"> <h2 class="page-header__title">
<h2 class="flex items-center truncate text-xl font-bold text-gray-800"> <slot name="icon" />
<slot name="icon" /> <span class="page-header__title-text">{{ title }}</span>
<span>{{ title }}</span> </h2>
</h2> <div class="page-header__actions">
</div>
<div class="self-center">
<slot name="actions" /> <slot name="actions" />
</div> </div>
</div> </div>
</template> </template>
<style lang="scss">
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 3.5rem;
background-color: theme("colors.white");
padding-left: 1rem;
padding-right: 1rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
flex-wrap: wrap;
gap: theme("spacing.2");
&__title {
display: flex;
align-items: center;
text-overflow: truncate;
font-size: 1.25rem;
font-weight: 700;
color: theme("colors.gray.800");
gap: theme("spacing.2");
}
&__title-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: theme("spacing.2");
flex-wrap: wrap;
}
}
</style>

View File

@ -34,7 +34,9 @@ fetchEditorProviders();
class="group flex w-full cursor-pointer items-center gap-2 rounded p-1 hover:bg-gray-100" class="group flex w-full cursor-pointer items-center gap-2 rounded p-1 hover:bg-gray-100"
> >
<VAvatar v-if="provider?.logo" :src="provider.logo" size="xs"></VAvatar> <VAvatar v-if="provider?.logo" :src="provider.logo" size="xs"></VAvatar>
<div class="select-none text-sm text-gray-600 group-hover:text-gray-900"> <div
class="select-none text-sm text-gray-600 group-hover:text-gray-900 whitespace-nowrap"
>
{{ provider?.displayName }} {{ provider?.displayName }}
</div> </div>
<IconExchange class="h-4 w-4 text-gray-600 group-hover:text-gray-900" /> <IconExchange class="h-4 w-4 text-gray-600 group-hover:text-gray-900" />

View File

@ -22,7 +22,6 @@ import {
Toast, Toast,
VButton, VButton,
VPageHeader, VPageHeader,
VSpace,
} from "@halo-dev/components"; } from "@halo-dev/components";
import type { EditorProvider } from "@halo-dev/console-shared"; import type { EditorProvider } from "@halo-dev/console-shared";
import { useMutation } from "@tanstack/vue-query"; import { useMutation } from "@tanstack/vue-query";
@ -467,51 +466,49 @@ useSlugify(
<template> <template>
<VPageHeader :title="$t('core.post.title')"> <VPageHeader :title="$t('core.post.title')">
<template #icon> <template #icon>
<IconBookRead class="mr-2 self-center" /> <IconBookRead />
</template> </template>
<template #actions> <template #actions>
<VSpace> <EditorProviderSelector
<EditorProviderSelector v-if="editorProviders.length > 1"
v-if="editorProviders.length > 1" :provider="currentEditorProvider"
:provider="currentEditorProvider" :allow-forced-select="!isUpdateMode"
:allow-forced-select="!isUpdateMode" @select="handleChangeEditorProvider"
@select="handleChangeEditorProvider" />
/> <VButton
size="sm"
type="default"
:loading="isSaving && !isPublishing"
@click="handleSaveClick"
>
<template #icon>
<IconSave />
</template>
{{ $t("core.common.buttons.save") }}
</VButton>
<VButton
v-if="isUpdateMode"
size="sm"
type="default"
@click="handleOpenPostSettingEditModal"
>
<template #icon>
<IconSettings />
</template>
{{ $t("core.common.buttons.setting") }}
</VButton>
<HasPermission :permissions="['uc:posts:publish']">
<VButton <VButton
size="sm" :loading="isPublishing"
type="default" type="secondary"
:loading="isSaving && !isPublishing" @click="handlePublishClick"
@click="handleSaveClick"
> >
<template #icon> <template #icon>
<IconSave /> <IconSendPlaneFill />
</template> </template>
{{ $t("core.common.buttons.save") }} {{ $t("core.common.buttons.publish") }}
</VButton> </VButton>
<VButton </HasPermission>
v-if="isUpdateMode"
size="sm"
type="default"
@click="handleOpenPostSettingEditModal"
>
<template #icon>
<IconSettings />
</template>
{{ $t("core.common.buttons.setting") }}
</VButton>
<HasPermission :permissions="['uc:posts:publish']">
<VButton
:loading="isPublishing"
type="secondary"
@click="handlePublishClick"
>
<template #icon>
<IconSendPlaneFill />
</template>
{{ $t("core.common.buttons.publish") }}
</VButton>
</HasPermission>
</VSpace>
</template> </template>
</VPageHeader> </VPageHeader>
<div class="editor border-t" style="height: calc(100vh - 3.5rem)"> <div class="editor border-t" style="height: calc(100vh - 3.5rem)">

View File

@ -104,7 +104,7 @@ const {
<template> <template>
<VPageHeader :title="$t('core.uc_post.title')"> <VPageHeader :title="$t('core.uc_post.title')">
<template #icon> <template #icon>
<IconBookRead class="mr-2 self-center" /> <IconBookRead />
</template> </template>
<template #actions> <template #actions>
<VButton :route="{ name: 'PostEditor' }" type="secondary"> <VButton :route="{ name: 'PostEditor' }" type="secondary">

View File

@ -131,7 +131,7 @@ function handleMarkAllAsRead() {
<template> <template>
<VPageHeader :title="$t('core.uc_notification.title')"> <VPageHeader :title="$t('core.uc_notification.title')">
<template #icon> <template #icon>
<IconNotificationBadgeLine class="mr-2 self-center" /> <IconNotificationBadgeLine />
</template> </template>
</VPageHeader> </VPageHeader>
<div class="m-0 md:m-4"> <div class="m-0 md:m-4">