fix table filter

This commit is contained in:
Chuck1sn
2025-06-14 13:07:00 +08:00
parent 8a8588588f
commit bb9cd2e529
9 changed files with 548 additions and 430 deletions

View File

@@ -4,60 +4,39 @@
<Breadcrumbs :names="['用户管理', '绑定部门']" :routes="[{ name: RouteName.USERVIEW }]" />
<h1 class="text-xl sm:text-2xl mb-4 sm:mb-6 font-semibold text-gray-900">绑定部门</h1>
</div>
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-4 gap-y-3 sm:gap-y-0">
<form class="w-full sm:w-auto flex flex-col xs:flex-row gap-2 xs:gap-3 items-stretch xs:items-center">
<div class="flex-grow">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only">Search</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
</svg>
</div>
<input type="search" id="default-search" v-model="departmentName"
class="block w-full p-2.5 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500"
placeholder="部门名" required />
</div>
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
if (checkedDepartmentIds.length === 0) {
alertStore.showAlert({
content: '没有选择部门',
level: 'error',
});
} else {
departmentBindModal?.show();
}
}">
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
if (checkedDepartmentIds.length === 0) {
alertStore.showAlert({
content: '没有选择部门',
level: 'error',
});
} else {
departmentUnbindModal?.show();
}
}">
解绑
</TableButton>
</div>
<select id="bind-state" v-model="bindState"
class="w-full xs:w-auto bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5">
<option value="BIND">已绑定</option>
<option value="UNBIND">未绑定</option>
<option value="ALL">全部</option>
</select>
<button type="submit"
class="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-4 py-2.5"
@click.prevent="handleSearch">搜索</button>
</form>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
if (checkedDepartmentIds.length === 0) {
alertStore.showAlert({
content: '没有选择部门',
level: 'error',
});
} else {
departmentBindModal?.show();
}
}">
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
if (checkedDepartmentIds.length === 0) {
alertStore.showAlert({
content: '没有选择部门',
level: 'error',
});
} else {
departmentUnbindModal?.show();
}
}">
解绑
</TableButton>
</div>
</div>
</template>
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden space-y-4">
<MobileCardListWithCheckbox :items="departments || []" v-model="checkedDepartmentIds">
@@ -114,24 +93,63 @@ import MobileCardListWithCheckbox from "@/components/MobileCardListWithCheckbox.
import BindModal from "@/components/PopupModal.vue";
import UnModal from "@/components/PopupModal.vue";
import TableButton from "@/components/TableButton.vue";
import TableFilterForm from "@/components/TableFilterForm.vue";
import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import { useDepartmentQuery } from "@/composables/department/useDepartmentQuery";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { RouteName } from "@/router/constants";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { onMounted, ref, watch } from "vue";
import { onMounted, reactive, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useDepartmentBind } from "../composables/department/useDepartmentBind";
import useAlertStore from "../composables/store/useAlertStore";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
const departmentName = ref<string>("");
// 定义筛选配置
const filterConfig: FilterItem[] = [
{
type: "input",
name: "departmentName",
placeholder: "部门名",
},
{
type: "select",
name: "bindState",
options: [
{ value: "BIND", label: "已绑定" },
{ value: "UNBIND", label: "未绑定" },
{ value: "ALL", label: "全部" },
],
},
];
// 筛选值
const filterValues = reactive<{
departmentName: string;
bindState: "BIND" | "ALL" | "UNBIND";
}>({
departmentName: "",
bindState: "ALL",
});
// 更新筛选值
const updateFilterValues = (
values: Record<string, string | number | boolean | Date[] | undefined>,
) => {
if (values.departmentName !== undefined) {
filterValues.departmentName = values.departmentName as string;
}
if (values.bindState !== undefined) {
filterValues.bindState = values.bindState as "BIND" | "ALL" | "UNBIND";
}
};
const checkedDepartmentIds = ref<number[]>([]);
const departmentBindModal = ref<ModalInterface>();
const departmentUnbindModal = ref<ModalInterface>();
const allChecked = ref<boolean>(false);
const $route = useRoute();
const bindState = ref<"BIND" | "ALL" | "UNBIND">("ALL");
const alertStore = useAlertStore();
const actionExcStore = useActionExcStore();
@@ -159,9 +177,9 @@ const handleBindDepartmentSubmit = async () => {
level: "success",
});
await fetchDepartmentWith({
name: departmentName.value,
name: filterValues.departmentName,
userId: Number($route.params.userId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
};
@@ -178,17 +196,17 @@ const handleUnbindDepartmentSubmit = async () => {
level: "success",
});
await fetchDepartmentWith({
name: departmentName.value,
name: filterValues.departmentName,
userId: Number($route.params.userId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
};
onMounted(async () => {
await fetchDepartmentWith({
name: departmentName.value,
name: filterValues.departmentName,
userId: Number($route.params.userId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
initFlowbite();
const $bindModalElement: HTMLElement | null = document.querySelector(
@@ -210,18 +228,18 @@ onMounted(async () => {
const handleSearch = async () => {
await fetchDepartmentWith({
name: departmentName.value,
name: filterValues.departmentName,
userId: Number($route.params.userId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
};
const handlePageChange = async (page: number, pageSize: number) => {
await fetchDepartmentWith(
{
name: departmentName.value,
name: filterValues.departmentName,
userId: Number($route.params.userId),
bindState: bindState.value,
bindState: filterValues.bindState,
},
page,
pageSize,

View File

@@ -4,60 +4,38 @@
<Breadcrumbs :names="['角色管理', '绑定权限']" :routes="[{ name: RouteName.ROLEVIEW }]" />
<h1 class="text-xl sm:text-2xl mb-4 sm:mb-6 font-semibold text-gray-900">绑定权限</h1>
</div>
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-4 gap-y-3 sm:gap-y-0">
<form class="w-full sm:w-auto flex flex-col xs:flex-row gap-2 xs:gap-3 items-stretch xs:items-center">
<div class="flex-grow">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only">Search</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
</svg>
</div>
<input type="search" id="default-search" v-model="permissionName"
class="block w-full p-2.5 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500"
placeholder="权限名" required />
</div>
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
if (checkedPermissionIds.length === 0) {
alertStore.showAlert({
content: '没有选择权限',
level: 'error',
});
} else {
permissionBindModal?.show();
}
}">
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
if (checkedPermissionIds.length === 0) {
alertStore.showAlert({
content: '没有选择权限',
level: 'error',
});
} else {
permissionUnbindModal?.show();
}
}">
解绑
</TableButton>
</div>
<select id="bind-state" v-model="bindState"
class="w-full xs:w-auto bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5">
<option value="BIND">已绑定</option>
<option value="UNBIND">未绑定</option>
<option value="ALL">全部</option>
</select>
<button type="submit"
class="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-4 py-2.5"
@click.prevent="handleSearch">搜索</button>
</form>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
if (checkedPermissionIds.length === 0) {
alertStore.showAlert({
content: '没有选择权限',
level: 'error',
});
} else {
permissionBindModal?.show();
}
}">
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
if (checkedPermissionIds.length === 0) {
alertStore.showAlert({
content: '没有选择权限',
level: 'error',
});
} else {
permissionUnbindModal?.show();
}
}">
解绑
</TableButton>
</div>
</div>
</template>
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden space-y-4">
@@ -116,24 +94,63 @@ import MobileCardListWithCheckbox from "@/components/MobileCardListWithCheckbox.
import BindModal from "@/components/PopupModal.vue";
import UnModal from "@/components/PopupModal.vue";
import TableButton from "@/components/TableButton.vue";
import TableFilterForm from "@/components/TableFilterForm.vue";
import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { RouteName } from "@/router/constants";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { onMounted, ref, watch } from "vue";
import { onMounted, reactive, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { usePermissionBind } from "../composables/permission/usePermissionBind";
import usePermissionsQuery from "../composables/permission/usePermissionQuery";
import useAlertStore from "../composables/store/useAlertStore";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
const permissionName = ref<string>("");
// 定义筛选配置
const filterConfig: FilterItem[] = [
{
type: "input",
name: "permissionName",
placeholder: "权限名",
},
{
type: "select",
name: "bindState",
options: [
{ value: "BIND", label: "已绑定" },
{ value: "UNBIND", label: "未绑定" },
{ value: "ALL", label: "全部" },
],
},
];
// 筛选值
const filterValues = reactive<{
permissionName: string;
bindState: "BIND" | "ALL" | "UNBIND";
}>({
permissionName: "",
bindState: "ALL",
});
// 更新筛选值
const updateFilterValues = (
values: Record<string, string | number | boolean | Date[] | undefined>,
) => {
if (values.permissionName !== undefined) {
filterValues.permissionName = values.permissionName as string;
}
if (values.bindState !== undefined) {
filterValues.bindState = values.bindState as "BIND" | "ALL" | "UNBIND";
}
};
const checkedPermissionIds = ref<number[]>([]);
const permissionBindModal = ref<ModalInterface>();
const permissionUnbindModal = ref<ModalInterface>();
const allChecked = ref<boolean>(false);
const $route = useRoute();
const bindState = ref<"BIND" | "ALL" | "UNBIND">("ALL");
const alertStore = useAlertStore();
const actionExcStore = useActionExcStore();
@@ -160,9 +177,9 @@ const handleBindPermissionSubmit = async () => {
clearCheckedRoleIds();
allChecked.value = false;
await fetchPermissionsWith({
name: permissionName.value,
name: filterValues.permissionName,
roleId: Number($route.params.roleId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
};
@@ -179,17 +196,17 @@ const handleUnbindPermissionSubmit = async () => {
clearCheckedRoleIds();
allChecked.value = false;
await fetchPermissionsWith({
name: permissionName.value,
name: filterValues.permissionName,
roleId: Number($route.params.roleId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
};
onMounted(async () => {
await fetchPermissionsWith({
name: permissionName.value,
name: filterValues.permissionName,
roleId: Number($route.params.roleId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
initFlowbite();
const $bindModalElement: HTMLElement | null = document.querySelector(
@@ -215,18 +232,18 @@ onMounted(async () => {
const handleSearch = async () => {
await fetchPermissionsWith({
name: permissionName.value,
name: filterValues.permissionName,
roleId: Number($route.params.roleId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
};
const handlePageChange = async (page: number, pageSize: number) => {
await fetchPermissionsWith(
{
name: permissionName.value,
name: filterValues.permissionName,
roleId: Number($route.params.roleId),
bindState: bindState.value,
bindState: filterValues.bindState,
},
page,
pageSize,

View File

@@ -4,60 +4,38 @@
<Breadcrumbs :names="['用户管理', '绑定岗位']" :routes="[{ name: RouteName.USERVIEW }]" />
<h1 class="text-xl sm:text-2xl mb-4 sm:mb-6 font-semibold text-gray-900">绑定岗位</h1>
</div>
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-4 gap-y-3 sm:gap-y-0">
<form class="w-full sm:w-auto flex flex-col xs:flex-row gap-2 xs:gap-3 items-stretch xs:items-center">
<div class="flex-grow">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only">Search</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
</svg>
</div>
<input type="search" id="default-search" v-model="positionName"
class="block w-full p-2.5 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500"
placeholder="岗位名" required />
</div>
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
if (checkedPositionIds.length === 0) {
alertStore.showAlert({
content: '没有选择岗位',
level: 'error',
});
} else {
positionBindModal?.show();
}
}">
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
if (checkedPositionIds.length === 0) {
alertStore.showAlert({
content: '没有选择岗位',
level: 'error',
});
} else {
positionUnbindModal?.show();
}
}">
解绑
</TableButton>
</div>
<select id="bind-state" v-model="bindState"
class="w-full xs:w-auto bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5">
<option value="BIND">已绑定</option>
<option value="UNBIND">未绑定</option>
<option value="ALL">全部</option>
</select>
<button type="submit"
class="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-4 py-2.5"
@click.prevent="handleSearch">搜索</button>
</form>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
if (checkedPositionIds.length === 0) {
alertStore.showAlert({
content: '没有选择岗位',
level: 'error',
});
} else {
positionBindModal?.show();
}
}">
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
if (checkedPositionIds.length === 0) {
alertStore.showAlert({
content: '没有选择岗位',
level: 'error',
});
} else {
positionUnbindModal?.show();
}
}">
解绑
</TableButton>
</div>
</div>
</template>
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden space-y-4">
@@ -107,25 +85,64 @@ import MobileCardListWithCheckbox from "@/components/MobileCardListWithCheckbox.
import BindModal from "@/components/PopupModal.vue";
import UnModal from "@/components/PopupModal.vue";
import TableButton from "@/components/TableButton.vue";
import TableFilterForm from "@/components/TableFilterForm.vue";
import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import { usePositionBind } from "@/composables/position/usePositionBind";
import { usePositionQuery } from "@/composables/position/usePositionQuery";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { useMobileStyles } from "@/composables/useMobileStyles";
import { RouteName } from "@/router/constants";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { onMounted, ref, watch } from "vue";
import { onMounted, reactive, ref, watch } from "vue";
import { useRoute } from "vue-router";
import useAlertStore from "../composables/store/useAlertStore";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
const positionName = ref<string>("");
// 定义筛选配置
const filterConfig: FilterItem[] = [
{
type: "input",
name: "positionName",
placeholder: "岗位名",
},
{
type: "select",
name: "bindState",
options: [
{ value: "BIND", label: "已绑定" },
{ value: "UNBIND", label: "未绑定" },
{ value: "ALL", label: "全部" },
],
},
];
// 筛选值
const filterValues = reactive<{
positionName: string;
bindState: "BIND" | "ALL" | "UNBIND";
}>({
positionName: "",
bindState: "ALL",
});
// 更新筛选值
const updateFilterValues = (
values: Record<string, string | number | boolean | Date[] | undefined>,
) => {
if (values.positionName !== undefined) {
filterValues.positionName = values.positionName as string;
}
if (values.bindState !== undefined) {
filterValues.bindState = values.bindState as "BIND" | "ALL" | "UNBIND";
}
};
const checkedPositionIds = ref<number[]>([]);
const positionBindModal = ref<ModalInterface>();
const positionUnbindModal = ref<ModalInterface>();
const allChecked = ref<boolean>(false);
const $route = useRoute();
const bindState = ref<"BIND" | "ALL" | "UNBIND">("ALL");
const alertStore = useAlertStore();
const actionExcStore = useActionExcStore();
@@ -147,9 +164,9 @@ const handleBindPositionSubmit = async () => {
level: "success",
});
await fetchPositionWith({
name: positionName.value,
name: filterValues.positionName,
userId: Number($route.params.userId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
clearCheckedPositionIds();
allChecked.value = false;
@@ -163,9 +180,9 @@ const handleUnbindPositionSubmit = async () => {
level: "success",
});
await fetchPositionWith({
name: positionName.value,
name: filterValues.positionName,
userId: Number($route.params.userId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
clearCheckedPositionIds();
allChecked.value = false;
@@ -173,9 +190,9 @@ const handleUnbindPositionSubmit = async () => {
onMounted(async () => {
await fetchPositionWith({
name: positionName.value,
name: filterValues.positionName,
userId: Number($route.params.userId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
initFlowbite();
const $bindModalElement: HTMLElement | null = document.querySelector(
@@ -201,18 +218,18 @@ onMounted(async () => {
const handleSearch = async () => {
await fetchPositionWith({
name: positionName.value,
name: filterValues.positionName,
userId: Number($route.params.userId),
bindState: bindState.value,
bindState: filterValues.bindState,
});
};
const handlePageChange = async (page: number, pageSize: number) => {
await fetchPositionWith(
{
name: positionName.value,
name: filterValues.positionName,
userId: Number($route.params.userId),
bindState: bindState.value,
bindState: filterValues.bindState,
},
page,
pageSize,

View File

@@ -4,35 +4,21 @@
<Breadcrumbs :names="['部门管理']" />
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl">部门管理</h1>
</div>
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-4 gap-y-3 sm:gap-y-0">
<form class="w-full sm:max-w-xs">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only">Search</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<Button :handleClick="() => handleUpsertDepartmentClick()" :isLoading="false" :abortable="false"
submitContent="新增部门" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</div>
<input type="search" id="default-search" v-model="name"
class="block w-full p-3 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500"
placeholder="部门名称" required />
<button type="submit"
class="text-white absolute end-1.5 bottom-1.5 bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-3 py-1.5 sm:px-4 sm:py-2"
@click.prevent="handleSearch">搜索</button>
</div>
</form>
<Button :handleClick="() => handleUpsertDepartmentClick()" :isLoading="false" :abortable="false"
submitContent="新增部门" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</template>
</Button>
</div>
</template>
</Button>
</template>
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden">
@@ -131,19 +117,45 @@ import DepartmentUpsertModal from "@/components/DepartmentUpsertModal.vue";
import MobileCardList from "@/components/MobileCardList.vue";
import DepartmentDeleteModal from "@/components/PopupModal.vue";
import TableButton from "@/components/TableButton.vue";
import TableFilterForm from "@/components/TableFilterForm.vue";
import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import type { DepartmentUpsertModel } from "@/types/department";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { nextTick, onMounted, ref } from "vue";
import { nextTick, onMounted, reactive, ref } from "vue";
import type { components } from "../api/types/schema";
import useDepartmentDelete from "../composables/department/useDepartmentDelete";
import { useDepartmentQuery } from "../composables/department/useDepartmentQuery";
import { useDepartmentUpsert } from "../composables/department/useDepartmentUpsert";
import useAlertStore from "../composables/store/useAlertStore";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
const name = ref<string>("");
// 定义筛选配置
const filterConfig = [
{
type: "input",
name: "departmentName",
placeholder: "部门名称",
},
] as FilterItem[];
// 筛选值
const filterValues = reactive<{
departmentName: string;
}>({
departmentName: "",
});
// 更新筛选值
const updateFilterValues = (
values: Record<string, string | number | boolean | Date[] | undefined>,
) => {
if (values.departmentName !== undefined) {
filterValues.departmentName = values.departmentName as string;
}
};
const selectedDepartment = ref<components["schemas"]["Department"]>();
const departmentUpsertModal = ref<ModalInterface>();
const departmentDeleteModal = ref<ModalInterface>();
@@ -170,7 +182,7 @@ const columns = [
onMounted(async () => {
await fetchDepartmentWith({
name: name.value,
name: filterValues.departmentName,
});
initFlowbite();
const $upsertModalElement: HTMLElement | null = document.querySelector(
@@ -202,7 +214,7 @@ const handleUpsertDepartmentSubmit = async (
level: "success",
});
await fetchDepartmentWith({
name: name.value,
name: filterValues.departmentName,
});
};
@@ -210,14 +222,14 @@ const handleUpsertDepartmentClick = async (
department?: components["schemas"]["Department"],
) => {
selectedDepartment.value = department;
await fetchAvailableDepartments(selectedDepartment.value?.id);
await fetchAvailableDepartments(department?.id);
await nextTick(() => {
departmentUpsertModal.value?.show();
});
};
const handleDeleteDepartmentSubmit = async () => {
if (!selectedDepartment?.value?.id) return;
if (!selectedDepartment.value?.id) return;
await deleteDepartment(selectedDepartment.value.id);
departmentDeleteModal.value?.hide();
alertStore.showAlert({
@@ -225,7 +237,7 @@ const handleDeleteDepartmentSubmit = async () => {
level: "success",
});
await fetchDepartmentWith({
name: name.value,
name: filterValues.departmentName,
});
};
@@ -240,17 +252,17 @@ const handleDeleteDepartmentClick = async (
const handleSearch = async () => {
await fetchDepartmentWith({
name: name.value,
name: filterValues.departmentName,
});
};
const handlePageChange = async (page: number, size: number) => {
const handlePageChange = async (page: number, pageSize: number) => {
await fetchDepartmentWith(
{
name: name.value,
name: filterValues.departmentName,
},
page,
size,
pageSize,
);
};
</script>

View File

@@ -4,26 +4,10 @@
<Breadcrumbs :names="['大模型管理']" />
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl">大模型管理</h1>
</div>
<div class="mb-4">
<form class="w-full sm:max-w-xs">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only">Search</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
</svg>
</div>
<input type="search" id="default-search" v-model="name"
class="block w-full p-3 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500"
placeholder="模型名称" required />
<button type="submit"
class="text-white absolute end-1.5 bottom-1.5 bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-3 py-1.5 sm:px-4 sm:py-2"
@click.prevent="handleSearch">搜索</button>
</div>
</form>
</div>
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden">
@@ -134,18 +118,44 @@
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import LlmUpdateModal from "@/components/LlmUpdateModal.vue";
import MobileCardList from "@/components/MobileCardList.vue";
import TableFilterForm from "@/components/TableFilterForm.vue";
import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import { useLlmQuery } from "@/composables/ai/useLlmQuery";
import { useLlmUpdate } from "@/composables/ai/useLlmUpdate";
import useAlertStore from "@/composables/store/useAlertStore";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { nextTick, onMounted, ref } from "vue";
import { nextTick, onMounted, reactive, ref } from "vue";
import type { components } from "../api/types/schema";
// 定义筛选配置
const filterConfig: FilterItem[] = [
{
type: "input",
name: "modelName",
placeholder: "模型名称",
},
];
// 筛选值
const filterValues = reactive<{
modelName: string;
}>({
modelName: "",
});
// 更新筛选值
const updateFilterValues = (
values: Record<string, string | number | boolean | Date[] | undefined>,
) => {
if (values.modelName !== undefined) {
filterValues.modelName = values.modelName as string;
}
};
const llmUpdateModal = ref<ModalInterface>();
const selectedLlm = ref<components["schemas"]["LlmVm"]>();
const name = ref<string>("");
const { llms, fetchLlmConfigs, total } = useLlmQuery();
const { updateLlmConfig } = useLlmUpdate();
@@ -171,15 +181,15 @@ const handleUpdateModalSubmit = async (llm: components["schemas"]["LlmVm"]) => {
level: "success",
content: "操作成功",
});
await fetchLlmConfigs();
await fetchLlmConfigs(1, 10, filterValues.modelName);
};
const handleSearch = async () => {
await fetchLlmConfigs();
await fetchLlmConfigs(1, 10, filterValues.modelName);
};
const handlePageChange = async (page: number, pageSize: number) => {
await fetchLlmConfigs(page, pageSize);
await fetchLlmConfigs(page, pageSize, filterValues.modelName);
};
const handleLlmUpdateClick = async (llm: components["schemas"]["LlmVm"]) => {
@@ -190,7 +200,7 @@ const handleLlmUpdateClick = async (llm: components["schemas"]["LlmVm"]) => {
};
onMounted(async () => {
await fetchLlmConfigs();
await fetchLlmConfigs(1, 10, filterValues.modelName);
initFlowbite();
const $llmUpdateModalElement: HTMLElement | null =
document.querySelector("#llm-update-modal");

View File

@@ -4,36 +4,21 @@
<Breadcrumbs :names="['权限管理']" />
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl">权限管理</h1>
</div>
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-4 gap-y-3 sm:gap-y-0">
<form class="w-full sm:max-w-xs">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only">Search</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<Button :handleClick="() => handleUpsertPermissionClick(undefined)" :isLoading="false" :abortable="false"
submitContent="新增权限" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</div>
<input type="search" id="default-search" v-model="permissionName"
class="block w-full p-3 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500"
placeholder="权限名" required />
<button type="submit"
class="text-white absolute end-1.5 bottom-1.5 bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-3 py-1.5 sm:px-4 sm:py-2"
@click.prevent="handleSearch">搜索</button>
</div>
</form>
<!-- Create Modal toggle -->
<Button :handleClick="() => handleUpsertPermissionClick(undefined)" :isLoading="false" :abortable="false"
submitContent="新增权限" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</template>
</Button>
</div>
</template>
</Button>
</template>
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden">
@@ -125,26 +110,51 @@
</template>
<script setup lang="ts">
import Button from "@/components/Button.vue";
import PermissionUpsertModal from "@/components/PermissionUpsertModal.vue";
import PermissionDeleteModal from "@/components/PopupModal.vue";
import usePermissionDelete from "@/composables/permission/usePermissionDelete";
import type { components } from "@/api/types/schema";
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import MobileCardList from "@/components/MobileCardList.vue";
import PermissionUpsertModal from "@/components/PermissionUpsertModal.vue";
import PermissionDeleteModal from "@/components/PopupModal.vue";
import TableButton from "@/components/TableButton.vue";
import TableFilterForm from "@/components/TableFilterForm.vue";
import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import usePermissionDelete from "@/composables/permission/usePermissionDelete";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { nextTick, onMounted, ref } from "vue";
import { nextTick, onMounted, reactive, ref } from "vue";
import usePermissionsQuery from "../composables/permission/usePermissionQuery";
import usePermissionUpsert from "../composables/permission/usePermissionUpsert";
import useAlertStore from "../composables/store/useAlertStore";
import type { PermissionUpsertModel } from "../types/permission";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
const permissionName = ref<string>("");
// 定义筛选配置
const filterConfig = [
{
type: "input",
name: "permissionName",
placeholder: "权限名",
},
] as FilterItem[];
// 筛选值
const filterValues = reactive<{
permissionName: string;
}>({
permissionName: "",
});
// 更新筛选值
const updateFilterValues = (
values: Record<string, string | number | boolean | Date[] | undefined>,
) => {
if (values.permissionName !== undefined) {
filterValues.permissionName = values.permissionName as string;
}
};
const selectedPermission = ref<components["schemas"]["PermissionRespDto"]>();
const permissionUpsertModal = ref<ModalInterface>();
const permissionDeleteModal = ref<ModalInterface>();
@@ -164,7 +174,7 @@ const columns = [
onMounted(async () => {
await fetchPermissionsWith({
name: permissionName.value,
name: filterValues.permissionName,
});
initFlowbite();
const $upsertModalElement: HTMLElement | null = document.querySelector(
@@ -189,7 +199,7 @@ onMounted(async () => {
const handleUpsertModalSubmit = async (data: PermissionUpsertModel) => {
await permissionUpsert.upsertPermission(data);
await fetchPermissionsWith({
name: permissionName.value,
name: filterValues.permissionName,
});
permissionUpsertModal.value?.hide();
alertStore.showAlert({
@@ -208,7 +218,7 @@ const handleUpsertPermissionClick = async (
};
const handleDeleteModalSubmit = async () => {
if (!selectedPermission?.value?.id) return;
if (!selectedPermission.value?.id) return;
await deletePermission(selectedPermission.value.id);
permissionDeleteModal.value?.hide();
alertStore.showAlert({
@@ -216,7 +226,7 @@ const handleDeleteModalSubmit = async () => {
level: "success",
});
await fetchPermissionsWith({
name: permissionName.value,
name: filterValues.permissionName,
});
};
@@ -231,14 +241,14 @@ const handleDeletePermissionClick = async (
const handleSearch = async () => {
await fetchPermissionsWith({
name: permissionName.value,
name: filterValues.permissionName,
});
};
const handlePageChange = async (page: number, pageSize: number) => {
await fetchPermissionsWith(
{
name: permissionName.value,
name: filterValues.permissionName,
},
page,
pageSize,

View File

@@ -4,36 +4,21 @@
<Breadcrumbs :names="['岗位管理']" />
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl">岗位管理</h1>
</div>
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-4 gap-y-3 sm:gap-y-0">
<form class="w-full sm:max-w-xs">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only">Search</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<Button :handleClick="() => handleUpsertPositionClick()" :isLoading="false" :abortable="false"
submitContent="新增岗位" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</div>
<input type="search" id="default-search" v-model="name"
class="block w-full p-3 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500"
placeholder="岗位名称" required />
<button type="submit"
class="text-white absolute end-1.5 bottom-1.5 bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-3 py-1.5 sm:px-4 sm:py-2"
@click.prevent="handleSearch">搜索</button>
</div>
</form>
<!-- Create Modal toggle -->
<Button :handleClick="() => handleUpsertPositionClick()" :isLoading="false" :abortable="false"
submitContent="新增岗位" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</template>
</Button>
</div>
</template>
</Button>
</template>
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden">
@@ -121,18 +106,44 @@ import Button from "@/components/Button.vue";
import MobileCardList from "@/components/MobileCardList.vue";
import PositionDeleteModal from "@/components/PopupModal.vue";
import PositionUpsertModal from "@/components/PositionUpsertModal.vue";
import TableFilterForm from "@/components/TableFilterForm.vue";
import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import usePositionDelete from "@/composables/position/usePositionDelete";
import { usePositionQuery } from "@/composables/position/usePositionQuery";
import { usePositionUpsert } from "@/composables/position/usePositionUpsert";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { nextTick, onMounted, ref } from "vue";
import { nextTick, onMounted, reactive, ref } from "vue";
import type { components } from "../api/types/schema";
import useAlertStore from "../composables/store/useAlertStore";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
const name = ref<string>("");
// 定义筛选配置
const filterConfig = [
{
type: "input",
name: "positionName",
placeholder: "岗位名称",
},
] as FilterItem[];
// 筛选值
const filterValues = reactive<{
positionName: string;
}>({
positionName: "",
});
// 更新筛选值
const updateFilterValues = (
values: Record<string, string | number | boolean | Date[] | undefined>,
) => {
if (values.positionName !== undefined) {
filterValues.positionName = values.positionName as string;
}
};
const selectedPosition = ref<components["schemas"]["Position"]>();
const positionUpsertModal = ref<ModalInterface>();
const positionDeleteModal = ref<ModalInterface>();
@@ -154,7 +165,7 @@ const columns = [
onMounted(async () => {
await fetchAllPositions();
await fetchPositionWith({
name: name.value,
name: filterValues.positionName,
});
initFlowbite();
const $upsertModalElement: HTMLElement | null = document.querySelector(
@@ -187,7 +198,7 @@ const handleUpsertPositionSubmit = async (
});
fetchAllPositions();
await fetchPositionWith({
name: name.value,
name: filterValues.positionName,
});
};
@@ -201,16 +212,15 @@ const handleUpsertPositionClick = async (
};
const handleDeletePositionSubmit = async () => {
if (!selectedPosition?.value?.id) return;
if (!selectedPosition.value?.id) return;
await deletePosition(selectedPosition.value.id);
positionDeleteModal.value?.hide();
alertStore.showAlert({
content: "删除成功",
level: "success",
});
fetchAllPositions();
await fetchPositionWith({
name: name.value,
name: filterValues.positionName,
});
};
@@ -225,17 +235,17 @@ const handleDeletePositionClick = async (
const handleSearch = async () => {
await fetchPositionWith({
name: name.value,
name: filterValues.positionName,
});
};
const handlePageChange = async (page: number, size: number) => {
const handlePageChange = async (page: number, pageSize: number) => {
await fetchPositionWith(
{
name: name.value,
name: filterValues.positionName,
},
page,
size,
pageSize,
);
};
</script>

View File

@@ -4,36 +4,21 @@
<Breadcrumbs :names="['角色管理']" />
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl">角色管理</h1>
</div>
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-4 gap-y-3 sm:gap-y-0">
<form class="w-full sm:max-w-xs">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only">Search</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<Button :handleClick="() => handleUpsertRoleClick(undefined)" :isLoading="false" :abortable="false"
submitContent="新增角色" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</div>
<input type="search" id="default-search" v-model="roleName"
class="block w-full p-3 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500"
placeholder="角色名" required />
<button type="submit"
class="text-white absolute end-1.5 bottom-1.5 bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-3 py-1.5 sm:px-4 sm:py-2"
@click.prevent="handleSearch">搜索</button>
</div>
</form>
<!-- Create Modal toggle -->
<Button :handleClick="() => handleUpsertRoleClick(undefined)" :isLoading="false" :abortable="false"
submitContent="新增角色" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</template>
</Button>
</div>
</template>
</Button>
</template>
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden">
@@ -145,21 +130,47 @@ import Button from "@/components/Button.vue";
import MobileCardList from "@/components/MobileCardList.vue";
import RoleDeleteModal from "@/components/PopupModal.vue";
import RoleUpsertModal from "@/components/RoleUpsertModal.vue";
import TableFilterForm from "@/components/TableFilterForm.vue";
import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import useRoleDelete from "@/composables/role/useRoleDelete";
import { useRolesQuery } from "@/composables/role/useRolesQuery";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { RouteName } from "@/router/constants";
import type { RoleUpsertModel } from "@/types/role";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { nextTick, onMounted, ref } from "vue";
import { nextTick, onMounted, reactive, ref } from "vue";
import { useRouter } from "vue-router";
import type { components } from "../api/types/schema";
import { useRoleUpsert } from "../composables/role/useRoleUpsert";
import useAlertStore from "../composables/store/useAlertStore";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
const roleName = ref<string>("");
// 定义筛选配置
const filterConfig = [
{
type: "input",
name: "roleName",
placeholder: "角色名",
},
] as FilterItem[];
// 筛选值
const filterValues = reactive<{
roleName: string;
}>({
roleName: "",
});
// 更新筛选值
const updateFilterValues = (
values: Record<string, string | number | boolean | Date[] | undefined>,
) => {
if (values.roleName !== undefined) {
filterValues.roleName = values.roleName as string;
}
};
const selectedRole = ref<components["schemas"]["RoleDto"]>();
const roleUpsertModal = ref<ModalInterface>();
const roleDeleteModal = ref<ModalInterface>();
@@ -181,7 +192,7 @@ const columns = [
onMounted(async () => {
await fetchRolesWith({
name: roleName.value,
name: filterValues.roleName,
});
initFlowbite();
const $upsertModalElement: HTMLElement | null =
@@ -203,14 +214,14 @@ onMounted(async () => {
const handleUpsertModalSubmit = async (data: RoleUpsertModel) => {
await upsertRole.upsertRole(data);
await fetchRolesWith({
name: roleName.value,
});
roleUpsertModal.value?.hide();
alertStore.showAlert({
content: "操作成功",
level: "success",
});
await fetchRolesWith({
name: filterValues.roleName,
});
};
const handleUpsertRoleClick = async (
@@ -222,17 +233,28 @@ const handleUpsertRoleClick = async (
});
};
const handleDeletedModalSubmit = async () => {
if (!selectedRole?.value?.id) return;
await deleteRole(selectedRole.value.id);
await fetchRolesWith({
name: roleName.value,
const handleBindPermissionClick = async (
role: components["schemas"]["RoleDto"],
) => {
router.push({
name: RouteName.BINDPERMISSIONVIEW,
params: {
roleId: role.id,
},
});
};
const handleDeletedModalSubmit = async () => {
if (!selectedRole.value?.id) return;
await deleteRole(selectedRole.value.id);
roleDeleteModal.value?.hide();
alertStore.showAlert({
content: "删除成功",
level: "success",
});
await fetchRolesWith({
name: filterValues.roleName,
});
};
const handleDeleteRoleClick = async (
@@ -244,24 +266,16 @@ const handleDeleteRoleClick = async (
});
};
const handleBindPermissionClick = async (
role: components["schemas"]["RoleDto"],
) => {
router.push({
name: RouteName.BINDPERMISSIONVIEW,
params: { roleId: role.id },
});
};
const handleSearch = async () => {
await fetchRolesWith({
name: roleName.value,
name: filterValues.roleName,
});
};
const handlePageChange = async (page: number, pageSize: number) => {
await fetchRolesWith(
{
name: roleName.value,
name: filterValues.roleName,
},
page,
pageSize,

View File

@@ -4,26 +4,10 @@
<Breadcrumbs :names="['任务管理']" />
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl">任务管理</h1>
</div>
<div class="mb-4">
<form class="w-full sm:max-w-xs">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only">Search</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
</svg>
</div>
<input type="search" id="default-search" v-model="jobName"
class="block w-full p-3 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500"
placeholder="任务名称" required />
<button type="submit"
class="text-white absolute end-1.5 bottom-1.5 bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-3 py-1.5 sm:px-4 sm:py-2"
@click.prevent="handleSearch">搜索</button>
</div>
</form>
</div>
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden">
@@ -167,6 +151,8 @@ import Breadcrumbs from "@/components/Breadcrumbs.vue";
import MobileCardList from "@/components/MobileCardList.vue";
import PopupModal from "@/components/PopupModal.vue";
import SchedulerUpdateModal from "@/components/SchedulerUpdateModal.vue";
import TableFilterForm from "@/components/TableFilterForm.vue";
import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import { useJobControl } from "@/composables/job/useJobControl";
@@ -175,10 +161,34 @@ import { useJobUpdate } from "@/composables/job/useJobUpdate";
import useAlertStore from "@/composables/store/useAlertStore";
import { dayjs } from "@/utils/dateUtil";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { nextTick, onMounted, ref } from "vue";
import { nextTick, onMounted, reactive, ref } from "vue";
import type { components } from "../api/types/schema";
const jobName = ref<string>("");
// 定义筛选配置
const filterConfig: FilterItem[] = [
{
type: "input",
name: "jobName",
placeholder: "任务名称",
},
];
// 筛选值
const filterValues = reactive<{
jobName: string;
}>({
jobName: "",
});
// 更新筛选值
const updateFilterValues = (
values: Record<string, string | number | boolean | Date[] | undefined>,
) => {
if (values.jobName !== undefined) {
filterValues.jobName = values.jobName as string;
}
};
const jobResumeModal = ref<ModalInterface>();
const jobPauseModal = ref<ModalInterface>();
const jobUpdateModal = ref<ModalInterface>();
@@ -249,7 +259,7 @@ const handleResumeModalSubmit = async () => {
content: "操作成功",
});
await fetchJobsWith({
name: jobName.value,
name: filterValues.jobName,
});
};
@@ -265,7 +275,7 @@ const handleUpdateModalSubmit = async (cronExpression: string) => {
content: "操作成功",
});
await fetchJobsWith({
name: jobName.value,
name: filterValues.jobName,
});
};
@@ -280,20 +290,20 @@ const handlePauseModalSubmit = async () => {
content: "操作成功",
});
await fetchJobsWith({
name: jobName.value,
name: filterValues.jobName,
});
};
const handleSearch = async () => {
await fetchJobsWith({
name: jobName.value,
name: filterValues.jobName,
});
};
const handlePageChange = async (page: number, pageSize: number) => {
await fetchJobsWith(
{
name: jobName.value,
name: filterValues.jobName,
},
page,
pageSize,
@@ -302,7 +312,7 @@ const handlePageChange = async (page: number, pageSize: number) => {
onMounted(async () => {
await fetchJobsWith({
name: jobName.value,
name: filterValues.jobName,
});
initFlowbite();
const $jobResumeModalElement: HTMLElement | null =