mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-20 20:39:55 +00:00
feat(settings): 添加项目引导可见性配置项
feat(api): 修改导出接口返回类型为Blob并统一处理导出逻辑 feat(router): 添加404路由匹配规则 fix(permission): 优化动态路由处理逻辑 feat(login): 登录成功后自动显示项目引导 feat(navbar): 优化用户头像显示和引导可见性控制 feat(store): 添加引导可见性状态管理 refactor(views): 统一导出功能实现方式 fix(backend): 添加导出数据中的创建者字段 refactor(user): 优化用户导入导出功能 refactor(job): 重构定时任务页面和导出逻辑
This commit is contained in:
@@ -41,7 +41,7 @@ const JobAPI = {
|
||||
},
|
||||
|
||||
exportJob(body: JobPageQuery) {
|
||||
return request<ApiResponse>({
|
||||
return request<Blob>({
|
||||
url: `/monitor/job/export`,
|
||||
method: "post",
|
||||
data: body,
|
||||
|
||||
@@ -57,7 +57,7 @@ const ConfigAPI = {
|
||||
},
|
||||
|
||||
exportConfig(body: ConfigPageQuery) {
|
||||
return request<ApiResponse>({
|
||||
return request<Blob>({
|
||||
url: `/system/config/export`,
|
||||
method: "post",
|
||||
data: body,
|
||||
|
||||
@@ -56,7 +56,7 @@ const DictAPI = {
|
||||
},
|
||||
|
||||
exportDictType(body: DictPageQuery) {
|
||||
return request<ApiResponse>({
|
||||
return request<Blob>({
|
||||
url: `/system/dict/type/export`,
|
||||
method: "post",
|
||||
data: body,
|
||||
@@ -112,7 +112,7 @@ const DictAPI = {
|
||||
},
|
||||
|
||||
exportDictData(body: DictDataPageQuery) {
|
||||
return request<ApiResponse>({
|
||||
return request<Blob>({
|
||||
url: `/system/dict/data/export`,
|
||||
method: "post",
|
||||
data: body,
|
||||
|
||||
@@ -24,11 +24,11 @@ const LogAPI = {
|
||||
});
|
||||
},
|
||||
|
||||
exportLog(query: LogPageQuery) {
|
||||
return request<ApiResponse>({
|
||||
exportLog(body: LogPageQuery) {
|
||||
return request<Blob>({
|
||||
url: `/system/log/export`,
|
||||
method: "post",
|
||||
data: query,
|
||||
data: body,
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
|
||||
@@ -56,7 +56,7 @@ const NoticeAPI = {
|
||||
},
|
||||
|
||||
exportNotice(body: NoticePageQuery) {
|
||||
return request<ApiResponse>({
|
||||
return request<Blob>({
|
||||
url: `/system/notice/export`,
|
||||
method: "post",
|
||||
data: body,
|
||||
|
||||
@@ -49,7 +49,7 @@ const PositionAPI = {
|
||||
},
|
||||
|
||||
exportPosition(body: PositionPageQuery) {
|
||||
return request<ApiResponse>({
|
||||
return request<Blob>({
|
||||
url: `/system/position/export`,
|
||||
method: "post",
|
||||
data: body,
|
||||
|
||||
@@ -56,11 +56,11 @@ const RoleAPI = {
|
||||
});
|
||||
},
|
||||
|
||||
exportRole(query: TablePageQuery) {
|
||||
return request<ApiResponse>({
|
||||
exportRole(body: TablePageQuery) {
|
||||
return request<Blob>({
|
||||
url: `/system/role/export`,
|
||||
method: "post",
|
||||
data: query,
|
||||
data: body,
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
@@ -79,8 +79,8 @@ export interface TablePageQuery extends PageQuery {
|
||||
|
||||
export interface RoleTable {
|
||||
index?: number;
|
||||
id?: number;
|
||||
name?: string;
|
||||
id: number;
|
||||
name: string;
|
||||
order?: number;
|
||||
data_scope?: number;
|
||||
status?: boolean;
|
||||
|
||||
@@ -105,11 +105,11 @@ export const UserAPI = {
|
||||
});
|
||||
},
|
||||
|
||||
exportUser(query: UserPageQuery) {
|
||||
return request<ApiResponse>({
|
||||
exportUser(body: UserPageQuery) {
|
||||
return request<Blob>({
|
||||
url: `/system/user/export`,
|
||||
method: "post",
|
||||
params: query,
|
||||
data: body,
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<el-dropdown trigger="click">
|
||||
<div class="user-profile">
|
||||
<template v-if="userStore.basicInfo.avatar">
|
||||
<el-avatar :src="userStore.basicInfo.avatar" />
|
||||
<el-avatar size="small" :src="userStore.basicInfo.avatar" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-avatar icon="UserFilled" />
|
||||
@@ -158,7 +158,13 @@ function handleGiteeClick() {
|
||||
/**
|
||||
* 项目引导
|
||||
*/
|
||||
const guideVisible = ref<boolean>(false)
|
||||
// 使用ref而不是computed,以便可以修改引导可见性
|
||||
// 使用 computed 实现双向绑定,减少 watch 的使用
|
||||
const guideVisible = computed({
|
||||
get: () => appStore.guideVisible,
|
||||
set: (newValue) => appStore.showGuide(newValue)
|
||||
});
|
||||
|
||||
function handleTourClick() {
|
||||
// 如果是移动端,直接跳转到引导页面
|
||||
if (appStore.device === DeviceEnum.MOBILE) {
|
||||
@@ -283,13 +289,9 @@ function logout() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 0 8px;
|
||||
|
||||
&__avatar {
|
||||
flex-shrink: 0;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,11 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
meta: { hidden: true, title: "500" },
|
||||
component: () => import("@/views/error/500.vue"),
|
||||
},
|
||||
{
|
||||
path: "/:pathMatch(.*)*",
|
||||
component: () => import('@/views/error/404.vue'),
|
||||
meta: { hidden: true, title: "404" },
|
||||
},
|
||||
// 以下内容必须放在后面
|
||||
{
|
||||
path: "/",
|
||||
|
||||
@@ -33,6 +33,8 @@ export const defaultSettings: AppSettings = {
|
||||
watermarkContent: pkg.name,
|
||||
// 侧边栏配色方案
|
||||
sidebarColorScheme: SidebarColor.CLASSIC_BLUE,
|
||||
// 项目引导
|
||||
guideVisible: false,
|
||||
};
|
||||
|
||||
// 主题色预设 - 经典配色方案
|
||||
|
||||
@@ -23,6 +23,8 @@ export const useAppStore = defineStore("app", () => {
|
||||
|
||||
// 顶部菜单激活路径
|
||||
const activeTopMenuPath = useStorage("activeTopMenuPath", "");
|
||||
// 项目引导
|
||||
const guideVisible = useStorage("guideVisible", defaultSettings.guideVisible);
|
||||
|
||||
/**
|
||||
* 根据语言标识读取对应的语言包
|
||||
@@ -35,32 +37,40 @@ export const useAppStore = defineStore("app", () => {
|
||||
}
|
||||
});
|
||||
|
||||
// 切换侧边栏
|
||||
/**
|
||||
* 切换侧边栏
|
||||
*/
|
||||
function toggleSidebar() {
|
||||
sidebar.opened = !sidebar.opened;
|
||||
sidebarStatus.value = sidebar.opened ? SidebarStatus.OPENED : SidebarStatus.CLOSED;
|
||||
}
|
||||
|
||||
// 关闭侧边栏
|
||||
/**
|
||||
* 关闭侧边栏
|
||||
*/
|
||||
function closeSideBar() {
|
||||
sidebar.opened = false;
|
||||
sidebarStatus.value = SidebarStatus.CLOSED;
|
||||
}
|
||||
|
||||
// 打开侧边栏
|
||||
/**
|
||||
* 打开侧边栏
|
||||
*/
|
||||
function openSideBar() {
|
||||
sidebar.opened = true;
|
||||
sidebarStatus.value = SidebarStatus.OPENED;
|
||||
}
|
||||
|
||||
// 切换设备
|
||||
/**
|
||||
* 切换设备
|
||||
* @param val 设备类型
|
||||
*/
|
||||
function toggleDevice(val: string) {
|
||||
device.value = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变布局大小
|
||||
*
|
||||
* @param val 布局大小 default | small | large
|
||||
*/
|
||||
function changeSize(val: string) {
|
||||
@@ -68,7 +78,6 @@ export const useAppStore = defineStore("app", () => {
|
||||
}
|
||||
/**
|
||||
* 切换语言
|
||||
*
|
||||
* @param val
|
||||
*/
|
||||
function changeLanguage(val: string) {
|
||||
@@ -76,10 +85,18 @@ export const useAppStore = defineStore("app", () => {
|
||||
}
|
||||
/**
|
||||
* 混合模式顶部切换
|
||||
* @param val
|
||||
*/
|
||||
function activeTopMenu(val: string) {
|
||||
activeTopMenuPath.value = val;
|
||||
}
|
||||
/**
|
||||
* 显示或隐藏引导
|
||||
* @param val 是否显示引导
|
||||
*/
|
||||
function showGuide(val: boolean) {
|
||||
guideVisible.value = val;
|
||||
}
|
||||
return {
|
||||
device,
|
||||
sidebar,
|
||||
@@ -88,12 +105,14 @@ export const useAppStore = defineStore("app", () => {
|
||||
size,
|
||||
activeTopMenu,
|
||||
toggleDevice,
|
||||
showGuide,
|
||||
changeSize,
|
||||
changeLanguage,
|
||||
toggleSidebar,
|
||||
closeSideBar,
|
||||
openSideBar,
|
||||
activeTopMenuPath,
|
||||
guideVisible,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -112,16 +112,16 @@ export const usePermissionStore = defineStore("permission", () => {
|
||||
const routersTree = listToTree(userStore.routeList);
|
||||
const routerMap = generator(routersTree);
|
||||
|
||||
// 从用户绑定的路由列表获取数据
|
||||
// 解析动态路由
|
||||
const dynamicRoutes = parseDynamicRoutes(routerMap);
|
||||
|
||||
// 重置路由,移除旧的动态路由
|
||||
resetRouter();
|
||||
// resetRouter();
|
||||
|
||||
// 重新添加所有路由
|
||||
constantRoutes.forEach(route => {
|
||||
router.addRoute(route);
|
||||
});
|
||||
// constantRoutes.forEach(route => {
|
||||
// router.addRoute(route);
|
||||
// });
|
||||
|
||||
routes.value = [...constantRoutes, ...dynamicRoutes];
|
||||
|
||||
|
||||
Vendored
+2
@@ -84,6 +84,8 @@ declare global {
|
||||
watermarkContent: string;
|
||||
/** 侧边栏配色方案 */
|
||||
sidebarColorScheme: "classic-blue" | "minimal-white";
|
||||
/** 项目引导 */
|
||||
guideVisible: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
<div class="app-container">
|
||||
<!-- 搜索区域 -->
|
||||
<div class="search-container">
|
||||
<el-form ref="queryFormRef" :model="queryFormData" :inline="true" label-suffix=":" >
|
||||
<el-form
|
||||
ref="queryFormRef"
|
||||
:model="queryFormData"
|
||||
:inline="true"
|
||||
label-suffix=":"
|
||||
>
|
||||
<el-form-item prop="name" label="任务名称">
|
||||
<el-input
|
||||
v-model="queryFormData.name"
|
||||
@@ -204,11 +209,21 @@
|
||||
删除
|
||||
</el-button>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="warning" size="small" link icon="ArrowDown">更多</el-button>
|
||||
<el-button type="warning" size="small" link icon="ArrowDown"
|
||||
>更多</el-button
|
||||
>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Check" @click="handleOption(scope.row, 1)">暂停</el-dropdown-item>
|
||||
<el-dropdown-item icon="CircleClose" @click="handleOption(scope.row, 2)">恢复</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
icon="Check"
|
||||
@click="handleOption(scope.row, 1)"
|
||||
>暂停</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
icon="CircleClose"
|
||||
@click="handleOption(scope.row, 2)"
|
||||
>恢复</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@@ -302,15 +317,30 @@
|
||||
</template>
|
||||
<!-- 新增、编辑表单 -->
|
||||
<template v-else>
|
||||
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-suffix=":" label-width="auto" inline>
|
||||
<el-form
|
||||
ref="dataFormRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
label-width="auto"
|
||||
inline
|
||||
>
|
||||
<el-form-item label="任务名称" prop="name" style="width: 40%">
|
||||
<el-input v-model="formData.name" placeholder="请输入任务名称" :maxlength="50"/>
|
||||
<el-input
|
||||
v-model="formData.name"
|
||||
placeholder="请输入任务名称"
|
||||
:maxlength="50"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="任务函数" prop="func" style="width: 40%">
|
||||
<el-input v-model="formData.func" placeholder="请输入任务函数" :maxlength="50"/>
|
||||
<el-input
|
||||
v-model="formData.func"
|
||||
placeholder="请输入任务函数"
|
||||
:maxlength="50"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="存储器" prop="jobstore" style="width: 40%">
|
||||
<el-select v-model="formData.jobstore" placeholder="请选择存储器" >
|
||||
<el-select v-model="formData.jobstore" placeholder="请选择存储器">
|
||||
<el-option value="default" label="默认(Memory)" />
|
||||
<el-option value="sqlalchemy" label="数据库" />
|
||||
<el-option value="redis" label="Redis存储器" />
|
||||
@@ -323,10 +353,18 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="位置参数" prop="args" style="width: 40%">
|
||||
<el-input v-model="formData.args" placeholder="请输入位置参数" :maxlength="50"/>
|
||||
<el-input
|
||||
v-model="formData.args"
|
||||
placeholder="请输入位置参数"
|
||||
:maxlength="50"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键字参数" prop="kwargs" style="width: 40%">
|
||||
<el-input v-model="formData.kwargs" placeholder="请输入关键字参数" :maxlength="50"/>
|
||||
<el-input
|
||||
v-model="formData.kwargs"
|
||||
placeholder="请输入关键字参数"
|
||||
:maxlength="50"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="并发执行" prop="coalesce" style="width: 40%">
|
||||
<el-radio-group v-model="formData.coalesce">
|
||||
@@ -334,8 +372,17 @@
|
||||
<el-radio :value="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="最大实例数" prop="max_instances" style="width: 40%">
|
||||
<el-input-number v-model="formData.max_instances" controls-position="right" :min="1" :max="10"/>
|
||||
<el-form-item
|
||||
label="最大实例数"
|
||||
prop="max_instances"
|
||||
style="width: 40%"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="formData.max_instances"
|
||||
controls-position="right"
|
||||
:min="1"
|
||||
:max="10"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="触发器" prop="trigger" style="width: 40%">
|
||||
<el-select v-model="formData.trigger" placeholder="请选择触发器">
|
||||
@@ -364,7 +411,9 @@
|
||||
v-else-if="formData.trigger === 'interval'"
|
||||
label="间隔时间"
|
||||
prop="trigger_args"
|
||||
:rules="[{ required: true, message: '请输入间隔时间', trigger: 'change' }]"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入间隔时间', trigger: 'change' },
|
||||
]"
|
||||
style="width: 40%"
|
||||
>
|
||||
<el-popover
|
||||
@@ -393,7 +442,13 @@
|
||||
v-else-if="formData.trigger === 'cron'"
|
||||
label="Cron表达式"
|
||||
prop="trigger_args"
|
||||
:rules="[{ required: true, message: '请输入Cron表达式', trigger: 'change' }]"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入Cron表达式',
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
style="width: 40%"
|
||||
>
|
||||
<el-popover
|
||||
@@ -411,7 +466,12 @@
|
||||
@click="openCron = true"
|
||||
/>
|
||||
</template>
|
||||
<vue3CronPlus @change="handlechangeCron" @close="openCron = false" max-height="500px" i18n="cn"></vue3CronPlus>
|
||||
<vue3CronPlus
|
||||
@change="handlechangeCron"
|
||||
@close="openCron = false"
|
||||
max-height="500px"
|
||||
i18n="cn"
|
||||
></vue3CronPlus>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<!-- 开始日期和结束日期 -->
|
||||
@@ -419,7 +479,9 @@
|
||||
v-if="formData.trigger && formData.trigger != 'date'"
|
||||
label="开始日期"
|
||||
prop="start_date"
|
||||
:rules="[{ required: false, message: '请选择开始日期', trigger: 'blur' }]"
|
||||
:rules="[
|
||||
{ required: false, message: '请选择开始日期', trigger: 'blur' },
|
||||
]"
|
||||
style="width: 40%"
|
||||
>
|
||||
<el-date-picker
|
||||
@@ -434,7 +496,9 @@
|
||||
v-if="formData.trigger && formData.trigger != 'date'"
|
||||
label="结束日期"
|
||||
prop="end_date"
|
||||
:rules="[{ required: false, message: '请选择结束日期', trigger: 'blur' }]"
|
||||
:rules="[
|
||||
{ required: false, message: '请选择结束日期', trigger: 'blur' },
|
||||
]"
|
||||
style="width: 40%"
|
||||
>
|
||||
<el-date-picker
|
||||
@@ -475,7 +539,6 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -488,8 +551,8 @@ defineOptions({
|
||||
import JobAPI, { JobTable, JobForm, JobPageQuery } from "@/api/monitor/job";
|
||||
import IntervalTab from "@/components/IntervalTab/index.vue";
|
||||
import { useDictStore } from "@/store/index";
|
||||
import { vue3CronPlus } from 'vue3-cron-plus'
|
||||
import 'vue3-cron-plus/dist/index.css' // 引入样式
|
||||
import { vue3CronPlus } from "vue3-cron-plus";
|
||||
import "vue3-cron-plus/dist/index.css"; // 引入样式
|
||||
|
||||
const dictStore = useDictStore();
|
||||
|
||||
@@ -575,9 +638,9 @@ const rules = reactive({
|
||||
});
|
||||
|
||||
// 列表刷新
|
||||
async function handleRefresh () {
|
||||
async function handleRefresh() {
|
||||
await loadingData();
|
||||
};
|
||||
}
|
||||
|
||||
// 加载表格数据
|
||||
async function loadingData() {
|
||||
@@ -714,43 +777,41 @@ async function handleExport() {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const body = {
|
||||
...queryFormData,
|
||||
page_no: 1,
|
||||
page_size: total.value,
|
||||
};
|
||||
ElMessage.warning("正在导出数据,请稍候...");
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
|
||||
const response = await JobAPI.exportJob(body);
|
||||
const blob = new Blob([JSON.stringify(response.data.data)], {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8",
|
||||
});
|
||||
// 从响应头获取文件名
|
||||
const contentDisposition = response.headers["content-disposition"];
|
||||
let fileName = "系统配置.xlsx";
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
|
||||
if (fileNameMatch) {
|
||||
fileName = decodeURIComponent(fileNameMatch[1]);
|
||||
}
|
||||
ElMessage.warning("正在导出数据,请稍候...");
|
||||
|
||||
const response = await JobAPI.exportJob(queryFormData);
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
|
||||
const fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
|
||||
// 从响应头获取文件名
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
ElMessage.success('导出成功');
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
} catch (error: any) {
|
||||
console.error("导出错误:", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
} catch (error: any) {
|
||||
console.error("导出错误:", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessageBox.close();
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessageBox.close();
|
||||
});
|
||||
}
|
||||
|
||||
function handleIntervalConfirm(interval: string) {
|
||||
@@ -760,7 +821,7 @@ function handleIntervalConfirm(interval: string) {
|
||||
|
||||
const handlechangeCron = (cronStr: string) => {
|
||||
// formData.trigger_args = cronStr;
|
||||
if (typeof (cronStr) == "string") {
|
||||
if (typeof cronStr == "string") {
|
||||
formData.trigger_args = cronStr;
|
||||
}
|
||||
};
|
||||
@@ -771,20 +832,22 @@ const handleClear = () => {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await JobAPI.clearJob();
|
||||
ElMessage.success("清空成功");
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessageBox.close();
|
||||
});
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await JobAPI.clearJob();
|
||||
ElMessage.success("清空成功");
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessageBox.close();
|
||||
});
|
||||
};
|
||||
|
||||
// 操作按钮:操作类型 1: 暂停 2: 恢复 3: 重启(暂时移除重启)
|
||||
|
||||
@@ -105,11 +105,12 @@ import { LocationQuery, RouteLocationRaw, useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import AuthAPI, {type LoginFormData, type CaptchaInfo } from "@/api/system/auth";
|
||||
import router from "@/router";
|
||||
import { useUserStore } from "@/store";
|
||||
import { useAppStore, useUserStore } from "@/store";
|
||||
import CommonWrapper from "@/components/CommonWrapper/index.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
@@ -203,6 +204,10 @@ async function handleLoginSubmit() {
|
||||
// 4. 记住我功能已实现,根据用户选择决定token的存储方式:
|
||||
// - 选中"记住我": token存储在localStorage中,浏览器关闭后仍然有效
|
||||
// - 未选中"记住我": token存储在sessionStorage中,浏览器关闭后失效
|
||||
|
||||
// 登录成功后自动开启项目引导
|
||||
appStore.showGuide(true);
|
||||
|
||||
} catch (error) {
|
||||
// 5. 统一错误处理
|
||||
getCaptcha(); // 刷新验证码
|
||||
|
||||
@@ -397,30 +397,28 @@ async function handleExport() {
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const body = {
|
||||
...queryFormData,
|
||||
page_no: 1,
|
||||
page_size: total.value
|
||||
};
|
||||
|
||||
ElMessage.warning('正在导出数据,请稍候...');
|
||||
|
||||
const response = await ConfigAPI.exportConfig(body);
|
||||
const blob = new Blob([JSON.stringify(response.data.data)], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' });
|
||||
const response = await ConfigAPI.exportConfig(queryFormData);
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
|
||||
const fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
|
||||
// 从响应头获取文件名
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
let fileName = '系统配置.xlsx';
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
|
||||
if (fileNameMatch) {
|
||||
fileName = decodeURIComponent(fileNameMatch[1]);
|
||||
}
|
||||
}
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
ElMessage.success('导出成功');
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
|
||||
@@ -260,7 +260,7 @@ const queryFormData = reactive<DictDataPageQuery>({
|
||||
page_no: 1,
|
||||
page_size: 10,
|
||||
dict_label: undefined,
|
||||
dict_type: undefined,
|
||||
dict_type: props.dictType,
|
||||
status: undefined,
|
||||
start_time: undefined,
|
||||
end_time: undefined,
|
||||
@@ -309,7 +309,6 @@ async function loadingData() {
|
||||
loading.value = true;
|
||||
try {
|
||||
// 在查询参数中添加 dictType
|
||||
queryFormData.dict_type = props.dictType;
|
||||
const response = await DictAPI.getDictDataList(queryFormData);
|
||||
pageTableData.value = response.data.data.items;
|
||||
total.value = response.data.data.total;
|
||||
@@ -440,30 +439,28 @@ async function handleExport() {
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const body = {
|
||||
...queryFormData,
|
||||
page_no: 1,
|
||||
page_size: total.value
|
||||
};
|
||||
|
||||
ElMessage.warning('正在导出数据,请稍候...');
|
||||
|
||||
const response = await DictAPI.exportDictData(body);
|
||||
const blob = new Blob([JSON.stringify(response.data.data)], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' });
|
||||
const response = await DictAPI.exportDictData(queryFormData);
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
|
||||
const fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
|
||||
// 从响应头获取文件名
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
let fileName = '字典数据.xlsx';
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
|
||||
if (fileNameMatch) {
|
||||
fileName = decodeURIComponent(fileNameMatch[1]);
|
||||
}
|
||||
}
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
ElMessage.success('导出成功');
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
|
||||
@@ -419,30 +419,28 @@ async function handleExport() {
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const body = {
|
||||
...queryFormData,
|
||||
page_no: 1,
|
||||
page_size: total.value
|
||||
};
|
||||
|
||||
ElMessage.warning('正在导出数据,请稍候...');
|
||||
|
||||
const response = await DictAPI.exportDictType(body);
|
||||
const blob = new Blob([JSON.stringify(response.data.data)], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' });
|
||||
const response = await DictAPI.exportDictType(queryFormData);
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
|
||||
const fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
|
||||
// 从响应头获取文件名
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
let fileName = '字典类型.xlsx';
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
|
||||
if (fileNameMatch) {
|
||||
fileName = decodeURIComponent(fileNameMatch[1]);
|
||||
}
|
||||
}
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
ElMessage.success('导出成功');
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
|
||||
@@ -355,30 +355,28 @@ async function handleExport() {
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const body = {
|
||||
...queryFormData,
|
||||
page_no: 1,
|
||||
page_size: total.value
|
||||
};
|
||||
|
||||
ElMessage.warning('正在导出数据,请稍候...');
|
||||
|
||||
const response = await LogAPI.exportLog(body);
|
||||
const blob = new Blob([JSON.stringify(response.data.data)], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' });
|
||||
const response = await LogAPI.exportLog(queryFormData);
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
|
||||
const fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
|
||||
// 从响应头获取文件名
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
let fileName = '系统配置.xlsx';
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
|
||||
if (fileNameMatch) {
|
||||
fileName = decodeURIComponent(fileNameMatch[1]);
|
||||
}
|
||||
}
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
ElMessage.success('导出成功');
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
|
||||
@@ -445,30 +445,28 @@ async function handleExport() {
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const body = {
|
||||
...queryFormData,
|
||||
page_no: 1,
|
||||
page_size: total.value
|
||||
};
|
||||
|
||||
ElMessage.warning('正在导出数据,请稍候...');
|
||||
|
||||
const response = await NoticeAPI.exportNotice(body);
|
||||
const blob = new Blob([JSON.stringify(response.data.data)], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' });
|
||||
const response = await NoticeAPI.exportNotice(queryFormData);
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
|
||||
const fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
|
||||
// 从响应头获取文件名
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
let fileName = '系统配置.xlsx';
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
|
||||
if (fileNameMatch) {
|
||||
fileName = decodeURIComponent(fileNameMatch[1]);
|
||||
}
|
||||
}
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
ElMessage.success('导出成功');
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
|
||||
@@ -397,30 +397,28 @@ async function handleExport() {
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const body = {
|
||||
...queryFormData,
|
||||
page_no: 1,
|
||||
page_size: total.value
|
||||
};
|
||||
|
||||
ElMessage.warning('正在导出数据,请稍候...');
|
||||
|
||||
const response = await PositionAPI.exportPosition(body);
|
||||
const blob = new Blob([JSON.stringify(response.data.data)], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' });
|
||||
const response = await PositionAPI.exportPosition(queryFormData);
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
|
||||
const fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
|
||||
// 从响应头获取文件名
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
let fileName = '系统配置.xlsx';
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
|
||||
if (fileNameMatch) {
|
||||
fileName = decodeURIComponent(fileNameMatch[1]);
|
||||
}
|
||||
}
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
ElMessage.success('导出成功');
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
|
||||
@@ -248,10 +248,10 @@ const tableColumns = ref([
|
||||
])
|
||||
|
||||
// 详情表单
|
||||
const detailFormData = ref<RoleTable>({});
|
||||
const detailFormData = ref<RoleTable>({} as RoleTable);
|
||||
|
||||
// 选中的角色
|
||||
const checkedRole = ref<RoleTable>({});
|
||||
const checkedRole = ref<RoleTable>({} as RoleTable);
|
||||
|
||||
const queryFormData = reactive<TablePageQuery>({
|
||||
page_no: 1,
|
||||
@@ -424,30 +424,28 @@ async function handleExport() {
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const body = {
|
||||
...queryFormData,
|
||||
page_no: 1,
|
||||
page_size: total.value
|
||||
};
|
||||
|
||||
ElMessage.warning('正在导出数据,请稍候...');
|
||||
|
||||
const response = await RoleAPI.exportRole(body);
|
||||
const blob = new Blob([JSON.stringify(response.data.data)], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' });
|
||||
const response = await RoleAPI.exportRole(queryFormData);
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
|
||||
const fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
|
||||
// 从响应头获取文件名
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
let fileName = '系统配置.xlsx';
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
|
||||
if (fileNameMatch) {
|
||||
fileName = decodeURIComponent(fileNameMatch[1]);
|
||||
}
|
||||
}
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
ElMessage.success('导出成功');
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
<em>点击上传</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
格式为*.xlsx / *.xls,文件不超过一个
|
||||
<el-link type="primary" icon="download" underline="never" @click="handleDownloadTemplate">
|
||||
<div class="el-upload__tip flex">
|
||||
<el-text type="warning" class="mx-1">注意事项:</el-text>
|
||||
<el-text type="danger" class="mx-1">格式为*.xlsx / *.xls,文件不超过 5MB </el-text>
|
||||
<el-link class="mx-1" type="primary" icon="download" underline="never" @click="handleDownloadTemplate">
|
||||
下载模板
|
||||
</el-link>
|
||||
</div>
|
||||
@@ -27,9 +28,6 @@
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<div style="padding-right: var(--el-dialog-padding-primary)">
|
||||
<el-button v-if="resultData.length > 0" type="primary" @click="handleShowResult">
|
||||
错误信息
|
||||
</el-button>
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@@ -41,27 +39,6 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="resultVisible" title="导入结果" width="600px">
|
||||
<el-alert
|
||||
:title="`导入结果:${invalidCount}条无效数据,${validCount}条有效数据`"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
/>
|
||||
<el-table :data="resultData" style="width: 100%; max-height: 400px">
|
||||
<el-table-column prop="index" align="center" width="100" type="index" label="序号" />
|
||||
<el-table-column prop="message" label="错误信息" width="400">
|
||||
<template #default="scope">
|
||||
{{ scope.row }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleCloseResult">关闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -77,10 +54,6 @@ const importModalVisible = defineModel("modelValue", {
|
||||
default: false,
|
||||
});
|
||||
|
||||
const resultVisible = ref(false);
|
||||
const resultData = ref<string[]>([]);
|
||||
const invalidCount = ref(0);
|
||||
const validCount = ref(0);
|
||||
|
||||
const importFormRef = ref(null);
|
||||
const uploadRef = ref(null);
|
||||
@@ -91,15 +64,6 @@ const importFormData = reactive<{
|
||||
files: [],
|
||||
});
|
||||
|
||||
watch(importModalVisible, (newValue) => {
|
||||
if (newValue) {
|
||||
resultData.value = [];
|
||||
resultVisible.value = false;
|
||||
invalidCount.value = 0;
|
||||
validCount.value = 0;
|
||||
}
|
||||
});
|
||||
|
||||
const importFormRules = {
|
||||
files: [{ required: true, message: "文件不能为空", trigger: "blur" }],
|
||||
};
|
||||
@@ -112,7 +76,7 @@ const handleFileExceed = () => {
|
||||
// 下载导入模板
|
||||
const handleDownloadTemplate = () => {
|
||||
UserAPI.downloadTemplate().then((response: any) => {
|
||||
const fileData = response.data.data;
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
|
||||
const fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
@@ -137,34 +101,20 @@ const handleUpload = async () => {
|
||||
ElMessage.warning("请选择文件");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await UserAPI.importUser(importFormData.files[0].raw as File);
|
||||
if (response.data.code === ResultEnum.SUCCESS) {
|
||||
ElMessage.success("导入成功,导入数据:" + response.data.data.count + "条");
|
||||
emit("import-success");
|
||||
handleClose();
|
||||
} else {
|
||||
ElMessage.error("上传失败");
|
||||
resultVisible.value = true;
|
||||
resultData.value = response.data.data.messageList;
|
||||
invalidCount.value = response.data.data.invalidCount;
|
||||
validCount.value = response.data.data.validCount;
|
||||
const file = importFormData.files[0].raw as File;
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const response = await UserAPI.importUser(formData);
|
||||
if (response.data.code === ResultEnum.SUCCESS) {
|
||||
ElMessage.success(`${response.data.msg},${response.data.data}`);
|
||||
emit("import-success");
|
||||
handleClose();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
ElMessage.error("上传失败:" + error);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
ElMessage.error("上传失败:" + error);
|
||||
}
|
||||
};
|
||||
|
||||
// 显示错误信息
|
||||
const handleShowResult = () => {
|
||||
resultVisible.value = true;
|
||||
};
|
||||
|
||||
// 关闭错误信息弹窗
|
||||
const handleCloseResult = () => {
|
||||
resultVisible.value = false;
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
<el-button type="info" icon="upload" circle @click="handleOpenImportDialog" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="导出">
|
||||
<el-button type="warning" icon="download" circle @click="handleOperation('export')" />
|
||||
<el-button type="warning" icon="download" circle @click="handleExport" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="刷新">
|
||||
<el-button type="default" icon="refresh" circle @click="handleRefresh" />
|
||||
@@ -560,77 +560,46 @@ async function handleSubmit() {
|
||||
});
|
||||
}
|
||||
|
||||
// 删除、导入、导出
|
||||
async function handleOperation(type: 'import' | 'export') {
|
||||
if (type === 'import') {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.xlsx, .xls';
|
||||
input.click();
|
||||
// 导出
|
||||
async function handleExport() {
|
||||
ElMessageBox.confirm('是否确认导出当前查询结果用户数据?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
|
||||
input.onchange = async (e) => {
|
||||
const file = (e.target as HTMLInputElement).files?.[0];
|
||||
if (file) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
try {
|
||||
loading.value = true;
|
||||
await UserAPI.importUser(formData);
|
||||
ElMessage.success('导入成功');
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
ElMessage.warning('正在导出数据,请稍候...');
|
||||
|
||||
const response = await UserAPI.exportUser(queryFormData);
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
|
||||
const fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
ElMessage.success('导出成功');
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
else if (type === 'export') {
|
||||
ElMessageBox.confirm('是否确认导出当前查询结果用户数据?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
|
||||
ElMessage.warning('正在导出数据,请稍候...');
|
||||
|
||||
UserAPI.exportUser(queryFormData).then((response: any) => {
|
||||
// const fileData = JSON.stringify(response.data.data;
|
||||
const fileData = response.data.data;
|
||||
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
|
||||
const fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
ElMessage.success('导出成功');
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
});
|
||||
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessageBox.close();
|
||||
});
|
||||
}
|
||||
else {
|
||||
ElMessage.error('未知操作类型');
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessageBox.close();
|
||||
});
|
||||
}
|
||||
|
||||
// 删除、批量删除
|
||||
|
||||
Reference in New Issue
Block a user