feat: add user dropdown selector component

pull/3445/head
Ryan Wang 2022-09-06 19:48:12 +08:00
parent 811f998a45
commit 6b1b6db751
8 changed files with 106 additions and 108 deletions

View File

@ -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>

View File

@ -19,12 +19,12 @@ import {
IconFolder, IconFolder,
} 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 AttachmentDetailModal from "./components/AttachmentDetailModal.vue"; import AttachmentDetailModal from "./components/AttachmentDetailModal.vue";
import AttachmentUploadModal from "./components/AttachmentUploadModal.vue"; import AttachmentUploadModal from "./components/AttachmentUploadModal.vue";
import AttachmentPoliciesModal from "./components/AttachmentPoliciesModal.vue"; import AttachmentPoliciesModal from "./components/AttachmentPoliciesModal.vue";
import AttachmentGroupList from "./components/AttachmentGroupList.vue"; import AttachmentGroupList from "./components/AttachmentGroupList.vue";
import { onMounted, ref } from "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 type { Attachment, Group, Policy, User } from "@halo-dev/api-client";
import { formatDatetime } from "@/utils/date"; import { formatDatetime } from "@/utils/date";
import prettyBytes from "pretty-bytes"; import prettyBytes from "pretty-bytes";
@ -43,7 +43,6 @@ const uploadVisible = ref(false);
const detailVisible = ref(false); const detailVisible = ref(false);
const selectVisible = ref(false); const selectVisible = ref(false);
const { users } = useUserFetch();
const { policies } = useFetchAttachmentPolicy({ fetchOnMounted: true }); const { policies } = useFetchAttachmentPolicy({ fetchOnMounted: true });
const { groups, handleFetchGroups } = useFetchAttachmentGroup({ const { groups, handleFetchGroups } = useFetchAttachmentGroup({
fetchOnMounted: true, fetchOnMounted: true,
@ -345,7 +344,10 @@ onMounted(() => {
</div> </div>
</template> </template>
</FloatingDropdown> </FloatingDropdown>
<FloatingDropdown> <UserDropdownSelector
v-model:selected="selectedUser"
@select="handleSelectUser"
>
<div <div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black" class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
> >
@ -354,56 +356,7 @@ onMounted(() => {
<IconArrowDown /> <IconArrowDown />
</span> </span>
</div> </div>
<template #popper> </UserDropdownSelector>
<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>
<FloatingDropdown> <FloatingDropdown>
<div <div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black" class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"

View File

@ -42,7 +42,7 @@ const pagesRef = ref([
const activeId = ref("functional"); const activeId = ref("functional");
const checkAll = ref(false); const checkAll = ref(false);
const { users } = useUserFetch(); const { users } = useUserFetch({ fetchOnMounted: true });
const pagesPublicState = ref<PagesPublicState>({ const pagesPublicState = ref<PagesPublicState>({
functionalPages: [], functionalPages: [],

View File

@ -16,15 +16,14 @@ import {
VPageHeader, VPageHeader,
VPagination, VPagination,
VSpace, VSpace,
VTag,
} from "@halo-dev/components"; } from "@halo-dev/components";
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
import PostSettingModal from "./components/PostSettingModal.vue"; import PostSettingModal from "./components/PostSettingModal.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 { ListedPostList, Post, PostRequest } from "@halo-dev/api-client"; import type { ListedPostList, Post, PostRequest } from "@halo-dev/api-client";
import { apiClient } from "@halo-dev/admin-shared"; import { apiClient } from "@halo-dev/admin-shared";
import { formatDatetime } from "@/utils/date"; 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 { usePostCategory } from "@/modules/contents/posts/categories/composables/use-post-category";
import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag"; import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag";
import cloneDeep from "lodash.clonedeep"; import cloneDeep from "lodash.clonedeep";
@ -53,7 +52,6 @@ const selectedPostWithContent = ref<PostRequest | null>(null);
const checkedAll = ref(false); const checkedAll = ref(false);
const selectedPostNames = ref<string[]>([]); const selectedPostNames = ref<string[]>([]);
const { users } = useUserFetch();
const { categories } = usePostCategory({ fetchOnMounted: true }); const { categories } = usePostCategory({ fetchOnMounted: true });
const { tags } = usePostTag({ fetchOnMounted: true }); const { tags } = usePostTag({ fetchOnMounted: true });
const dialog = useDialog(); const dialog = useDialog();
@ -525,7 +523,7 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
</div> </div>
</template> </template>
</FloatingDropdown> </FloatingDropdown>
<FloatingDropdown> <UserDropdownSelector>
<div <div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black" 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 /> <IconArrowDown />
</span> </span>
</div> </div>
<template #popper> </UserDropdownSelector>
<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>
<FloatingDropdown> <FloatingDropdown>
<div <div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black" class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"

View File

@ -2,7 +2,7 @@
import { VCard } from "@halo-dev/components"; import { VCard } from "@halo-dev/components";
import { useUserFetch } from "@/modules/system/users/composables/use-user"; import { useUserFetch } from "@/modules/system/users/composables/use-user";
const { users } = useUserFetch(); const { users } = useUserFetch({ fetchOnMounted: true });
</script> </script>
<template> <template>
<VCard <VCard

View File

@ -1,7 +1,7 @@
<script lang="ts" name="UserStatsWidget" setup> <script lang="ts" name="UserStatsWidget" setup>
import { VCard } from "@halo-dev/components"; import { VCard } from "@halo-dev/components";
import { useUserFetch } from "@/modules/system/users/composables/use-user"; import { useUserFetch } from "@/modules/system/users/composables/use-user";
const { users } = useUserFetch(); const { users } = useUserFetch({ fetchOnMounted: true });
</script> </script>
<template> <template>
<VCard class="h-full"> <VCard class="h-full">

View File

@ -28,7 +28,7 @@ const { roleTemplateGroups, handleRoleTemplateSelect, selectedRoleTemplates } =
const { formState, saving, handleCreateOrUpdate } = useRoleForm(); const { formState, saving, handleCreateOrUpdate } = useRoleForm();
const { users } = useUserFetch(); const { users } = useUserFetch({ fetchOnMounted: true });
watch( watch(
() => selectedRoleTemplates.value, () => selectedRoleTemplates.value,

View File

@ -9,7 +9,11 @@ interface useUserFetchReturn {
handleFetchUsers: () => void; handleFetchUsers: () => void;
} }
export function useUserFetch(): useUserFetchReturn { export function useUserFetch(options?: {
fetchOnMounted: boolean;
}): useUserFetchReturn {
const { fetchOnMounted } = options || {};
const users = ref<User[]>([] as User[]); const users = ref<User[]>([] as User[]);
const loading = ref(false); const loading = ref(false);
@ -25,7 +29,9 @@ export function useUserFetch(): useUserFetchReturn {
} }
}; };
onMounted(handleFetchUsers); onMounted(() => {
fetchOnMounted && handleFetchUsers();
});
return { return {
users, users,