mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-23 05:10:57 +00:00
refactor(backend): 重构任务模块并优化菜单权限
- 移除系统模块中的任务相关代码 - 新增批量设置字典类型和数据状态的功能 - 更新菜单权限获取逻辑,增加对链接类型菜单的支持 - 调整任务模块到 monitor 监控模块下
This commit is contained in:
+36
-44
@@ -120,15 +120,23 @@
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column prop="cache_name" label="缓存名称" align="center" :ellipsis="true">
|
||||
<el-table-column prop="cache_name" label="缓存名称" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="getCacheKeyList(row)">{{ row.cache_name }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" align="center" :ellipsis="true" />
|
||||
<el-table-column prop="remark" label="备注" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="60" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="danger" size="small" link icon="delete" @click="handleClearCacheName(row)" />
|
||||
<el-popconfirm
|
||||
class="box-item"
|
||||
:title="`确认删除缓存 ${row.cache_name} 吗?`"
|
||||
placement="top"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button type="danger" size="small" link icon="delete" @click="handleClearCacheName(row)" />
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -162,14 +170,22 @@
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column prop="cacheKey" label="缓存键名" align="center" :ellipsis="true">
|
||||
<el-table-column prop="cacheKey" label="缓存键名" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="handleCacheValue(row.cacheKey)">{{ row.cacheKey }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="60" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="danger" size="small" link icon="delete" @click="handleClearCacheKey(row.cacheKey)" />
|
||||
<el-popconfirm
|
||||
class="box-item"
|
||||
:title="`确认删除键 ${row.cacheKey} 吗?`"
|
||||
placement="top"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button type="danger" size="small" link icon="delete" @click="handleClearCacheKey(row.cacheKey)" />
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -272,25 +288,13 @@ const refreshCacheNames = () => {
|
||||
};
|
||||
|
||||
// 清理缓存名称
|
||||
const handleClearCacheName = (row: CacheInfo) => {
|
||||
ElMessageBox.confirm(
|
||||
`确定要清理缓存名称[${row.cache_name}]吗?`,
|
||||
'确认清理',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(async () => {
|
||||
const handleClearCacheName = async (row: CacheInfo) => {
|
||||
try {
|
||||
await CacheAPI.deleteCacheName(row.cache_name);
|
||||
getCacheNameList();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error !== 'cancel') {
|
||||
console.error('清理缓存失败:', error);
|
||||
}
|
||||
});
|
||||
refreshCacheNames();
|
||||
} catch (error) {
|
||||
console.error('清理缓存名称出错:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 缓存键名相关方法
|
||||
@@ -321,25 +325,13 @@ const refreshCacheKeys = () => {
|
||||
};
|
||||
|
||||
// 清理缓存键名
|
||||
const handleClearCacheKey = (cacheKey: string) => {
|
||||
ElMessageBox.confirm(
|
||||
`确定要清理缓存键名[${cacheKey}]吗?`,
|
||||
'确认清理',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
CacheAPI.deleteCacheKey(cacheKey);
|
||||
const handleClearCacheKey = async (cacheKey: string) => {
|
||||
try {
|
||||
await CacheAPI.deleteCacheKey(cacheKey);
|
||||
getCacheKeyList();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error !== 'cancel') {
|
||||
console.error('清理缓存键名失败:', error);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('清理缓存键名出错:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 缓存内容相关方法
|
||||
@@ -359,15 +351,15 @@ const handleCacheValue = async (cacheKey: string) => {
|
||||
const handleClearCacheAll = async () => {
|
||||
ElMessageBox.confirm(
|
||||
'确定要清理全部缓存吗?',
|
||||
'确认清理',
|
||||
'危险!',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
return CacheAPI.deleteCacheAll();
|
||||
.then(async () => {
|
||||
return await CacheAPI.deleteCacheAll();
|
||||
})
|
||||
.then(() => {
|
||||
getCacheNameList();
|
||||
|
||||
+34
-80
@@ -8,7 +8,7 @@
|
||||
<el-input v-model="queryFormData.name" placeholder="请输入任务名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="状态">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable style="width: 160px">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable>
|
||||
<el-option value="true" label="启用" />
|
||||
<el-option value="false" label="暂停" />
|
||||
</el-select>
|
||||
@@ -59,30 +59,6 @@
|
||||
<el-button type="success" icon="plus" @click="handleOpenDialog('create')">新增</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0"
|
||||
@click="handleOperation('delete')">批量删除</el-button>
|
||||
<el-dropdown>
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">更多
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-menu @click="handleMoreClick">
|
||||
<el-menu-item key="1">
|
||||
<span>
|
||||
<el-icon>
|
||||
<Check />
|
||||
</el-icon>
|
||||
<span>批量启用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item key="2">
|
||||
<span>
|
||||
<el-icon>
|
||||
<CircleClose />
|
||||
</el-icon>
|
||||
<span>批量停用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="data-table__toolbar--tools">
|
||||
<el-tooltip content="导入">
|
||||
@@ -99,14 +75,15 @@
|
||||
</el-tooltip>
|
||||
<el-tooltip content="列表筛选">
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" icon="operation" circle />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="column in tableColumns" :key="column.prop" :command="column">
|
||||
<el-checkbox v-model="column.show">{{ column.label }}</el-checkbox>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
<el-button type="default" icon="operation" circle />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="column in tableColumns" :key="column.prop"
|
||||
:command="column">
|
||||
<el-checkbox v-model="column.show">{{ column.label }}</el-checkbox>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
@@ -119,21 +96,21 @@
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection"
|
||||
width="55" align="center" />
|
||||
min-width="55" align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed
|
||||
label="序号" width="60" />
|
||||
label="序号" min-width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'name')?.show" key="name" label="任务名称"
|
||||
prop="name" min-width="80" />
|
||||
prop="name" min-width="140" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'func')?.show" key="func" label="执行函数"
|
||||
prop="func" min-width="80" />
|
||||
prop="func" min-width="140" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'trigger')?.show" key="trigger" label="触发器"
|
||||
prop="trigger" min-width="80" />
|
||||
prop="trigger" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'jobstore')?.show" key="jobstore"
|
||||
label="存储器" prop="jobstore" min-width="80" />
|
||||
label="存储器" prop="jobstore" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'executor')?.show" key="executor"
|
||||
label="执行器" prop="executor" min-width="80" />
|
||||
label="执行器" prop="executor" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'coalesce')?.show" key="coalesce"
|
||||
label="并发执行" prop="coalesce" min-width="80">
|
||||
label="并发执行" prop="coalesce" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.coalesce === true ? 'success' : 'danger'">
|
||||
{{ scope.row.coalesce === true ? "是" : "否" }}
|
||||
@@ -141,7 +118,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'status')?.show" key="status" label="状态"
|
||||
prop="status" min-width="60">
|
||||
prop="status" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === true ? 'success' : 'danger'">
|
||||
{{ scope.row.status === true ? "启用" : "停用" }}
|
||||
@@ -151,12 +128,12 @@
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" key="description"
|
||||
label="描述" prop="description" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" key="created_at"
|
||||
label="创建时间" prop="created_at" min-width="120" sortable />
|
||||
label="创建时间" prop="created_at" min-width="200" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" key="updated_at"
|
||||
label="更新时间" prop="updated_at" min-width="120" sortable />
|
||||
label="更新时间" prop="updated_at" min-width="200" sortable />
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right"
|
||||
label="操作" min-width="120">
|
||||
label="操作" min-width="240">
|
||||
<template #default="scope">
|
||||
<el-button type="info" size="small" link icon="document"
|
||||
@click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
|
||||
@@ -164,16 +141,16 @@
|
||||
@click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete"
|
||||
@click="handleOperation('delete', scope.row.id)">删除</el-button>
|
||||
<el-dropdown>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" size="small" link icon="ArrowDown">
|
||||
更多
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-menu @click="handleOption(scope.row, $event.key)">
|
||||
<el-menu-item key="1">暂停</el-menu-item>
|
||||
<el-menu-item key="2">恢复</el-menu-item>
|
||||
<!-- <el-menu-item key="3">重启</el-menu-item> -->
|
||||
</el-menu>
|
||||
<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="CircleClose" @click="handleOption(scope.row, 3)">重启</el-dropdown-item> -->
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
@@ -205,9 +182,9 @@
|
||||
<el-tag v-else type="danger">否</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="最大实例数" :span="2">{{ detailFormData.max_instances
|
||||
}}</el-descriptions-item>
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="触发器参数" :span="2">{{ detailFormData.trigger_args
|
||||
}}</el-descriptions-item>
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="开始时间" :span="2">{{ detailFormData.start_date }}</el-descriptions-item>
|
||||
<el-descriptions-item label="结束时间" :span="2">{{ detailFormData.end_date }}</el-descriptions-item>
|
||||
<el-descriptions-item label="日志信息" :span="2">{{ detailFormData.message }}</el-descriptions-item>
|
||||
@@ -217,7 +194,7 @@
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="描述" :span="2">{{ detailFormData.description }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人" :span="2">{{ detailFormData.creator?.name
|
||||
}}</el-descriptions-item>
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间" :span="2">{{ detailFormData.created_at }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间" :span="2">{{ detailFormData.updated_at }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
@@ -338,13 +315,13 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import JobAPI, { JobTable, JobForm, JobPageQuery } from "@/api/system/job";
|
||||
import JobAPI, { JobTable, JobForm, JobPageQuery } from "@/api/monitor/job";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
|
||||
import IntervalTab from '@/components/IntervalTab/index.vue';
|
||||
import 'vue3-cron-plus-picker/style.css'
|
||||
import { Vue3CronPlusPicker } from 'vue3-cron-plus-picker'
|
||||
import 'vue3-cron-plus-picker/style.css';
|
||||
import { Vue3CronPlusPicker } from 'vue3-cron-plus-picker';
|
||||
import { useDictStore } from "@/store/index";
|
||||
|
||||
const dictStore = useDictStore();
|
||||
@@ -652,29 +629,6 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
}
|
||||
}
|
||||
|
||||
// 批量启用/停用
|
||||
async function handleMoreClick(id: number) {
|
||||
if (id && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除启用或停用该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await JobAPI.batchAvailableJob({ id: id ? id : selectIds.value });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消批量操作');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const getOptions = async () => {
|
||||
const dictOptions = await dictStore.getDict(['sys_job_group', 'sys_job_executor', 'sys_job_function', 'sys_job_trigger'])
|
||||
return dictOptions
|
||||
@@ -65,12 +65,12 @@
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" width="55"
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" min-width="55"
|
||||
align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号"
|
||||
width="60" />
|
||||
min-width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'session_id')?.show" key="session_id" label="会话编号"
|
||||
prop="session_id" min-width="80" />
|
||||
prop="session_id" min-width="250" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'ipaddr')?.show" key="ipaddr" label="IP地址"
|
||||
prop="ipaddr" min-width="80" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'name')?.show" key="name" label="用户名" prop="name"
|
||||
@@ -82,7 +82,7 @@
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'os')?.show" key="os" label="操作系统" prop="os"
|
||||
min-width="80" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'login_time')?.show" key="login_time" label="登录时间"
|
||||
prop="login_time" min-width="80" />
|
||||
prop="login_time" min-width="200" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" key="operation" fixed="right"
|
||||
label="操作" min-width="60">
|
||||
<template #default="scope">
|
||||
@@ -186,8 +186,8 @@ async function handleSelectionChange(selection: any) {
|
||||
}
|
||||
|
||||
// 强制退出
|
||||
async function handleSubmit(id?: number) {
|
||||
if (!id && !selectIds.value.length) {
|
||||
async function handleSubmit(ids?: number[]) {
|
||||
if (!ids && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认强制退出选中项?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
@@ -195,7 +195,7 @@ async function handleSubmit(id?: number) {
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await OnlineAPI.deleteOnline({ id: id ? id : selectIds.value });
|
||||
await OnlineAPI.deleteOnline({ ids });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
|
||||
@@ -11,14 +11,15 @@
|
||||
<el-input v-model="queryFormData.config_key" placeholder="请输入配置键名" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="config_type" label="系统内置">
|
||||
<el-select v-model="queryFormData.config_type" placeholder="请选择系统内置" clearable style="width: 160px">
|
||||
<el-select v-model="queryFormData.config_type" placeholder="请选择系统内置" clearable>
|
||||
<el-option value="true" label="是" />
|
||||
<el-option value="false" label="否" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 时间范围,收起状态下隐藏 -->
|
||||
<el-form-item v-if="isExpand" prop="start_time" label="创建时间">
|
||||
<el-date-picker v-model="queryFormData.start_time" type="daterange" value-format="yyyy-MM-dd" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" />
|
||||
<el-date-picker v-model="queryFormData.start_time" type="daterange" value-format="yyyy-MM-dd"
|
||||
range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" />
|
||||
</el-form-item>
|
||||
<el-form-item class="search-buttons">
|
||||
<el-button type="primary" icon="search" @click="handleQuery">查询</el-button>
|
||||
@@ -58,14 +59,12 @@
|
||||
<div class="data-table__toolbar">
|
||||
<div class="data-table__toolbar--actions">
|
||||
<el-button type="success" icon="plus" @click="handleOpenDialog('create')">新增</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0" @click="handleOperation('delete')">批量删除</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0"
|
||||
@click="handleDelete(selectIds)">批量删除</el-button>
|
||||
</div>
|
||||
<div class="data-table__toolbar--tools">
|
||||
<el-tooltip content="导入">
|
||||
<el-button type="info" icon="upload" circle @click="handleOperation('import')" />
|
||||
</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="primary" icon="refresh" circle @click="handleRefresh" />
|
||||
@@ -91,33 +90,52 @@
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号" width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'config_name')?.show" key="config_name" label="配置名称" prop="config_name" min-width="80" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'config_key')?.show" key="config_key" label="配置键" prop="config_key" min-width="120" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'config_value')?.show" key="config_value" label="配置值" prop="config_value" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'config_type')?.show" key="config_type" label="系统内置" prop="config_type" min-width="60">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" min-width="55"
|
||||
align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号"
|
||||
min-width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'config_name')?.show" key="config_name"
|
||||
label="配置名称" prop="config_name" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'config_key')?.show" key="config_key" label="配置键"
|
||||
prop="config_key" min-width="200" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'config_value')?.show" key="config_value"
|
||||
label="配置值" prop="config_value" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'config_type')?.show" key="config_type"
|
||||
label="系统内置" prop="config_type" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.config_type" type="success">是</el-tag>
|
||||
<el-tag v-else type="danger">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" key="description" label="描述" prop="description" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" key="created_at" label="创建时间" prop="created_at" min-width="120" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" key="updated_at" label="更新时间" prop="updated_at" min-width="120" sortable />
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作" min-width="120">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" key="description" label="描述"
|
||||
prop="description" min-width="120" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'creator')?.show" key="creator" label="创建人"
|
||||
min-width="120" sortable>
|
||||
<template #default="scope">
|
||||
<el-button type="info" size="small" link icon="document" @click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
|
||||
<el-button type="primary" size="small" link icon="edit" @click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete" @click="handleOperation('delete', scope.row.id)">删除</el-button>
|
||||
{{ scope.row.creator?.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" key="created_at" label="创建时间"
|
||||
prop="created_at" min-width="200" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" key="updated_at" label="更新时间"
|
||||
prop="updated_at" min-width="200" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作"
|
||||
min-width="200">
|
||||
<template #default="scope">
|
||||
<el-button type="info" size="small" link icon="document"
|
||||
@click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
|
||||
<el-button type="primary" size="small" link icon="edit"
|
||||
@click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete"
|
||||
@click="handleDelete([scope.row.id])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<template #footer>
|
||||
<pagination v-model:total="total" v-model:page="queryFormData.page_no" v-model:limit="queryFormData.page_size" @pagination="loadingData" />
|
||||
<pagination v-model:total="total" v-model:page="queryFormData.page_no" v-model:limit="queryFormData.page_size"
|
||||
@pagination="loadingData" />
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
@@ -158,7 +176,8 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input v-model="formData.description" :rows="4" :maxlength="100" show-word-limit type="textarea" placeholder="请输入描述" />
|
||||
<el-input v-model="formData.description" :rows="4" :maxlength="100" show-word-limit type="textarea"
|
||||
placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
@@ -173,86 +192,7 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导出弹窗 -->
|
||||
<el-dialog v-model="exportsModalVisible" :align-center="true" title="导出数据" width="600px" style="padding-right: 0"
|
||||
@close="handleCloseExportsModal">
|
||||
<!-- 滚动 -->
|
||||
<el-scrollbar max-height="60vh">
|
||||
<!-- 表单 -->
|
||||
<el-form ref="exportsFormRef" style="padding-right: var(--el-dialog-padding-primary)" :model="exportsFormData"
|
||||
:rules="exportsFormRules">
|
||||
<el-form-item label="文件名" prop="filename">
|
||||
<el-input v-model="exportsFormData.filename" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="工作表名" prop="sheetname">
|
||||
<el-input v-model="exportsFormData.sheetname" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="数据源" prop="origin">
|
||||
<el-select v-model="exportsFormData.origin">
|
||||
<el-option label="当前数据 (当前页的数据)" :value="ExportsOriginEnum.CURRENT" />
|
||||
<el-option label="选中数据 (所有选中的数据)" :value="ExportsOriginEnum.SELECTED"
|
||||
:disabled="selectionData.length <= 0" />
|
||||
<el-option label="全量数据 (所有分页的数据)" :value="ExportsOriginEnum.REMOTE"
|
||||
:disabled="contentConfig.exportsAction === undefined" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="字段" prop="fields">
|
||||
<el-checkbox-group v-model="exportsFormData.fields">
|
||||
<template v-for="col in cols" :key="col.prop">
|
||||
<el-checkbox v-if="col.prop" :value="col.prop" :label="col.label" />
|
||||
</template>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<div style="padding-right: var(--el-dialog-padding-primary)">
|
||||
<el-button type="primary" @click="handleExportsSubmit">确 定</el-button>
|
||||
<el-button @click="handleCloseExportsModal">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 导入弹窗 -->
|
||||
<el-dialog v-model="importModalVisible" :align-center="true" title="导入数据" width="600px" style="padding-right: 0"
|
||||
@close="handleCloseImportModal">
|
||||
<!-- 滚动 -->
|
||||
<el-scrollbar max-height="60vh">
|
||||
<!-- 表单 -->
|
||||
<el-form ref="importFormRef" style="padding-right: var(--el-dialog-padding-primary)" :model="importFormData"
|
||||
:rules="importFormRules">
|
||||
<el-form-item label="文件名" prop="files">
|
||||
<el-upload ref="uploadRef" v-model:file-list="importFormData.files" class="w-full"
|
||||
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
||||
:drag="true" :limit="1" :auto-upload="false" :on-exceed="handleFileExceed">
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
<span>将文件拖到此处,或</span>
|
||||
<em>点击上传</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
*.xlsx / *.xls
|
||||
<el-link v-if="contentConfig.importTemplate" type="primary" icon="download" underline="never"
|
||||
@click="handleDownloadTemplate">
|
||||
下载模板
|
||||
</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<div style="padding-right: var(--el-dialog-padding-primary)">
|
||||
<el-button type="primary" :disabled="importFormData.files.length === 0" @click="handleImportSubmit">
|
||||
确 定
|
||||
</el-button>
|
||||
<el-button @click="handleCloseImportModal">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -290,6 +230,7 @@ const tableColumns = ref([
|
||||
{ prop: 'description', label: '描述', show: true },
|
||||
{ prop: 'created_at', label: '创建时间', show: true },
|
||||
{ prop: 'updated_at', label: '更新时间', show: true },
|
||||
{ prop: 'creator', label: '创建人', show: true },
|
||||
{ prop: 'operation', label: '操作', show: true }
|
||||
])
|
||||
|
||||
@@ -315,7 +256,7 @@ const formData = reactive<ConfigForm>({
|
||||
config_value: '',
|
||||
config_type: false,
|
||||
description: ''
|
||||
})
|
||||
});
|
||||
|
||||
// 弹窗状态
|
||||
const dialogVisible = reactive({
|
||||
@@ -440,9 +381,9 @@ async function handleSubmit() {
|
||||
});
|
||||
}
|
||||
|
||||
// 删除系统配置
|
||||
async function handleOperation(type: 'delete' | 'import' | 'export', id?: number) {
|
||||
if (type === 'delete' && !id && !selectIds.value.length) {
|
||||
// 删除、批量删除
|
||||
async function handleDelete(ids: number[]) {
|
||||
if (!ids && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
@@ -450,8 +391,7 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await ConfigAPI.deleteConfig({ id: id ? id : selectIds.value });
|
||||
ElMessage.success("删除成功");
|
||||
await ConfigAPI.deleteConfig({ ids });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -462,198 +402,50 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
ElMessage.info('已取消删除');
|
||||
});
|
||||
}
|
||||
else if (type === 'import') {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.xlsx, .xls';
|
||||
input.click();
|
||||
|
||||
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 ConfigAPI.uploadFile(formData);
|
||||
ElMessage.success('导入成功');
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type === 'export') {
|
||||
ElMessageBox.confirm('是否确认导出当前系统配置?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).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)], { 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]);
|
||||
}
|
||||
}
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
ElMessage.success('导出成功');
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
else {
|
||||
ElMessage.error('未知操作类型');
|
||||
}
|
||||
}
|
||||
|
||||
// 导出
|
||||
function handleExport() {
|
||||
const filename = exportsFormData.filename
|
||||
? exportsFormData.filename
|
||||
: props.contentConfig.permPrefix || "export";
|
||||
const sheetname = exportsFormData.sheetname ? exportsFormData.sheetname : "sheet";
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet(sheetname);
|
||||
const columns: Partial<ExcelJS.Column>[] = [];
|
||||
cols.value.forEach((col) => {
|
||||
if (col.label && col.prop && exportsFormData.fields.includes(col.prop)) {
|
||||
columns.push({ header: col.label, key: col.prop });
|
||||
async function handleExport() {
|
||||
ElMessageBox.confirm('是否确认导出当前系统配置?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).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 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();
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
worksheet.columns = columns;
|
||||
if (exportsFormData.origin === ExportsOriginEnum.REMOTE) {
|
||||
if (props.contentConfig.exportsAction) {
|
||||
props.contentConfig.exportsAction(lastFormData).then((res) => {
|
||||
worksheet.addRows(res);
|
||||
workbook.xlsx
|
||||
.writeBuffer()
|
||||
.then((buffer) => {
|
||||
saveXlsx(buffer, filename as string);
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("未配置exportsAction");
|
||||
}
|
||||
} else {
|
||||
worksheet.addRows(
|
||||
exportsFormData.origin === ExportsOriginEnum.SELECTED ? selectionData.value : pageData.value
|
||||
);
|
||||
workbook.xlsx
|
||||
.writeBuffer()
|
||||
.then((buffer) => {
|
||||
saveXlsx(buffer, filename as string);
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
}
|
||||
}
|
||||
|
||||
// 下载导入模板
|
||||
function handleDownloadTemplate() {
|
||||
const importTemplate = props.contentConfig.importTemplate;
|
||||
if (typeof importTemplate === "string") {
|
||||
window.open(importTemplate);
|
||||
} else if (typeof importTemplate === "function") {
|
||||
importTemplate().then((response) => {
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(
|
||||
response.headers["content-disposition"].split(";")[1].split("=")[1]
|
||||
);
|
||||
saveXlsx(fileData, fileName);
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("未配置importTemplate");
|
||||
}
|
||||
}
|
||||
|
||||
// 导入
|
||||
function handleImport() {
|
||||
const importsAction = props.contentConfig.importsAction;
|
||||
if (importsAction === undefined) {
|
||||
ElMessage.error("未配置importsAction");
|
||||
return;
|
||||
}
|
||||
// 获取选择的文件
|
||||
const file = importFormData.files[0].raw as File;
|
||||
// 创建Workbook实例
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
// 使用FileReader对象来读取文件内容
|
||||
const fileReader = new FileReader();
|
||||
// 二进制字符串的形式加载文件
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
fileReader.onload = (ev) => {
|
||||
if (ev.target !== null && ev.target.result !== null) {
|
||||
const result = ev.target.result as ArrayBuffer;
|
||||
// 从 buffer中加载数据解析
|
||||
workbook.xlsx
|
||||
.load(result)
|
||||
.then((workbook) => {
|
||||
// 解析后的数据
|
||||
const data = [];
|
||||
// 获取第一个worksheet内容
|
||||
const worksheet = workbook.getWorksheet(1);
|
||||
if (worksheet) {
|
||||
// 获取第一行的标题
|
||||
const fields: any[] = [];
|
||||
worksheet.getRow(1).eachCell((cell) => {
|
||||
fields.push(cell.value);
|
||||
});
|
||||
// 遍历工作表的每一行(从第二行开始,因为第一行通常是标题行)
|
||||
for (let rowNumber = 2; rowNumber <= worksheet.rowCount; rowNumber++) {
|
||||
const rowData: IObject = {};
|
||||
const row = worksheet.getRow(rowNumber);
|
||||
// 遍历当前行的每个单元格
|
||||
row.eachCell((cell, colNumber) => {
|
||||
// 获取标题对应的键,并将当前单元格的值存储到相应的属性名中
|
||||
rowData[fields[colNumber - 1]] = cell.value;
|
||||
});
|
||||
// 将当前行的数据对象添加到数组中
|
||||
data.push(rowData);
|
||||
}
|
||||
}
|
||||
if (data.length === 0) {
|
||||
ElMessage.error("未解析到数据");
|
||||
return;
|
||||
}
|
||||
importsAction(data).then(() => {
|
||||
ElMessage.success("导入数据成功");
|
||||
handleCloseImportModal();
|
||||
handleRefresh(true);
|
||||
});
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
} else {
|
||||
ElMessage.error("读取文件失败");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-input v-model="queryFormData.name" placeholder="请输入部门名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="状态">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable style="width: 160px">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态asdasda" clearable>
|
||||
<el-option value="true" label="启用" />
|
||||
<el-option value="false" label="停用" />
|
||||
</el-select>
|
||||
@@ -58,39 +58,20 @@
|
||||
<div class="data-table__toolbar--actions">
|
||||
<el-button type="success" icon="plus" @click="handleOpenDialog('create')">新增</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0"
|
||||
@click="handleOperation('delete')">批量删除</el-button>
|
||||
<el-dropdown>
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">更多
|
||||
@click="handleDelete(selectIds)">批量删除</el-button>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">
|
||||
更多
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-menu @click="handleMoreClick">
|
||||
<el-menu-item key="1">
|
||||
<span>
|
||||
<el-icon>
|
||||
<Check />
|
||||
</el-icon>
|
||||
<span>批量启用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item key="2">
|
||||
<span>
|
||||
<el-icon>
|
||||
<CircleClose />
|
||||
</el-icon>
|
||||
<span>批量停用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Check" @click="handleMoreClick(true)">批量启用</el-dropdown-item>
|
||||
<el-dropdown-item icon="CircleClose" @click="handleMoreClick(false)">批量停用</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="data-table__toolbar--tools">
|
||||
<el-tooltip content="导入">
|
||||
<el-button type="info" icon="upload" circle @click="handleOperation('import')" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="导出">
|
||||
<el-button type="warning" icon="download" circle @click="handleOperation('export')" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="刷新">
|
||||
<el-button type="primary" icon="refresh" circle @click="handleRefresh" />
|
||||
</el-tooltip>
|
||||
@@ -113,7 +94,7 @@
|
||||
<el-table ref="dataTableRef" v-loading="loading" row-key="id" :data="pageTableData" :tree-props="{
|
||||
children: 'children',
|
||||
hasChildren: 'hasChildren',
|
||||
}" highlight-current-row class="data-table__content" height="450" border stripe
|
||||
}" highlight-current-row class="data-table__content" height="540" border stripe
|
||||
@selection-change="handleSelectionChange">
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
@@ -123,9 +104,9 @@
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号"
|
||||
width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'name')?.show" key="name" label="部门名称" prop="name"
|
||||
min-width="80" />
|
||||
min-width="120" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'status')?.show" key="status" label="状态"
|
||||
prop="status" min-width="60">
|
||||
prop="status" min-width="80">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === true ? 'success' : 'danger'">
|
||||
{{ scope.row.status ? "启用" : "停用" }}
|
||||
@@ -133,7 +114,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'order')?.show" key="order" label="排序" prop="order"
|
||||
min-width="100" show-overflow-tooltip />
|
||||
min-width="80" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" key="description" label="描述"
|
||||
prop="description" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" key="created_at" label="创建时间"
|
||||
@@ -150,16 +131,11 @@
|
||||
<el-button type="primary" size="small" link icon="edit"
|
||||
@click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete"
|
||||
@click="handleOperation('delete', scope.row.id)">删除</el-button>
|
||||
@click="handleDelete([scope.row.id])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<template #footer>
|
||||
<pagination v-if="total > 0" v-model:total="total" v-model:page="queryFormData.page_no" v-model:limit="queryFormData.page_size"
|
||||
@pagination="loadingData" />
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
<!-- 弹窗区域 -->
|
||||
@@ -174,7 +150,6 @@
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="排序" :span="2">{{ detailFormData.order }}</el-descriptions-item>
|
||||
<el-descriptions-item label="描述" :span="2">{{ detailFormData.description }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人" :span="2">{{ detailFormData.creator?.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间" :span="2">{{ detailFormData.created_at }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间" :span="2">{{ detailFormData.updated_at }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
@@ -262,8 +237,6 @@ const detailFormData = ref<DeptTable>({});
|
||||
|
||||
// 分页查询参数
|
||||
const queryFormData = reactive<DeptPageQuery>({
|
||||
page_no: 1,
|
||||
page_size: 10,
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
start_time: undefined,
|
||||
@@ -318,14 +291,12 @@ async function loadingData() {
|
||||
|
||||
// 查询(重置页码后获取数据)
|
||||
async function handleQuery() {
|
||||
queryFormData.page_no = 1;
|
||||
loadingData();
|
||||
}
|
||||
|
||||
// 重置查询
|
||||
async function handleResetQuery() {
|
||||
queryFormRef.value.resetFields();
|
||||
queryFormData.page_no = 1;
|
||||
loadingData();
|
||||
}
|
||||
|
||||
@@ -351,13 +322,13 @@ async function handleCloseDialog() {
|
||||
async function handleOpenDialog(type: 'create' | 'update' | 'detail', id?: number) {
|
||||
dialogVisible.type = type;
|
||||
if (id) {
|
||||
const data = await DeptAPI.getDeptDetail({ id });
|
||||
const response = await DeptAPI.getDeptDetail({ id });
|
||||
if (type === 'detail') {
|
||||
dialogVisible.title = "部门详情";
|
||||
Object.assign(detailFormData.value, data);
|
||||
Object.assign(detailFormData.value, response.data.data);
|
||||
} else if (type === 'update') {
|
||||
dialogVisible.title = "修改部门";
|
||||
Object.assign(formData, data);
|
||||
Object.assign(formData, response.data.data);
|
||||
}
|
||||
} else {
|
||||
dialogVisible.title = "新增部门";
|
||||
@@ -376,7 +347,7 @@ async function handleSubmit() {
|
||||
const id = formData.id;
|
||||
if (id) {
|
||||
try {
|
||||
await DeptAPI.updateDept(formData)
|
||||
await DeptAPI.updateDept({ id, ...formData })
|
||||
dialogVisible.visible = false;
|
||||
resetForm();
|
||||
handleResetQuery();
|
||||
@@ -401,9 +372,9 @@ async function handleSubmit() {
|
||||
});
|
||||
}
|
||||
|
||||
// 删除、导入、导出
|
||||
async function handleOperation(type: 'delete' | 'import' | 'export', id?: number) {
|
||||
if (type === 'delete' && !id && !selectIds.value.length) {
|
||||
// 删除、批量删除
|
||||
async function handleDelete(ids: number[]) {
|
||||
if (!ids && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
@@ -411,8 +382,7 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await DeptAPI.deleteDept({ id: id ? id : selectIds.value });
|
||||
ElMessage.success("删除成功");
|
||||
await DeptAPI.deleteDept({ ids });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -423,89 +393,19 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
ElMessage.info('已取消删除');
|
||||
});
|
||||
}
|
||||
else if (type === 'import') {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.xlsx, .xls';
|
||||
input.click();
|
||||
|
||||
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 DeptAPI.uploadFile(formData);
|
||||
ElMessage.success('导入成功');
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type === 'export') {
|
||||
ElMessageBox.confirm('是否确认导出当前部门?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const body = {
|
||||
...queryFormData,
|
||||
page_no: 1,
|
||||
page_size: total.value
|
||||
};
|
||||
ElMessage.warning('正在导出数据,请稍候...');
|
||||
|
||||
const response = await DeptAPI.exportDept(body);
|
||||
const blob = new Blob([JSON.stringify(response.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]);
|
||||
}
|
||||
}
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
ElMessage.success('导出成功');
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
else {
|
||||
ElMessage.error('未知操作类型');
|
||||
}
|
||||
}
|
||||
|
||||
// 批量启用/停用
|
||||
async function handleMoreClick(id: number) {
|
||||
if (id && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除启用或停用该项数据?", "警告", {
|
||||
async function handleMoreClick(status: boolean) {
|
||||
if (selectIds.value.length) {
|
||||
ElMessageBox.confirm(`确认${status ? '启用' : '停用'}该项数据?`, "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await DeptAPI.batchAvailableDept({ id: id ? id : selectIds.value });
|
||||
await DeptAPI.batchAvailableDept({ ids: selectIds.value, status });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
|
||||
+96
-125
@@ -1,17 +1,15 @@
|
||||
<!-- 字典数据 -->
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-drawer v-model="drawerVisible" title="字典数据" size="70%">
|
||||
<!-- 搜索区域 -->
|
||||
<div class="search-container">
|
||||
<el-form ref="queryFormRef" :model="queryFormData" :inline="true">
|
||||
|
||||
<el-form-item prop="dict_label" label="字典数据标签">
|
||||
<el-input v-model="queryFormData.dict_label" placeholder="请输入字典标签" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="dict_type" label="字典数据类型">
|
||||
<el-input v-model="queryFormData.dict_type" placeholder="请输入字典类型" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="状态">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable style="width: 160px">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable>
|
||||
<el-option value="true" label="启用" />
|
||||
<el-option value="false" label="停用" />
|
||||
</el-select>
|
||||
@@ -61,38 +59,22 @@
|
||||
<div class="data-table__toolbar--actions">
|
||||
<el-button type="success" icon="plus" @click="handleOpenDialog('create')">新增</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0"
|
||||
@click="handleOperation('delete')">批量删除</el-button>
|
||||
<el-dropdown>
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">更多
|
||||
@click="handleDelete(selectIds)">批量删除</el-button>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">
|
||||
更多
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-menu @click="handleMoreClick">
|
||||
<el-menu-item key="1">
|
||||
<span>
|
||||
<el-icon>
|
||||
<Check />
|
||||
</el-icon>
|
||||
<span>批量启用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item key="2">
|
||||
<span>
|
||||
<el-icon>
|
||||
<CircleClose />
|
||||
</el-icon>
|
||||
<span>批量停用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Check" @click="handleMoreClick(true)">批量启用</el-dropdown-item>
|
||||
<el-dropdown-item icon="CircleClose" @click="handleMoreClick(false)">批量停用</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="data-table__toolbar--tools">
|
||||
<el-tooltip content="导入">
|
||||
<el-button type="info" icon="upload" circle @click="handleOperation('import')" />
|
||||
</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="primary" icon="refresh" circle @click="handleRefresh" />
|
||||
@@ -114,19 +96,19 @@
|
||||
|
||||
<!-- 表格区域:系统配置列表 -->
|
||||
<el-table ref="dataTableRef" v-loading="loading" :data="pageTableData" highlight-current-row
|
||||
class="data-table__content" height="450" border stripe @selection-change="handleSelectionChange">
|
||||
class="data-table__content" height="460" border stripe @selection-change="handleSelectionChange">
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" width="55"
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" min-width="55"
|
||||
align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号"
|
||||
width="60" />
|
||||
min-width="60" />
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'dict_label')?.show" key="dict_label" label="标签"
|
||||
prop="dict_label" min-width="80" show-overflow-tooltip />
|
||||
prop="dict_label" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'status')?.show" key="status" label="状态"
|
||||
prop="status" min-width="60" show-overflow-tooltip>
|
||||
prop="status" min-width="100" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === true ? 'success' : 'danger'">
|
||||
{{ scope.row.status ? "启用" : "停用" }}
|
||||
@@ -140,36 +122,42 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'dict_value')?.show" key="dict_value" label="值"
|
||||
prop="dict_value" min-width="60" show-overflow-tooltip />
|
||||
prop="dict_value" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'css_class')?.show" key="css_class" label="样式属性"
|
||||
prop="css_class" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'list_class')?.show" key="list_class" label="列表类样式"
|
||||
prop="list_class" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'dict_sort')?.show" key="dict_sort" label="排序"
|
||||
prop="dict_sort" min-width="40" />
|
||||
prop="dict_sort" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'is_default')?.show" key="is_default" label="是否默认"
|
||||
prop="is_default" min-width="60">
|
||||
prop="is_default" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.is_default" type="success">是</el-tag>
|
||||
<el-tag v-else type="danger">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" key="description" label="描述"
|
||||
prop="description" min-width="80" />
|
||||
prop="description" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'creator')?.show" label="创建人" prop="creator"
|
||||
min-width="100">
|
||||
<template #default="scope">
|
||||
{{ scope.row.creator?.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" key="created_at" label="创建时间"
|
||||
prop="created_at" min-width="120" sortable />
|
||||
prop="created_at" min-width="200" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" key="updated_at" label="更新时间"
|
||||
prop="updated_at" min-width="120" sortable />
|
||||
prop="updated_at" min-width="200" sortable />
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作"
|
||||
min-width="120">
|
||||
min-width="240">
|
||||
<template #default="scope">
|
||||
<el-button type="info" size="small" link icon="document"
|
||||
@click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
|
||||
<el-button type="primary" size="small" link icon="edit"
|
||||
@click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete"
|
||||
@click="handleOperation('delete', scope.row.id)">删除</el-button>
|
||||
@click="handleDelete([scope.row.id])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -222,7 +210,7 @@
|
||||
<el-input v-model="formData.css_class" placeholder="请输入样式属性" :maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="列表类样式" prop="list_class">
|
||||
<el-select v-model="formData.list_class" placeholder="请选择列表类样式" clearable style="width: 160px">
|
||||
<el-select v-model="formData.list_class" placeholder="请选择列表类样式" clearable>
|
||||
<el-option value="default" label="默认(default)" />
|
||||
<el-option value="primary" label="主要(primary)" />
|
||||
<el-option value="success" label="成功(success)" />
|
||||
@@ -262,14 +250,11 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: "DictData",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
|
||||
import DictAPI, { DictDataTable, DictDataForm, DictDataPageQuery } from "@/api/system/dict";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
@@ -284,6 +269,7 @@ const loading = ref(false);
|
||||
|
||||
const isExpand = ref(false);
|
||||
const isExpandable = ref(true);
|
||||
const drawerVisible = ref<boolean>(false);
|
||||
|
||||
// 分页表单
|
||||
const pageTableData = ref<DictDataTable[]>([]);
|
||||
@@ -302,6 +288,7 @@ const tableColumns = ref([
|
||||
{ prop: 'is_default', label: '是否默认', show: true },
|
||||
{ prop: 'status', label: '状态', show: true },
|
||||
{ prop: 'description', label: '描述', show: true },
|
||||
{ prop: 'creator', label: '创建人', show: true },
|
||||
{ prop: 'created_at', label: '创建时间', show: true },
|
||||
{ prop: 'updated_at', label: '更新时间', show: true },
|
||||
{ prop: 'operation', label: '操作', show: true }
|
||||
@@ -436,7 +423,7 @@ async function handleSubmit() {
|
||||
const id = formData.id;
|
||||
if (id) {
|
||||
try {
|
||||
await DictAPI.updateDictData(formData)
|
||||
await DictAPI.updateDictData({ id, ...formData })
|
||||
dialogVisible.visible = false;
|
||||
resetForm();
|
||||
handleResetQuery();
|
||||
@@ -461,9 +448,9 @@ async function handleSubmit() {
|
||||
});
|
||||
}
|
||||
|
||||
// 删除、导入、导出
|
||||
async function handleOperation(type: 'delete' | 'import' | 'export', id?: number) {
|
||||
if (type === 'delete' && !id && !selectIds.value.length) {
|
||||
// 删除、批量删除
|
||||
async function handleDelete(ids: number[]) {
|
||||
if (!ids && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
@@ -471,8 +458,7 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await DictAPI.deleteDictData({ id: id ? id : selectIds.value });
|
||||
ElMessage.success("删除成功");
|
||||
await DictAPI.deleteDictData({ ids });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -483,89 +469,63 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
ElMessage.info('已取消删除');
|
||||
});
|
||||
}
|
||||
else if (type === 'import') {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.xlsx, .xls';
|
||||
input.click();
|
||||
}
|
||||
|
||||
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 DictAPI.uploadFile(formData);
|
||||
ElMessage.success('导入成功');
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
// 导出
|
||||
async function handleExport() {
|
||||
ElMessageBox.confirm('是否确认导出当前系统配置?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).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 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();
|
||||
} 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;
|
||||
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 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();
|
||||
ElMessage.success('导出成功');
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
else {
|
||||
ElMessage.error('未知操作类型');
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
|
||||
// 批量启用/停用
|
||||
async function handleMoreClick(id: number) {
|
||||
if (id && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除启用或停用该项数据?", "警告", {
|
||||
async function handleMoreClick(status: boolean) {
|
||||
if (selectIds.value.length) {
|
||||
ElMessageBox.confirm(`确认${status ? '启用' : '停用'}该项数据?`, "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await DictAPI.batchUpdateDictData({ id: id ? id : selectIds.value });
|
||||
await DictAPI.batchAvailableDictData({ ids: selectIds.value, status });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -578,6 +538,17 @@ async function handleMoreClick(id: number) {
|
||||
}
|
||||
}
|
||||
|
||||
// 监听抽屉显示状态,设置字典类型过滤
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(visible) => {
|
||||
if (visible && props.dictType) {
|
||||
queryFormData.dict_type = props.dictType;
|
||||
loadingData();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
loadingData();
|
||||
});
|
||||
@@ -11,7 +11,7 @@
|
||||
<el-input v-model="queryFormData.dict_type" placeholder="请输入字典类型" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="状态">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable style="width: 160px">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable>
|
||||
<el-option value="true" label="启用" />
|
||||
<el-option value="false" label="停用" />
|
||||
</el-select>
|
||||
@@ -61,38 +61,22 @@
|
||||
<div class="data-table__toolbar--actions">
|
||||
<el-button type="success" icon="plus" @click="handleOpenDialog('create')">新增</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0"
|
||||
@click="handleOperation('delete')">批量删除</el-button>
|
||||
<el-dropdown>
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">更多
|
||||
@click="handleDelete(selectIds)">批量删除</el-button>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">
|
||||
更多
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-menu @click="handleMoreClick">
|
||||
<el-menu-item key="1">
|
||||
<span>
|
||||
<el-icon>
|
||||
<Check />
|
||||
</el-icon>
|
||||
<span>批量启用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item key="2">
|
||||
<span>
|
||||
<el-icon>
|
||||
<CircleClose />
|
||||
</el-icon>
|
||||
<span>批量停用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Check" @click="handleMoreClick(true)">批量启用</el-dropdown-item>
|
||||
<el-dropdown-item icon="CircleClose" @click="handleMoreClick(false)">批量停用</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="data-table__toolbar--tools">
|
||||
<el-tooltip content="导入">
|
||||
<el-button type="info" icon="upload" circle @click="handleOperation('import')" />
|
||||
</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="primary" icon="refresh" circle @click="handleRefresh" />
|
||||
@@ -118,19 +102,14 @@
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" width="55"
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" min-width="55"
|
||||
align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号"
|
||||
width="60" />
|
||||
min-width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'dict_name')?.show" key="dict_name" label="字典名称"
|
||||
prop="dict_name" min-width="80" />
|
||||
prop="dict_name" min-width="120" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'dict_type')?.show" key="dict_type" label="字典类型"
|
||||
prop="dict_type" min-width="120">
|
||||
<template #default="scope">
|
||||
<router-link :to="'/system/dict_data?dict_type=' + scope.row.dict_type">
|
||||
<el-tag type="primary">{{ scope.row.dict_type }}</el-tag>
|
||||
</router-link>
|
||||
</template>
|
||||
prop="dict_type" min-width="160">
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'status')?.show" key="status" label="状态"
|
||||
prop="status" min-width="60">
|
||||
@@ -141,21 +120,29 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" key="description" label="描述"
|
||||
prop="description" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" key="created_at" label="创建时间"
|
||||
prop="created_at" min-width="120" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" key="updated_at" label="更新时间"
|
||||
prop="updated_at" min-width="120" sortable />
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作"
|
||||
prop="description" min-width="140" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'creator')?.show" label="创建人" prop="creator"
|
||||
min-width="120">
|
||||
<template #default="scope">
|
||||
{{ scope.row.creator?.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" key="created_at" label="创建时间"
|
||||
prop="created_at" min-width="200" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" key="updated_at" label="更新时间"
|
||||
prop="updated_at" min-width="200" sortable />
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作"
|
||||
min-width="260">
|
||||
<template #default="scope">
|
||||
<el-button type="warning" size="small" link icon="document"
|
||||
@click="handleDictDataDrawer(scope.row.dict_type)">字典</el-button>
|
||||
<el-button type="info" size="small" link icon="document"
|
||||
@click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
|
||||
<el-button type="primary" size="small" link icon="edit"
|
||||
@click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete"
|
||||
@click="handleOperation('delete', scope.row.id)">删除</el-button>
|
||||
@click="handleDelete([scope.row.id])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -218,6 +205,8 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<DataDrawer v-if="drawerVisible" v-model="drawerVisible" :dict-type="currentDictType" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -230,6 +219,7 @@ defineOptions({
|
||||
import DictAPI, { DictTable, DictForm, DictPageQuery } from "@/api/system/dict";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
import DataDrawer from "@/views/system/dict/components/DataDrawer.vue"
|
||||
|
||||
const queryFormRef = ref();
|
||||
const dataFormRef = ref();
|
||||
@@ -252,6 +242,7 @@ const tableColumns = ref([
|
||||
{ prop: 'dict_type', label: '字典类型', show: true },
|
||||
{ prop: 'status', label: '状态', show: true },
|
||||
{ prop: 'description', label: '描述', show: true },
|
||||
{ prop: 'creator', label: '创建人', show: true },
|
||||
{ prop: 'created_at', label: '创建时间', show: true },
|
||||
{ prop: 'updated_at', label: '更新时间', show: true },
|
||||
{ prop: 'operation', label: '操作', show: true }
|
||||
@@ -300,6 +291,13 @@ const handleRefresh = useDebounceFn(() => {
|
||||
ElMessage.success("刷新成功");
|
||||
}, 1000);
|
||||
|
||||
|
||||
// 抽屉显隐
|
||||
const drawerVisible = ref(false);
|
||||
|
||||
// 添加字典类型变量
|
||||
const currentDictType = ref('');
|
||||
|
||||
// 加载表格数据
|
||||
async function loadingData() {
|
||||
loading.value = true;
|
||||
@@ -376,7 +374,7 @@ async function handleSubmit() {
|
||||
const id = formData.id;
|
||||
if (id) {
|
||||
try {
|
||||
await DictAPI.updateDictType(formData)
|
||||
await DictAPI.updateDictType({ id, ...formData })
|
||||
dialogVisible.visible = false;
|
||||
resetForm();
|
||||
handleResetQuery();
|
||||
@@ -401,9 +399,9 @@ async function handleSubmit() {
|
||||
});
|
||||
}
|
||||
|
||||
// 删除、导入、导出
|
||||
async function handleOperation(type: 'delete' | 'import' | 'export', id?: number) {
|
||||
if (type === 'delete' && !id && !selectIds.value.length) {
|
||||
// 删除、批量删除
|
||||
async function handleDelete(ids: number[]) {
|
||||
if (!ids && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
@@ -411,8 +409,7 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await DictAPI.deleteDictType({ id: id ? id : selectIds.value });
|
||||
ElMessage.success("删除成功");
|
||||
await DictAPI.deleteDictType({ ids });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -423,89 +420,63 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
ElMessage.info('已取消删除');
|
||||
});
|
||||
}
|
||||
else if (type === 'import') {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.xlsx, .xls';
|
||||
input.click();
|
||||
}
|
||||
|
||||
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 DictAPI.uploadFile(formData);
|
||||
ElMessage.success('导入成功');
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
// 导出
|
||||
async function handleExport() {
|
||||
ElMessageBox.confirm('是否确认导出当前系统配置?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).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 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();
|
||||
} 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;
|
||||
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 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();
|
||||
ElMessage.success('导出成功');
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
else {
|
||||
ElMessage.error('未知操作类型');
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
|
||||
// 批量启用/停用
|
||||
async function handleMoreClick(id: number) {
|
||||
if (id && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除启用或停用该项数据?", "警告", {
|
||||
async function handleMoreClick(status: boolean) {
|
||||
if (selectIds.value.length) {
|
||||
ElMessageBox.confirm(`确认${status ? '启用' : '停用'}该项数据?`, "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await DictAPI.batchAvailableDictType({ id: id ? id : selectIds.value });
|
||||
await DictAPI.batchAvailableDict({ ids: selectIds.value, status });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -518,6 +489,11 @@ async function handleMoreClick(id: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleDictDataDrawer(dictType: string) {
|
||||
currentDictType.value = dictType;
|
||||
drawerVisible.value = true;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadingData();
|
||||
});
|
||||
|
||||
@@ -54,15 +54,27 @@
|
||||
<div class="data-table__toolbar">
|
||||
<div class="data-table__toolbar--actions">
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0"
|
||||
@click="handleOperation('delete')">批量删除</el-button>
|
||||
@click="handleDelete(selectIds)">批量删除</el-button>
|
||||
</div>
|
||||
<div class="data-table__toolbar--tools">
|
||||
<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="primary" icon="refresh" circle @click="handleRefresh" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="列表筛选">
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" icon="operation" circle />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="column in tableColumns" :key="column.prop" :command="column">
|
||||
<el-checkbox v-model="column.show">{{ column.label }}</el-checkbox>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -72,44 +84,57 @@
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column prop='selection' type="selection" width="55" align="center" />
|
||||
<el-table-column type="index" fixed label="序号" width="60" />
|
||||
<el-table-column label="请求路径" prop="request_path" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="请求方法" prop="request_method" min-width="80">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" prop='selection'
|
||||
type="selection" min-width="55" align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号"
|
||||
min-width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'request_path')?.show" label="请求路径"
|
||||
prop="request_path" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'request_method')?.show" label="请求方法"
|
||||
prop="request_method" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getMethodType(scope.row.request_method)">
|
||||
{{ scope.row.request_method }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态码" prop="response_code" min-width="60">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'response_code')?.show" label="状态码"
|
||||
prop="response_code" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusCodeType(scope.row.response_code)">
|
||||
{{ scope.row.response_code }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="请求IP" min-width="90">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'request_ip')?.show" label="请求IP" prop="request_ip"
|
||||
min-width="150">
|
||||
<template #default="scope">
|
||||
<el-text>{{ scope.row.request_ip }}</el-text>
|
||||
<CopyButton v-if="scope.row.request_ip" :text="scope.row.request_ip" style="margin-left: 2px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="浏览器" prop="request_browser" min-width="60" />
|
||||
<el-table-column label="系统" prop="request_os" min-width="80" />
|
||||
<el-table-column label="描述" prop="description" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="创建时间" prop="created_at" min-width="120" sortable />
|
||||
<el-table-column label="创建人" min-width="100">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'process_time')?.show" label="处理时间"
|
||||
prop="process_time" min-width="200" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'request_browser')?.show" label="浏览器"
|
||||
prop="request_browser" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'request_os')?.show" label="系统" prop="request_os"
|
||||
min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" label="描述" prop="description"
|
||||
min-width="120" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" label="创建时间" prop="created_at"
|
||||
min-width="200" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'creator')?.show" label="创建人" prop="creator"
|
||||
min-width="120">
|
||||
<template #default="scope">
|
||||
{{ scope.row.creator?.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" min-width="100">
|
||||
<el-table-column label="操作" fixed="right" min-width="150">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" size="small" link icon="document"
|
||||
@click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete"
|
||||
@click="handleOperation('delete', scope.row.id)">删除</el-button>
|
||||
@click="handleDelete([scope.row.id])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -143,9 +168,9 @@
|
||||
class="long-text-editor" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="响应数据" :span="4">
|
||||
<el-input v-model="detailFormData.response_json" type="textarea" :rows="5" readonly
|
||||
class="long-text-editor" />
|
||||
<!-- <el-scrollbar max-height="72vh">
|
||||
<!-- <el-input v-model="detailFormData.response_json" type="textarea" :rows="5" readonly
|
||||
class="long-text-editor" /> -->
|
||||
<el-scrollbar max-height="72vh">
|
||||
<div class="absolute z-36 right-5 top-2">
|
||||
<el-link type="primary" @click="handleCopyCode">
|
||||
<el-icon>
|
||||
@@ -153,19 +178,11 @@
|
||||
</el-icon>
|
||||
一键复制
|
||||
</el-link>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<!-- <Codemirror
|
||||
ref="cmRef"
|
||||
v-model:value="code"
|
||||
placeholder="请输入代码"
|
||||
:options="cmOptions"
|
||||
border
|
||||
:readonly="true"
|
||||
:width="700"
|
||||
:height="100"
|
||||
/> -->
|
||||
<!-- </el-scrollbar> -->
|
||||
<Codemirror ref="cmRef" v-model:value="code" placeholder="请输入代码" :options="cmOptions" border
|
||||
:readonly="true" :width="700" :height="100" />
|
||||
</el-scrollbar>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="处理时间" :span="2">{{ detailFormData.process_time }}</el-descriptions-item>
|
||||
<el-descriptions-item label="浏览器" :span="2">{{ detailFormData.request_browser }}</el-descriptions-item>
|
||||
@@ -210,13 +227,31 @@ const dataFormRef = ref();
|
||||
const total = ref(0);
|
||||
const selectIds = ref<number[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const isExpand = ref(false);
|
||||
const isExpandable = ref(true);
|
||||
|
||||
// 分页表单
|
||||
const pageTableData = ref<LogTable[]>([]);
|
||||
|
||||
// 表格列配置
|
||||
const tableColumns = ref([
|
||||
{ prop: 'selection', label: '选择框', show: true },
|
||||
{ prop: 'index', label: '序号', show: true },
|
||||
{ prop: 'request_path', label: '请求路径', show: true },
|
||||
{ prop: 'request_method', label: '请求方法', show: true },
|
||||
{ prop: 'response_code', label: '响应状态码', show: true },
|
||||
{ prop: 'request_ip', label: '请求IP', show: true },
|
||||
{ prop: 'process_time', label: '处理时间', show: true },
|
||||
{ prop: 'request_browser', label: '浏览器', show: true },
|
||||
{ prop: 'request_os', label: '操作系统', show: true },
|
||||
{ prop: 'login_location', label: '登录地点', show: true },
|
||||
{ prop: 'description', label: '描述', show: true },
|
||||
{ prop: 'creator', label: '创建人', show: true },
|
||||
{ prop: 'created_at', label: '创建时间', show: true },
|
||||
{ prop: 'updated_at', label: '更新时间', show: true },
|
||||
{ prop: 'operation', label: '操作', show: true }
|
||||
])
|
||||
|
||||
// 详情表单
|
||||
const detailFormData = ref<LogTable>({});
|
||||
|
||||
@@ -237,14 +272,12 @@ const dialogVisible = reactive({
|
||||
type: 'create' as 'create' | 'update' | 'detail',
|
||||
});
|
||||
|
||||
|
||||
// 刷新数据(防抖)
|
||||
const handleRefresh = useDebounceFn(() => {
|
||||
loadingData();
|
||||
ElMessage.success("刷新成功");
|
||||
}, 1000);
|
||||
|
||||
|
||||
const getStatusCodeType = (code?: number) => {
|
||||
if (code === undefined) {
|
||||
return 'info';
|
||||
@@ -324,8 +357,8 @@ async function handleCloseDialog() {
|
||||
resetForm();
|
||||
}
|
||||
|
||||
// 打开系统配置弹窗
|
||||
async function handleOpenDialog(type: 'create' | 'update' | 'detail', id?: number) {
|
||||
// 打开日志详情弹窗
|
||||
async function handleOpenDialog(type: 'create' | 'update' | 'detail', id: number) {
|
||||
dialogVisible.type = type;
|
||||
if (id) {
|
||||
const response = await LogAPI.getLogDetail({ id });
|
||||
@@ -338,10 +371,9 @@ async function handleOpenDialog(type: 'create' | 'update' | 'detail', id?: numbe
|
||||
dialogVisible.visible = true;
|
||||
}
|
||||
|
||||
// 删除、导出
|
||||
async function handleOperation(type: 'delete' | 'export', id?: number) {
|
||||
|
||||
if (type === 'delete' && !id && !selectIds.value.length) {
|
||||
// 删除、批量删除
|
||||
async function handleDelete(ids: number[]) {
|
||||
if (!ids && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
@@ -349,8 +381,7 @@ async function handleOperation(type: 'delete' | 'export', id?: number) {
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await LogAPI.deleteLog({ id: id ? id : selectIds.value });
|
||||
ElMessage.success("删除成功");
|
||||
await LogAPI.deleteLog({ ids });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -361,57 +392,54 @@ async function handleOperation(type: 'delete' | 'export', id?: number) {
|
||||
ElMessage.info('已取消删除');
|
||||
});
|
||||
}
|
||||
else if (type === 'export') {
|
||||
ElMessageBox.confirm('是否确认导出日志?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).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 contentDisposition = response.headers['content-disposition'];
|
||||
let fileName = '系统配置.xlsx';
|
||||
if (contentDisposition) {
|
||||
const fileNameMatch = contentDisposition.match(/filename=(.*?)(;|$)/);
|
||||
if (fileNameMatch) {
|
||||
fileName = decodeURIComponent(fileNameMatch[1]);
|
||||
}
|
||||
// 导出
|
||||
async function handleExport() {
|
||||
ElMessageBox.confirm('是否确认导出当前系统配置?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).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 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();
|
||||
ElMessage.success('导出成功');
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
else {
|
||||
ElMessage.error('未知操作类型');
|
||||
}
|
||||
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) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
|
||||
const cmOptions: EditorConfiguration = {
|
||||
// mode: "text/javascript",
|
||||
mode: "text/json",
|
||||
mode: "text/javascript",
|
||||
};
|
||||
const code = ref();
|
||||
const cmRef = ref<CmComponentRef>();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-input v-model="queryFormData.name" placeholder="请输入标题" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="状态">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable style="width: 160px">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable>
|
||||
<el-option value="true" label="启用" />
|
||||
<el-option value="false" label="停用" />
|
||||
</el-select>
|
||||
@@ -45,10 +45,10 @@
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>
|
||||
<el-tooltip content="公告通知管理维护系统的公告和通知。">
|
||||
<el-tooltip content="菜单管理系统的菜单和权限。">
|
||||
<QuestionFilled class="w-4 h-4 mx-1" />
|
||||
</el-tooltip>
|
||||
公告通知列表
|
||||
菜单列表
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -58,53 +58,22 @@
|
||||
<div class="data-table__toolbar--actions">
|
||||
<el-button type="success" icon="plus" @click="handleOpenDialog('create')">新增</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0"
|
||||
@click="handleOperation('delete')">批量删除</el-button>
|
||||
<el-dropdown>
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">更多
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-menu @click="handleMoreClick">
|
||||
<el-menu-item key="1">
|
||||
<span>
|
||||
<el-icon>
|
||||
<Check />
|
||||
</el-icon>
|
||||
<span>批量启用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item key="2">
|
||||
<span>
|
||||
<el-icon>
|
||||
<CircleClose />
|
||||
</el-icon>
|
||||
<span>批量停用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@click="handleDelete(selectIds)">批量删除</el-button>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">
|
||||
更多
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Check" @click="handleMoreClick(true)">批量启用</el-dropdown-item>
|
||||
<el-dropdown-item icon="CircleClose" @click="handleMoreClick(false)">批量停用</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="data-table__toolbar--tools">
|
||||
<el-tooltip content="导入">
|
||||
<el-button type="info" icon="upload" circle @click="handleOperation('import')" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="导出">
|
||||
<el-button type="warning" icon="download" circle @click="handleOperation('export')" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="刷新">
|
||||
<el-button type="primary" icon="refresh" circle @click="handleRefresh" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="列表筛选">
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" icon="operation" circle />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="column in tableColumns" :key="column.prop" :command="column">
|
||||
<el-checkbox v-model="column.show">{{ column.label }}</el-checkbox>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-button type="default" icon="refresh" circle @click="handleRefresh" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -118,19 +87,15 @@
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" width="55"
|
||||
align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号"
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column type="index" fixed label="序号"
|
||||
width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'name')?.show" key="name" label="菜单名称" prop="name"
|
||||
min-width="120">
|
||||
<el-table-column label="菜单名称" prop="name" min-width="120">
|
||||
<template #default="scope">
|
||||
|
||||
{{ scope.row.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'icon')?.show" key="icon" label="图标" prop="icon"
|
||||
min-width="80" align="center">
|
||||
<el-table-column label="图标" prop="icon" min-width="80" align="center">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.icon && scope.row.icon.startsWith('el-icon')">
|
||||
<el-icon style="vertical-align: -0.15em">
|
||||
@@ -142,16 +107,14 @@
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'status')?.show" key="status" label="状态"
|
||||
prop="status" min-width="80" align="center">
|
||||
<el-table-column label="状态" prop="status" min-width="80" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === true ? 'success' : 'danger'">
|
||||
{{ scope.row.status ? "启用" : "停用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'type')?.show" key="type" label="类型" prop="type"
|
||||
min-width="80" align="center">
|
||||
<el-table-column label="类型" prop="type" min-width="80" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.type === MenuTypeEnum.CATALOG" type="warning">目录</el-tag>
|
||||
<el-tag v-if="scope.row.type === MenuTypeEnum.MENU" type="success">菜单</el-tag>
|
||||
@@ -159,46 +122,33 @@
|
||||
<el-tag v-if="scope.row.type === MenuTypeEnum.EXTLINK" type="info">外链</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'order')?.show" key="order" label="排序" prop="order"
|
||||
min-width="80" align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'permission')?.show" key="permission" label="权限标识"
|
||||
prop="permission" min-width="100" align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'router_name')?.show" key="router_name"
|
||||
label="路由名称" prop="router_name" min-width="80" align="left" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'route_path')?.show" key="route_path" label="路由路径"
|
||||
prop="router_name" min-width="80" align="left" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'component_path')?.show" key="component_path"
|
||||
label="组件路径" prop="component_path" min-width="80" align="left" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'redirect')?.show" key="redirect" label="重定向"
|
||||
prop="redirect" min-width="80" align="left" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'keep_alive')?.show" key="keep_alive" label="是否缓存"
|
||||
prop="keep_alive" min-width="80" align="center">
|
||||
<el-table-column label="排序" prop="order" min-width="80" />
|
||||
<el-table-column label="权限标识" prop="permission" min-width="100" />
|
||||
<el-table-column label="路由名称" prop="router_name" min-width="100" />
|
||||
<el-table-column label="路由路径" prop="router_name" min-width="100" />
|
||||
<el-table-column label="组件路径" prop="component_path" min-width="100" />
|
||||
<el-table-column label="重定向" prop="redirect" min-width="100" />
|
||||
<el-table-column label="是否缓存" prop="keep_alive" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.keep_alive" type="success">是</el-tag>
|
||||
<el-tag v-else type="danger">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'hidden')?.show" key="hidden" label="是否隐藏"
|
||||
prop="hidden" min-width="80" align="center">
|
||||
<el-table-column label="是否隐藏" prop="hidden" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.hidden" type="success">是</el-tag>
|
||||
<el-tag v-else type="danger">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'always_show')?.show" key="always_show"
|
||||
label="显示根路由" prop="always_show" min-width="80" align="center">
|
||||
<el-table-column label="显示根路由" prop="always_show" min-width="120">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.always_show" type="success">是</el-tag>
|
||||
<el-tag v-else type="danger">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'title')?.show" key="title" label="菜单标题"
|
||||
prop="title" min-width="80" align="left" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'params')?.show" key="params" label="路由参数"
|
||||
prop="params" min-width="80" align="left" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'affix')?.show" key="affix" label="固定路由"
|
||||
prop="affix" min-width="80" align="center">
|
||||
<el-table-column label="菜单标题" prop="title" min-width="100"/>
|
||||
<el-table-column label="路由参数" prop="params" min-width="100" />
|
||||
<el-table-column label="固定路由" prop="affix" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.affix ? 'success' : 'danger'">
|
||||
{{ scope.row.affix ? '是' : '否' }}
|
||||
@@ -206,15 +156,11 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" key="description" label="描述"
|
||||
prop="description" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" key="created_at" label="创建时间"
|
||||
prop="created_at" min-width="120" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" key="updated_at" label="更新时间"
|
||||
prop="updated_at" min-width="120" sortable />
|
||||
<el-table-column label="描述" prop="description" min-width="100" />
|
||||
<el-table-column label="创建时间" prop="created_at" min-width="200" sortable />
|
||||
<el-table-column label="更新时间" prop="updated_at" min-width="200" sortable />
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" align="center"
|
||||
label="操作" min-width="220">
|
||||
<el-table-column fixed="right" align="center" label="操作" min-width="260">
|
||||
<template #default="scope">
|
||||
<el-button v-if="scope.row.type == MenuTypeEnum.CATALOG || scope.row.type == MenuTypeEnum.MENU"
|
||||
type="primary" link size="small" icon="plus" @click.stop="handleOpenDialog(scope.row.id)">
|
||||
@@ -226,15 +172,15 @@
|
||||
<el-button type="primary" size="small" link icon="edit"
|
||||
@click.stop="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete"
|
||||
@click.stop="handleOperation('delete', scope.row.id)">删除</el-button>
|
||||
@click.stop="handleDelete([scope.row.id])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<template #footer>
|
||||
<pagination v-if="total > 0" v-model:total="total" v-model:page="queryFormData.page_no" v-model:limit="queryFormData.page_size"
|
||||
@pagination="loadingData" />
|
||||
<pagination v-if="total > 0" v-model:total="total" v-model:page="queryFormData.page_no"
|
||||
v-model:limit="queryFormData.page_size" @pagination="loadingData" />
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
@@ -518,34 +464,6 @@ const isExpandable = ref(true);
|
||||
// 分页表单
|
||||
const pageTableData = ref<MenuTable[]>([]);
|
||||
|
||||
// 表格列配置
|
||||
const tableColumns = ref([
|
||||
{ prop: 'selection', label: '选择框', show: true },
|
||||
{ prop: 'index', label: '序号', show: true },
|
||||
{ prop: 'name', label: '标题', show: true },
|
||||
{ prop: 'type', label: '类型', show: true },
|
||||
{ prop: 'icon', label: '图标', show: true },
|
||||
{ prop: 'order', label: '排序', show: true },
|
||||
{ prop: 'permission', label: '权限标识', show: true },
|
||||
{ prop: 'route_name', label: '路由名称', show: true },
|
||||
{ prop: 'route_path', label: '路由路径', show: true },
|
||||
{ prop: 'component_path', label: '组件路径', show: true },
|
||||
{ prop: 'redirect', label: '跳转路由', show: true },
|
||||
{ prop: 'parent_id', label: '父级菜单', show: true },
|
||||
{ prop: 'parent_name', label: '父级菜单名称', show: true },
|
||||
{ prop: 'keep_alive', label: '缓存页面', show: true },
|
||||
{ prop: 'hidden', label: '显示状态', show: true },
|
||||
{ prop: 'always_show', label: '始终显示', show: true },
|
||||
{ prop: 'title', label: '菜单标题', show: true },
|
||||
{ prop: 'params', label: '路由参数', show: true },
|
||||
{ prop: 'affix', label: '固定路由', show: true },
|
||||
{ prop: 'status', label: '状态', show: true },
|
||||
{ prop: 'description', label: '描述', show: true },
|
||||
{ prop: 'created_at', label: '创建时间', show: true },
|
||||
{ prop: 'updated_at', label: '更新时间', show: true },
|
||||
{ prop: 'operation', label: '操作', show: true }
|
||||
])
|
||||
|
||||
// 详情表单
|
||||
const detailFormData = ref<MenuTable>({});
|
||||
|
||||
@@ -736,9 +654,9 @@ async function handleSubmit() {
|
||||
});
|
||||
}
|
||||
|
||||
// 删除、导入、导出
|
||||
async function handleOperation(type: 'delete' | 'import' | 'export', id?: number) {
|
||||
if (type === 'delete' && !id && !selectIds.value.length) {
|
||||
// 删除、批量删除
|
||||
async function handleDelete(ids: number[]) {
|
||||
if (!ids && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
@@ -746,8 +664,7 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await MenuAPI.deleteMenu({ id: id ? id : selectIds.value });
|
||||
ElMessage.success("删除成功");
|
||||
await DictAPI.deleteDictType({ ids });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -758,76 +675,29 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
ElMessage.info('已取消删除');
|
||||
});
|
||||
}
|
||||
else if (type === 'import') {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.xlsx, .xls';
|
||||
input.click();
|
||||
}
|
||||
|
||||
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 MenuAPI.uploadFile(formData);
|
||||
ElMessage.success('导入成功');
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type === 'export') {
|
||||
ElMessageBox.confirm('是否确认导出当前系统配置?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
// 批量启用/停用
|
||||
async function handleMoreClick(status: boolean) {
|
||||
if (selectIds.value.length) {
|
||||
ElMessageBox.confirm(`确认${status ? '启用' : '停用'}该项数据?`, "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const body = {
|
||||
...queryFormData,
|
||||
page_no: 1,
|
||||
page_size: total.value
|
||||
};
|
||||
ElMessage.warning('正在导出数据,请稍候...');
|
||||
|
||||
const response = await MenuAPI.exportMenu(body);
|
||||
const blob = new Blob([JSON.stringify(response.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]);
|
||||
}
|
||||
}
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
ElMessage.success('导出成功');
|
||||
await MenuAPI.batchAvailableMenu({ ids: selectIds.value, status });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
ElMessage.info('已取消批量操作');
|
||||
});
|
||||
}
|
||||
else {
|
||||
ElMessage.error('未知操作类型');
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -11,19 +11,24 @@
|
||||
<el-input v-model="queryFormData.notice_type" placeholder="请输入类型" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="状态">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable style="width: 160px">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable>
|
||||
<el-option value="true" label="启用" />
|
||||
<el-option value="false" label="停用" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 时间范围,收起状态下隐藏 -->
|
||||
<el-form-item v-if="isExpand" prop="start_time" label="创建时间">
|
||||
<el-date-picker v-model="queryFormData.start_time" type="daterange" value-format="yyyy-MM-dd" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" />
|
||||
<el-date-picker v-model="queryFormData.start_time" type="daterange" value-format="yyyy-MM-dd"
|
||||
range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" />
|
||||
</el-form-item>
|
||||
<!-- 查询、重置、展开/收起按钮 -->
|
||||
<el-form-item class="search-buttons">
|
||||
<el-button type="primary" icon="search" @click="handleQuery">查询</el-button>
|
||||
<el-button icon="refresh" @click="handleResetQuery">重置</el-button>
|
||||
<el-button type="primary" icon="search" @click="handleQuery">
|
||||
查询
|
||||
</el-button>
|
||||
<el-button icon="refresh" @click="handleResetQuery">
|
||||
重置
|
||||
</el-button>
|
||||
<!-- 展开/收起 -->
|
||||
<template v-if="isExpandable">
|
||||
<el-link class="ml-3" type="primary" underline="never" @click="isExpand = !isExpand">
|
||||
@@ -58,39 +63,27 @@
|
||||
<!-- 功能区域 -->
|
||||
<div class="data-table__toolbar">
|
||||
<div class="data-table__toolbar--actions">
|
||||
<el-button type="success" icon="plus" @click="handleOpenDialog('create')">新增</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0" @click="handleOperation('delete')">批量删除</el-button>
|
||||
<el-dropdown>
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">更多
|
||||
<el-button type="success" icon="plus" @click="handleOpenDialog('create')">
|
||||
新增
|
||||
</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0" @click="handleDelete(selectIds)">
|
||||
批量删除
|
||||
</el-button>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">
|
||||
更多
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-menu @click="handleMoreClick">
|
||||
<el-menu-item key="1">
|
||||
<span>
|
||||
<el-icon>
|
||||
<Check />
|
||||
</el-icon>
|
||||
<span>批量启用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item key="2">
|
||||
<span>
|
||||
<el-icon>
|
||||
<CircleClose />
|
||||
</el-icon>
|
||||
<span>批量停用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Check" @click="handleMoreClick(true)">批量启用</el-dropdown-item>
|
||||
<el-dropdown-item icon="CircleClose" @click="handleMoreClick(false)">批量停用</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="data-table__toolbar--tools">
|
||||
<el-tooltip content="导入">
|
||||
<el-button type="info" icon="upload" circle @click="handleOperation('import')" />
|
||||
</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="primary" icon="refresh" circle @click="handleRefresh" />
|
||||
@@ -101,7 +94,9 @@
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="column in tableColumns" :key="column.prop" :command="column">
|
||||
<el-checkbox v-model="column.show">{{ column.label }}</el-checkbox>
|
||||
<el-checkbox v-model="column.show">
|
||||
{{ column.label }}
|
||||
</el-checkbox>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
@@ -111,53 +106,73 @@
|
||||
</div>
|
||||
|
||||
<!-- 表格区域:系统配置列表 -->
|
||||
<el-table ref="dataTableRef" v-loading="loading" :data="pageTableData" highlight-current-row class="data-table__content" height="450" border stripe @selection-change="handleSelectionChange">
|
||||
<el-table ref="dataTableRef" v-loading="loading" :data="pageTableData" highlight-current-row
|
||||
class="data-table__content" height="450" border stripe @selection-change="handleSelectionChange">
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号" width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'notice_title')?.show" label="通知标题" prop="notice_title" min-width="80" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'status')?.show" label="状态" prop="status" min-width="60">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" min-width="55"
|
||||
align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号"
|
||||
min-width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'notice_title')?.show" label="通知标题"
|
||||
prop="notice_title" min-width="140" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'status')?.show" label="状态" prop="status"
|
||||
min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === true ? 'success' : 'danger'">
|
||||
{{ scope.row.status === true ? "启用" : "停用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'notice_content')?.show" label="通知内容" prop="notice_content" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'notice_type')?.show" label="通知类型" prop="notice_type" min-width="60">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'notice_content')?.show" label="通知内容"
|
||||
prop="notice_content" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'notice_type')?.show" label="通知类型"
|
||||
prop="notice_type" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.notice_type === '1' ? 'primary' : 'warning'">
|
||||
{{ scope.row.notice_type === '1' ? "通知" : "公告" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" label="描述" prop="description" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" label="创建时间" prop="created_at" min-width="120" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" label="更新时间" prop="updated_at" min-width="120" sortable />
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作" min-width="120">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" label="描述" prop="description"
|
||||
min-width="140" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" label="创建时间" prop="created_at"
|
||||
min-width="200" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" label="更新时间" prop="updated_at"
|
||||
min-width="200" sortable />
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作"
|
||||
min-width="200">
|
||||
<template #default="scope">
|
||||
<el-button type="info" size="small" link icon="document" @click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
|
||||
<el-button type="primary" size="small" link icon="edit" @click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete" @click="handleOperation('delete', scope.row.id)">删除</el-button>
|
||||
<el-button type="info" size="small" link icon="document" @click="handleOpenDialog('detail', scope.row.id)">
|
||||
详情
|
||||
</el-button>
|
||||
<el-button type="primary" size="small" link icon="edit" @click="handleOpenDialog('update', scope.row.id)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete" @click="handleDelete([scope.row.id])">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<template #footer>
|
||||
<pagination v-model:total="total" v-model:page="queryFormData.page_no" v-model:limit="queryFormData.page_size" @pagination="loadingData" />
|
||||
<pagination v-model:total="total" v-model:page="queryFormData.page_no" v-model:limit="queryFormData.page_size"
|
||||
@pagination="loadingData" />
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
<!-- 弹窗区域 -->
|
||||
<el-dialog v-model="dialogVisible.visible" :title="dialogVisible.title" top="3vh" @close="handleCloseDialog">
|
||||
<el-dialog v-model="dialogVisible.visible" :title="dialogVisible.title" top="3vh" @close="handleCloseDialog">
|
||||
<!-- 详情 -->
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<el-descriptions :column="4" border>
|
||||
<el-descriptions-item label="标题" :span="2">{{ detailFormData.notice_title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="标题" :span="2">
|
||||
{{ detailFormData.notice_title }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="类型" :span="2">
|
||||
<el-tag :type="detailFormData.notice_type === '1' ? 'primary' : 'warning'">
|
||||
{{ detailFormData.notice_type === '1' ? '通知' : '公告' }}
|
||||
@@ -168,11 +183,21 @@
|
||||
{{ detailFormData.status ? '启用' : '停用' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="内容" :span="2">{{ detailFormData.notice_content }}</el-descriptions-item>
|
||||
<el-descriptions-item label="描述" :span="2">{{ detailFormData.description }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人" :span="2">{{ detailFormData.creator?.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间" :span="2">{{ detailFormData.created_at }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间" :span="2">{{ detailFormData.updated_at }}</el-descriptions-item>
|
||||
<el-descriptions-item label="内容" :span="2">
|
||||
{{ detailFormData.notice_content }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="描述" :span="2">
|
||||
{{ detailFormData.description }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人" :span="2">
|
||||
{{ detailFormData.creator?.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间" :span="2">
|
||||
{{ detailFormData.created_at }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_at }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<!-- 新增、编辑表单 -->
|
||||
@@ -189,8 +214,12 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :value="true">启用</el-radio>
|
||||
<el-radio :value="false">停用</el-radio>
|
||||
<el-radio :value="true">
|
||||
启用
|
||||
</el-radio>
|
||||
<el-radio :value="false">
|
||||
停用
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="内容" prop="notice_content">
|
||||
@@ -206,13 +235,99 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<!-- 详情弹窗不需要确定按钮的提交逻辑 -->
|
||||
<el-button v-if="dialogVisible.type !== 'detail'" type="primary" @click="handleSubmit">确定</el-button>
|
||||
<el-button v-else type="primary" @click="handleCloseDialog">确定</el-button>
|
||||
<el-button @click="handleCloseDialog">取消</el-button>
|
||||
<el-button v-if="dialogVisible.type !== 'detail'" type="primary" @click="handleSubmit">
|
||||
确定
|
||||
</el-button>
|
||||
<el-button v-else type="primary" @click="handleCloseDialog">
|
||||
确定
|
||||
</el-button>
|
||||
<el-button @click="handleCloseDialog">
|
||||
取消
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导出弹窗 -->
|
||||
<el-dialog v-model="exportsModalVisible" :align-center="true" title="导出数据" width="600px" style="padding-right: 0"
|
||||
@close="handleCloseExportsModal">
|
||||
<!-- 滚动 -->
|
||||
<el-scrollbar max-height="60vh">
|
||||
<!-- 表单 -->
|
||||
<el-form ref="exportsFormRef" style="padding-right: var(--el-dialog-padding-primary)" :model="exportsFormData"
|
||||
:rules="exportsFormRules">
|
||||
<el-form-item label="文件名" prop="filename">
|
||||
<el-input v-model="exportsFormData.filename" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="工作表名" prop="sheetname">
|
||||
<el-input v-model="exportsFormData.sheetname" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="数据源" prop="origin">
|
||||
<el-select v-model="exportsFormData.origin">
|
||||
<el-option label="当前数据 (当前页的数据)" :value="ExportsOriginEnum.CURRENT" />
|
||||
<el-option label="选中数据 (所有选中的数据)" :value="ExportsOriginEnum.SELECTED"
|
||||
:disabled="selectionData.length <= 0" />
|
||||
<el-option label="全量数据 (所有分页的数据)" :value="ExportsOriginEnum.REMOTE"
|
||||
:disabled="contentConfig.exportsAction === undefined" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="字段" prop="fields">
|
||||
<el-checkbox-group v-model="exportsFormData.fields">
|
||||
<template v-for="col in cols" :key="col.prop">
|
||||
<el-checkbox v-if="col.prop" :value="col.prop" :label="col.label" />
|
||||
</template>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<div style="padding-right: var(--el-dialog-padding-primary)">
|
||||
<el-button type="primary" @click="handleExportsSubmit">确 定</el-button>
|
||||
<el-button @click="handleCloseExportsModal">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 导入弹窗 -->
|
||||
<el-dialog v-model="importModalVisible" :align-center="true" title="导入数据" width="600px" style="padding-right: 0"
|
||||
@close="handleCloseImportModal">
|
||||
<!-- 滚动 -->
|
||||
<el-scrollbar max-height="60vh">
|
||||
<!-- 表单 -->
|
||||
<el-form ref="importFormRef" style="padding-right: var(--el-dialog-padding-primary)" :model="importFormData"
|
||||
:rules="importFormRules">
|
||||
<el-form-item label="文件名" prop="files">
|
||||
<el-upload ref="uploadRef" v-model:file-list="importFormData.files" class="w-full"
|
||||
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
||||
:drag="true" :limit="1" :auto-upload="false" :on-exceed="handleFileExceed">
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
<span>将文件拖到此处,或</span>
|
||||
<em>点击上传</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
*.xlsx / *.xls
|
||||
<el-link v-if="contentConfig.importTemplate" type="primary" icon="download" underline="never"
|
||||
@click="handleDownloadTemplate">
|
||||
下载模板
|
||||
</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<div style="padding-right: var(--el-dialog-padding-primary)">
|
||||
<el-button type="primary" :disabled="importFormData.files.length === 0" @click="handleImportSubmit">
|
||||
确 定
|
||||
</el-button>
|
||||
<el-button @click="handleCloseImportModal">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -231,14 +346,12 @@ const dataFormRef = ref();
|
||||
const total = ref(0);
|
||||
const selectIds = ref<number[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const isExpand = ref(false);
|
||||
const isExpandable = ref(true);
|
||||
|
||||
// 分页表单
|
||||
const pageTableData = ref<NoticeTable[]>([]);
|
||||
|
||||
|
||||
// 表格列配置
|
||||
const tableColumns = ref([
|
||||
{ prop: 'selection', label: '选择框', show: true },
|
||||
@@ -250,6 +363,7 @@ const tableColumns = ref([
|
||||
{ prop: 'description', label: '描述', show: true },
|
||||
{ prop: 'created_at', label: '创建时间', show: true },
|
||||
{ prop: 'updated_at', label: '更新时间', show: true },
|
||||
{ prop: 'creator', label: '创建人', show: true },
|
||||
{ prop: 'operation', label: '操作', show: true }
|
||||
])
|
||||
|
||||
@@ -374,7 +488,7 @@ async function handleSubmit() {
|
||||
const id = formData.id;
|
||||
if (id) {
|
||||
try {
|
||||
await NoticeAPI.updateNotice(formData)
|
||||
await NoticeAPI.updateNotice({ id, ...formData })
|
||||
dialogVisible.visible = false;
|
||||
resetForm();
|
||||
handleCloseDialog();
|
||||
@@ -401,9 +515,9 @@ async function handleSubmit() {
|
||||
});
|
||||
}
|
||||
|
||||
// 删除、导入、导出
|
||||
async function handleOperation(type: 'delete' | 'import' | 'export', id?: number) {
|
||||
if (type === 'delete' && !id && !selectIds.value.length) {
|
||||
// 删除、批量删除
|
||||
async function handleDelete(ids: number[]) {
|
||||
if (!ids && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
@@ -411,8 +525,7 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await NoticeAPI.deleteNotice({ id: id ? id : selectIds.value });
|
||||
ElMessage.success("删除成功");
|
||||
await NoticeAPI.deleteNotice({ ids });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -423,89 +536,63 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
ElMessage.info('已取消删除');
|
||||
});
|
||||
}
|
||||
else if (type === 'import') {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.xlsx, .xls';
|
||||
input.click();
|
||||
}
|
||||
|
||||
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 NoticeAPI.uploadFile(formData);
|
||||
ElMessage.success('导入成功');
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
// 导出
|
||||
async function handleExport() {
|
||||
ElMessageBox.confirm('是否确认导出当前系统配置?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).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 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();
|
||||
} 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;
|
||||
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 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();
|
||||
ElMessage.success('导出成功');
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
else {
|
||||
ElMessage.error('未知操作类型');
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
|
||||
// 批量启用/停用
|
||||
async function handleMoreClick(id: number) {
|
||||
if (id && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除启用或停用该项数据?", "警告", {
|
||||
async function handleMoreClick(status: boolean) {
|
||||
if (selectIds.value.length) {
|
||||
ElMessageBox.confirm(`确认${status ? '启用' : '停用'}该项数据?`, "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await NoticeAPI.batchAvailableNotice({ id: id ? id : selectIds.value });
|
||||
await NoticeAPI.batchAvailableNotice({ ids: selectIds.value, status });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -518,6 +605,129 @@ async function handleMoreClick(id: number) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// // 导出
|
||||
// function handleExport() {
|
||||
// const filename = exportsFormData.filename
|
||||
// ? exportsFormData.filename
|
||||
// : props.contentConfig.permPrefix || "export";
|
||||
// const sheetname = exportsFormData.sheetname ? exportsFormData.sheetname : "sheet";
|
||||
// const workbook = new ExcelJS.Workbook();
|
||||
// const worksheet = workbook.addWorksheet(sheetname);
|
||||
// const columns: Partial<ExcelJS.Column>[] = [];
|
||||
// cols.value.forEach((col) => {
|
||||
// if (col.label && col.prop && exportsFormData.fields.includes(col.prop)) {
|
||||
// columns.push({ header: col.label, key: col.prop });
|
||||
// }
|
||||
// });
|
||||
// worksheet.columns = columns;
|
||||
// if (exportsFormData.origin === ExportsOriginEnum.REMOTE) {
|
||||
// if (props.contentConfig.exportsAction) {
|
||||
// props.contentConfig.exportsAction(lastFormData).then((res) => {
|
||||
// worksheet.addRows(res);
|
||||
// workbook.xlsx
|
||||
// .writeBuffer()
|
||||
// .then((buffer) => {
|
||||
// saveXlsx(buffer, filename as string);
|
||||
// })
|
||||
// .catch((error) => console.log(error));
|
||||
// });
|
||||
// } else {
|
||||
// ElMessage.error("未配置exportsAction");
|
||||
// }
|
||||
// } else {
|
||||
// worksheet.addRows(
|
||||
// exportsFormData.origin === ExportsOriginEnum.SELECTED ? selectionData.value : pageData.value
|
||||
// );
|
||||
// workbook.xlsx
|
||||
// .writeBuffer()
|
||||
// .then((buffer) => {
|
||||
// saveXlsx(buffer, filename as string);
|
||||
// })
|
||||
// .catch((error) => console.log(error));
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 下载导入模板
|
||||
// function handleDownloadTemplate() {
|
||||
// const importTemplate = props.contentConfig.importTemplate;
|
||||
// if (typeof importTemplate === "string") {
|
||||
// window.open(importTemplate);
|
||||
// } else if (typeof importTemplate === "function") {
|
||||
// importTemplate().then((response) => {
|
||||
// const fileData = response.data;
|
||||
// const fileName = decodeURI(
|
||||
// response.headers["content-disposition"].split(";")[1].split("=")[1]
|
||||
// );
|
||||
// saveXlsx(fileData, fileName);
|
||||
// });
|
||||
// } else {
|
||||
// ElMessage.error("未配置importTemplate");
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 导入
|
||||
// function handleImport() {
|
||||
// const importsAction = props.contentConfig.importsAction;
|
||||
// if (importsAction === undefined) {
|
||||
// ElMessage.error("未配置importsAction");
|
||||
// return;
|
||||
// }
|
||||
// // 获取选择的文件
|
||||
// const file = importFormData.files[0].raw as File;
|
||||
// // 创建Workbook实例
|
||||
// const workbook = new ExcelJS.Workbook();
|
||||
// // 使用FileReader对象来读取文件内容
|
||||
// const fileReader = new FileReader();
|
||||
// // 二进制字符串的形式加载文件
|
||||
// fileReader.readAsArrayBuffer(file);
|
||||
// fileReader.onload = (ev) => {
|
||||
// if (ev.target !== null && ev.target.result !== null) {
|
||||
// const result = ev.target.result as ArrayBuffer;
|
||||
// // 从 buffer中加载数据解析
|
||||
// workbook.xlsx
|
||||
// .load(result)
|
||||
// .then((workbook) => {
|
||||
// // 解析后的数据
|
||||
// const data = [];
|
||||
// // 获取第一个worksheet内容
|
||||
// const worksheet = workbook.getWorksheet(1);
|
||||
// if (worksheet) {
|
||||
// // 获取第一行的标题
|
||||
// const fields: any[] = [];
|
||||
// worksheet.getRow(1).eachCell((cell) => {
|
||||
// fields.push(cell.value);
|
||||
// });
|
||||
// // 遍历工作表的每一行(从第二行开始,因为第一行通常是标题行)
|
||||
// for (let rowNumber = 2; rowNumber <= worksheet.rowCount; rowNumber++) {
|
||||
// const rowData: IObject = {};
|
||||
// const row = worksheet.getRow(rowNumber);
|
||||
// // 遍历当前行的每个单元格
|
||||
// row.eachCell((cell, colNumber) => {
|
||||
// // 获取标题对应的键,并将当前单元格的值存储到相应的属性名中
|
||||
// rowData[fields[colNumber - 1]] = cell.value;
|
||||
// });
|
||||
// // 将当前行的数据对象添加到数组中
|
||||
// data.push(rowData);
|
||||
// }
|
||||
// }
|
||||
// if (data.length === 0) {
|
||||
// ElMessage.error("未解析到数据");
|
||||
// return;
|
||||
// }
|
||||
// importsAction(data).then(() => {
|
||||
// ElMessage.success("导入数据成功");
|
||||
// handleCloseImportModal();
|
||||
// handleRefresh(true);
|
||||
// });
|
||||
// })
|
||||
// .catch((error) => console.log(error));
|
||||
// } else {
|
||||
// ElMessage.error("读取文件失败");
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
onMounted(() => {
|
||||
loadingData();
|
||||
});
|
||||
|
||||
@@ -8,14 +8,15 @@
|
||||
<el-input v-model="queryFormData.name" placeholder="请输入岗位名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="状态">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable style="width: 160px">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable>
|
||||
<el-option value="true" label="启用" />
|
||||
<el-option value="false" label="停用" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 时间范围,收起状态下隐藏 -->
|
||||
<el-form-item v-if="isExpand" prop="start_time" label="创建时间">
|
||||
<el-date-picker v-model="queryFormData.start_time" type="daterange" value-format="yyyy-MM-dd" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" />
|
||||
<el-date-picker v-model="queryFormData.start_time" type="daterange" value-format="yyyy-MM-dd"
|
||||
range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" />
|
||||
</el-form-item>
|
||||
<!-- 查询、重置、展开/收起按钮 -->
|
||||
<el-form-item class="search-buttons">
|
||||
@@ -56,38 +57,23 @@
|
||||
<div class="data-table__toolbar">
|
||||
<div class="data-table__toolbar--actions">
|
||||
<el-button type="success" icon="plus" @click="handleOpenDialog('create')">新增</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0" @click="handleOperation('delete')">批量删除</el-button>
|
||||
<el-dropdown>
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">更多
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0"
|
||||
@click="handleDelete(selectIds)">批量删除</el-button>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">
|
||||
更多
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-menu @click="handleMoreClick">
|
||||
<el-menu-item key="1">
|
||||
<span>
|
||||
<el-icon>
|
||||
<Check />
|
||||
</el-icon>
|
||||
<span>批量启用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item key="2">
|
||||
<span>
|
||||
<el-icon>
|
||||
<CircleClose />
|
||||
</el-icon>
|
||||
<span>批量停用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Check" @click="handleMoreClick(true)">批量启用</el-dropdown-item>
|
||||
<el-dropdown-item icon="CircleClose" @click="handleMoreClick(false)">批量停用</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="data-table__toolbar--tools">
|
||||
<el-tooltip content="导入">
|
||||
<el-button type="info" icon="upload" circle @click="handleOperation('import')" />
|
||||
</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="primary" icon="refresh" circle @click="handleRefresh" />
|
||||
@@ -104,43 +90,55 @@
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-tooltip>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域:系统配置列表 -->
|
||||
<el-table ref="dataTableRef" v-loading="loading" :data="pageTableData" highlight-current-row class="data-table__content" height="450" border stripe @selection-change="handleSelectionChange">
|
||||
<el-table ref="dataTableRef" v-loading="loading" :data="pageTableData" highlight-current-row
|
||||
class="data-table__content" height="450" border stripe @selection-change="handleSelectionChange">
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号" width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'name')?.show" key="name" label="岗位名称" prop="name" min-width="80" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'status')?.show" key="status" label="状态" prop="status" min-width="60">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" width="55"
|
||||
align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号"
|
||||
width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'name')?.show" key="name" label="岗位名称" prop="name"
|
||||
min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'status')?.show" key="status" label="状态"
|
||||
prop="status" min-width="80">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === true ? 'success' : 'danger'">
|
||||
{{ scope.row.status === true ? "启用" : "停用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'order')?.show" key="order" label="岗位排序" prop="order" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" key="description" label="描述" prop="description" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" key="created_at" label="创建时间" prop="created_at" min-width="120" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" key="updated_at" label="更新时间" prop="updated_at" min-width="120" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'order')?.show" key="order" label="岗位排序"
|
||||
prop="order" min-width="80" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" key="description" label="描述"
|
||||
prop="description" min-width="120" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" key="created_at" label="创建时间"
|
||||
prop="created_at" min-width="120" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" key="updated_at" label="更新时间"
|
||||
prop="updated_at" min-width="120" sortable />
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作" min-width="120">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作"
|
||||
min-width="150">
|
||||
<template #default="scope">
|
||||
<el-button type="info" size="small" link icon="document" @click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
|
||||
<el-button type="primary" size="small" link icon="edit" @click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete" @click="handleOperation('delete', scope.row.id)">删除</el-button>
|
||||
<el-button type="info" size="small" link icon="document"
|
||||
@click="handleOpenDialog('detail', scope.row.id)">详情</el-button>
|
||||
<el-button type="primary" size="small" link icon="edit"
|
||||
@click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete"
|
||||
@click="handleDelete([scope.row.id])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<template #footer>
|
||||
<pagination v-model:total="total" v-model:page="queryFormData.page_no" v-model:limit="queryFormData.page_size" @pagination="loadingData" />
|
||||
<pagination v-model:total="total" v-model:page="queryFormData.page_no" v-model:limit="queryFormData.page_size"
|
||||
@pagination="loadingData" />
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
@@ -179,7 +177,8 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input v-model="formData.description" :rows="4" :maxlength="100" show-word-limit type="textarea" placeholder="请输入描述" />
|
||||
<el-input v-model="formData.description" :rows="4" :maxlength="100" show-word-limit type="textarea"
|
||||
placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
@@ -230,6 +229,7 @@ const tableColumns = ref([
|
||||
{ prop: 'description', label: '描述', show: true },
|
||||
{ prop: 'created_at', label: '创建时间', show: true },
|
||||
{ prop: 'updated_at', label: '更新时间', show: true },
|
||||
{ prop: 'creator', label: '创建人', show: true },
|
||||
{ prop: 'operation', label: '操作', show: true }
|
||||
])
|
||||
|
||||
@@ -376,9 +376,9 @@ async function handleSubmit() {
|
||||
});
|
||||
}
|
||||
|
||||
// 删除、导入、导出
|
||||
async function handleOperation(type: 'delete' | 'import' | 'export', id?: number) {
|
||||
if (type === 'delete' && !id && !selectIds.value.length) {
|
||||
// 删除、批量删除
|
||||
async function handleDelete(ids: number[]) {
|
||||
if (!ids && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
@@ -386,8 +386,7 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await PositionAPI.deletePosition({ id: id ? id : selectIds.value });
|
||||
ElMessage.success("删除成功");
|
||||
await PositionAPI.deletePosition({ ids });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -398,89 +397,63 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
ElMessage.info('已取消删除');
|
||||
});
|
||||
}
|
||||
else if (type === 'import') {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.xlsx, .xls';
|
||||
input.click();
|
||||
}
|
||||
|
||||
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 PositionAPI.uploadFile(formData);
|
||||
ElMessage.success('导入成功');
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
// 导出
|
||||
async function handleExport() {
|
||||
ElMessageBox.confirm('是否确认导出当前系统配置?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).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 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();
|
||||
} 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;
|
||||
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 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();
|
||||
ElMessage.success('导出成功');
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
else {
|
||||
ElMessage.error('未知操作类型');
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
|
||||
// 批量启用/停用
|
||||
async function handleMoreClick(id: number) {
|
||||
if (id && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除启用或停用该项数据?", "警告", {
|
||||
async function handleMoreClick(status: boolean) {
|
||||
if (selectIds.value.length) {
|
||||
ElMessageBox.confirm(`确认${status ? '启用' : '停用'}该项数据?`, "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await PositionAPI.batchAvailablePosition({ id: id ? id : selectIds.value });
|
||||
await PositionAPI.batchAvailablePosition({ ids: selectIds.value, status });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-input v-model="queryFormData.name" placeholder="请输入岗位名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="状态">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable style="width: 160px">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable>
|
||||
<el-option value="true" label="启用" />
|
||||
<el-option value="false" label="停用" />
|
||||
</el-select>
|
||||
@@ -58,38 +58,22 @@
|
||||
<div class="data-table__toolbar--actions">
|
||||
<el-button type="success" icon="plus" @click="handleOpenDialog('create')">新增</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0"
|
||||
@click="handleOperation('delete')">批量删除</el-button>
|
||||
<el-dropdown>
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">更多
|
||||
@click="handleDelete(selectIds)">批量删除</el-button>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">
|
||||
更多
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-menu @click="handleMoreClick">
|
||||
<el-menu-item key="1">
|
||||
<span>
|
||||
<el-icon>
|
||||
<Check />
|
||||
</el-icon>
|
||||
<span>批量启用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item key="2">
|
||||
<span>
|
||||
<el-icon>
|
||||
<CircleClose />
|
||||
</el-icon>
|
||||
<span>批量停用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Check" @click="handleMoreClick(true)">批量启用</el-dropdown-item>
|
||||
<el-dropdown-item icon="CircleClose" @click="handleMoreClick(false)">批量停用</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="data-table__toolbar--tools">
|
||||
<el-tooltip content="导入">
|
||||
<el-button type="info" icon="upload" circle @click="handleOperation('import')"/>
|
||||
</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="primary" icon="refresh" circle @click="handleRefresh" />
|
||||
@@ -119,11 +103,11 @@
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号"
|
||||
width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'name')?.show" key="name" label="角色名称" prop="name"
|
||||
min-width="80" />
|
||||
min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'data_scope')?.show" key="data_scope" label="数据权限"
|
||||
prop="data_scope" min-width="80" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'order')?.show" key="order" label="岗位排序"
|
||||
prop="order" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'order')?.show" key="order" label="排序"
|
||||
prop="order" min-width="80" show-overflow-tooltip />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'status')?.show" key="status" label="状态"
|
||||
prop="status" min-width="60">
|
||||
<template #default="scope">
|
||||
@@ -135,12 +119,12 @@
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'description')?.show" key="description" label="描述"
|
||||
prop="description" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" key="created_at" label="创建时间"
|
||||
prop="created_at" min-width="120" sortable />
|
||||
prop="created_at" min-width="200" sortable />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" key="updated_at" label="更新时间"
|
||||
prop="updated_at" min-width="120" sortable />
|
||||
prop="updated_at" min-width="200" sortable />
|
||||
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作"
|
||||
min-width="220">
|
||||
min-width="260">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" size="small" link icon="position"
|
||||
@click="handleOpenAssignPermDialog(scope.row)">分配权限</el-button>
|
||||
@@ -149,21 +133,43 @@
|
||||
<el-button type="primary" size="small" link icon="edit"
|
||||
@click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete"
|
||||
@click="handleOperation('delete', scope.row.id)">删除</el-button>
|
||||
@click="handleDelete([scope.row.id])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<template #footer>
|
||||
<pagination v-model:total="total" v-model:page="queryFormData.page_no"
|
||||
v-model:limit="queryFormData.page_size" @pagination="loadingData" />
|
||||
<pagination v-model:total="total" v-model:page="queryFormData.page_no" v-model:limit="queryFormData.page_size"
|
||||
@pagination="loadingData" />
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
<!-- 角色表单弹窗 -->
|
||||
<el-dialog v-model="dialogVisible.visible" :title="dialogVisible.title" width="500px" @close="handleCloseDialog">
|
||||
<el-form ref="roleFormRef" :model="formData" :rules="rules" label-width="100px">
|
||||
<!-- 详情 -->
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="角色名称" :span="2">{{ detailFormData.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="排序" :span="2">
|
||||
{{ detailFormData.order }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="数据权限" :span="2">
|
||||
{{ detailFormData.data_scope }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="状态" :span="2">
|
||||
<el-tag v-if="detailFormData.status" type="success">启用</el-tag>
|
||||
<el-tag v-else type="danger">停用</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="描述" :span="2">{{ detailFormData.description }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人" :span="2">{{ detailFormData.creator?.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间" :span="2">{{ detailFormData.created_at }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间" :span="2">{{ detailFormData.updated_at }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
<!-- 新增、编辑表单 -->
|
||||
<template v-else>
|
||||
<el-form ref="roleFormRef" :model="formData" :rules="rules" label-width="100px">
|
||||
<el-form-item label="角色名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入角色名称" />
|
||||
</el-form-item>
|
||||
@@ -185,6 +191,9 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
@@ -195,78 +204,85 @@
|
||||
|
||||
<!-- 分配权限弹窗 -->
|
||||
<el-drawer v-model="assignPermDialogVisible" :title="'【' + checkedRole.name + '】权限分配'" :size="drawerSize">
|
||||
<div style="min-width: 300px;">
|
||||
<div style="display: flex; gap: 10px; ">
|
||||
<div style="width: 10px; background-color: #1677ff;"></div>
|
||||
<div>
|
||||
<span style="font-size: 16px;">数据授权</span>
|
||||
<a-tooltip placement="right">
|
||||
<template #title>
|
||||
<span>授权用户可操作的数据范围</span>
|
||||
</template>
|
||||
<QuestionCircleOutlined style="margin-left: 5px;" />
|
||||
</a-tooltip>
|
||||
<!-- 使用 element-plus 的 row 和 col 组件实现左右两部分并排显示 -->
|
||||
<el-row :gutter="20" style="height: calc(100% - 60px);">
|
||||
<!-- 左侧数据权限设置,占据 8 列 -->
|
||||
<el-col :span="8">
|
||||
<div style="display: flex; gap: 10px; ">
|
||||
<div style="width: 10px; background-color: #1677ff;"></div>
|
||||
<div>
|
||||
<span style="font-size: 16px;">数据授权</span>
|
||||
<a-tooltip placement="right">
|
||||
<template #title>
|
||||
<span>授权用户可操作的数据范围</span>
|
||||
</template>
|
||||
<QuestionCircleOutlined style="margin-left: 5px;" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form ref="roleFormRef" :model="formData" :rules="rules" label-width="100px">
|
||||
<el-form-item label="数据权限" prop="data_scope">
|
||||
<el-select v-model="permissionState.data_scope">
|
||||
<el-option :key="1" label="全部数据" :value="1" />
|
||||
<el-option :key="2" label="部门及子部门数据" :value="2" />
|
||||
<el-option :key="3" label="本部门数据" :value="3" />
|
||||
<el-option :key="4" label="本人数据" :value="4" />
|
||||
<el-option :key="5" label="自定义数据" :value="5" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form ref="roleFormRef" :model="formData" :rules="rules" label-width="100px">
|
||||
<el-form-item label="数据权限" prop="data_scope">
|
||||
<el-select v-model="permissionState.data_scope">
|
||||
<el-option :key="1" label="全部数据" :value="1" />
|
||||
<el-option :key="2" label="部门及子部门数据" :value="2" />
|
||||
<el-option :key="3" label="本部门数据" :value="3" />
|
||||
<el-option :key="4" label="仅本人数据" :value="4" />
|
||||
<el-option :key="5" label="自定义数据" :value="5" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-tree v-if="permissionState.data_scope === 5 && deptTreeData.length" :checked-keys="permissionState.dept_ids"
|
||||
:row-key="record => record.id" :tree-data="deptTreeData" :default-expand-all=true
|
||||
:field-names="{ children: 'children', title: 'name', key: 'id' }" checkable check-strictly
|
||||
style="margin-top: 15px;" @check="deptTreeCheck" />
|
||||
</div>
|
||||
<el-tree v-if="permissionState.data_scope === 5 && deptTreeData.length"
|
||||
:checked-keys="permissionState.dept_ids" :row-key="record => record.id" :tree-data="deptTreeData"
|
||||
:default-expand-all=true :field-names="{ children: 'children', title: 'name', key: 'id' }" checkable
|
||||
check-strictly style="margin-top: 15px; height: calc(100% - 100px); overflow: auto;"
|
||||
@check="deptTreeCheck" />
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧菜单功能权限设置,占据 16 列 -->
|
||||
<el-col :span="16">
|
||||
<div class="flex-x-between">
|
||||
<div style="width: 10px; background-color: #1677ff;"></div>
|
||||
<span style="font-size: 16px;">菜单授权</span>
|
||||
<el-input v-model="permKeywords" clearable class="w-[150px]" placeholder="菜单权限名称">
|
||||
<template #prefix>
|
||||
<Search />
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<div class="flex-center ml-5">
|
||||
<el-button type="primary" size="small" plain @click="togglePermTree">
|
||||
<template #icon>
|
||||
<Switch />
|
||||
</template>
|
||||
{{ isExpanded ? "收缩" : "展开" }}
|
||||
</el-button>
|
||||
<el-checkbox v-model="parentChildLinked" class="ml-5" @change="handleparentChildLinkedChange">
|
||||
父子联动
|
||||
</el-checkbox>
|
||||
|
||||
<div class="flex-x-between">
|
||||
<div style="width: 10px; background-color: #1677ff;"></div>
|
||||
<span style="font-size: 16px;">菜单授权</span>
|
||||
<el-input v-model="permKeywords" clearable class="w-[150px]" placeholder="菜单权限名称">
|
||||
<template #prefix>
|
||||
<Search />
|
||||
</template>
|
||||
</el-input>
|
||||
<el-tooltip placement="bottom">
|
||||
<template #content>
|
||||
如果只需勾选菜单权限,不需要勾选子菜单或者按钮权限,请关闭父子联动
|
||||
</template>
|
||||
<el-icon class="ml-1 color-[--el-color-primary] inline-block cursor-pointer">
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-center ml-5">
|
||||
<el-button type="primary" size="small" plain @click="togglePermTree">
|
||||
<template #icon>
|
||||
<Switch />
|
||||
<el-tree ref="permTreeRef" node-key="value" show-checkbox :data="menuTreeData"
|
||||
:filter-node-method="handlePermFilter" :default-expand-all="true" :check-strictly="!parentChildLinked"
|
||||
class="mt-5" style="height: calc(100% - 60px); overflow: auto;">
|
||||
<template #default="{ data }">
|
||||
{{ data.label }}
|
||||
</template>
|
||||
{{ isExpanded ? "收缩" : "展开" }}
|
||||
</el-button>
|
||||
<el-checkbox v-model="parentChildLinked" class="ml-5" @change="handleparentChildLinkedChange">
|
||||
父子联动
|
||||
</el-checkbox>
|
||||
</el-tree>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-tooltip placement="bottom">
|
||||
<template #content>
|
||||
如果只需勾选菜单权限,不需要勾选子菜单或者按钮权限,请关闭父子联动
|
||||
</template>
|
||||
<el-icon class="ml-1 color-[--el-color-primary] inline-block cursor-pointer">
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-tree ref="permTreeRef" node-key="value" show-checkbox :data="menuPermOptions"
|
||||
:filter-node-method="handlePermFilter" :default-expand-all="true" :check-strictly="!parentChildLinked"
|
||||
class="mt-5">
|
||||
<template #default="{ data }">
|
||||
{{ data.label }}
|
||||
</template>
|
||||
</el-tree>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleAssignPermSubmit">确 定</el-button>
|
||||
@@ -316,8 +332,6 @@ const menuTreeData = ref<permissionMenuType[]>([]);
|
||||
// 分页表单
|
||||
const pageTableData = ref<RoleTable[]>([]);
|
||||
|
||||
// 菜单权限下拉
|
||||
const menuPermOptions = ref<OptionType[]>([]);
|
||||
|
||||
// 表格列配置
|
||||
const tableColumns = ref([
|
||||
@@ -443,14 +457,14 @@ async function handleOpenDialog(type: 'create' | 'update' | 'detail', id?: numbe
|
||||
if (id) {
|
||||
const response = await RoleAPI.getRoleDetail({ id });
|
||||
if (type === 'detail') {
|
||||
dialogVisible.title = "岗位详情";
|
||||
dialogVisible.title = "角色详情";
|
||||
Object.assign(detailFormData.value, response.data.data);
|
||||
} else if (type === 'update') {
|
||||
dialogVisible.title = "修改岗位";
|
||||
dialogVisible.title = "修改角色";
|
||||
Object.assign(formData, response.data.data);
|
||||
}
|
||||
} else {
|
||||
dialogVisible.title = "新增岗位";
|
||||
dialogVisible.title = "新增角色";
|
||||
formData.id = undefined;
|
||||
}
|
||||
dialogVisible.visible = true;
|
||||
@@ -491,9 +505,9 @@ async function handleSubmit() {
|
||||
});
|
||||
}
|
||||
|
||||
// 删除、导入、导出
|
||||
async function handleOperation(type: 'delete' | 'import' | 'export', id?: number) {
|
||||
if (type === 'delete' && !id && !selectIds.value.length) {
|
||||
// 删除、批量删除
|
||||
async function handleDelete(ids: number[]) {
|
||||
if (!ids && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
@@ -501,8 +515,7 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await RoleAPI.deleteRole({ id: id ? id : selectIds.value });
|
||||
ElMessage.success("删除成功");
|
||||
await RoleAPI.deleteRole({ ids });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -513,89 +526,63 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
ElMessage.info('已取消删除');
|
||||
});
|
||||
}
|
||||
else if (type === 'import') {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.xlsx, .xls';
|
||||
input.click();
|
||||
}
|
||||
|
||||
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 RoleAPI.uploadFile(formData);
|
||||
ElMessage.success('导入成功');
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
// 导出
|
||||
async function handleExport() {
|
||||
ElMessageBox.confirm('是否确认导出当前系统配置?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).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 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();
|
||||
} 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;
|
||||
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 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();
|
||||
ElMessage.success('导出成功');
|
||||
} catch (error: any) {
|
||||
ElMessage.error('文件处理失败', error.message);
|
||||
console.error('导出错误:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
else {
|
||||
ElMessage.error('未知操作类型');
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消导出');
|
||||
});
|
||||
}
|
||||
|
||||
// 批量启用/停用
|
||||
async function handleMoreClick(id: number) {
|
||||
if (id && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除启用或停用该项数据?", "警告", {
|
||||
async function handleMoreClick(status: boolean) {
|
||||
if (selectIds.value.length) {
|
||||
ElMessageBox.confirm(`确认${status ? '启用' : '停用'}该项数据?`, "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await RoleAPI.batchAvailableRole({ id: id ? id : selectIds.value });
|
||||
await RoleAPI.batchAvailableRole({ ids: selectIds.value, status });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
@@ -619,7 +606,7 @@ async function handleOpenAssignPermDialog(row: RoleTable) {
|
||||
checkedRole.value.name = row.name;
|
||||
|
||||
// 获取所有的菜单
|
||||
menuPermOptions.value = await MenuAPI.getOptions();
|
||||
menuTreeData.value = await MenuAPI.getMenuList();
|
||||
|
||||
// 回显角色已拥有的菜单
|
||||
RoleAPI.getRoleMenuIds(roleId)
|
||||
@@ -642,7 +629,7 @@ function handleAssignPermSubmit() {
|
||||
.map((node: any) => node.value);
|
||||
|
||||
loading.value = true;
|
||||
RoleAPI.updateRoleMenus(roleId, checkedMenuIds)
|
||||
RoleAPI.setPermission(roleId, checkedMenuIds)
|
||||
.then(() => {
|
||||
ElMessage.success("分配权限成功");
|
||||
assignPermDialogVisible.value = false;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<el-row :gutter="20">
|
||||
<!-- 部门树 -->
|
||||
<el-col :lg="4" :xs="24" class="mb-[12px]">
|
||||
<DeptTree v-model="UserPageQuery.dept_id" @node-click="handleQuery" />
|
||||
<DeptTree v-model="queryFormData.dept_id" @node-click="handleQuery" />
|
||||
</el-col>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
@@ -19,7 +19,7 @@
|
||||
<el-input v-model="queryFormData.name" placeholder="请输入姓名" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="状态">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable style="width: 160px">
|
||||
<el-select v-model="queryFormData.status" placeholder="请选择状态" clearable>
|
||||
<el-option value="true" label="启用" />
|
||||
<el-option value="false" label="停用" />
|
||||
</el-select>
|
||||
@@ -68,29 +68,16 @@
|
||||
<div class="data-table__toolbar--actions">
|
||||
<el-button type="success" icon="plus" @click="handleOpenDialog('create')">新增</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectIds.length === 0"
|
||||
@click="handleOperation('delete')">批量删除</el-button>
|
||||
<el-dropdown>
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">更多
|
||||
@click="handleDelete(selectIds)">批量删除</el-button>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="default" :disabled="selectIds.length === 0" icon="ArrowDown">
|
||||
更多
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-menu @click="handleMoreClick">
|
||||
<el-menu-item key="1">
|
||||
<span>
|
||||
<el-icon>
|
||||
<Check />
|
||||
</el-icon>
|
||||
<span>批量启用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item key="2">
|
||||
<span>
|
||||
<el-icon>
|
||||
<CircleClose />
|
||||
</el-icon>
|
||||
<span>批量停用</span>
|
||||
</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Check" @click="handleMoreClick(true)">批量启用</el-dropdown-item>
|
||||
<el-dropdown-item icon="CircleClose" @click="handleMoreClick(false)">批量停用</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
@@ -135,46 +122,42 @@
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" width="55"
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'selection')?.show" type="selection" min-width="55"
|
||||
align="center" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号"
|
||||
width="60" />
|
||||
<el-table-column label="头像" prop="avatar">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'index')?.show" type="index" fixed label="序号" align="center" min-width="60" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'avatar')?.show" label="头像" prop="avatar" min-width="80" align="center">
|
||||
<template #default="scope">
|
||||
<el-avatar :src="scope.row.avatar" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="账号" prop="username" />
|
||||
<el-table-column label="用户名" align="center" prop="name" />
|
||||
<el-table-column label="状态" align="center" prop="status" width="80">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'username')?.show" label="账号" prop="username" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'name')?.show" label="用户名" prop="name" min-width="100" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'status')?.show" label="状态" prop="status" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === true ? 'success' : 'danger'">
|
||||
{{ scope.row.status === true ? "启用" : "停用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否超管" align="center" prop="is_superuser">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'is_superuser')?.show" label="是否超管" prop="is_superuser" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.is_superuser ? 'success' : 'info'">
|
||||
{{ scope.row.is_superuser ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="性别" align="center">
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'gender')?.show" label="性别" min-width="100">
|
||||
<template #default="scope">
|
||||
<DictLabel v-model="scope.row.gender" code="gender" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="部门" align="center" prop="dept" />
|
||||
<el-table-column label="角色" align="center" prop="roles" />
|
||||
<el-table-column label="岗位" align="center" prop="positions" />
|
||||
<el-table-column label="手机号" align="center" prop="mobile" width="120" />
|
||||
<el-table-column label="邮箱" align="center" prop="email" width="160" />
|
||||
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="150" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'mobile')?.show" label="手机号" prop="mobile" min-width="160" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'email')?.show" label="邮箱" prop="email" min-width="160" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'created_at')?.show" label="创建时间" prop="created_at" min-width="200" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" label="更新时间" prop="updated_at" min-width="200" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" fixed="right" label="操作"
|
||||
min-width="120">
|
||||
min-width="280">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" icon="RefreshLeft" size="small" link @click="hancleResetPassword(scope.row)">
|
||||
重置密码
|
||||
@@ -184,7 +167,7 @@
|
||||
<el-button type="primary" size="small" link icon="edit"
|
||||
@click="handleOpenDialog('update', scope.row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="small" link icon="delete"
|
||||
@click="handleOperation('delete', scope.row.id)">删除</el-button>
|
||||
@click="handleDelete([scope.row.id])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -296,7 +279,7 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<!-- 详情弹窗不需要确定按钮的提交逻辑 -->
|
||||
<el-button v-if="dialogVisible.type !== 'detail'" type="primary" @click="handleSubmit">确定</el-button>
|
||||
<el-button v-if="dialogVisible.type === 'create' || dialogVisible.type === 'update'" type="primary" @click="handleSubmit">确定</el-button>
|
||||
<el-button v-else type="primary" @click="handleCloseDialog">确定</el-button>
|
||||
<el-button @click="handleCloseDialog">取消</el-button>
|
||||
</div>
|
||||
@@ -304,7 +287,6 @@
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
@@ -366,17 +348,12 @@ const tableColumns = ref([
|
||||
{ prop: 'index', label: '序号', show: true },
|
||||
{ prop: 'avatar', label: '头像', show: true },
|
||||
{ prop: 'username', label: '账号', show: true },
|
||||
{ prop: 'name', label: '昵称', show: true },
|
||||
{ prop: 'dept_name', label: '部门', show: true },
|
||||
{ prop: 'roleNames', label: '角色', show: true },
|
||||
{ prop: 'positionNames', label: '岗位', show: true },
|
||||
{ prop: 'password', label: '密码', show: true },
|
||||
{ prop: 'name', label: '用户名', show: true },
|
||||
{ prop: 'gender', label: '性别', show: true },
|
||||
{ prop: 'email', label: '邮箱', show: true },
|
||||
{ prop: 'mobile', label: '手机号', show: true },
|
||||
{ prop: 'is_superuser', label: '是否超管', show: true },
|
||||
{ prop: 'status', label: '状态', show: true },
|
||||
{ prop: 'status', label: '状态', show: true },
|
||||
{ prop: 'description', label: '描述', show: true },
|
||||
{ prop: 'created_at', label: '创建时间', show: true },
|
||||
{ prop: 'updated_at', label: '更新时间', show: true },
|
||||
@@ -506,7 +483,7 @@ function hancleResetPassword(row: UserInfo) {
|
||||
ElMessage.warning("密码至少需要6位字符,请重新输入");
|
||||
return false;
|
||||
}
|
||||
UserAPI.resetPassword(row.id, value).then(() => {
|
||||
UserAPI.changeCurrentUserPassword(row.id, value).then(() => {
|
||||
ElMessage.success("密码重置成功,新密码是:" + value);
|
||||
});
|
||||
},
|
||||
@@ -579,28 +556,8 @@ const handleSubmit = useDebounceFn(async () => {
|
||||
}, 1000)
|
||||
|
||||
// 删除、导入、导出
|
||||
async function handleOperation(type: 'delete' | 'import' | 'export', id?: number) {
|
||||
if (type === 'delete' && !id && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await UserAPI.deleteUser({ id: id ? id : selectIds.value });
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消删除');
|
||||
});
|
||||
}
|
||||
else if (type === 'import') {
|
||||
async function handleOperation(type: 'import' | 'export') {
|
||||
if (type === 'import') {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.xlsx, .xls';
|
||||
@@ -692,17 +649,40 @@ async function handleOperation(type: 'delete' | 'import' | 'export', id?: number
|
||||
}
|
||||
}
|
||||
|
||||
// 批量启用/停用
|
||||
async function handleMoreClick(id: number) {
|
||||
if (id && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除启用或停用该项数据?", "警告", {
|
||||
// 删除、批量删除
|
||||
async function handleDelete(ids: number[]) {
|
||||
if (!ids && !selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认删除该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await UserAPI.batchAvailableUser({ id: id ? id : selectIds.value });
|
||||
await UserAPI.deleteUser({ids});
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.info('已取消删除');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 批量启用/停用
|
||||
async function handleMoreClick(status: boolean) {
|
||||
if (selectIds.value.length) {
|
||||
ElMessageBox.confirm("确认启用或停用该项数据?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await UserAPI.batchAvailableUser({ ids: selectIds.value, status });
|
||||
handleResetQuery();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message);
|
||||
|
||||
Reference in New Issue
Block a user