mirror of https://github.com/halo-dev/halo
feat: add user dropdown selector component
parent
811f998a45
commit
6b1b6db751
|
@ -0,0 +1,85 @@
|
|||
<script lang="ts" setup>
|
||||
import type { User } from "@halo-dev/api-client";
|
||||
import { useUserFetch } from "@/modules/system/users/composables/use-user";
|
||||
import { IconCheckboxCircle } from "@halo-dev/components";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
selected?: User;
|
||||
}>(),
|
||||
{
|
||||
selected: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "update:selected", user?: User): void;
|
||||
(event: "select", user?: User): void;
|
||||
}>();
|
||||
|
||||
const { users, handleFetchUsers } = useUserFetch();
|
||||
|
||||
const handleSelect = (user: User) => {
|
||||
if (props.selected && user.metadata.name === props.selected.metadata.name) {
|
||||
emit("update:selected", undefined);
|
||||
emit("select", undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
emit("update:selected", user);
|
||||
emit("select", user);
|
||||
};
|
||||
|
||||
function onDropdownShow() {
|
||||
handleFetchUsers();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FloatingDropdown @show="onDropdownShow">
|
||||
<slot />
|
||||
<template #popper>
|
||||
<div class="h-96 w-80">
|
||||
<div class="bg-white p-4">
|
||||
<!--TODO: Auto Focus-->
|
||||
<FormKit placeholder="输入关键词搜索" type="text"></FormKit>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<ul class="divide-y divide-gray-200" role="list">
|
||||
<li
|
||||
v-for="(user, index) in users"
|
||||
:key="index"
|
||||
v-close-popper
|
||||
class="cursor-pointer hover:bg-gray-50"
|
||||
:class="{
|
||||
'bg-gray-100': selected?.metadata.name === user.metadata.name,
|
||||
}"
|
||||
@click="handleSelect(user)"
|
||||
>
|
||||
<div class="flex items-center space-x-4 px-4 py-3">
|
||||
<div class="flex-shrink-0">
|
||||
<img
|
||||
:alt="user.spec.displayName"
|
||||
:src="user.spec.avatar"
|
||||
class="h-10 w-10 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="truncate text-sm font-medium text-gray-900">
|
||||
{{ user.spec.displayName }}
|
||||
</p>
|
||||
<p class="truncate text-sm text-gray-500">
|
||||
@{{ user.metadata.name }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="selected?.metadata.name === user.metadata.name">
|
||||
<IconCheckboxCircle class="text-primary" />
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</FloatingDropdown>
|
||||
</template>
|
|
@ -19,12 +19,12 @@ import {
|
|||
IconFolder,
|
||||
} from "@halo-dev/components";
|
||||
import LazyImage from "@/components/image/LazyImage.vue";
|
||||
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
|
||||
import AttachmentDetailModal from "./components/AttachmentDetailModal.vue";
|
||||
import AttachmentUploadModal from "./components/AttachmentUploadModal.vue";
|
||||
import AttachmentPoliciesModal from "./components/AttachmentPoliciesModal.vue";
|
||||
import AttachmentGroupList from "./components/AttachmentGroupList.vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useUserFetch } from "@/modules/system/users/composables/use-user";
|
||||
import type { Attachment, Group, Policy, User } from "@halo-dev/api-client";
|
||||
import { formatDatetime } from "@/utils/date";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
|
@ -43,7 +43,6 @@ const uploadVisible = ref(false);
|
|||
const detailVisible = ref(false);
|
||||
const selectVisible = ref(false);
|
||||
|
||||
const { users } = useUserFetch();
|
||||
const { policies } = useFetchAttachmentPolicy({ fetchOnMounted: true });
|
||||
const { groups, handleFetchGroups } = useFetchAttachmentGroup({
|
||||
fetchOnMounted: true,
|
||||
|
@ -345,7 +344,10 @@ onMounted(() => {
|
|||
</div>
|
||||
</template>
|
||||
</FloatingDropdown>
|
||||
<FloatingDropdown>
|
||||
<UserDropdownSelector
|
||||
v-model:selected="selectedUser"
|
||||
@select="handleSelectUser"
|
||||
>
|
||||
<div
|
||||
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
|
||||
>
|
||||
|
@ -354,56 +356,7 @@ onMounted(() => {
|
|||
<IconArrowDown />
|
||||
</span>
|
||||
</div>
|
||||
<template #popper>
|
||||
<div class="h-96 w-80">
|
||||
<div class="bg-white p-4">
|
||||
<!--TODO: Auto Focus-->
|
||||
<FormKit
|
||||
placeholder="输入关键词搜索"
|
||||
type="text"
|
||||
></FormKit>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<ul class="divide-y divide-gray-200" role="list">
|
||||
<li
|
||||
v-for="(user, index) in users"
|
||||
:key="index"
|
||||
v-close-popper
|
||||
class="cursor-pointer hover:bg-gray-50"
|
||||
:class="{
|
||||
'bg-gray-100':
|
||||
selectedUser?.metadata.name ===
|
||||
user.metadata.name,
|
||||
}"
|
||||
@click="handleSelectUser(user)"
|
||||
>
|
||||
<div
|
||||
class="flex items-center space-x-4 px-4 py-3"
|
||||
>
|
||||
<div class="flex-shrink-0">
|
||||
<img
|
||||
:alt="user.spec.displayName"
|
||||
:src="user.spec.avatar"
|
||||
class="h-10 w-10 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p
|
||||
class="truncate text-sm font-medium text-gray-900"
|
||||
>
|
||||
{{ user.spec.displayName }}
|
||||
</p>
|
||||
<p class="truncate text-sm text-gray-500">
|
||||
@{{ user.metadata.name }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</FloatingDropdown>
|
||||
</UserDropdownSelector>
|
||||
<FloatingDropdown>
|
||||
<div
|
||||
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
|
||||
|
|
|
@ -42,7 +42,7 @@ const pagesRef = ref([
|
|||
const activeId = ref("functional");
|
||||
const checkAll = ref(false);
|
||||
|
||||
const { users } = useUserFetch();
|
||||
const { users } = useUserFetch({ fetchOnMounted: true });
|
||||
|
||||
const pagesPublicState = ref<PagesPublicState>({
|
||||
functionalPages: [],
|
||||
|
|
|
@ -16,15 +16,14 @@ import {
|
|||
VPageHeader,
|
||||
VPagination,
|
||||
VSpace,
|
||||
VTag,
|
||||
} from "@halo-dev/components";
|
||||
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
|
||||
import PostSettingModal from "./components/PostSettingModal.vue";
|
||||
import PostTag from "../posts/tags/components/PostTag.vue";
|
||||
import { onMounted, ref, watch, watchEffect } from "vue";
|
||||
import type { ListedPostList, Post, PostRequest } from "@halo-dev/api-client";
|
||||
import { apiClient } from "@halo-dev/admin-shared";
|
||||
import { formatDatetime } from "@/utils/date";
|
||||
import { useUserFetch } from "@/modules/system/users/composables/use-user";
|
||||
import { usePostCategory } from "@/modules/contents/posts/categories/composables/use-post-category";
|
||||
import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag";
|
||||
import cloneDeep from "lodash.clonedeep";
|
||||
|
@ -53,7 +52,6 @@ const selectedPostWithContent = ref<PostRequest | null>(null);
|
|||
const checkedAll = ref(false);
|
||||
const selectedPostNames = ref<string[]>([]);
|
||||
|
||||
const { users } = useUserFetch();
|
||||
const { categories } = usePostCategory({ fetchOnMounted: true });
|
||||
const { tags } = usePostTag({ fetchOnMounted: true });
|
||||
const dialog = useDialog();
|
||||
|
@ -525,7 +523,7 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
|
|||
</div>
|
||||
</template>
|
||||
</FloatingDropdown>
|
||||
<FloatingDropdown>
|
||||
<UserDropdownSelector>
|
||||
<div
|
||||
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
|
||||
>
|
||||
|
@ -534,51 +532,7 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
|
|||
<IconArrowDown />
|
||||
</span>
|
||||
</div>
|
||||
<template #popper>
|
||||
<div class="h-96 w-80">
|
||||
<div class="bg-white p-4">
|
||||
<!--TODO: Auto Focus-->
|
||||
<FormKit
|
||||
placeholder="输入关键词搜索"
|
||||
type="text"
|
||||
></FormKit>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<ul class="divide-y divide-gray-200" role="list">
|
||||
<li
|
||||
v-for="(user, index) in users"
|
||||
:key="index"
|
||||
v-close-popper
|
||||
class="cursor-pointer hover:bg-gray-50"
|
||||
>
|
||||
<div class="flex items-center space-x-4 px-4 py-3">
|
||||
<div class="flex-shrink-0">
|
||||
<img
|
||||
:alt="user.spec.displayName"
|
||||
:src="user.spec.avatar"
|
||||
class="h-10 w-10 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p
|
||||
class="truncate text-sm font-medium text-gray-900"
|
||||
>
|
||||
{{ user.spec.displayName }}
|
||||
</p>
|
||||
<p class="truncate text-sm text-gray-500">
|
||||
@{{ user.metadata.name }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<VTag>{{ index + 1 }} 篇</VTag>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</FloatingDropdown>
|
||||
</UserDropdownSelector>
|
||||
<FloatingDropdown>
|
||||
<div
|
||||
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { VCard } from "@halo-dev/components";
|
||||
import { useUserFetch } from "@/modules/system/users/composables/use-user";
|
||||
|
||||
const { users } = useUserFetch();
|
||||
const { users } = useUserFetch({ fetchOnMounted: true });
|
||||
</script>
|
||||
<template>
|
||||
<VCard
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts" name="UserStatsWidget" setup>
|
||||
import { VCard } from "@halo-dev/components";
|
||||
import { useUserFetch } from "@/modules/system/users/composables/use-user";
|
||||
const { users } = useUserFetch();
|
||||
const { users } = useUserFetch({ fetchOnMounted: true });
|
||||
</script>
|
||||
<template>
|
||||
<VCard class="h-full">
|
||||
|
|
|
@ -28,7 +28,7 @@ const { roleTemplateGroups, handleRoleTemplateSelect, selectedRoleTemplates } =
|
|||
|
||||
const { formState, saving, handleCreateOrUpdate } = useRoleForm();
|
||||
|
||||
const { users } = useUserFetch();
|
||||
const { users } = useUserFetch({ fetchOnMounted: true });
|
||||
|
||||
watch(
|
||||
() => selectedRoleTemplates.value,
|
||||
|
|
|
@ -9,7 +9,11 @@ interface useUserFetchReturn {
|
|||
handleFetchUsers: () => void;
|
||||
}
|
||||
|
||||
export function useUserFetch(): useUserFetchReturn {
|
||||
export function useUserFetch(options?: {
|
||||
fetchOnMounted: boolean;
|
||||
}): useUserFetchReturn {
|
||||
const { fetchOnMounted } = options || {};
|
||||
|
||||
const users = ref<User[]>([] as User[]);
|
||||
const loading = ref(false);
|
||||
|
||||
|
@ -25,7 +29,9 @@ export function useUserFetch(): useUserFetchReturn {
|
|||
}
|
||||
};
|
||||
|
||||
onMounted(handleFetchUsers);
|
||||
onMounted(() => {
|
||||
fetchOnMounted && handleFetchUsers();
|
||||
});
|
||||
|
||||
return {
|
||||
users,
|
||||
|
|
Loading…
Reference in New Issue