mirror of
https://github.com/ccmjga/zhilu-admin
synced 2026-04-12 01:17:21 +00:00
add field order
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
package com.zl.mjga.dto;
|
package com.zl.mjga.dto;
|
||||||
|
|
||||||
|
import static com.zl.mjga.utils.StringCaseUtils.convertCamelCaseToSnake;
|
||||||
import static org.jooq.impl.DSL.field;
|
import static org.jooq.impl.DSL.field;
|
||||||
import static org.jooq.impl.DSL.name;
|
import static org.jooq.impl.DSL.name;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -17,11 +19,12 @@ public class PageRequestDto {
|
|||||||
|
|
||||||
public static final String REGEX = "^[a-zA-Z][a-zA-Z0-9_]*$";
|
public static final String REGEX = "^[a-zA-Z][a-zA-Z0-9_]*$";
|
||||||
|
|
||||||
public static final String SPACE = " ";
|
public static final String COLON = ":";
|
||||||
|
|
||||||
private long page;
|
private long page;
|
||||||
private long size;
|
private long size;
|
||||||
|
|
||||||
|
@Schema(description = "排序字段", example = "name:asc,age:desc", type = "string")
|
||||||
private Map<String, Direction> sortBy = new HashMap<>();
|
private Map<String, Direction> sortBy = new HashMap<>();
|
||||||
|
|
||||||
public PageRequestDto(int page, int size) {
|
public PageRequestDto(int page, int size) {
|
||||||
@@ -68,10 +71,12 @@ public class PageRequestDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<SortField<Object>> getSortFields() {
|
public List<SortField<Object>> getSortFields() {
|
||||||
List<SortField<Object>> sortFields = sortBy.entrySet().stream()
|
List<SortField<Object>> sortFields =
|
||||||
|
sortBy.entrySet().stream()
|
||||||
.map(
|
.map(
|
||||||
(entry) ->
|
(entry) ->
|
||||||
field(name(entry.getKey())).sort(SortOrder.valueOf(entry.getValue().getKeyword())))
|
field(name(convertCamelCaseToSnake(entry.getKey())))
|
||||||
|
.sort(SortOrder.valueOf(entry.getValue().getKeyword())))
|
||||||
.toList();
|
.toList();
|
||||||
if (sortFields.isEmpty()) {
|
if (sortFields.isEmpty()) {
|
||||||
return List.of(field(name("id")).sort(SortOrder.ASC));
|
return List.of(field(name("id")).sort(SortOrder.ASC));
|
||||||
@@ -108,7 +113,7 @@ public class PageRequestDto {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
for (String fieldSpaceDirection : sortBy.split(",")) {
|
for (String fieldSpaceDirection : sortBy.split(",")) {
|
||||||
String[] fieldDirectionArray = fieldSpaceDirection.split(SPACE);
|
String[] fieldDirectionArray = fieldSpaceDirection.split(COLON);
|
||||||
if (fieldDirectionArray.length != 2) {
|
if (fieldDirectionArray.length != 2) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
String.format(
|
String.format(
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ import lombok.NoArgsConstructor;
|
|||||||
@Data
|
@Data
|
||||||
public class UserUpsertDto {
|
public class UserUpsertDto {
|
||||||
private Long id;
|
private Long id;
|
||||||
@NotEmpty
|
@NotEmpty private String username;
|
||||||
private String username;
|
|
||||||
private String password;
|
private String password;
|
||||||
@NotNull private Boolean enable;
|
@NotNull private Boolean enable;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package com.zl.mjga.repository;
|
|||||||
import static org.jooq.generated.mjga.tables.Permission.PERMISSION;
|
import static org.jooq.generated.mjga.tables.Permission.PERMISSION;
|
||||||
import static org.jooq.generated.mjga.tables.Role.ROLE;
|
import static org.jooq.generated.mjga.tables.Role.ROLE;
|
||||||
import static org.jooq.impl.DSL.*;
|
import static org.jooq.impl.DSL.*;
|
||||||
import static org.jooq.impl.DSL.noField;
|
|
||||||
|
|
||||||
import com.zl.mjga.dto.PageRequestDto;
|
import com.zl.mjga.dto.PageRequestDto;
|
||||||
import com.zl.mjga.dto.urp.PermissionQueryDto;
|
import com.zl.mjga.dto.urp.PermissionQueryDto;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.zl.mjga.repository;
|
|||||||
|
|
||||||
import static org.jooq.generated.mjga.Tables.*;
|
import static org.jooq.generated.mjga.Tables.*;
|
||||||
import static org.jooq.impl.DSL.noCondition;
|
import static org.jooq.impl.DSL.noCondition;
|
||||||
import static org.jooq.impl.DSL.noField;
|
|
||||||
|
|
||||||
import com.zl.mjga.dto.PageRequestDto;
|
import com.zl.mjga.dto.PageRequestDto;
|
||||||
import com.zl.mjga.dto.position.PositionQueryDto;
|
import com.zl.mjga.dto.position.PositionQueryDto;
|
||||||
|
|||||||
15
backend/src/main/java/com/zl/mjga/utils/StringCaseUtils.java
Normal file
15
backend/src/main/java/com/zl/mjga/utils/StringCaseUtils.java
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package com.zl.mjga.utils;
|
||||||
|
|
||||||
|
public class StringCaseUtils {
|
||||||
|
public static String convertCamelCaseToSnake(String input) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
for (char c : input.toCharArray()) {
|
||||||
|
if (Character.isUpperCase(c)) {
|
||||||
|
result.append("_").append(Character.toLowerCase(c));
|
||||||
|
} else {
|
||||||
|
result.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1219,14 +1219,9 @@
|
|||||||
"format": "int64"
|
"format": "int64"
|
||||||
},
|
},
|
||||||
"sortBy": {
|
"sortBy": {
|
||||||
"type": "object",
|
"type": "string",
|
||||||
"additionalProperties": {
|
"description": "排序字段",
|
||||||
"type": "string",
|
"example": "name:asc,age:desc"
|
||||||
"enum": [
|
|
||||||
"ASC",
|
|
||||||
"DESC"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"offset": {
|
"offset": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
|||||||
8
frontend/src/api/types/schema.d.ts
vendored
8
frontend/src/api/types/schema.d.ts
vendored
@@ -544,9 +544,11 @@ export interface components {
|
|||||||
page?: number;
|
page?: number;
|
||||||
/** Format: int64 */
|
/** Format: int64 */
|
||||||
size?: number;
|
size?: number;
|
||||||
sortBy?: {
|
/**
|
||||||
[key: string]: "ASC" | "DESC";
|
* @description 排序字段
|
||||||
};
|
* @example name:asc,age:desc
|
||||||
|
*/
|
||||||
|
sortBy?: string;
|
||||||
/** Format: int64 */
|
/** Format: int64 */
|
||||||
offset?: number;
|
offset?: number;
|
||||||
sortFields?: components["schemas"]["SortFieldObject"][];
|
sortFields?: components["schemas"]["SortFieldObject"][];
|
||||||
|
|||||||
18
frontend/src/components/SortIcon.vue
Normal file
18
frontend/src/components/SortIcon.vue
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<template>
|
||||||
|
<span class="ml-1">
|
||||||
|
<svg class="w-3.5 h-3.5" :class="{'text-blue-700': sortField?.order}" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path v-if="sortField?.order === 'asc'" d="M8 12l6-6 6 6"/>
|
||||||
|
<path v-else-if="sortField?.order === 'desc'" d="M8 12l6 6 6-6"/>
|
||||||
|
<path v-else d="M8 9l6-6 6 6M8 15l6 6 6-6"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
sortField?: {
|
||||||
|
field: string;
|
||||||
|
order: "asc" | "desc" | undefined;
|
||||||
|
};
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
42
frontend/src/composables/sort.ts
Normal file
42
frontend/src/composables/sort.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { computed, ref } from "vue";
|
||||||
|
|
||||||
|
export const useSort = () => {
|
||||||
|
const sortFields = ref<
|
||||||
|
{
|
||||||
|
field: string;
|
||||||
|
order: "asc" | "desc" | undefined;
|
||||||
|
}[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const getSortField = (field: string) => {
|
||||||
|
return sortFields.value.find((item) => item.field === field);
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortBy = computed(() => {
|
||||||
|
return sortFields.value
|
||||||
|
.map((item) => `${item.field}:${item.order}`)
|
||||||
|
.join(",");
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSort = async (field: string) => {
|
||||||
|
if (sortFields.value?.find((item) => item.field === field)) {
|
||||||
|
sortFields.value = sortFields.value?.map((item) =>
|
||||||
|
item.field === field
|
||||||
|
? { ...item, order: item.order === "asc" ? "desc" : undefined }
|
||||||
|
: item,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
sortFields.value.push({ field, order: "asc" });
|
||||||
|
}
|
||||||
|
sortFields.value = sortFields.value?.filter(
|
||||||
|
(item) => item.order !== undefined,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
sortFields,
|
||||||
|
sortBy,
|
||||||
|
handleSort,
|
||||||
|
getSortField,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -24,6 +24,7 @@ export const useUserQuery = () => {
|
|||||||
},
|
},
|
||||||
page = 1,
|
page = 1,
|
||||||
size = 10,
|
size = 10,
|
||||||
|
sortBy = "id:desc",
|
||||||
) => {
|
) => {
|
||||||
const { data } = await client.GET("/iam/users", {
|
const { data } = await client.GET("/iam/users", {
|
||||||
params: {
|
params: {
|
||||||
@@ -31,6 +32,7 @@ export const useUserQuery = () => {
|
|||||||
pageRequestDto: {
|
pageRequestDto: {
|
||||||
page,
|
page,
|
||||||
size,
|
size,
|
||||||
|
sortBy,
|
||||||
},
|
},
|
||||||
userQueryDto: param,
|
userQueryDto: param,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
</form>
|
</form>
|
||||||
<!-- Create Modal toggle -->
|
<!-- Create Modal toggle -->
|
||||||
<button @click="handleUpsertUserClick(undefined)"
|
<button @click="handleUpsertUserClick(undefined)"
|
||||||
class="flex items-center block text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center absolute right-5 bottom-2"
|
class="flex items-center text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center absolute right-5 bottom-2"
|
||||||
type="button">
|
type="button">
|
||||||
新增用户
|
新增用户
|
||||||
</button>
|
</button>
|
||||||
@@ -42,7 +42,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" class="px-6 py-3">用户名</th>
|
<th scope="col" class="px-6 py-3">用户名</th>
|
||||||
<th scope="col" class="px-6 py-3">创建时间</th>
|
<th scope="col" class="px-6 py-3 cursor-pointer" @click="handleSortClick('createTime')">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span>创建时间</span>
|
||||||
|
<SortIcon :sortField="getSortField('createTime')" />
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
<th scope="col" class="px-6 py-3">状态</th>
|
<th scope="col" class="px-6 py-3">状态</th>
|
||||||
<th scope="col" class="px-6 py-3">分配</th>
|
<th scope="col" class="px-6 py-3">分配</th>
|
||||||
<th scope="col" class="px-6 py-3">操作</th>
|
<th scope="col" class="px-6 py-3">操作</th>
|
||||||
@@ -147,18 +152,18 @@ import { useRouter } from "vue-router";
|
|||||||
import type { components } from "../api/types/schema";
|
import type { components } from "../api/types/schema";
|
||||||
import useAlertStore from "../composables/store/useAlertStore";
|
import useAlertStore from "../composables/store/useAlertStore";
|
||||||
import { useUserUpsert } from "../composables/user/useUserUpsert";
|
import { useUserUpsert } from "../composables/user/useUserUpsert";
|
||||||
|
import { useSort } from "@/composables/sort";
|
||||||
|
import SortIcon from "@/components/SortIcon.vue";
|
||||||
|
|
||||||
const username = ref<string>("");
|
const username = ref<string>("");
|
||||||
const selectedUser = ref<components["schemas"]["UserRolePermissionDto"]>();
|
const selectedUser = ref<components["schemas"]["UserRolePermissionDto"]>();
|
||||||
const userUpsertModal = ref<ModalInterface>();
|
const userUpsertModal = ref<ModalInterface>();
|
||||||
const userDeleteModal = ref<ModalInterface>();
|
const userDeleteModal = ref<ModalInterface>();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { total, users, fetchUsersWith } = useUserQuery();
|
const { total, users, fetchUsersWith } = useUserQuery();
|
||||||
|
|
||||||
const { deleteUser } = useUserDelete();
|
const { deleteUser } = useUserDelete();
|
||||||
const userUpsert = useUserUpsert();
|
const userUpsert = useUserUpsert();
|
||||||
|
const { sortFields, sortBy, handleSort, getSortField } = useSort();
|
||||||
const alertStore = useAlertStore();
|
const alertStore = useAlertStore();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -193,9 +198,14 @@ const handleUpsertUserSubmit = async (data: UserUpsertSubmitModel) => {
|
|||||||
content: "操作成功",
|
content: "操作成功",
|
||||||
level: "success",
|
level: "success",
|
||||||
});
|
});
|
||||||
await fetchUsersWith({
|
await fetchUsersWith(
|
||||||
username: username.value,
|
{
|
||||||
});
|
username: username.value,
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
sortBy.value,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpsertUserClick = async (
|
const handleUpsertUserClick = async (
|
||||||
@@ -240,6 +250,18 @@ const handleBindPositionClick = async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSortClick = async (field: string) => {
|
||||||
|
handleSort(field);
|
||||||
|
await fetchUsersWith(
|
||||||
|
{
|
||||||
|
username: username.value,
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
sortBy.value,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const handleDeleteUserSubmit = async () => {
|
const handleDeleteUserSubmit = async () => {
|
||||||
if (!selectedUser?.value?.id) return;
|
if (!selectedUser?.value?.id) return;
|
||||||
await deleteUser(selectedUser.value.id);
|
await deleteUser(selectedUser.value.id);
|
||||||
@@ -248,9 +270,14 @@ const handleDeleteUserSubmit = async () => {
|
|||||||
content: "删除成功",
|
content: "删除成功",
|
||||||
level: "success",
|
level: "success",
|
||||||
});
|
});
|
||||||
await fetchUsersWith({
|
await fetchUsersWith(
|
||||||
username: username.value,
|
{
|
||||||
});
|
username: username.value,
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
sortBy.value,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteUserClick = async (
|
const handleDeleteUserClick = async (
|
||||||
@@ -263,9 +290,14 @@ const handleDeleteUserClick = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSearch = async () => {
|
const handleSearch = async () => {
|
||||||
await fetchUsersWith({
|
await fetchUsersWith(
|
||||||
username: username.value,
|
{
|
||||||
});
|
username: username.value,
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
sortBy.value,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePageChange = async (page: number, pageSize: number) => {
|
const handlePageChange = async (page: number, pageSize: number) => {
|
||||||
@@ -275,6 +307,7 @@ const handlePageChange = async (page: number, pageSize: number) => {
|
|||||||
},
|
},
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
|
sortBy.value,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user