refactor: move entity component to @halo-dev/components package

Signed-off-by: Ryan Wang <i@ryanc.cc>
pull/613/head
Ryan Wang 2022-09-14 11:17:15 +08:00
parent a49d03d025
commit 6892135644
18 changed files with 283 additions and 196 deletions

View File

@ -19,3 +19,4 @@ export * from "./components/pagination";
export * from "./components/codemirror"; export * from "./components/codemirror";
export * from "./components/empty"; export * from "./components/empty";
export * from "./components/status"; export * from "./components/status";
export * from "./components/entity";

View File

@ -0,0 +1,89 @@
<script lang="ts" setup>
import { IconMore, VSpace } from "@halo-dev/components";
import { computed } from "vue";
const props = withDefaults(
defineProps<{
isSelected?: boolean;
}>(),
{
isSelected: false,
}
);
const classes = computed(() => {
const result = [];
if (props.isSelected) {
result.push("entity-selected");
}
return result;
});
</script>
<template>
<div :class="classes" class="entity-wrapper group">
<div v-show="isSelected" class="entity-selected-indicator"></div>
<slot name="prepend" />
<div class="entity-body">
<div v-if="$slots.checkbox" class="entity-checkbox">
<slot name="checkbox" />
</div>
<div class="entity-start">
<slot name="start" />
</div>
<div class="entity-end">
<slot name="end" />
</div>
<div v-if="$slots.dropdownItems" class="entity-dropdown">
<FloatingDropdown>
<div class="entity-dropdown-trigger group-hover:bg-gray-100">
<IconMore />
</div>
<template #popper>
<div class="w-48 p-2">
<VSpace class="w-full" direction="column">
<slot name="dropdownItems"></slot>
</VSpace>
</div>
</template>
</FloatingDropdown>
</div>
</div>
</div>
</template>
<style lang="scss">
.entity-wrapper {
@apply relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50;
&.entity-selected {
@apply bg-gray-100;
}
.entity-selected-indicator {
@apply absolute inset-y-0 left-0 w-0.5 bg-primary;
}
.entity-body {
@apply relative flex flex-row items-center;
}
.entity-checkbox {
@apply mr-4 hidden items-center sm:flex;
}
.entity-start {
@apply flex flex-1 items-center gap-4;
}
.entity-end {
@apply flex flex-col items-end gap-4 sm:flex-row sm:items-center sm:gap-6;
}
.entity-dropdown {
@apply ml-4 inline-flex items-center;
}
.entity-dropdown-trigger {
@apply cursor-pointer rounded p-1 transition-all hover:text-blue-600;
}
}
</style>

View File

@ -20,16 +20,10 @@ const emit = defineEmits<{
</script> </script>
<template> <template>
<div class="inline-flex flex-col gap-1"> <div class="entity-field-wrapper">
<div <div v-if="title || $slots.title" class="entity-field-title-body">
v-if="title || $slots.title"
class="inline-flex flex-col items-center sm:flex-row"
>
<slot name="title"> <slot name="title">
<div <div class="entity-field-title" @click="emit('click')">
class="mr-0 truncate text-sm font-medium text-gray-900 sm:mr-2"
@click="emit('click')"
>
<RouterLink v-if="route" :to="route"> <RouterLink v-if="route" :to="route">
{{ title }} {{ title }}
</RouterLink> </RouterLink>
@ -42,13 +36,35 @@ const emit = defineEmits<{
</div> </div>
<div <div
v-if="description || $slots.description" v-if="description || $slots.description"
class="inline-flex items-center" class="entity-field-description-body"
> >
<slot name="description"> <slot name="description">
<span v-if="description" class="text-xs text-gray-500"> <span v-if="description" class="entity-field-description">
{{ description }} {{ description }}
</span> </span>
</slot> </slot>
</div> </div>
</div> </div>
</template> </template>
<style lang="scss">
.entity-field-wrapper {
@apply inline-flex flex-col gap-1;
.entity-field-title-body {
@apply inline-flex flex-col items-center sm:flex-row;
.entity-field-title {
@apply mr-0 truncate text-sm font-medium text-gray-900 sm:mr-2;
}
}
.entity-field-description-body {
@apply inline-flex items-center;
.entity-field-description {
@apply text-xs text-gray-500;
}
}
}
</style>

View File

@ -0,0 +1,9 @@
import { mount } from "@vue/test-utils";
import { describe, expect, it } from "vitest";
import { VEntity } from "..";
describe("Entity", () => {
it("should render", () => {
expect(mount(VEntity)).toBeDefined();
});
});

View File

@ -0,0 +1,9 @@
import { mount } from "@vue/test-utils";
import { describe, expect, it } from "vitest";
import { VEntityField } from "..";
describe("EntityField", () => {
it("should render", () => {
expect(mount(VEntityField)).toBeDefined();
});
});

View File

@ -0,0 +1,2 @@
export { default as VEntity } from "./Entity.vue";
export { default as VEntityField } from "./EntityField.vue";

View File

@ -1,55 +0,0 @@
<script lang="ts" setup>
import { IconMore, VSpace } from "@halo-dev/components";
withDefaults(
defineProps<{
isSelected?: boolean;
}>(),
{
isSelected: false,
}
);
</script>
<template>
<div
:class="{
'bg-gray-100': isSelected,
}"
class="group relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50"
>
<div
v-show="isSelected"
class="absolute inset-y-0 left-0 w-0.5 bg-primary"
></div>
<slot name="prepend" />
<div class="relative flex flex-row items-center">
<div v-if="$slots.checkbox" class="mr-4 hidden items-center sm:flex">
<slot name="checkbox" />
</div>
<div class="flex flex-1 items-center gap-4">
<slot name="start" />
</div>
<div
class="flex flex-col items-end gap-4 sm:flex-row sm:items-center sm:gap-6"
>
<slot name="end" />
</div>
<div v-if="$slots.menuItems" class="ml-4 inline-flex items-center">
<FloatingDropdown>
<div
class="cursor-pointer rounded p-1 transition-all hover:text-blue-600 group-hover:bg-gray-100"
>
<IconMore />
</div>
<template #popper>
<div class="w-48 p-2">
<VSpace class="w-full" direction="column">
<slot name="menuItems"></slot>
</VSpace>
</div>
</template>
</FloatingDropdown>
</div>
</div>
</div>
</template>

View File

@ -17,6 +17,8 @@ import {
IconCloseCircle, IconCloseCircle,
IconFolder, IconFolder,
VStatusDot, VStatusDot,
VEntity,
VEntityField,
} from "@halo-dev/components"; } from "@halo-dev/components";
import LazyImage from "@/components/image/LazyImage.vue"; import LazyImage from "@/components/image/LazyImage.vue";
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue"; import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
@ -36,8 +38,6 @@ import cloneDeep from "lodash.clonedeep";
import { isImage } from "@/utils/image"; import { isImage } from "@/utils/image";
import { useRouteQuery } from "@vueuse/router"; import { useRouteQuery } from "@vueuse/router";
import { useFetchAttachmentGroup } from "./composables/use-attachment-group"; import { useFetchAttachmentGroup } from "./composables/use-attachment-group";
import Entity from "@/components/entity/Entity.vue";
import EntityField from "@/components/entity/EntityField.vue";
const policyVisible = ref(false); const policyVisible = ref(false);
const uploadVisible = ref(false); const uploadVisible = ref(false);
@ -557,7 +557,7 @@ onMounted(() => {
role="list" role="list"
> >
<li v-for="(attachment, index) in attachments.items" :key="index"> <li v-for="(attachment, index) in attachments.items" :key="index">
<Entity :is-selected="isChecked(attachment)"> <VEntity :is-selected="isChecked(attachment)">
<template #checkbox> <template #checkbox>
<input <input
:checked="selectedAttachments.has(attachment)" :checked="selectedAttachments.has(attachment)"
@ -567,7 +567,7 @@ onMounted(() => {
/> />
</template> </template>
<template #start> <template #start>
<EntityField> <VEntityField>
<template #description> <template #description>
<div <div
class="h-10 w-10 rounded border bg-white p-1 hover:shadow-sm" class="h-10 w-10 rounded border bg-white p-1 hover:shadow-sm"
@ -580,8 +580,8 @@ onMounted(() => {
/> />
</div> </div>
</template> </template>
</EntityField> </VEntityField>
<EntityField <VEntityField
:title="attachment.spec.displayName" :title="attachment.spec.displayName"
@click="handleClickItem(attachment)" @click="handleClickItem(attachment)"
> >
@ -595,15 +595,15 @@ onMounted(() => {
</span> </span>
</VSpace> </VSpace>
</template> </template>
</EntityField> </VEntityField>
</template> </template>
<template #end> <template #end>
<EntityField <VEntityField
:description=" :description="
getPolicyName(attachment.spec.policyRef?.name) getPolicyName(attachment.spec.policyRef?.name)
" "
/> />
<EntityField> <VEntityField>
<template #description> <template #description>
<RouterLink <RouterLink
:to="{ :to="{
@ -615,8 +615,8 @@ onMounted(() => {
{{ attachment.spec.uploadedBy?.name }} {{ attachment.spec.uploadedBy?.name }}
</RouterLink> </RouterLink>
</template> </template>
</EntityField> </VEntityField>
<EntityField v-if="attachment.metadata.deletionTimestamp"> <VEntityField v-if="attachment.metadata.deletionTimestamp">
<template #description> <template #description>
<VStatusDot <VStatusDot
v-tooltip="`删除中`" v-tooltip="`删除中`"
@ -624,17 +624,17 @@ onMounted(() => {
animate animate
/> />
</template> </template>
</EntityField> </VEntityField>
<EntityField <VEntityField
:description=" :description="
formatDatetime(attachment.metadata.creationTimestamp) formatDatetime(attachment.metadata.creationTimestamp)
" "
/> />
</template> </template>
<template #menuItems> <template #dropdownItems>
<VButton v-close-popper block type="danger"> 删除 </VButton> <VButton v-close-popper block type="danger"> 删除 </VButton>
</template> </template>
</Entity> </VEntity>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -1,10 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import { VEmpty, VSpace, VButton, IconAddCircle } from "@halo-dev/components"; import {
VEmpty,
VSpace,
VButton,
IconAddCircle,
VEntity,
VEntityField,
} from "@halo-dev/components";
import type { PagesPublicState } from "@halo-dev/admin-shared"; import type { PagesPublicState } from "@halo-dev/admin-shared";
import { useExtensionPointsState } from "@/composables/usePlugins"; import { useExtensionPointsState } from "@/composables/usePlugins";
import Entity from "@/components/entity/Entity.vue";
import EntityField from "@/components/entity/EntityField.vue";
const pagesPublicState = ref<PagesPublicState>({ const pagesPublicState = ref<PagesPublicState>({
functionalPages: [], functionalPages: [],
@ -40,15 +45,15 @@ useExtensionPointsState("PAGES", pagesPublicState);
:key="index" :key="index"
v-permission="page.permissions" v-permission="page.permissions"
> >
<Entity> <VEntity>
<template #start> <template #start>
<EntityField <VEntityField
:title="page.name" :title="page.name"
:route="page.path" :route="page.path"
:description="page.url" :description="page.url"
></EntityField> ></VEntityField>
</template> </template>
</Entity> </VEntity>
</li> </li>
</ul> </ul>
</template> </template>

View File

@ -16,6 +16,8 @@ import {
VEmpty, VEmpty,
VAvatar, VAvatar,
VStatusDot, VStatusDot,
VEntity,
VEntityField,
} from "@halo-dev/components"; } from "@halo-dev/components";
import SinglePageSettingModal from "./components/SinglePageSettingModal.vue"; import SinglePageSettingModal from "./components/SinglePageSettingModal.vue";
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue"; import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
@ -30,8 +32,6 @@ import { apiClient } from "@halo-dev/admin-shared";
import { formatDatetime } from "@/utils/date"; import { formatDatetime } from "@/utils/date";
import { RouterLink } from "vue-router"; import { RouterLink } from "vue-router";
import cloneDeep from "lodash.clonedeep"; import cloneDeep from "lodash.clonedeep";
import Entity from "../../../components/entity/Entity.vue";
import EntityField from "../../../components/entity/EntityField.vue";
enum SinglePagePhase { enum SinglePagePhase {
DRAFT = "未发布", DRAFT = "未发布",
@ -334,7 +334,7 @@ const handleSelectUser = (user?: User) => {
role="list" role="list"
> >
<li v-for="(singlePage, index) in singlePages.items" :key="index"> <li v-for="(singlePage, index) in singlePages.items" :key="index">
<Entity :is-selected="checkAll"> <VEntity :is-selected="checkAll">
<template #checkbox> <template #checkbox>
<input <input
v-model="checkAll" v-model="checkAll"
@ -343,7 +343,7 @@ const handleSelectUser = (user?: User) => {
/> />
</template> </template>
<template #start> <template #start>
<EntityField <VEntityField
:title="singlePage.page.spec.title" :title="singlePage.page.spec.title"
:description="singlePage.page.status?.permalink" :description="singlePage.page.status?.permalink"
:route="{ :route="{
@ -364,10 +364,10 @@ const handleSelectUser = (user?: User) => {
<VStatusDot state="success" animate /> <VStatusDot state="success" animate />
</RouterLink> </RouterLink>
</template> </template>
</EntityField> </VEntityField>
</template> </template>
<template #end> <template #end>
<EntityField> <VEntityField>
<template #description> <template #description>
<RouterLink <RouterLink
v-for="( v-for="(
@ -389,9 +389,9 @@ const handleSelectUser = (user?: User) => {
></VAvatar> ></VAvatar>
</RouterLink> </RouterLink>
</template> </template>
</EntityField> </VEntityField>
<EntityField :description="finalStatus(singlePage.page)" /> <VEntityField :description="finalStatus(singlePage.page)" />
<EntityField> <VEntityField>
<template #description> <template #description>
<IconEye <IconEye
v-if="singlePage.page.spec.visible === 'PUBLIC'" v-if="singlePage.page.spec.visible === 'PUBLIC'"
@ -409,14 +409,14 @@ const handleSelectUser = (user?: User) => {
class="cursor-pointer text-sm transition-all hover:text-blue-600" class="cursor-pointer text-sm transition-all hover:text-blue-600"
/> />
</template> </template>
</EntityField> </VEntityField>
<EntityField <VEntityField
:description=" :description="
formatDatetime(singlePage.page.metadata.creationTimestamp) formatDatetime(singlePage.page.metadata.creationTimestamp)
" "
/> />
</template> </template>
<template #menuItems> <template #dropdownItems>
<VButton <VButton
v-close-popper v-close-popper
block block
@ -434,7 +434,7 @@ const handleSelectUser = (user?: User) => {
删除 删除
</VButton> </VButton>
</template> </template>
</Entity> </VEntity>
</li> </li>
</ul> </ul>

View File

@ -18,11 +18,11 @@ import {
VSpace, VSpace,
VAvatar, VAvatar,
VStatusDot, VStatusDot,
VEntity,
VEntityField,
} from "@halo-dev/components"; } from "@halo-dev/components";
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue"; import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
import PostSettingModal from "./components/PostSettingModal.vue"; import PostSettingModal from "./components/PostSettingModal.vue";
import Entity from "@/components/entity/Entity.vue";
import EntityField from "@/components/entity/EntityField.vue";
import PostTag from "../posts/tags/components/PostTag.vue"; import PostTag from "../posts/tags/components/PostTag.vue";
import { onMounted, ref, watch, watchEffect } from "vue"; import { onMounted, ref, watch, watchEffect } from "vue";
import type { import type {
@ -740,7 +740,7 @@ function handleContributorFilterItemChange(user?: User) {
role="list" role="list"
> >
<li v-for="(post, index) in posts.items" :key="index"> <li v-for="(post, index) in posts.items" :key="index">
<Entity :is-selected="checkSelection(post.post)"> <VEntity :is-selected="checkSelection(post.post)">
<template #checkbox> <template #checkbox>
<input <input
v-model="selectedPostNames" v-model="selectedPostNames"
@ -751,7 +751,7 @@ function handleContributorFilterItemChange(user?: User) {
/> />
</template> </template>
<template #start> <template #start>
<EntityField <VEntityField
:title="post.post.spec.title" :title="post.post.spec.title"
:route="{ :route="{
name: 'PostEditor', name: 'PostEditor',
@ -797,10 +797,10 @@ function handleContributorFilterItemChange(user?: User) {
<span class="text-xs text-gray-500"> 评论 0 </span> <span class="text-xs text-gray-500"> 评论 0 </span>
</VSpace> </VSpace>
</template> </template>
</EntityField> </VEntityField>
</template> </template>
<template #end> <template #end>
<EntityField> <VEntityField>
<template #description> <template #description>
<RouterLink <RouterLink
v-for="(contributor, contributorIndex) in post.contributors" v-for="(contributor, contributorIndex) in post.contributors"
@ -820,9 +820,11 @@ function handleContributorFilterItemChange(user?: User) {
></VAvatar> ></VAvatar>
</RouterLink> </RouterLink>
</template> </template>
</EntityField> </VEntityField>
<EntityField :description="finalStatus(post.post)"></EntityField> <VEntityField
<EntityField> :description="finalStatus(post.post)"
></VEntityField>
<VEntityField>
<template #description> <template #description>
<IconEye <IconEye
v-if="post.post.spec.visible === 'PUBLIC'" v-if="post.post.spec.visible === 'PUBLIC'"
@ -840,14 +842,14 @@ function handleContributorFilterItemChange(user?: User) {
class="cursor-pointer text-sm transition-all hover:text-blue-600" class="cursor-pointer text-sm transition-all hover:text-blue-600"
/> />
</template> </template>
</EntityField> </VEntityField>
<EntityField <VEntityField
:description=" :description="
formatDatetime(post.post.metadata.creationTimestamp) formatDatetime(post.post.metadata.creationTimestamp)
" "
/> />
</template> </template>
<template #menuItems> <template #dropdownItems>
<VButton <VButton
v-close-popper v-close-popper
block block
@ -865,7 +867,7 @@ function handleContributorFilterItemChange(user?: User) {
删除 删除
</VButton> </VButton>
</template> </template>
</Entity> </VEntity>
</li> </li>
</ul> </ul>

View File

@ -1,11 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import { IconList, VButton, VStatusDot } from "@halo-dev/components"; import {
IconList,
VButton,
VStatusDot,
VEntity,
VEntityField,
} from "@halo-dev/components";
import Draggable from "vuedraggable"; import Draggable from "vuedraggable";
import type { CategoryTree } from "../utils"; import type { CategoryTree } from "../utils";
import { ref } from "vue"; import { ref } from "vue";
import { formatDatetime } from "@/utils/date"; import { formatDatetime } from "@/utils/date";
import Entity from "@/components/entity/Entity.vue";
import EntityField from "@/components/entity/EntityField.vue";
withDefaults( withDefaults(
defineProps<{ defineProps<{
@ -51,7 +55,7 @@ function onDelete(category: CategoryTree) {
> >
<template #item="{ element: category }"> <template #item="{ element: category }">
<li> <li>
<Entity> <VEntity>
<template #prepend> <template #prepend>
<div <div
class="drag-element absolute inset-y-0 left-0 hidden w-3.5 cursor-move items-center bg-gray-100 transition-all hover:bg-gray-200 group-hover:flex" class="drag-element absolute inset-y-0 left-0 hidden w-3.5 cursor-move items-center bg-gray-100 transition-all hover:bg-gray-200 group-hover:flex"
@ -60,25 +64,25 @@ function onDelete(category: CategoryTree) {
</div> </div>
</template> </template>
<template #start> <template #start>
<EntityField <VEntityField
:title="category.spec.displayName" :title="category.spec.displayName"
:description="category.status.permalink" :description="category.status.permalink"
/> />
</template> </template>
<template #end> <template #end>
<EntityField v-if="category.metadata.deletionTimestamp"> <VEntityField v-if="category.metadata.deletionTimestamp">
<template #description> <template #description>
<VStatusDot v-tooltip="``" state="warning" animate /> <VStatusDot v-tooltip="``" state="warning" animate />
</template> </template>
</EntityField> </VEntityField>
<EntityField <VEntityField
:description="`${category.status?.posts?.length || 0} 篇文章`" :description="`${category.status?.posts?.length || 0} 篇文章`"
/> />
<EntityField <VEntityField
:description="formatDatetime(category.metadata.creationTimestamp)" :description="formatDatetime(category.metadata.creationTimestamp)"
/> />
</template> </template>
<template #menuItems> <template #dropdownItems>
<VButton <VButton
v-close-popper v-close-popper
block block
@ -96,7 +100,7 @@ function onDelete(category: CategoryTree) {
删除 删除
</VButton> </VButton>
</template> </template>
</Entity> </VEntity>
<CategoryListItem <CategoryListItem
:categories="category.spec.children" :categories="category.spec.children"
class="pl-10 transition-all duration-300" class="pl-10 transition-all duration-300"

View File

@ -14,11 +14,11 @@ import {
VPageHeader, VPageHeader,
VSpace, VSpace,
VStatusDot, VStatusDot,
VEntity,
VEntityField,
} from "@halo-dev/components"; } from "@halo-dev/components";
import TagEditingModal from "./components/TagEditingModal.vue"; import TagEditingModal from "./components/TagEditingModal.vue";
import PostTag from "./components/PostTag.vue"; import PostTag from "./components/PostTag.vue";
import Entity from "@/components/entity/Entity.vue";
import EntityField from "@/components/entity/EntityField.vue";
// types // types
import type { Tag } from "@halo-dev/api-client"; import type { Tag } from "@halo-dev/api-client";
@ -175,30 +175,30 @@ onMounted(async () => {
role="list" role="list"
> >
<li v-for="(tag, index) in tags" :key="index"> <li v-for="(tag, index) in tags" :key="index">
<Entity <VEntity
:is-selected="selectedTag?.metadata.name === tag.metadata.name" :is-selected="selectedTag?.metadata.name === tag.metadata.name"
> >
<template #start> <template #start>
<EntityField :description="tag.status?.permalink"> <VEntityField :description="tag.status?.permalink">
<template #title> <template #title>
<PostTag :tag="tag" /> <PostTag :tag="tag" />
</template> </template>
</EntityField> </VEntityField>
</template> </template>
<template #end> <template #end>
<EntityField v-if="tag.metadata.deletionTimestamp"> <VEntityField v-if="tag.metadata.deletionTimestamp">
<template #description> <template #description>
<VStatusDot v-tooltip="``" state="warning" animate /> <VStatusDot v-tooltip="``" state="warning" animate />
</template> </template>
</EntityField> </VEntityField>
<EntityField <VEntityField
:description="`${tag.status?.posts?.length || 0} 篇文章`" :description="`${tag.status?.posts?.length || 0} 篇文章`"
/> />
<EntityField <VEntityField
:description="formatDatetime(tag.metadata.creationTimestamp)" :description="formatDatetime(tag.metadata.creationTimestamp)"
/> />
</template> </template>
<template #menuItems> <template #dropdownItems>
<VButton <VButton
v-close-popper v-close-popper
block block
@ -216,7 +216,7 @@ onMounted(async () => {
删除 删除
</VButton> </VButton>
</template> </template>
</Entity> </VEntity>
</li> </li>
</ul> </ul>

View File

@ -1,10 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import { IconList, VButton, VTag, VStatusDot } from "@halo-dev/components"; import {
IconList,
VButton,
VTag,
VStatusDot,
VEntity,
VEntityField,
} from "@halo-dev/components";
import Draggable from "vuedraggable"; import Draggable from "vuedraggable";
import { ref } from "vue"; import { ref } from "vue";
import type { MenuTreeItem } from "@/modules/interface/menus/utils"; import type { MenuTreeItem } from "@/modules/interface/menus/utils";
import Entity from "@/components/entity/Entity.vue";
import EntityField from "@/components/entity/EntityField.vue";
withDefaults( withDefaults(
defineProps<{ defineProps<{
@ -66,7 +71,7 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
> >
<template #item="{ element: menuItem }"> <template #item="{ element: menuItem }">
<li> <li>
<Entity> <VEntity>
<template #prepend> <template #prepend>
<div <div
class="drag-element absolute inset-y-0 left-0 hidden w-3.5 cursor-move items-center bg-gray-100 transition-all hover:bg-gray-200 group-hover:flex" class="drag-element absolute inset-y-0 left-0 hidden w-3.5 cursor-move items-center bg-gray-100 transition-all hover:bg-gray-200 group-hover:flex"
@ -75,7 +80,7 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
</div> </div>
</template> </template>
<template #start> <template #start>
<EntityField <VEntityField
:title="menuItem.status.displayName" :title="menuItem.status.displayName"
:description="menuItem.status.href" :description="menuItem.status.href"
> >
@ -84,16 +89,16 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
{{ getMenuItemRefDisplayName(menuItem) }} {{ getMenuItemRefDisplayName(menuItem) }}
</VTag> </VTag>
</template> </template>
</EntityField> </VEntityField>
</template> </template>
<template #end> <template #end>
<EntityField v-if="menuItem.metadata.deletionTimestamp"> <VEntityField v-if="menuItem.metadata.deletionTimestamp">
<template #description> <template #description>
<VStatusDot v-tooltip="``" state="warning" animate /> <VStatusDot v-tooltip="``" state="warning" animate />
</template> </template>
</EntityField> </VEntityField>
</template> </template>
<template #menuItems> <template #dropdownItems>
<VButton <VButton
v-close-popper v-close-popper
block block
@ -111,7 +116,7 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
删除 删除
</VButton> </VButton>
</template> </template>
</Entity> </VEntity>
<MenuItemListItem <MenuItemListItem
:menu-tree-items="menuItem.spec.children" :menu-tree-items="menuItem.spec.children"
class="pl-10 transition-all duration-300" class="pl-10 transition-all duration-300"

View File

@ -6,14 +6,14 @@ import {
VEmpty, VEmpty,
VSpace, VSpace,
VStatusDot, VStatusDot,
VEntity,
VEntityField,
} from "@halo-dev/components"; } from "@halo-dev/components";
import MenuEditingModal from "./MenuEditingModal.vue"; import MenuEditingModal from "./MenuEditingModal.vue";
import { defineExpose, onMounted, ref } from "vue"; import { defineExpose, onMounted, ref } from "vue";
import type { Menu } from "@halo-dev/api-client"; import type { Menu } from "@halo-dev/api-client";
import { apiClient } from "@halo-dev/admin-shared"; import { apiClient } from "@halo-dev/admin-shared";
import { useRouteQuery } from "@vueuse/router"; import { useRouteQuery } from "@vueuse/router";
import Entity from "@/components/entity/Entity.vue";
import EntityField from "@/components/entity/EntityField.vue";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -145,23 +145,23 @@ defineExpose({
:key="index" :key="index"
@click="handleSelect(menu)" @click="handleSelect(menu)"
> >
<Entity <VEntity
:is-selected="selectedMenu?.metadata.name === menu.metadata.name" :is-selected="selectedMenu?.metadata.name === menu.metadata.name"
> >
<template #start> <template #start>
<EntityField <VEntityField
:title="menu.spec?.displayName" :title="menu.spec?.displayName"
:description="`${menu.spec.menuItems?.length || 0} 个菜单项`" :description="`${menu.spec.menuItems?.length || 0} 个菜单项`"
></EntityField> ></VEntityField>
</template> </template>
<template #end> <template #end>
<EntityField v-if="menu.metadata.deletionTimestamp"> <VEntityField v-if="menu.metadata.deletionTimestamp">
<template #description> <template #description>
<VStatusDot v-tooltip="``" state="warning" animate /> <VStatusDot v-tooltip="``" state="warning" animate />
</template> </template>
</EntityField> </VEntityField>
</template> </template>
<template #menuItems> <template #dropdownItems>
<VButton <VButton
v-close-popper v-close-popper
block block
@ -179,7 +179,7 @@ defineExpose({
删除 删除
</VButton> </VButton>
</template> </template>
</Entity> </VEntity>
</li> </li>
</ul> </ul>
<template #footer> <template #footer>

View File

@ -5,9 +5,9 @@ import {
VSwitch, VSwitch,
VTag, VTag,
VStatusDot, VStatusDot,
VEntity,
VEntityField,
} from "@halo-dev/components"; } from "@halo-dev/components";
import Entity from "@/components/entity/Entity.vue";
import EntityField from "@/components/entity/EntityField.vue";
import { toRefs } from "vue"; import { toRefs } from "vue";
import { usePluginLifeCycle } from "../composables/use-plugin"; import { usePluginLifeCycle } from "../composables/use-plugin";
import type { Plugin } from "@halo-dev/api-client"; import type { Plugin } from "@halo-dev/api-client";
@ -27,9 +27,9 @@ const { plugin } = toRefs(props);
const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin); const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
</script> </script>
<template> <template>
<Entity> <VEntity>
<template #start> <template #start>
<EntityField> <VEntityField>
<template #description> <template #description>
<div class="h-12 w-12 rounded border bg-white p-1 hover:shadow-sm"> <div class="h-12 w-12 rounded border bg-white p-1 hover:shadow-sm">
<img <img
@ -39,8 +39,8 @@ const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
/> />
</div> </div>
</template> </template>
</EntityField> </VEntityField>
<EntityField <VEntityField
:title="plugin?.spec.displayName" :title="plugin?.spec.displayName"
:description="plugin?.spec.description" :description="plugin?.spec.description"
:route="{ :route="{
@ -55,10 +55,10 @@ const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
</VTag> </VTag>
</VSpace> </VSpace>
</template> </template>
</EntityField> </VEntityField>
</template> </template>
<template #end> <template #end>
<EntityField v-if="plugin?.status?.phase === 'FAILED'"> <VEntityField v-if="plugin?.status?.phase === 'FAILED'">
<template #description> <template #description>
<VStatusDot <VStatusDot
v-tooltip="`${plugin?.status?.reason}:${plugin?.status?.message}`" v-tooltip="`${plugin?.status?.reason}:${plugin?.status?.message}`"
@ -66,8 +66,8 @@ const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
animate animate
/> />
</template> </template>
</EntityField> </VEntityField>
<EntityField> <VEntityField>
<template #description> <template #description>
<a <a
:href="plugin?.spec.homepage" :href="plugin?.spec.homepage"
@ -77,13 +77,13 @@ const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
@{{ plugin?.spec.author }} @{{ plugin?.spec.author }}
</a> </a>
</template> </template>
</EntityField> </VEntityField>
<EntityField :description="plugin?.spec.version" /> <VEntityField :description="plugin?.spec.version" />
<EntityField <VEntityField
v-if="plugin?.metadata.creationTimestamp" v-if="plugin?.metadata.creationTimestamp"
:description="formatDatetime(plugin?.metadata.creationTimestamp)" :description="formatDatetime(plugin?.metadata.creationTimestamp)"
/> />
<EntityField> <VEntityField>
<template #description> <template #description>
<div <div
v-permission="['system:plugins:manage']" v-permission="['system:plugins:manage']"
@ -95,12 +95,12 @@ const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
/> />
</div> </div>
</template> </template>
</EntityField> </VEntityField>
</template> </template>
<template #menuItems> <template #dropdownItems>
<VButton v-close-popper block type="danger" @click="uninstall"> <VButton v-close-popper block type="danger" @click="uninstall">
卸载 卸载
</VButton> </VButton>
</template> </template>
</Entity> </VEntity>
</template> </template>

View File

@ -16,10 +16,10 @@ import {
VSpace, VSpace,
VTag, VTag,
VStatusDot, VStatusDot,
VEntity,
VEntityField,
} from "@halo-dev/components"; } from "@halo-dev/components";
import RoleEditingModal from "./components/RoleEditingModal.vue"; import RoleEditingModal from "./components/RoleEditingModal.vue";
import Entity from "@/components/entity/Entity.vue";
import EntityField from "@/components/entity/EntityField.vue";
// constants // constants
import { rbacAnnotations } from "@/constants/annotations"; import { rbacAnnotations } from "@/constants/annotations";
@ -201,9 +201,9 @@ const handleDelete = async (role: Role) => {
</template> </template>
<ul class="box-border h-full w-full divide-y divide-gray-100" role="list"> <ul class="box-border h-full w-full divide-y divide-gray-100" role="list">
<li v-for="(role, index) in roles" :key="index"> <li v-for="(role, index) in roles" :key="index">
<Entity> <VEntity>
<template #start> <template #start>
<EntityField <VEntityField
:title=" :title="
role.metadata.annotations?.[rbacAnnotations.DISPLAY_NAME] || role.metadata.annotations?.[rbacAnnotations.DISPLAY_NAME] ||
role.metadata.name role.metadata.name
@ -223,25 +223,25 @@ const handleDelete = async (role: Role) => {
name: role.metadata.name, name: role.metadata.name,
}, },
}" }"
></EntityField> ></VEntityField>
</template> </template>
<template #end> <template #end>
<EntityField v-if="role.metadata.deletionTimestamp"> <VEntityField v-if="role.metadata.deletionTimestamp">
<template #description> <template #description>
<VStatusDot v-tooltip="``" state="warning" animate /> <VStatusDot v-tooltip="``" state="warning" animate />
</template> </template>
</EntityField> </VEntityField>
<EntityField description="0 个用户" /> <VEntityField description="0 个用户" />
<EntityField> <VEntityField>
<template #description> <template #description>
<VTag> 系统保留</VTag> <VTag> 系统保留</VTag>
</template> </template>
</EntityField> </VEntityField>
<EntityField <VEntityField
:description="formatDatetime(role.metadata.creationTimestamp)" :description="formatDatetime(role.metadata.creationTimestamp)"
/> />
</template> </template>
<template #menuItems> <template #dropdownItems>
<VButton <VButton
v-close-popper v-close-popper
block block
@ -262,7 +262,7 @@ const handleDelete = async (role: Role) => {
基于此角色创建 基于此角色创建
</VButton> </VButton>
</template> </template>
</Entity> </VEntity>
</li> </li>
</ul> </ul>

View File

@ -11,6 +11,8 @@ import {
VSpace, VSpace,
VTag, VTag,
VAvatar, VAvatar,
VEntity,
VEntityField,
} from "@halo-dev/components"; } from "@halo-dev/components";
import UserEditingModal from "./components/UserEditingModal.vue"; import UserEditingModal from "./components/UserEditingModal.vue";
import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue"; import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue";
@ -20,8 +22,6 @@ import type { User, UserList } from "@halo-dev/api-client";
import { rbacAnnotations } from "@/constants/annotations"; import { rbacAnnotations } from "@/constants/annotations";
import { formatDatetime } from "@/utils/date"; import { formatDatetime } from "@/utils/date";
import { useRouteQuery } from "@vueuse/router"; import { useRouteQuery } from "@vueuse/router";
import Entity from "@/components/entity/Entity.vue";
import EntityField from "@/components/entity/EntityField.vue";
const checkAll = ref(false); const checkAll = ref(false);
const editingModal = ref<boolean>(false); const editingModal = ref<boolean>(false);
@ -276,7 +276,7 @@ onMounted(() => {
</template> </template>
<ul class="box-border h-full w-full divide-y divide-gray-100" role="list"> <ul class="box-border h-full w-full divide-y divide-gray-100" role="list">
<li v-for="(user, index) in users.items" :key="index"> <li v-for="(user, index) in users.items" :key="index">
<Entity :is-selected="checkAll"> <VEntity :is-selected="checkAll">
<template #checkbox> <template #checkbox>
<input <input
v-model="checkAll" v-model="checkAll"
@ -286,7 +286,7 @@ onMounted(() => {
/> />
</template> </template>
<template #start> <template #start>
<EntityField> <VEntityField>
<template #description> <template #description>
<VAvatar <VAvatar
:alt="user.spec.displayName" :alt="user.spec.displayName"
@ -294,8 +294,8 @@ onMounted(() => {
size="md" size="md"
></VAvatar> ></VAvatar>
</template> </template>
</EntityField> </VEntityField>
<EntityField <VEntityField
:title="user.spec.displayName" :title="user.spec.displayName"
:description="user.metadata.name" :description="user.metadata.name"
:route="{ :route="{
@ -305,7 +305,7 @@ onMounted(() => {
/> />
</template> </template>
<template #end> <template #end>
<EntityField> <VEntityField>
<template #description> <template #description>
<div <div
v-for="(role, roleIndex) in getRoles(user)" v-for="(role, roleIndex) in getRoles(user)"
@ -317,12 +317,12 @@ onMounted(() => {
</VTag> </VTag>
</div> </div>
</template> </template>
</EntityField> </VEntityField>
<EntityField <VEntityField
:description="formatDatetime(user.metadata.creationTimestamp)" :description="formatDatetime(user.metadata.creationTimestamp)"
/> />
</template> </template>
<template #menuItems> <template #dropdownItems>
<VButton <VButton
v-close-popper v-close-popper
block block
@ -339,7 +339,7 @@ onMounted(() => {
修改密码 修改密码
</VButton> </VButton>
</template> </template>
</Entity> </VEntity>
</li> </li>
</ul> </ul>