重构错误处理逻辑,移除旧的错误处理工具,新增 useErrorHandler 组合式 API,优化模态框组件,添加 ID 属性以支持更灵活的 DOM 操作

This commit is contained in:
Chuck1sn
2025-06-16 15:41:09 +08:00
parent ca42fbbda9
commit 772ad547bf
13 changed files with 225 additions and 224 deletions

View File

@@ -1,5 +1,5 @@
<template>
<div tabindex="-1" aria-hidden="true"
<div :id="id" tabindex="-1" aria-hidden="true"
class="bg-gray-900/50 hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
<div class="relative p-4 w-full" :class="[maxWidthClass]">
<!-- Modal content -->
@@ -26,7 +26,8 @@
</template>
<script setup lang="ts">
import { computed } from "vue";
import { computed, onMounted } from "vue";
import { initFlowbite, Modal } from "flowbite";
export type ModalSize = "xs" | "sm" | "md" | "lg" | "xl";
@@ -37,6 +38,8 @@ const props = defineProps<{
size?: ModalSize;
/** 关闭模态框的回调函数 */
closeModal: () => void;
/** 模态框ID用于DOM选择 */
id?: string;
}>();
/** 根据size属性计算最大宽度类名 */
@@ -51,4 +54,9 @@ const maxWidthClass = computed(() => {
return sizes[props.size || "md"];
});
// 确保Flowbite初始化这对于PopupModal的正常工作至关重要
onMounted(() => {
initFlowbite();
});
</script>

View File

@@ -1,30 +1,30 @@
<template>
<BaseModal title="部门管理" size="md" :closeModal="closeModal">
<!-- Modal body -->
<div class="p-4 md:p-5">
<div class="grid gap-4 mb-4 grid-cols-1">
<div class="col-span-full">
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">部门名称</label>
<input type="text" id="name" v-model="formData.name"
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 p-2.5"
required />
</div>
<div class="col-span-full">
<label for="category" class="block mb-2 text-sm font-medium text-gray-900">上级部门</label>
<select id="category" v-model="formData.parentId"
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 p-2.5">
<option :value="null"></option>
<option v-for="dept in availableDepartments" :key="dept.id" :value="dept.id"
:selected="dept.id === formData.parentId">{{ dept.name }}</option>
</select>
</div>
</div>
<button type="submit" @click="handleSubmit"
class="w-auto text-sm px-4 py-2 text-white flex items-center bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-center self-start mt-5">
保存
</button>
</div>
</BaseModal>
<BaseModal :id="id" title="部门管理" size="md" :closeModal="closeModal">
<!-- Modal body -->
<div class="p-4 md:p-5">
<div class="grid gap-4 mb-4 grid-cols-1">
<div class="col-span-full">
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">部门名称</label>
<input type="text" id="name" v-model="formData.name"
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 p-2.5"
required />
</div>
<div class="col-span-full">
<label for="category" class="block mb-2 text-sm font-medium text-gray-900">上级部门</label>
<select id="category" v-model="formData.parentId"
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 p-2.5">
<option :value="null"></option>
<option v-for="dept in availableDepartments" :key="dept.id" :value="dept.id"
:selected="dept.id === formData.parentId">{{ dept.name }}</option>
</select>
</div>
</div>
<button type="submit" @click="handleSubmit"
class="w-auto text-sm px-4 py-2 text-white flex items-center bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-center self-start mt-5">
保存
</button>
</div>
</BaseModal>
</template>
<script setup lang="ts">
@@ -36,12 +36,14 @@ import { onMounted, ref, watch } from "vue";
import { z } from "zod";
import BaseModal from "./BaseModal.vue";
const { department, availableDepartments, onSubmit, closeModal } = defineProps<{
department?: components["schemas"]["Department"];
availableDepartments?: components["schemas"]["Department"][];
closeModal: () => void;
onSubmit: (department: DepartmentUpsertModel) => Promise<void>;
}>();
const { department, availableDepartments, onSubmit, closeModal, id } =
defineProps<{
department?: components["schemas"]["Department"];
availableDepartments?: components["schemas"]["Department"][];
closeModal: () => void;
onSubmit: (department: DepartmentUpsertModel) => Promise<void>;
id?: string;
}>();
const formData = ref<DepartmentUpsertModel>({
name: "",
@@ -92,8 +94,4 @@ const handleSubmit = async () => {
}
}
};
onMounted(() => {
initFlowbite();
});
</script>

View File

@@ -1,5 +1,5 @@
<template>
<BaseModal title="大模型配置" size="md" :closeModal="closeModal">
<BaseModal :id="id" title="大模型配置" size="md" :closeModal="closeModal">
<!-- Modal body -->
<div class="p-4 md:p-5">
<div class="grid gap-4 mb-4 grid-cols-1">
@@ -63,10 +63,11 @@ import { onMounted, ref, watch } from "vue";
import { z } from "zod";
import BaseModal from "./BaseModal.vue";
const { llm, onSubmit } = defineProps<{
const { llm, onSubmit, id } = defineProps<{
llm?: components["schemas"]["LlmVm"];
closeModal: () => void;
onSubmit: (data: components["schemas"]["LlmVm"]) => Promise<void>;
id: string;
}>();
// 初始化默认值避免undefined错误
@@ -139,8 +140,4 @@ const handleSubmit = async () => {
await onSubmit(validatedData);
updateFormData(undefined);
};
onMounted(() => {
initFlowbite();
});
</script>

View File

@@ -1,43 +1,43 @@
<template>
<BaseModal title="权限管理" size="md" :closeModal="closeModal">
<!-- Modal body -->
<div class="p-4 md:p-5">
<div class="grid gap-4 mb-4 grid-cols-1">
<div class="col-span-full">
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">权限名称</label>
<input type="text" id="name" v-model="formData.name"
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 p-2.5"
required />
</div>
<div class="col-span-full">
<label for="code" class="block mb-2 text-sm font-medium text-gray-900">权限代码</label>
<input type="text" id="code" v-model="formData.code"
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 p-2.5"
required />
</div>
</div>
<button type="submit" @click="handleSubmit"
class="w-auto text-sm px-4 py-2 text-white flex items-center bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-center self-start mt-5">
保存
</button>
</div>
</BaseModal>
<BaseModal :id="id" title="权限管理" size="md" :closeModal="closeModal">
<!-- Modal body -->
<div class="p-4 md:p-5">
<div class="grid gap-4 mb-4 grid-cols-1">
<div class="col-span-full">
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">权限名称</label>
<input type="text" id="name" v-model="formData.name"
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 p-2.5"
required />
</div>
<div class="col-span-full">
<label for="code" class="block mb-2 text-sm font-medium text-gray-900">权限代码</label>
<input type="text" id="code" v-model="formData.code"
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 p-2.5"
required />
</div>
</div>
<button type="submit" @click="handleSubmit"
class="w-auto text-sm px-4 py-2 text-white flex items-center bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-center self-start mt-5">
保存
</button>
</div>
</BaseModal>
</template>
<script setup lang="ts">
import type { components } from "@/api/types/schema";
import useAlertStore from "@/composables/store/useAlertStore";
import type { PermissionUpsertModel } from "@/types/permission";
import { initFlowbite } from "flowbite";
import { onMounted, ref, watch } from "vue";
import { ref, watch } from "vue";
import { z } from "zod";
import BaseModal from "./BaseModal.vue";
const alertStore = useAlertStore();
const { permission, onSubmit, closeModal } = defineProps<{
const { permission, onSubmit, closeModal, id } = defineProps<{
permission?: components["schemas"]["PermissionRespDto"];
onSubmit: (data: PermissionUpsertModel) => Promise<void>;
closeModal: () => void;
id: string;
}>();
const formData = ref<PermissionUpsertModel>({

View File

@@ -1,5 +1,5 @@
<template>
<BaseModal :closeModal="closeModal" size="sm">
<BaseModal :id="id" :closeModal="closeModal" size="sm">
<div class="p-5 md:p-6 text-center">
<svg class="w-14 h-14 sm:w-16 sm:h-16 mx-auto text-red-600 mb-4" fill="none" stroke="currentColor"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,37 +1,37 @@
<template>
<BaseModal title="岗位管理" size="md" :closeModal="closeModal">
<!-- Modal body -->
<div class="p-4 md:p-5">
<div class="grid gap-4 mb-4 grid-cols-1">
<div class="col-span-full">
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">岗位名称</label>
<input type="text" id="name" v-model="formData.name"
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 p-2.5"
required />
</div>
</div>
<button type="submit" @click="handleSubmit"
class="w-auto text-sm px-4 py-2 text-white flex items-center bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-center self-start mt-5">
保存
</button>
</div>
</BaseModal>
<BaseModal :id="id" title="岗位管理" size="md" :closeModal="closeModal">
<!-- Modal body -->
<div class="p-4 md:p-5">
<div class="grid gap-4 mb-4 grid-cols-1">
<div class="col-span-full">
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">岗位名称</label>
<input type="text" id="name" v-model="formData.name"
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 p-2.5"
required />
</div>
</div>
<button type="submit" @click="handleSubmit"
class="w-auto text-sm px-4 py-2 text-white flex items-center bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-center self-start mt-5">
保存
</button>
</div>
</BaseModal>
</template>
<script setup lang="ts">
import type { components } from "@/api/types/schema";
import useAlertStore from "@/composables/store/useAlertStore";
import type { PositionUpsertModel } from "@/types/position";
import { initFlowbite } from "flowbite";
import { onMounted, ref, watch } from "vue";
import { ref, watch } from "vue";
import { z } from "zod";
import BaseModal from "./BaseModal.vue";
const alertStore = useAlertStore();
const { position, closeModal, onSubmit } = defineProps<{
const { position, closeModal, onSubmit, id } = defineProps<{
position?: components["schemas"]["Position"];
closeModal: () => void;
onSubmit: (data: PositionUpsertModel) => Promise<void>;
id: string;
}>();
const formData = ref<PositionUpsertModel>({
@@ -78,8 +78,4 @@ const handleSubmit = async () => {
}
}
};
onMounted(() => {
initFlowbite();
});
</script>

View File

@@ -1,43 +1,43 @@
<template>
<BaseModal title="角色管理" size="md" :closeModal="closeModal">
<!-- Modal body -->
<div class="p-4 md:p-5">
<div class="grid gap-4 mb-4 grid-cols-1">
<div class="col-span-full">
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">角色名称</label>
<input type="text" id="name" v-model="formData.name"
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 p-2.5"
required />
</div>
<div class="col-span-full">
<label for="code" class="block mb-2 text-sm font-medium text-gray-900">角色代码</label>
<input type="text" id="code" v-model="formData.code"
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 p-2.5"
required />
</div>
</div>
<button type="submit" @click="handleSubmit"
class="w-auto text-sm px-4 py-2 text-white flex items-center bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-center self-start mt-5">
保存
</button>
</div>
</BaseModal>
<BaseModal :id="id" title="角色管理" size="md" :closeModal="closeModal">
<!-- Modal body -->
<div class="p-4 md:p-5">
<div class="grid gap-4 mb-4 grid-cols-1">
<div class="col-span-full">
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">角色名称</label>
<input type="text" id="name" v-model="formData.name"
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 p-2.5"
required />
</div>
<div class="col-span-full">
<label for="code" class="block mb-2 text-sm font-medium text-gray-900">角色代码</label>
<input type="text" id="code" v-model="formData.code"
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 p-2.5"
required />
</div>
</div>
<button type="submit" @click="handleSubmit"
class="w-auto text-sm px-4 py-2 text-white flex items-center bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-center self-start mt-5">
保存
</button>
</div>
</BaseModal>
</template>
<script setup lang="ts">
import type { components } from "@/api/types/schema";
import useAlertStore from "@/composables/store/useAlertStore";
import type { RoleUpsertModel } from "@/types/role";
import { initFlowbite } from "flowbite";
import { onMounted, ref, watch } from "vue";
import { ref, watch } from "vue";
import { z } from "zod";
import BaseModal from "./BaseModal.vue";
const alertStore = useAlertStore();
const { role, closeModal, onSubmit } = defineProps<{
const { role, closeModal, onSubmit, id } = defineProps<{
role?: components["schemas"]["RoleRespDto"];
closeModal: () => void;
onSubmit: (data: RoleUpsertModel) => Promise<void>;
id: string;
}>();
const formData = ref<RoleUpsertModel>({
@@ -92,8 +92,4 @@ const handleSubmit = async () => {
}
}
};
onMounted(() => {
initFlowbite();
});
</script>

View File

@@ -1,34 +1,34 @@
<template>
<BaseModal title="定时任务配置" size="md" :closeModal="closeModal">
<!-- Modal body -->
<div class="p-4 md:p-5">
<div class="grid gap-4 mb-4 grid-cols-1">
<div class="col-span-full">
<label for="cronExpression" class="block mb-2 text-sm font-medium text-gray-900">CRON表达式</label>
<input type="text" id="cronExpression" v-model="formData.cronExpression"
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 p-2.5"
required />
</div>
</div>
<button type="submit" @click="handleSubmit"
class="w-auto text-sm px-4 py-2 text-white flex items-center bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-center self-start mt-5">
保存
</button>
</div>
</BaseModal>
<BaseModal :id="id" title="定时任务配置" size="md" :closeModal="closeModal">
<!-- Modal body -->
<div class="p-4 md:p-5">
<div class="grid gap-4 mb-4 grid-cols-1">
<div class="col-span-full">
<label for="cronExpression" class="block mb-2 text-sm font-medium text-gray-900">CRON表达式</label>
<input type="text" id="cronExpression" v-model="formData.cronExpression"
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 p-2.5"
required />
</div>
</div>
<button type="submit" @click="handleSubmit"
class="w-auto text-sm px-4 py-2 text-white flex items-center bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-center self-start mt-5">
保存
</button>
</div>
</BaseModal>
</template>
<script setup lang="ts">
import type { components } from "@/api/types/schema";
import { initFlowbite } from "flowbite";
import { onMounted, ref, watch } from "vue";
import { ref, watch } from "vue";
import { z } from "zod";
import BaseModal from "./BaseModal.vue";
const { job, closeModal, onSubmit } = defineProps<{
const { job, closeModal, onSubmit, id } = defineProps<{
job?: components["schemas"]["JobTriggerDto"];
closeModal: () => void;
onSubmit: (cronExpression: string) => Promise<void>;
id: string;
}>();
const formData = ref({

View File

@@ -1,5 +1,5 @@
<template>
<BaseModal title="用户管理" size="md" :closeModal="closeModal">
<BaseModal :id="id" title="用户管理" size="md" :closeModal="closeModal">
<!-- Modal body -->
<form class="p-4 md:p-5">
<div class="space-y-4">
@@ -53,10 +53,11 @@ import { onMounted, ref, watch } from "vue";
import { z } from "zod";
import BaseModal from "./BaseModal.vue";
const { user, onSubmit } = defineProps<{
const { user, onSubmit, id } = defineProps<{
user?: components["schemas"]["UserRolePermissionDto"];
closeModal: () => void;
onSubmit: (data: UserUpsertSubmitModel) => Promise<void>;
id: string;
}>();
const formData = ref();