mirror of
https://github.com/ccmjga/zhilu-admin
synced 2026-03-13 21:27:19 +08:00
add notify ai
This commit is contained in:
@@ -30,5 +30,18 @@
|
||||
"formatter": {
|
||||
"quoteStyle": "double"
|
||||
}
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"include": ["*.vue"],
|
||||
"linter": {
|
||||
"rules": {
|
||||
"style": {
|
||||
"useConst": "off",
|
||||
"useImportType": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ import LoadingIcon from "@/components/icons/LoadingIcon.vue";
|
||||
import { useAiAction } from "@/composables/ai/useAiAction";
|
||||
import { useDepartmentQuery } from "@/composables/department/useDepartmentQuery";
|
||||
import { useDepartmentUpsert } from "@/composables/department/useDepartmentUpsert";
|
||||
import { useActionExcStore } from "@/composables/store/useActionExcStore";
|
||||
import useAlertStore from "@/composables/store/useAlertStore";
|
||||
import type { DepartmentUpsertModel } from "@/types/department";
|
||||
import DOMPurify from "dompurify";
|
||||
@@ -118,7 +119,7 @@ const { deleteUserByUsername, deleteDepartmentByName } = useAiAction();
|
||||
const departmentDeleteModal = ref<ModalInterface>();
|
||||
const currentDeleteUsername = ref<string>();
|
||||
const currentDeleteDepartmentName = ref<string>();
|
||||
|
||||
const actionExcStore = useActionExcStore();
|
||||
const { availableDepartments, fetchAvailableDepartments } =
|
||||
useDepartmentQuery();
|
||||
|
||||
@@ -194,6 +195,7 @@ const handleUpsertUserSubmit = async (data: UserUpsertSubmitModel) => {
|
||||
content: "操作成功",
|
||||
level: "success",
|
||||
});
|
||||
actionExcStore.notify(true);
|
||||
};
|
||||
|
||||
const handleUpsertDepartmentSubmit = async (
|
||||
@@ -205,6 +207,7 @@ const handleUpsertDepartmentSubmit = async (
|
||||
content: "操作成功",
|
||||
level: "success",
|
||||
});
|
||||
actionExcStore.notify(true);
|
||||
};
|
||||
|
||||
const handleDeleteUserSubmit = async () => {
|
||||
@@ -214,6 +217,7 @@ const handleDeleteUserSubmit = async () => {
|
||||
content: "操作成功",
|
||||
level: "success",
|
||||
});
|
||||
actionExcStore.notify(true);
|
||||
};
|
||||
|
||||
const handleDeleteDepartmentSubmit = async () => {
|
||||
@@ -223,6 +227,7 @@ const handleDeleteDepartmentSubmit = async () => {
|
||||
content: "操作成功",
|
||||
level: "success",
|
||||
});
|
||||
actionExcStore.notify(true);
|
||||
};
|
||||
|
||||
watch(
|
||||
@@ -261,6 +266,7 @@ const chatByMode = async (
|
||||
await searchAction(message);
|
||||
} else if (mode === "execute") {
|
||||
await executeAction(message);
|
||||
actionExcStore.notify(true);
|
||||
} else {
|
||||
await chat(message);
|
||||
}
|
||||
|
||||
@@ -35,21 +35,21 @@ import { computed } from "vue";
|
||||
import type { RouteLocationRaw } from "vue-router";
|
||||
|
||||
interface BreadcrumbItem {
|
||||
name: string;
|
||||
route?: RouteLocationRaw;
|
||||
name: string;
|
||||
route?: RouteLocationRaw;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
names: string[];
|
||||
routes?: RouteLocationRaw[];
|
||||
names: string[];
|
||||
routes?: RouteLocationRaw[];
|
||||
}>();
|
||||
|
||||
const breadcrumbs = computed<BreadcrumbItem[]>(() => {
|
||||
return props.names.map((name, index) => {
|
||||
return {
|
||||
name,
|
||||
route: props.routes?.[index]
|
||||
};
|
||||
});
|
||||
return props.names.map((name, index) => {
|
||||
return {
|
||||
name,
|
||||
route: props.routes?.[index],
|
||||
};
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
35
frontend/src/components/DateRangePicker.vue
Normal file
35
frontend/src/components/DateRangePicker.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div id="date-range-picker" date-rangepicker class="flex items-center">
|
||||
<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 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2h20V4ZM0 18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8H0v10Zm5-8h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<input id="datepicker-range-start" name="start" type="text"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="Select date start">
|
||||
</div>
|
||||
<span class="mx-4 text-gray-500">to</span>
|
||||
<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 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2h20V4ZM0 18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8H0v10Zm5-8h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<input id="datepicker-range-end" name="end" type="text"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="Select date end">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center justify-start rtl:justify-end">
|
||||
<button type="button" @click="handleSidebarToggle"
|
||||
class="inline-flex items-center p-2 text-sm text-gray-500 rounded-lg sm:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200">
|
||||
class="inline-flex items-center p-2 text-sm text-gray-500 rounded-lg sm:hidden hover:bg-gray-100">
|
||||
<span class="sr-only">Open sidebar</span>
|
||||
<svg class="w-6 h-6" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
@@ -13,7 +13,7 @@
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="https://github.com/ccmjga/zhilu-admin" target="_blank" class="flex items-center ms-2 md:me-24">
|
||||
<a href="https://github.com/ccmjga/zhilu-admin" target="_blank" class="flex items-center ms-2 md:me-24 ">
|
||||
<img class="me-3" src="/logo.svg" alt="logo">
|
||||
<span class="self-center text-lg sm:text-xl md:text-2xl font-semibold whitespace-nowrap">知路后台管理</span>
|
||||
</a>
|
||||
@@ -36,8 +36,7 @@
|
||||
</button>
|
||||
<div class="flex items-center ms-2 sm:ms-3">
|
||||
<div>
|
||||
<button type="button" id="dropdown-button"
|
||||
class="flex text-sm bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300 cursor-pointer"
|
||||
<button type="button" id="dropdown-button" class="flex text-sm bg-gray-800 rounded-full cursor-pointer"
|
||||
aria-expanded="false" data-dropdown-toggle="dropdown-user">
|
||||
<span class="sr-only">打开用户菜单</span>
|
||||
<img class="w-8 h-8 rounded-full" src="/java.svg" alt="user photo">
|
||||
|
||||
@@ -32,18 +32,18 @@
|
||||
</template>
|
||||
|
||||
<script setup generic="T" lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { ref } from "vue";
|
||||
|
||||
/** 通用对象类型 */
|
||||
type ItemRecord = Record<string, unknown>;
|
||||
|
||||
const props = defineProps<{
|
||||
/** 数据项数组 */
|
||||
items: T[] | undefined;
|
||||
/** 数据项ID字段名 */
|
||||
idField?: string;
|
||||
/** 数据项唯一键字段名 */
|
||||
keyField?: string;
|
||||
/** 数据项数组 */
|
||||
items: T[] | undefined;
|
||||
/** 数据项ID字段名 */
|
||||
idField?: string;
|
||||
/** 数据项唯一键字段名 */
|
||||
keyField?: string;
|
||||
}>();
|
||||
|
||||
/**
|
||||
@@ -53,17 +53,17 @@ const props = defineProps<{
|
||||
* @returns 唯一键
|
||||
*/
|
||||
const getItemKey = (item: T, index: number): string | number => {
|
||||
if (props.keyField) {
|
||||
const key = (item as ItemRecord)[props.keyField];
|
||||
if (key !== undefined) return String(key);
|
||||
}
|
||||
|
||||
if (props.idField) {
|
||||
const id = (item as ItemRecord)[props.idField];
|
||||
if (id !== undefined) return String(id);
|
||||
}
|
||||
|
||||
const id = (item as ItemRecord).id;
|
||||
return id !== undefined ? String(id) : index;
|
||||
if (props.keyField) {
|
||||
const key = (item as ItemRecord)[props.keyField];
|
||||
if (key !== undefined) return String(key);
|
||||
}
|
||||
|
||||
if (props.idField) {
|
||||
const id = (item as ItemRecord)[props.idField];
|
||||
if (id !== undefined) return String(id);
|
||||
}
|
||||
|
||||
const id = (item as ItemRecord).id;
|
||||
return id !== undefined ? String(id) : index;
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -37,24 +37,24 @@
|
||||
</template>
|
||||
|
||||
<script setup generic="T" lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
/** 通用对象类型 */
|
||||
type ItemRecord = Record<string, unknown>;
|
||||
|
||||
const props = defineProps<{
|
||||
/** 数据项数组 */
|
||||
items: T[] | undefined;
|
||||
/** 数据项ID字段名 */
|
||||
idField?: string;
|
||||
/** 数据项唯一键字段名 */
|
||||
keyField?: string;
|
||||
/** 选中项的值数组 */
|
||||
modelValue?: (string | number)[];
|
||||
/** 数据项数组 */
|
||||
items: T[] | undefined;
|
||||
/** 数据项ID字段名 */
|
||||
idField?: string;
|
||||
/** 数据项唯一键字段名 */
|
||||
keyField?: string;
|
||||
/** 选中项的值数组 */
|
||||
modelValue?: (string | number)[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [checkedItems: (string | number)[]];
|
||||
"update:modelValue": [checkedItems: (string | number)[]];
|
||||
}>();
|
||||
|
||||
const checkedItems = ref<(string | number)[]>(props.modelValue || []);
|
||||
@@ -66,13 +66,13 @@ const checkedItems = ref<(string | number)[]>(props.modelValue || []);
|
||||
* @returns 唯一键
|
||||
*/
|
||||
const getItemKey = (item: T, index: number): string | number => {
|
||||
if (props.keyField) {
|
||||
const key = (item as ItemRecord)[props.keyField];
|
||||
if (key !== undefined) return String(key);
|
||||
}
|
||||
|
||||
const id = getItemId(item);
|
||||
return id !== undefined ? id : index;
|
||||
if (props.keyField) {
|
||||
const key = (item as ItemRecord)[props.keyField];
|
||||
if (key !== undefined) return String(key);
|
||||
}
|
||||
|
||||
const id = getItemId(item);
|
||||
return id !== undefined ? id : index;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -81,21 +81,28 @@ const getItemKey = (item: T, index: number): string | number => {
|
||||
* @returns ID值
|
||||
*/
|
||||
const getItemId = (item: T): string | number => {
|
||||
if (props.idField) {
|
||||
return (item as ItemRecord)[props.idField] as string | number;
|
||||
}
|
||||
return (item as ItemRecord).id as string | number || item as unknown as string | number;
|
||||
if (props.idField) {
|
||||
return (item as ItemRecord)[props.idField] as string | number;
|
||||
}
|
||||
return (
|
||||
((item as ItemRecord).id as string | number) ||
|
||||
(item as unknown as string | number)
|
||||
);
|
||||
};
|
||||
|
||||
// 监听选中项变化
|
||||
watch(checkedItems, (newVal) => {
|
||||
emit('update:modelValue', newVal);
|
||||
emit("update:modelValue", newVal);
|
||||
});
|
||||
|
||||
// 监听modelValue变化
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
if (newVal) {
|
||||
checkedItems.value = newVal;
|
||||
}
|
||||
}, { deep: true });
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
checkedItems.value = newVal;
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -14,70 +14,78 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { computed } from "vue";
|
||||
|
||||
export type ButtonVariant = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info';
|
||||
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg';
|
||||
export type ButtonVariant =
|
||||
| "primary"
|
||||
| "secondary"
|
||||
| "success"
|
||||
| "danger"
|
||||
| "warning"
|
||||
| "info";
|
||||
export type ButtonSize = "xs" | "sm" | "md" | "lg";
|
||||
|
||||
const props = defineProps<{
|
||||
/** 按钮变体类型 */
|
||||
variant?: ButtonVariant;
|
||||
/** 按钮尺寸 */
|
||||
size?: ButtonSize;
|
||||
/** 是否禁用 */
|
||||
disabled?: boolean;
|
||||
/** 自定义CSS类名 */
|
||||
className?: string;
|
||||
/** 是否为移动端尺寸 */
|
||||
isMobile?: boolean;
|
||||
/** 按钮变体类型 */
|
||||
variant?: ButtonVariant;
|
||||
/** 按钮尺寸 */
|
||||
size?: ButtonSize;
|
||||
/** 是否禁用 */
|
||||
disabled?: boolean;
|
||||
/** 自定义CSS类名 */
|
||||
className?: string;
|
||||
/** 是否为移动端尺寸 */
|
||||
isMobile?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'click': [event: MouseEvent];
|
||||
click: [event: MouseEvent];
|
||||
}>();
|
||||
|
||||
/** 按钮颜色样式映射 */
|
||||
const colorClasses = computed(() => {
|
||||
const variants: Record<ButtonVariant, string> = {
|
||||
primary: 'text-white bg-blue-700 hover:bg-blue-800 focus:ring-blue-300',
|
||||
secondary: 'text-gray-900 bg-white border border-gray-300 hover:bg-gray-100 focus:ring-gray-100',
|
||||
success: 'text-white bg-green-700 hover:bg-green-800 focus:ring-green-300',
|
||||
danger: 'text-white bg-red-700 hover:bg-red-800 focus:ring-red-300',
|
||||
warning: 'text-gray-900 bg-yellow-400 hover:bg-yellow-500 focus:ring-yellow-300',
|
||||
info: 'text-white bg-cyan-700 hover:bg-cyan-800 focus:ring-cyan-300'
|
||||
};
|
||||
|
||||
return variants[props.variant || 'primary'];
|
||||
const variants: Record<ButtonVariant, string> = {
|
||||
primary: "text-white bg-blue-700 hover:bg-blue-800 focus:ring-blue-300",
|
||||
secondary:
|
||||
"text-gray-900 bg-white border border-gray-300 hover:bg-gray-100 focus:ring-gray-100",
|
||||
success: "text-white bg-green-700 hover:bg-green-800 focus:ring-green-300",
|
||||
danger: "text-white bg-red-700 hover:bg-red-800 focus:ring-red-300",
|
||||
warning:
|
||||
"text-gray-900 bg-yellow-400 hover:bg-yellow-500 focus:ring-yellow-300",
|
||||
info: "text-white bg-cyan-700 hover:bg-cyan-800 focus:ring-cyan-300",
|
||||
};
|
||||
|
||||
return variants[props.variant || "primary"];
|
||||
});
|
||||
|
||||
/** 按钮尺寸样式映射 */
|
||||
const sizeClasses = computed(() => {
|
||||
// 移动端尺寸
|
||||
if (props.isMobile) {
|
||||
const sizes: Record<ButtonSize, string> = {
|
||||
xs: 'text-xs px-2 py-1',
|
||||
sm: 'text-xs px-3 py-1.5',
|
||||
md: 'text-sm px-3 py-2',
|
||||
lg: 'text-sm px-4 py-2.5'
|
||||
};
|
||||
return sizes[props.size || 'sm'];
|
||||
}
|
||||
|
||||
// PC端尺寸
|
||||
const sizes: Record<ButtonSize, string> = {
|
||||
xs: 'text-xs px-3 py-1.5',
|
||||
sm: 'text-sm px-3 py-2',
|
||||
md: 'text-sm px-4 py-2.5',
|
||||
lg: 'text-base px-5 py-3'
|
||||
};
|
||||
|
||||
return sizes[props.size || 'md'];
|
||||
// 移动端尺寸
|
||||
if (props.isMobile) {
|
||||
const sizes: Record<ButtonSize, string> = {
|
||||
xs: "text-xs px-2 py-1",
|
||||
sm: "text-xs px-3 py-1.5",
|
||||
md: "text-sm px-3 py-2",
|
||||
lg: "text-sm px-4 py-2.5",
|
||||
};
|
||||
return sizes[props.size || "sm"];
|
||||
}
|
||||
|
||||
// PC端尺寸
|
||||
const sizes: Record<ButtonSize, string> = {
|
||||
xs: "text-xs px-3 py-1.5",
|
||||
sm: "text-sm px-3 py-2",
|
||||
md: "text-sm px-4 py-2.5",
|
||||
lg: "text-base px-5 py-3",
|
||||
};
|
||||
|
||||
return sizes[props.size || "md"];
|
||||
});
|
||||
|
||||
/** 处理点击事件 */
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
if (!props.disabled) {
|
||||
emit('click', event);
|
||||
}
|
||||
if (!props.disabled) {
|
||||
emit("click", event);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -51,44 +51,44 @@
|
||||
</template>
|
||||
|
||||
<script setup generic="T" lang="ts">
|
||||
import { defineEmits, ref, watch } from 'vue';
|
||||
import { defineEmits, ref, watch } from "vue";
|
||||
|
||||
/**
|
||||
* 表格列配置接口
|
||||
*/
|
||||
export interface Column {
|
||||
/** 列标题 */
|
||||
title: string;
|
||||
/** 数据字段名 */
|
||||
field: string;
|
||||
/** 是否可排序 */
|
||||
sortable?: boolean;
|
||||
/** 自定义CSS类名 */
|
||||
class?: string;
|
||||
/** 列标题 */
|
||||
title: string;
|
||||
/** 数据字段名 */
|
||||
field: string;
|
||||
/** 是否可排序 */
|
||||
sortable?: boolean;
|
||||
/** 自定义CSS类名 */
|
||||
class?: string;
|
||||
}
|
||||
|
||||
/** 通用对象类型 */
|
||||
type ItemRecord = Record<string, unknown>;
|
||||
|
||||
const props = defineProps<{
|
||||
/** 数据项数组 */
|
||||
items: T[];
|
||||
/** 列配置数组 */
|
||||
columns: Column[];
|
||||
/** 是否显示复选框 */
|
||||
hasCheckbox?: boolean;
|
||||
/** 数据项ID字段名 */
|
||||
idField?: string;
|
||||
/** 数据项唯一键字段名 */
|
||||
keyField?: string;
|
||||
/** 选中项的值数组,用于v-model绑定 */
|
||||
modelValue?: (string | number)[];
|
||||
/** 数据项数组 */
|
||||
items: T[];
|
||||
/** 列配置数组 */
|
||||
columns: Column[];
|
||||
/** 是否显示复选框 */
|
||||
hasCheckbox?: boolean;
|
||||
/** 数据项ID字段名 */
|
||||
idField?: string;
|
||||
/** 数据项唯一键字段名 */
|
||||
keyField?: string;
|
||||
/** 选中项的值数组,用于v-model绑定 */
|
||||
modelValue?: (string | number)[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [checkedItems: (string | number)[]];
|
||||
'sort': [field: string];
|
||||
'all-checked-change': [checked: boolean];
|
||||
"update:modelValue": [checkedItems: (string | number)[]];
|
||||
sort: [field: string];
|
||||
"all-checked-change": [checked: boolean];
|
||||
}>();
|
||||
|
||||
const checkedItems = ref<(string | number)[]>(props.modelValue || []);
|
||||
@@ -101,13 +101,13 @@ const allChecked = ref(false);
|
||||
* @returns 唯一键
|
||||
*/
|
||||
const getItemKey = (item: T, index: number): string | number => {
|
||||
if (props.keyField) {
|
||||
const key = (item as ItemRecord)[props.keyField];
|
||||
if (key !== undefined) return String(key);
|
||||
}
|
||||
|
||||
const id = getItemId(item);
|
||||
return id !== undefined ? id : index;
|
||||
if (props.keyField) {
|
||||
const key = (item as ItemRecord)[props.keyField];
|
||||
if (key !== undefined) return String(key);
|
||||
}
|
||||
|
||||
const id = getItemId(item);
|
||||
return id !== undefined ? id : index;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -116,10 +116,13 @@ const getItemKey = (item: T, index: number): string | number => {
|
||||
* @returns ID值
|
||||
*/
|
||||
const getItemId = (item: T): string | number => {
|
||||
if (props.idField) {
|
||||
return (item as ItemRecord)[props.idField] as string | number;
|
||||
}
|
||||
return (item as ItemRecord).id as string | number || item as unknown as string | number;
|
||||
if (props.idField) {
|
||||
return (item as ItemRecord)[props.idField] as string | number;
|
||||
}
|
||||
return (
|
||||
((item as ItemRecord).id as string | number) ||
|
||||
(item as unknown as string | number)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -129,26 +132,34 @@ const getItemId = (item: T): string | number => {
|
||||
* @returns 字段值
|
||||
*/
|
||||
const getItemValue = (item: T, field: string): string => {
|
||||
if (!field) return '';
|
||||
|
||||
return String(field.split('.').reduce<unknown>((obj, key) =>
|
||||
obj && typeof obj === 'object' && key in (obj as Record<string, unknown>)
|
||||
? (obj as Record<string, unknown>)[key]
|
||||
: '',
|
||||
item as ItemRecord));
|
||||
if (!field) return "";
|
||||
|
||||
return String(
|
||||
field
|
||||
.split(".")
|
||||
.reduce<unknown>(
|
||||
(obj, key) =>
|
||||
obj &&
|
||||
typeof obj === "object" &&
|
||||
key in (obj as Record<string, unknown>)
|
||||
? (obj as Record<string, unknown>)[key]
|
||||
: "",
|
||||
item as ItemRecord,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理全选/取消全选
|
||||
*/
|
||||
const handleAllCheckedChange = () => {
|
||||
if (allChecked.value) {
|
||||
checkedItems.value = props.items.map(getItemId);
|
||||
} else {
|
||||
checkedItems.value = [];
|
||||
}
|
||||
emit('all-checked-change', allChecked.value);
|
||||
emit('update:modelValue', checkedItems.value);
|
||||
if (allChecked.value) {
|
||||
checkedItems.value = props.items.map(getItemId);
|
||||
} else {
|
||||
checkedItems.value = [];
|
||||
}
|
||||
emit("all-checked-change", allChecked.value);
|
||||
emit("update:modelValue", checkedItems.value);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -156,32 +167,40 @@ const handleAllCheckedChange = () => {
|
||||
* @param field 排序字段
|
||||
*/
|
||||
const handleSortClick = (field: string) => {
|
||||
emit('sort', field);
|
||||
emit("sort", field);
|
||||
};
|
||||
|
||||
// 监听选中项变化
|
||||
watch(checkedItems, (newVal) => {
|
||||
emit('update:modelValue', newVal);
|
||||
// 更新全选状态
|
||||
if (props.items.length > 0) {
|
||||
allChecked.value = newVal.length === props.items.length;
|
||||
} else {
|
||||
allChecked.value = false;
|
||||
}
|
||||
emit("update:modelValue", newVal);
|
||||
// 更新全选状态
|
||||
if (props.items.length > 0) {
|
||||
allChecked.value = newVal.length === props.items.length;
|
||||
} else {
|
||||
allChecked.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
// 监听modelValue变化
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
if (newVal) {
|
||||
checkedItems.value = newVal;
|
||||
}
|
||||
}, { deep: true });
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
checkedItems.value = newVal;
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
// 监听items变化,重置选中状态
|
||||
watch(() => props.items, () => {
|
||||
if (allChecked.value) {
|
||||
checkedItems.value = props.items.map(getItemId);
|
||||
emit('update:modelValue', checkedItems.value);
|
||||
}
|
||||
}, { deep: true });
|
||||
watch(
|
||||
() => props.items,
|
||||
() => {
|
||||
if (allChecked.value) {
|
||||
checkedItems.value = props.items.map(getItemId);
|
||||
emit("update:modelValue", checkedItems.value);
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
19
frontend/src/composables/store/useActionExcStore.ts
Normal file
19
frontend/src/composables/store/useActionExcStore.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export const useActionExcStore = defineStore("refresh", {
|
||||
state: () => {
|
||||
return {
|
||||
callback: (result: boolean) => {
|
||||
console.debug("callback", result);
|
||||
},
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
notify(result: boolean) {
|
||||
this.callback(result);
|
||||
},
|
||||
setCallback(callback: (result: boolean) => void) {
|
||||
this.callback = callback;
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -123,6 +123,7 @@ import { onMounted, 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 checkedDepartmentIds = ref<number[]>([]);
|
||||
@@ -133,16 +134,16 @@ const $route = useRoute();
|
||||
const bindState = ref<"BIND" | "ALL" | "UNBIND">("ALL");
|
||||
|
||||
const alertStore = useAlertStore();
|
||||
|
||||
const actionExcStore = useActionExcStore();
|
||||
const { total, departments, fetchDepartmentWith } = useDepartmentQuery();
|
||||
|
||||
const { bindDepartment, unbindDepartment } = useDepartmentBind();
|
||||
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{ title: '上级部门', field: 'parentName' },
|
||||
{ title: '部门名称', field: 'name' },
|
||||
{ title: '绑定状态', field: 'bindState' }
|
||||
{ title: "上级部门", field: "parentName" },
|
||||
{ title: "部门名称", field: "name" },
|
||||
{ title: "绑定状态", field: "bindState" },
|
||||
];
|
||||
|
||||
const handleBindDepartmentSubmit = async () => {
|
||||
@@ -200,6 +201,11 @@ onMounted(async () => {
|
||||
"#department-unbind-modal",
|
||||
);
|
||||
departmentUnbindModal.value = new Modal($unbindModalElement, {});
|
||||
actionExcStore.setCallback((result) => {
|
||||
if (result) {
|
||||
handleSearch();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const handleSearch = async () => {
|
||||
|
||||
@@ -125,6 +125,7 @@ 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 checkedPermissionIds = ref<number[]>([]);
|
||||
@@ -135,14 +136,15 @@ const $route = useRoute();
|
||||
const bindState = ref<"BIND" | "ALL" | "UNBIND">("ALL");
|
||||
|
||||
const alertStore = useAlertStore();
|
||||
const actionExcStore = useActionExcStore();
|
||||
const { total, permissions, fetchPermissionsWith } = usePermissionsQuery();
|
||||
const { bindPermission, unbindPermission } = usePermissionBind();
|
||||
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{ title: '权限编码', field: 'code' },
|
||||
{ title: '权限名称', field: 'name' },
|
||||
{ title: '绑定状态', field: 'bindState' }
|
||||
{ title: "权限编码", field: "code" },
|
||||
{ title: "权限名称", field: "name" },
|
||||
{ title: "绑定状态", field: "bindState" },
|
||||
];
|
||||
|
||||
const handleBindPermissionSubmit = async () => {
|
||||
@@ -204,6 +206,11 @@ onMounted(async () => {
|
||||
{},
|
||||
{ id: "permission-unbind-modal" },
|
||||
);
|
||||
actionExcStore.setCallback((result) => {
|
||||
if (result) {
|
||||
handleSearch();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const handleSearch = async () => {
|
||||
|
||||
@@ -117,6 +117,7 @@ import { Modal, type ModalInterface, initFlowbite } from "flowbite";
|
||||
import { onMounted, 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 checkedPositionIds = ref<number[]>([]);
|
||||
@@ -127,15 +128,15 @@ const $route = useRoute();
|
||||
const bindState = ref<"BIND" | "ALL" | "UNBIND">("ALL");
|
||||
|
||||
const alertStore = useAlertStore();
|
||||
|
||||
const actionExcStore = useActionExcStore();
|
||||
const { total, positions, fetchPositionWith } = usePositionQuery();
|
||||
|
||||
const { bindPosition, unbindPosition } = usePositionBind();
|
||||
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{ title: '岗位名称', field: 'name' },
|
||||
{ title: '绑定状态', field: 'bindState' }
|
||||
{ title: "岗位名称", field: "name" },
|
||||
{ title: "绑定状态", field: "bindState" },
|
||||
];
|
||||
|
||||
const handleBindPositionSubmit = async () => {
|
||||
@@ -191,6 +192,11 @@ onMounted(async () => {
|
||||
{},
|
||||
{ id: "position-unbind-modal" },
|
||||
);
|
||||
actionExcStore.setCallback((result) => {
|
||||
if (result) {
|
||||
handleSearch();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const handleSearch = async () => {
|
||||
|
||||
@@ -126,6 +126,7 @@ import { onMounted, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useRoleBind } from "../composables/role/useRoleBind";
|
||||
import useAlertStore from "../composables/store/useAlertStore";
|
||||
import { useActionExcStore } from "@/composables/store/useActionExcStore";
|
||||
|
||||
const roleName = ref<string>("");
|
||||
const checkedRoleIds = ref<number[]>([]);
|
||||
@@ -138,12 +139,12 @@ const bindState = ref<"BIND" | "ALL" | "UNBIND">("ALL");
|
||||
const alertStore = useAlertStore();
|
||||
const { total, roles, fetchRolesWith } = useRolesQuery();
|
||||
const { bindRole, unbindRole } = useRoleBind();
|
||||
|
||||
const actionExcStore = useActionExcStore();
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{ title: '角色编码', field: 'code' },
|
||||
{ title: '角色名称', field: 'name' },
|
||||
{ title: '绑定状态', field: 'bindState' }
|
||||
{ title: "角色编码", field: "code" },
|
||||
{ title: "角色名称", field: "name" },
|
||||
{ title: "绑定状态", field: "bindState" },
|
||||
];
|
||||
|
||||
const handleBindRoleSubmit = async () => {
|
||||
@@ -200,6 +201,11 @@ onMounted(async () => {
|
||||
{},
|
||||
{ id: "role-unbind-modal" },
|
||||
);
|
||||
actionExcStore.setCallback((result) => {
|
||||
if (result) {
|
||||
handleSearch();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const handleSearch = async () => {
|
||||
|
||||
@@ -141,6 +141,7 @@ 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 selectedDepartment = ref<components["schemas"]["Department"]>();
|
||||
@@ -159,12 +160,12 @@ const { deleteDepartment } = useDepartmentDelete();
|
||||
const { upsertDepartment } = useDepartmentUpsert();
|
||||
|
||||
const alertStore = useAlertStore();
|
||||
|
||||
const actionExcStore = useActionExcStore();
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{ title: '上级部门', field: 'parentName' },
|
||||
{ title: '部门名称', field: 'name' },
|
||||
{ title: '操作', field: 'actions' }
|
||||
{ title: "上级部门", field: "parentName" },
|
||||
{ title: "部门名称", field: "name" },
|
||||
{ title: "操作", field: "actions" },
|
||||
];
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -184,6 +185,11 @@ onMounted(async () => {
|
||||
if ($deleteModalElement) {
|
||||
departmentDeleteModal.value = new Modal($deleteModalElement, {});
|
||||
}
|
||||
actionExcStore.setCallback((result) => {
|
||||
if (result) {
|
||||
handleSearch();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const handleUpsertDepartmentSubmit = async (
|
||||
|
||||
@@ -154,14 +154,14 @@ const alertStore = useAlertStore();
|
||||
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{ title: '名称', field: 'name' },
|
||||
{ title: '模型名称', field: 'modelName' },
|
||||
{ title: '类型', field: 'type' },
|
||||
{ title: 'apiKey', field: 'apiKey', class: 'hidden lg:table-cell' },
|
||||
{ title: 'url', field: 'url', class: 'hidden lg:table-cell' },
|
||||
{ title: '状态', field: 'status' },
|
||||
{ title: '优先级', field: 'priority' },
|
||||
{ title: '操作', field: 'actions' }
|
||||
{ title: "名称", field: "name" },
|
||||
{ title: "模型名称", field: "modelName" },
|
||||
{ title: "类型", field: "type" },
|
||||
{ title: "apiKey", field: "apiKey", class: "hidden lg:table-cell" },
|
||||
{ title: "url", field: "url", class: "hidden lg:table-cell" },
|
||||
{ title: "状态", field: "status" },
|
||||
{ title: "优先级", field: "priority" },
|
||||
{ title: "操作", field: "actions" },
|
||||
];
|
||||
|
||||
const handleUpdateModalSubmit = async (llm: components["schemas"]["LlmVm"]) => {
|
||||
|
||||
@@ -142,6 +142,7 @@ 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 selectedPermission = ref<components["schemas"]["PermissionRespDto"]>();
|
||||
@@ -153,12 +154,12 @@ const { total, permissions, fetchPermissionsWith } = usePermissionsQuery();
|
||||
const { deletePermission } = usePermissionDelete();
|
||||
const permissionUpsert = usePermissionUpsert();
|
||||
const alertStore = useAlertStore();
|
||||
|
||||
const actionExcStore = useActionExcStore();
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{ title: '权限名称', field: 'name' },
|
||||
{ title: '权限编码', field: 'code' },
|
||||
{ title: '操作', field: 'actions' }
|
||||
{ title: "权限名称", field: "name" },
|
||||
{ title: "权限编码", field: "code" },
|
||||
{ title: "操作", field: "actions" },
|
||||
];
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -178,6 +179,11 @@ onMounted(async () => {
|
||||
if ($deleteModalElement) {
|
||||
permissionDeleteModal.value = new Modal($deleteModalElement, {});
|
||||
}
|
||||
actionExcStore.setCallback((result) => {
|
||||
if (result) {
|
||||
handleSearch();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const handleUpsertModalSubmit = async (data: PermissionUpsertModel) => {
|
||||
|
||||
@@ -126,11 +126,11 @@ 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 { useMobileStyles } from "@/composables/useMobileStyles";
|
||||
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
|
||||
import { nextTick, onMounted, 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 selectedPosition = ref<components["schemas"]["Position"]>();
|
||||
@@ -144,11 +144,11 @@ const { deletePosition } = usePositionDelete();
|
||||
const { upsertPosition } = usePositionUpsert();
|
||||
|
||||
const alertStore = useAlertStore();
|
||||
|
||||
const actionExcStore = useActionExcStore();
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{ title: '岗位名称', field: 'name' },
|
||||
{ title: '操作', field: 'actions' }
|
||||
{ title: "岗位名称", field: "name" },
|
||||
{ title: "操作", field: "actions" },
|
||||
];
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -169,6 +169,11 @@ onMounted(async () => {
|
||||
if ($deleteModalElement) {
|
||||
positionDeleteModal.value = new Modal($deleteModalElement, {});
|
||||
}
|
||||
actionExcStore.setCallback((result) => {
|
||||
if (result) {
|
||||
handleSearch();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const handleUpsertPositionSubmit = async (
|
||||
|
||||
@@ -157,12 +157,13 @@ 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 selectedRole = ref<components["schemas"]["RoleDto"]>();
|
||||
const roleUpsertModal = ref<ModalInterface>();
|
||||
const roleDeleteModal = ref<ModalInterface>();
|
||||
|
||||
const actionExcStore = useActionExcStore();
|
||||
const { total, roles, fetchRolesWith } = useRolesQuery();
|
||||
|
||||
const { deleteRole } = useRoleDelete();
|
||||
@@ -172,10 +173,10 @@ const upsertRole = useRoleUpsert();
|
||||
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{ title: '角色名称', field: 'name' },
|
||||
{ title: '角色编码', field: 'code' },
|
||||
{ title: '分配', field: 'assign' },
|
||||
{ title: '操作', field: 'actions' }
|
||||
{ title: "角色名称", field: "name" },
|
||||
{ title: "角色编码", field: "code" },
|
||||
{ title: "分配", field: "assign" },
|
||||
{ title: "操作", field: "actions" },
|
||||
];
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -193,6 +194,11 @@ onMounted(async () => {
|
||||
if ($deleteModalElement) {
|
||||
roleDeleteModal.value = new Modal($deleteModalElement, {});
|
||||
}
|
||||
actionExcStore.setCallback((result) => {
|
||||
if (result) {
|
||||
handleSearch();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const handleUpsertModalSubmit = async (data: RoleUpsertModel) => {
|
||||
|
||||
@@ -195,17 +195,21 @@ const { updateCron } = useJobUpdate();
|
||||
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{ title: '任务', field: 'name' },
|
||||
{ title: '触发器', field: 'trigger', class: 'hidden lg:table-cell' },
|
||||
{ title: '开始', field: 'startTime', class: 'hidden lg:table-cell' },
|
||||
{ title: '结束', field: 'endTime', class: 'hidden lg:table-cell' },
|
||||
{ title: '下次执行', field: 'nextFireTime', class: 'hidden md:table-cell' },
|
||||
{ title: '上次执行', field: 'previousFireTime', class: 'hidden lg:table-cell' },
|
||||
{ title: '类型', field: 'schedulerType', class: 'hidden md:table-cell' },
|
||||
{ title: 'Cron', field: 'cronExpression', class: 'hidden md:table-cell' },
|
||||
{ title: '状态', field: 'triggerState' },
|
||||
{ title: '编辑', field: 'edit', class: 'hidden sm:table-cell' },
|
||||
{ title: '操作', field: 'actions' }
|
||||
{ title: "任务", field: "name" },
|
||||
{ title: "触发器", field: "trigger", class: "hidden lg:table-cell" },
|
||||
{ title: "开始", field: "startTime", class: "hidden lg:table-cell" },
|
||||
{ title: "结束", field: "endTime", class: "hidden lg:table-cell" },
|
||||
{ title: "下次执行", field: "nextFireTime", class: "hidden md:table-cell" },
|
||||
{
|
||||
title: "上次执行",
|
||||
field: "previousFireTime",
|
||||
class: "hidden lg:table-cell",
|
||||
},
|
||||
{ title: "类型", field: "schedulerType", class: "hidden md:table-cell" },
|
||||
{ title: "Cron", field: "cronExpression", class: "hidden md:table-cell" },
|
||||
{ title: "状态", field: "triggerState" },
|
||||
{ title: "编辑", field: "edit", class: "hidden sm:table-cell" },
|
||||
{ title: "操作", field: "actions" },
|
||||
];
|
||||
|
||||
const handleResumeJobClick = async (
|
||||
|
||||
@@ -189,6 +189,7 @@ import { useRouter } from "vue-router";
|
||||
import type { components } from "../api/types/schema";
|
||||
import useAlertStore from "../composables/store/useAlertStore";
|
||||
import { useUserUpsert } from "../composables/user/useUserUpsert";
|
||||
import { useActionExcStore } from "@/composables/store/useActionExcStore";
|
||||
|
||||
const username = ref<string>("");
|
||||
const selectedUser = ref<components["schemas"]["UserRolePermissionDto"]>();
|
||||
@@ -200,14 +201,14 @@ const { deleteUser } = useUserDelete();
|
||||
const userUpsert = useUserUpsert();
|
||||
const { sortBy, handleSort, getSortField } = useSort();
|
||||
const alertStore = useAlertStore();
|
||||
|
||||
const actionExcStore = useActionExcStore();
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{ title: '用户名', field: 'username', sortable: true },
|
||||
{ title: '创建时间', field: 'createTime', sortable: true },
|
||||
{ title: '状态', field: 'status' },
|
||||
{ title: '分配', field: 'assign' },
|
||||
{ title: '操作', field: 'actions' }
|
||||
{ title: "用户名", field: "username", sortable: true },
|
||||
{ title: "创建时间", field: "createTime", sortable: true },
|
||||
{ title: "状态", field: "status" },
|
||||
{ title: "分配", field: "assign" },
|
||||
{ title: "操作", field: "actions" },
|
||||
];
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -225,6 +226,11 @@ onMounted(async () => {
|
||||
if ($deleteModalElement) {
|
||||
userDeleteModal.value = new Modal($deleteModalElement, {});
|
||||
}
|
||||
actionExcStore.setCallback((result) => {
|
||||
if (result) {
|
||||
handleSearch();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const handleUpsertUserSubmit = async (data: UserUpsertSubmitModel) => {
|
||||
|
||||
Reference in New Issue
Block a user