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,
|
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"
|
||||||
|
|
|
@ -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: [],
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue