refactor: optimize auth provider sorting with drag-and-drop support (#5914)

#### What type of PR is this?
/kind feature
/area core
/area ui
/milestone 2.16.x

#### What this PR does / why we need it:
优化认证方式的排序并支持拖动

#### Which issue(s) this PR fixes:
Fixes #5813

#### Does this PR introduce a user-facing change?
```release-note
优化认证方式的排序并支持拖动
```
pull/5953/head^2
guqing 2024-05-20 16:30:42 +08:00 committed by GitHub
parent 94d625fbb0
commit c22b4e9ef4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 103 additions and 10 deletions

View File

@ -13221,6 +13221,10 @@
"logo": {
"type": "string"
},
"priority": {
"type": "integer",
"format": "int32"
},
"settingRef": {
"$ref": "#/components/schemas/SettingRef"
},

View File

@ -52,6 +52,8 @@ public class AuthProvider extends AbstractExtension {
private String unbindUrl;
private int priority;
@Schema(requiredMode = NOT_REQUIRED)
private SettingRef settingRef;

View File

@ -58,7 +58,7 @@ public class AuthProviderServiceImpl implements AuthProviderService {
public Mono<List<ListedAuthProvider>> listAll() {
return client.list(AuthProvider.class, provider ->
provider.getMetadata().getDeletionTimestamp() == null,
Comparator.comparing(item -> item.getMetadata().getCreationTimestamp())
defaultComparator()
)
.map(this::convertTo)
.collectList()
@ -105,6 +105,12 @@ public class AuthProviderServiceImpl implements AuthProviderService {
);
}
private static Comparator<AuthProvider> defaultComparator() {
return Comparator.comparing((AuthProvider item) -> item.getSpec().getPriority())
.thenComparing(item -> item.getMetadata().getName())
.thenComparing(item -> item.getMetadata().getCreationTimestamp());
}
private Mono<ConfigMap> updateAuthProviderEnabled(Consumer<Set<String>> consumer) {
return client.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG)
.switchIfEmpty(Mono.defer(() -> {

View File

@ -7,22 +7,22 @@ import {
} from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query";
import { apiClient } from "@/utils/api-client";
import type { ListedAuthProvider } from "@halo-dev/api-client";
import type { AuthProvider, ListedAuthProvider } from "@halo-dev/api-client";
import AuthProviderListItem from "./components/AuthProviderListItem.vue";
import { computed, ref } from "vue";
import Fuse from "fuse.js";
import { VueDraggable } from "vue-draggable-plus";
const {
data: authProviders,
isLoading,
refetch,
} = useQuery<ListedAuthProvider[]>({
const authProviders = ref<ListedAuthProvider[]>([]);
const { isLoading, refetch } = useQuery<ListedAuthProvider[]>({
queryKey: ["auth-providers"],
queryFn: async () => {
const { data } = await apiClient.authProvider.listAuthProviders();
return data;
},
onSuccess(data) {
authProviders.value = data;
fuse = new Fuse(data, {
keys: ["name", "displayName"],
useExtendedSearch: true,
@ -41,6 +41,44 @@ const searchResults = computed(() => {
return fuse?.search(keyword.value).map((item) => item.item);
});
// Drag and drop
const updating = ref(false);
async function onSortUpdate() {
try {
updating.value = true;
const { data: rawAuthProviders } =
await apiClient.extension.authProvider.listauthHaloRunV1alpha1AuthProvider();
const authProviderNames = authProviders.value.map((item) => item.name);
const sortedAuthProviders = authProviderNames
.map((name) => {
const authProvider = rawAuthProviders.items.find(
(item) => item.metadata.name === name
);
if (authProvider) {
authProvider.spec.priority = authProviderNames.indexOf(name);
}
return authProvider;
})
.filter(Boolean) as AuthProvider[];
for (const authProvider of sortedAuthProviders) {
await apiClient.extension.authProvider.updateauthHaloRunV1alpha1AuthProvider(
{
name: authProvider.metadata.name,
authProvider: authProvider,
}
);
}
} finally {
await refetch();
updating.value = false;
}
}
</script>
<template>
@ -69,9 +107,18 @@ const searchResults = computed(() => {
</template>
<VLoading v-if="isLoading" />
<Transition v-else appear name="fade">
<ul
<VueDraggable
v-model="authProviders"
ghost-class="opacity-50"
handle=".drag-element"
class="box-border h-full w-full divide-y divide-gray-100"
:class="{
'cursor-progress opacity-60': updating,
}"
role="list"
tag="ul"
:disabled="updating"
@update="onSortUpdate"
>
<li v-for="(authProvider, index) in searchResults" :key="index">
<AuthProviderListItem
@ -79,7 +126,7 @@ const searchResults = computed(() => {
@reload="refetch"
/>
</li>
</ul>
</VueDraggable>
</Transition>
</VCard>
</div>

View File

@ -3,6 +3,7 @@ import { apiClient } from "@/utils/api-client";
import type { ListedAuthProvider } from "@halo-dev/api-client";
import {
Dialog,
IconList,
IconSettings,
Toast,
VAvatar,
@ -55,6 +56,13 @@ const handleChangeStatus = async () => {
<template>
<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"
>
<IconList class="h-3.5 w-3.5" />
</div>
</template>
<template #start>
<VEntityField>
<template #description>

View File

@ -97,6 +97,7 @@
"transliteration": "^2.3.5",
"vue": "^3.4.19",
"vue-demi": "^0.14.7",
"vue-draggable-plus": "^0.4.1",
"vue-grid-layout": "3.0.0-beta1",
"vue-i18n": "^9.9.1",
"vue-router": "^4.2.5",

View File

@ -68,6 +68,12 @@ export interface AuthProviderSpec {
* @memberof AuthProviderSpec
*/
'logo'?: string;
/**
*
* @type {number}
* @memberof AuthProviderSpec
*/
'priority'?: number;
/**
*
* @type {SettingRef}

View File

@ -185,6 +185,9 @@ importers:
vue-demi:
specifier: ^0.14.7
version: 0.14.7(vue@3.4.19)
vue-draggable-plus:
specifier: ^0.4.1
version: 0.4.1(@types/sortablejs@1.15.8)
vue-grid-layout:
specifier: 3.0.0-beta1
version: 3.0.0-beta1(@interactjs/core@1.10.17)(@interactjs/utils@1.10.17)
@ -8101,6 +8104,10 @@ packages:
resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==}
dev: true
/@types/sortablejs@1.15.8:
resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==}
dev: false
/@types/tough-cookie@4.0.2:
resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==}
dev: true
@ -20089,6 +20096,18 @@ packages:
vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.19)
dev: true
/vue-draggable-plus@0.4.1(@types/sortablejs@1.15.8):
resolution: {integrity: sha512-KNi+c482OQUZTZ2kXIGc41fEwknkNF+LlngjBr5TVtBLNvpX2dmwRJJ3J7dy5dGcijXb7V1j+mhqce4iHOoi6Q==}
peerDependencies:
'@types/sortablejs': ^1.15.0
'@vue/composition-api': '*'
peerDependenciesMeta:
'@vue/composition-api':
optional: true
dependencies:
'@types/sortablejs': 1.15.8
dev: false
/vue-eslint-parser@9.3.0(eslint@8.43.0):
resolution: {integrity: sha512-48IxT9d0+wArT1+3wNIy0tascRoywqSUe2E1YalIC1L8jsUGe5aJQItWfRok7DVFGz3UYvzEI7n5wiTXsCMAcQ==}
engines: {node: ^14.17.0 || >=16.0.0}