mirror of https://github.com/halo-dev/halo-admin
refactor: move entity component to @halo-dev/components package
Signed-off-by: Ryan Wang <i@ryanc.cc>pull/613/head
parent
a49d03d025
commit
6892135644
|
@ -19,3 +19,4 @@ export * from "./components/pagination";
|
|||
export * from "./components/codemirror";
|
||||
export * from "./components/empty";
|
||||
export * from "./components/status";
|
||||
export * from "./components/entity";
|
||||
|
|
|
@ -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>
|
|
@ -20,16 +20,10 @@ const emit = defineEmits<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="inline-flex flex-col gap-1">
|
||||
<div
|
||||
v-if="title || $slots.title"
|
||||
class="inline-flex flex-col items-center sm:flex-row"
|
||||
>
|
||||
<div class="entity-field-wrapper">
|
||||
<div v-if="title || $slots.title" class="entity-field-title-body">
|
||||
<slot name="title">
|
||||
<div
|
||||
class="mr-0 truncate text-sm font-medium text-gray-900 sm:mr-2"
|
||||
@click="emit('click')"
|
||||
>
|
||||
<div class="entity-field-title" @click="emit('click')">
|
||||
<RouterLink v-if="route" :to="route">
|
||||
{{ title }}
|
||||
</RouterLink>
|
||||
|
@ -42,13 +36,35 @@ const emit = defineEmits<{
|
|||
</div>
|
||||
<div
|
||||
v-if="description || $slots.description"
|
||||
class="inline-flex items-center"
|
||||
class="entity-field-description-body"
|
||||
>
|
||||
<slot name="description">
|
||||
<span v-if="description" class="text-xs text-gray-500">
|
||||
<span v-if="description" class="entity-field-description">
|
||||
{{ description }}
|
||||
</span>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
export { default as VEntity } from "./Entity.vue";
|
||||
export { default as VEntityField } from "./EntityField.vue";
|
|
@ -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>
|
|
@ -17,6 +17,8 @@ import {
|
|||
IconCloseCircle,
|
||||
IconFolder,
|
||||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
} from "@halo-dev/components";
|
||||
import LazyImage from "@/components/image/LazyImage.vue";
|
||||
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
|
||||
|
@ -36,8 +38,6 @@ import cloneDeep from "lodash.clonedeep";
|
|||
import { isImage } from "@/utils/image";
|
||||
import { useRouteQuery } from "@vueuse/router";
|
||||
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 uploadVisible = ref(false);
|
||||
|
@ -557,7 +557,7 @@ onMounted(() => {
|
|||
role="list"
|
||||
>
|
||||
<li v-for="(attachment, index) in attachments.items" :key="index">
|
||||
<Entity :is-selected="isChecked(attachment)">
|
||||
<VEntity :is-selected="isChecked(attachment)">
|
||||
<template #checkbox>
|
||||
<input
|
||||
:checked="selectedAttachments.has(attachment)"
|
||||
|
@ -567,7 +567,7 @@ onMounted(() => {
|
|||
/>
|
||||
</template>
|
||||
<template #start>
|
||||
<EntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<div
|
||||
class="h-10 w-10 rounded border bg-white p-1 hover:shadow-sm"
|
||||
|
@ -580,8 +580,8 @@ onMounted(() => {
|
|||
/>
|
||||
</div>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:title="attachment.spec.displayName"
|
||||
@click="handleClickItem(attachment)"
|
||||
>
|
||||
|
@ -595,15 +595,15 @@ onMounted(() => {
|
|||
</span>
|
||||
</VSpace>
|
||||
</template>
|
||||
</EntityField>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template #end>
|
||||
<EntityField
|
||||
<VEntityField
|
||||
:description="
|
||||
getPolicyName(attachment.spec.policyRef?.name)
|
||||
"
|
||||
/>
|
||||
<EntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<RouterLink
|
||||
:to="{
|
||||
|
@ -615,8 +615,8 @@ onMounted(() => {
|
|||
{{ attachment.spec.uploadedBy?.name }}
|
||||
</RouterLink>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField v-if="attachment.metadata.deletionTimestamp">
|
||||
</VEntityField>
|
||||
<VEntityField v-if="attachment.metadata.deletionTimestamp">
|
||||
<template #description>
|
||||
<VStatusDot
|
||||
v-tooltip="`删除中`"
|
||||
|
@ -624,17 +624,17 @@ onMounted(() => {
|
|||
animate
|
||||
/>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:description="
|
||||
formatDatetime(attachment.metadata.creationTimestamp)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template #menuItems>
|
||||
<template #dropdownItems>
|
||||
<VButton v-close-popper block type="danger"> 删除 </VButton>
|
||||
</template>
|
||||
</Entity>
|
||||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
<script lang="ts" setup>
|
||||
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 { useExtensionPointsState } from "@/composables/usePlugins";
|
||||
import Entity from "@/components/entity/Entity.vue";
|
||||
import EntityField from "@/components/entity/EntityField.vue";
|
||||
|
||||
const pagesPublicState = ref<PagesPublicState>({
|
||||
functionalPages: [],
|
||||
|
@ -40,15 +45,15 @@ useExtensionPointsState("PAGES", pagesPublicState);
|
|||
:key="index"
|
||||
v-permission="page.permissions"
|
||||
>
|
||||
<Entity>
|
||||
<VEntity>
|
||||
<template #start>
|
||||
<EntityField
|
||||
<VEntityField
|
||||
:title="page.name"
|
||||
:route="page.path"
|
||||
:description="page.url"
|
||||
></EntityField>
|
||||
></VEntityField>
|
||||
</template>
|
||||
</Entity>
|
||||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
|
|
@ -16,6 +16,8 @@ import {
|
|||
VEmpty,
|
||||
VAvatar,
|
||||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
} from "@halo-dev/components";
|
||||
import SinglePageSettingModal from "./components/SinglePageSettingModal.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 { RouterLink } from "vue-router";
|
||||
import cloneDeep from "lodash.clonedeep";
|
||||
import Entity from "../../../components/entity/Entity.vue";
|
||||
import EntityField from "../../../components/entity/EntityField.vue";
|
||||
|
||||
enum SinglePagePhase {
|
||||
DRAFT = "未发布",
|
||||
|
@ -334,7 +334,7 @@ const handleSelectUser = (user?: User) => {
|
|||
role="list"
|
||||
>
|
||||
<li v-for="(singlePage, index) in singlePages.items" :key="index">
|
||||
<Entity :is-selected="checkAll">
|
||||
<VEntity :is-selected="checkAll">
|
||||
<template #checkbox>
|
||||
<input
|
||||
v-model="checkAll"
|
||||
|
@ -343,7 +343,7 @@ const handleSelectUser = (user?: User) => {
|
|||
/>
|
||||
</template>
|
||||
<template #start>
|
||||
<EntityField
|
||||
<VEntityField
|
||||
:title="singlePage.page.spec.title"
|
||||
:description="singlePage.page.status?.permalink"
|
||||
:route="{
|
||||
|
@ -364,10 +364,10 @@ const handleSelectUser = (user?: User) => {
|
|||
<VStatusDot state="success" animate />
|
||||
</RouterLink>
|
||||
</template>
|
||||
</EntityField>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template #end>
|
||||
<EntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<RouterLink
|
||||
v-for="(
|
||||
|
@ -389,9 +389,9 @@ const handleSelectUser = (user?: User) => {
|
|||
></VAvatar>
|
||||
</RouterLink>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField :description="finalStatus(singlePage.page)" />
|
||||
<EntityField>
|
||||
</VEntityField>
|
||||
<VEntityField :description="finalStatus(singlePage.page)" />
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<IconEye
|
||||
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"
|
||||
/>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:description="
|
||||
formatDatetime(singlePage.page.metadata.creationTimestamp)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template #menuItems>
|
||||
<template #dropdownItems>
|
||||
<VButton
|
||||
v-close-popper
|
||||
block
|
||||
|
@ -434,7 +434,7 @@ const handleSelectUser = (user?: User) => {
|
|||
删除
|
||||
</VButton>
|
||||
</template>
|
||||
</Entity>
|
||||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -18,11 +18,11 @@ import {
|
|||
VSpace,
|
||||
VAvatar,
|
||||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
} from "@halo-dev/components";
|
||||
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.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 { onMounted, ref, watch, watchEffect } from "vue";
|
||||
import type {
|
||||
|
@ -740,7 +740,7 @@ function handleContributorFilterItemChange(user?: User) {
|
|||
role="list"
|
||||
>
|
||||
<li v-for="(post, index) in posts.items" :key="index">
|
||||
<Entity :is-selected="checkSelection(post.post)">
|
||||
<VEntity :is-selected="checkSelection(post.post)">
|
||||
<template #checkbox>
|
||||
<input
|
||||
v-model="selectedPostNames"
|
||||
|
@ -751,7 +751,7 @@ function handleContributorFilterItemChange(user?: User) {
|
|||
/>
|
||||
</template>
|
||||
<template #start>
|
||||
<EntityField
|
||||
<VEntityField
|
||||
:title="post.post.spec.title"
|
||||
:route="{
|
||||
name: 'PostEditor',
|
||||
|
@ -797,10 +797,10 @@ function handleContributorFilterItemChange(user?: User) {
|
|||
<span class="text-xs text-gray-500"> 评论 0 </span>
|
||||
</VSpace>
|
||||
</template>
|
||||
</EntityField>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template #end>
|
||||
<EntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<RouterLink
|
||||
v-for="(contributor, contributorIndex) in post.contributors"
|
||||
|
@ -820,9 +820,11 @@ function handleContributorFilterItemChange(user?: User) {
|
|||
></VAvatar>
|
||||
</RouterLink>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField :description="finalStatus(post.post)"></EntityField>
|
||||
<EntityField>
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:description="finalStatus(post.post)"
|
||||
></VEntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<IconEye
|
||||
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"
|
||||
/>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:description="
|
||||
formatDatetime(post.post.metadata.creationTimestamp)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template #menuItems>
|
||||
<template #dropdownItems>
|
||||
<VButton
|
||||
v-close-popper
|
||||
block
|
||||
|
@ -865,7 +867,7 @@ function handleContributorFilterItemChange(user?: User) {
|
|||
删除
|
||||
</VButton>
|
||||
</template>
|
||||
</Entity>
|
||||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
<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 type { CategoryTree } from "../utils";
|
||||
import { ref } from "vue";
|
||||
import { formatDatetime } from "@/utils/date";
|
||||
import Entity from "@/components/entity/Entity.vue";
|
||||
import EntityField from "@/components/entity/EntityField.vue";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
|
@ -51,7 +55,7 @@ function onDelete(category: CategoryTree) {
|
|||
>
|
||||
<template #item="{ element: category }">
|
||||
<li>
|
||||
<Entity>
|
||||
<VEntity>
|
||||
<template #prepend>
|
||||
<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"
|
||||
|
@ -60,25 +64,25 @@ function onDelete(category: CategoryTree) {
|
|||
</div>
|
||||
</template>
|
||||
<template #start>
|
||||
<EntityField
|
||||
<VEntityField
|
||||
:title="category.spec.displayName"
|
||||
:description="category.status.permalink"
|
||||
/>
|
||||
</template>
|
||||
<template #end>
|
||||
<EntityField v-if="category.metadata.deletionTimestamp">
|
||||
<VEntityField v-if="category.metadata.deletionTimestamp">
|
||||
<template #description>
|
||||
<VStatusDot v-tooltip="`删除中`" state="warning" animate />
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:description="`${category.status?.posts?.length || 0} 篇文章`"
|
||||
/>
|
||||
<EntityField
|
||||
<VEntityField
|
||||
:description="formatDatetime(category.metadata.creationTimestamp)"
|
||||
/>
|
||||
</template>
|
||||
<template #menuItems>
|
||||
<template #dropdownItems>
|
||||
<VButton
|
||||
v-close-popper
|
||||
block
|
||||
|
@ -96,7 +100,7 @@ function onDelete(category: CategoryTree) {
|
|||
删除
|
||||
</VButton>
|
||||
</template>
|
||||
</Entity>
|
||||
</VEntity>
|
||||
<CategoryListItem
|
||||
:categories="category.spec.children"
|
||||
class="pl-10 transition-all duration-300"
|
||||
|
|
|
@ -14,11 +14,11 @@ import {
|
|||
VPageHeader,
|
||||
VSpace,
|
||||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
} from "@halo-dev/components";
|
||||
import TagEditingModal from "./components/TagEditingModal.vue";
|
||||
import PostTag from "./components/PostTag.vue";
|
||||
import Entity from "@/components/entity/Entity.vue";
|
||||
import EntityField from "@/components/entity/EntityField.vue";
|
||||
|
||||
// types
|
||||
import type { Tag } from "@halo-dev/api-client";
|
||||
|
@ -175,30 +175,30 @@ onMounted(async () => {
|
|||
role="list"
|
||||
>
|
||||
<li v-for="(tag, index) in tags" :key="index">
|
||||
<Entity
|
||||
<VEntity
|
||||
:is-selected="selectedTag?.metadata.name === tag.metadata.name"
|
||||
>
|
||||
<template #start>
|
||||
<EntityField :description="tag.status?.permalink">
|
||||
<VEntityField :description="tag.status?.permalink">
|
||||
<template #title>
|
||||
<PostTag :tag="tag" />
|
||||
</template>
|
||||
</EntityField>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template #end>
|
||||
<EntityField v-if="tag.metadata.deletionTimestamp">
|
||||
<VEntityField v-if="tag.metadata.deletionTimestamp">
|
||||
<template #description>
|
||||
<VStatusDot v-tooltip="`删除中`" state="warning" animate />
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:description="`${tag.status?.posts?.length || 0} 篇文章`"
|
||||
/>
|
||||
<EntityField
|
||||
<VEntityField
|
||||
:description="formatDatetime(tag.metadata.creationTimestamp)"
|
||||
/>
|
||||
</template>
|
||||
<template #menuItems>
|
||||
<template #dropdownItems>
|
||||
<VButton
|
||||
v-close-popper
|
||||
block
|
||||
|
@ -216,7 +216,7 @@ onMounted(async () => {
|
|||
删除
|
||||
</VButton>
|
||||
</template>
|
||||
</Entity>
|
||||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
<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 { ref } from "vue";
|
||||
import type { MenuTreeItem } from "@/modules/interface/menus/utils";
|
||||
import Entity from "@/components/entity/Entity.vue";
|
||||
import EntityField from "@/components/entity/EntityField.vue";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
|
@ -66,7 +71,7 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
|
|||
>
|
||||
<template #item="{ element: menuItem }">
|
||||
<li>
|
||||
<Entity>
|
||||
<VEntity>
|
||||
<template #prepend>
|
||||
<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"
|
||||
|
@ -75,7 +80,7 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
|
|||
</div>
|
||||
</template>
|
||||
<template #start>
|
||||
<EntityField
|
||||
<VEntityField
|
||||
:title="menuItem.status.displayName"
|
||||
:description="menuItem.status.href"
|
||||
>
|
||||
|
@ -84,16 +89,16 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
|
|||
{{ getMenuItemRefDisplayName(menuItem) }}
|
||||
</VTag>
|
||||
</template>
|
||||
</EntityField>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template #end>
|
||||
<EntityField v-if="menuItem.metadata.deletionTimestamp">
|
||||
<VEntityField v-if="menuItem.metadata.deletionTimestamp">
|
||||
<template #description>
|
||||
<VStatusDot v-tooltip="`删除中`" state="warning" animate />
|
||||
</template>
|
||||
</EntityField>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template #menuItems>
|
||||
<template #dropdownItems>
|
||||
<VButton
|
||||
v-close-popper
|
||||
block
|
||||
|
@ -111,7 +116,7 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
|
|||
删除
|
||||
</VButton>
|
||||
</template>
|
||||
</Entity>
|
||||
</VEntity>
|
||||
<MenuItemListItem
|
||||
:menu-tree-items="menuItem.spec.children"
|
||||
class="pl-10 transition-all duration-300"
|
||||
|
|
|
@ -6,14 +6,14 @@ import {
|
|||
VEmpty,
|
||||
VSpace,
|
||||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
} from "@halo-dev/components";
|
||||
import MenuEditingModal from "./MenuEditingModal.vue";
|
||||
import { defineExpose, onMounted, ref } from "vue";
|
||||
import type { Menu } from "@halo-dev/api-client";
|
||||
import { apiClient } from "@halo-dev/admin-shared";
|
||||
import { useRouteQuery } from "@vueuse/router";
|
||||
import Entity from "@/components/entity/Entity.vue";
|
||||
import EntityField from "@/components/entity/EntityField.vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
@ -145,23 +145,23 @@ defineExpose({
|
|||
:key="index"
|
||||
@click="handleSelect(menu)"
|
||||
>
|
||||
<Entity
|
||||
<VEntity
|
||||
:is-selected="selectedMenu?.metadata.name === menu.metadata.name"
|
||||
>
|
||||
<template #start>
|
||||
<EntityField
|
||||
<VEntityField
|
||||
:title="menu.spec?.displayName"
|
||||
:description="`${menu.spec.menuItems?.length || 0} 个菜单项`"
|
||||
></EntityField>
|
||||
></VEntityField>
|
||||
</template>
|
||||
<template #end>
|
||||
<EntityField v-if="menu.metadata.deletionTimestamp">
|
||||
<VEntityField v-if="menu.metadata.deletionTimestamp">
|
||||
<template #description>
|
||||
<VStatusDot v-tooltip="`删除中`" state="warning" animate />
|
||||
</template>
|
||||
</EntityField>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template #menuItems>
|
||||
<template #dropdownItems>
|
||||
<VButton
|
||||
v-close-popper
|
||||
block
|
||||
|
@ -179,7 +179,7 @@ defineExpose({
|
|||
删除
|
||||
</VButton>
|
||||
</template>
|
||||
</Entity>
|
||||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
<template #footer>
|
||||
|
|
|
@ -5,9 +5,9 @@ import {
|
|||
VSwitch,
|
||||
VTag,
|
||||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
} from "@halo-dev/components";
|
||||
import Entity from "@/components/entity/Entity.vue";
|
||||
import EntityField from "@/components/entity/EntityField.vue";
|
||||
import { toRefs } from "vue";
|
||||
import { usePluginLifeCycle } from "../composables/use-plugin";
|
||||
import type { Plugin } from "@halo-dev/api-client";
|
||||
|
@ -27,9 +27,9 @@ const { plugin } = toRefs(props);
|
|||
const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
|
||||
</script>
|
||||
<template>
|
||||
<Entity>
|
||||
<VEntity>
|
||||
<template #start>
|
||||
<EntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<div class="h-12 w-12 rounded border bg-white p-1 hover:shadow-sm">
|
||||
<img
|
||||
|
@ -39,8 +39,8 @@ const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
|
|||
/>
|
||||
</div>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:title="plugin?.spec.displayName"
|
||||
:description="plugin?.spec.description"
|
||||
:route="{
|
||||
|
@ -55,10 +55,10 @@ const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
|
|||
</VTag>
|
||||
</VSpace>
|
||||
</template>
|
||||
</EntityField>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template #end>
|
||||
<EntityField v-if="plugin?.status?.phase === 'FAILED'">
|
||||
<VEntityField v-if="plugin?.status?.phase === 'FAILED'">
|
||||
<template #description>
|
||||
<VStatusDot
|
||||
v-tooltip="`${plugin?.status?.reason}:${plugin?.status?.message}`"
|
||||
|
@ -66,8 +66,8 @@ const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
|
|||
animate
|
||||
/>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField>
|
||||
</VEntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<a
|
||||
:href="plugin?.spec.homepage"
|
||||
|
@ -77,13 +77,13 @@ const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
|
|||
@{{ plugin?.spec.author }}
|
||||
</a>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField :description="plugin?.spec.version" />
|
||||
<EntityField
|
||||
</VEntityField>
|
||||
<VEntityField :description="plugin?.spec.version" />
|
||||
<VEntityField
|
||||
v-if="plugin?.metadata.creationTimestamp"
|
||||
:description="formatDatetime(plugin?.metadata.creationTimestamp)"
|
||||
/>
|
||||
<EntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<div
|
||||
v-permission="['system:plugins:manage']"
|
||||
|
@ -95,12 +95,12 @@ const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
|
|||
/>
|
||||
</div>
|
||||
</template>
|
||||
</EntityField>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template #menuItems>
|
||||
<template #dropdownItems>
|
||||
<VButton v-close-popper block type="danger" @click="uninstall">
|
||||
卸载
|
||||
</VButton>
|
||||
</template>
|
||||
</Entity>
|
||||
</VEntity>
|
||||
</template>
|
||||
|
|
|
@ -16,10 +16,10 @@ import {
|
|||
VSpace,
|
||||
VTag,
|
||||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
} from "@halo-dev/components";
|
||||
import RoleEditingModal from "./components/RoleEditingModal.vue";
|
||||
import Entity from "@/components/entity/Entity.vue";
|
||||
import EntityField from "@/components/entity/EntityField.vue";
|
||||
|
||||
// constants
|
||||
import { rbacAnnotations } from "@/constants/annotations";
|
||||
|
@ -201,9 +201,9 @@ const handleDelete = async (role: Role) => {
|
|||
</template>
|
||||
<ul class="box-border h-full w-full divide-y divide-gray-100" role="list">
|
||||
<li v-for="(role, index) in roles" :key="index">
|
||||
<Entity>
|
||||
<VEntity>
|
||||
<template #start>
|
||||
<EntityField
|
||||
<VEntityField
|
||||
:title="
|
||||
role.metadata.annotations?.[rbacAnnotations.DISPLAY_NAME] ||
|
||||
role.metadata.name
|
||||
|
@ -223,25 +223,25 @@ const handleDelete = async (role: Role) => {
|
|||
name: role.metadata.name,
|
||||
},
|
||||
}"
|
||||
></EntityField>
|
||||
></VEntityField>
|
||||
</template>
|
||||
<template #end>
|
||||
<EntityField v-if="role.metadata.deletionTimestamp">
|
||||
<VEntityField v-if="role.metadata.deletionTimestamp">
|
||||
<template #description>
|
||||
<VStatusDot v-tooltip="`删除中`" state="warning" animate />
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField description="0 个用户" />
|
||||
<EntityField>
|
||||
</VEntityField>
|
||||
<VEntityField description="0 个用户" />
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<VTag> 系统保留</VTag>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:description="formatDatetime(role.metadata.creationTimestamp)"
|
||||
/>
|
||||
</template>
|
||||
<template #menuItems>
|
||||
<template #dropdownItems>
|
||||
<VButton
|
||||
v-close-popper
|
||||
block
|
||||
|
@ -262,7 +262,7 @@ const handleDelete = async (role: Role) => {
|
|||
基于此角色创建
|
||||
</VButton>
|
||||
</template>
|
||||
</Entity>
|
||||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import {
|
|||
VSpace,
|
||||
VTag,
|
||||
VAvatar,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
} from "@halo-dev/components";
|
||||
import UserEditingModal from "./components/UserEditingModal.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 { formatDatetime } from "@/utils/date";
|
||||
import { useRouteQuery } from "@vueuse/router";
|
||||
import Entity from "@/components/entity/Entity.vue";
|
||||
import EntityField from "@/components/entity/EntityField.vue";
|
||||
|
||||
const checkAll = ref(false);
|
||||
const editingModal = ref<boolean>(false);
|
||||
|
@ -276,7 +276,7 @@ onMounted(() => {
|
|||
</template>
|
||||
<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">
|
||||
<Entity :is-selected="checkAll">
|
||||
<VEntity :is-selected="checkAll">
|
||||
<template #checkbox>
|
||||
<input
|
||||
v-model="checkAll"
|
||||
|
@ -286,7 +286,7 @@ onMounted(() => {
|
|||
/>
|
||||
</template>
|
||||
<template #start>
|
||||
<EntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<VAvatar
|
||||
:alt="user.spec.displayName"
|
||||
|
@ -294,8 +294,8 @@ onMounted(() => {
|
|||
size="md"
|
||||
></VAvatar>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:title="user.spec.displayName"
|
||||
:description="user.metadata.name"
|
||||
:route="{
|
||||
|
@ -305,7 +305,7 @@ onMounted(() => {
|
|||
/>
|
||||
</template>
|
||||
<template #end>
|
||||
<EntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<div
|
||||
v-for="(role, roleIndex) in getRoles(user)"
|
||||
|
@ -317,12 +317,12 @@ onMounted(() => {
|
|||
</VTag>
|
||||
</div>
|
||||
</template>
|
||||
</EntityField>
|
||||
<EntityField
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:description="formatDatetime(user.metadata.creationTimestamp)"
|
||||
/>
|
||||
</template>
|
||||
<template #menuItems>
|
||||
<template #dropdownItems>
|
||||
<VButton
|
||||
v-close-popper
|
||||
block
|
||||
|
@ -339,7 +339,7 @@ onMounted(() => {
|
|||
修改密码
|
||||
</VButton>
|
||||
</template>
|
||||
</Entity>
|
||||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
|
Loading…
Reference in New Issue