You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

441 lines
12 KiB
Vue

1 year ago
<template>
<div class="h-full">
<my-card title="搜索条件" search>
<n-form ref="formRef" inline label-width="auto" :model="formValue" size="medium">
<n-form-item label="系统模块">
<n-input v-model:value="formValue.title" placeholder="请输入系统模块" />
</n-form-item>
<n-form-item label="操作人员">
<n-input v-model:value="formValue.operName" placeholder="请输入操作人员" />
</n-form-item>
<n-form-item label="类型">
<n-select v-model:value="formValue.businessType" :options="businessTypeSelect.options" style="width: 200px" />
</n-form-item>
<n-form-item label="状态">
<n-select v-model:value="formValue.status" :options="statusSelect.options" style="width: 200px" />
</n-form-item>
<n-form-item label="操作时间">
<n-date-picker v-model:value="daterange" type="daterange" :format="format" />
</n-form-item>
<n-form-item>
<n-button type="primary" class="mr-10px" @click="handleValidateClick">
<icon-ic-round-search class="mr-4px text-20px" />
搜索
</n-button>
<n-button @click="reset">
<icon-ic-round-refresh class="mr-4px text-20px" />
重置
</n-button>
</n-form-item>
</n-form>
</my-card>
<my-card title="列表">
<template #right>
<div class="flex-center">
<n-button
type="error"
size="small"
class="mr-5px ml-5px"
:disabled="isBacthDelete"
@click="
showModal = true;
flag = 0;
"
>
<icon-ic-round-delete class="mr-4px text-20px" />
删除
</n-button>
<n-button
type="warning"
size="small"
class="mr-5px"
@click="
showModal = true;
flag = 1;
"
>
<icon-ic-round-delete class="mr-4px text-20px" />
清空
</n-button>
<n-button size="small" class="mr-5px" type="primary" color="#2cc2d9" @click="handleExport">
<icon-tdesign-arrow-right-up-circle class="mr-4px text-20px" />
导出
</n-button>
<n-button size="small" class="mr-5px" type="primary" @click="getOperaData">
<icon-mdi-refresh class="mr-4px text-16px" :class="{ 'animate-spin': loading }" />
刷新表格
</n-button>
<cx-columns v-model:columns="columns" size="small"></cx-columns>
</div>
</template>
<div>
<n-data-table
:columns="columns"
:data="operaData"
:loading="loading"
:row-key="rowKey"
:max-height="dataTableConfig.maxHeight"
:scroll-x="dataTableConfig.scrollWidth(columns)"
@update:checked-row-keys="handleCheck"
/>
<n-pagination
:page="page"
:page-size="formValue.pageSize"
:page-sizes="[10, 20, 50, 100]"
show-size-picker
:item-count="total"
style="margin-top: 20px; float: right"
@update:page="changePage"
@update-page-size="changePageSize"
>
<template #prefix="{ itemCount }"> {{ itemCount }} </template>
</n-pagination>
</div>
<n-modal
v-model:show="showModal"
:mask-closable="false"
preset="dialog"
title="用户退出"
:content="`你真的要${flag ? '清空' : '删除'}这些记录吗?`"
positive-text="确认"
negative-text="算了"
@positive-click="onPositiveClick"
/>
<n-drawer v-model:show="show" :width="502">
<n-drawer-content title="操作日志详情" closable>
<n-form ref="formRef" :label-width="80" :model="form">
<n-grid :cols="24" :x-gap="18">
<!-- <n-form-item-grid-item :span="12" label="操作模块">
{{ form.title }} / {{ typeFormat(form) }}
</n-form-item-grid-item> -->
<n-form-item-grid-item :span="12" label="请求地址">{{ form.operUrl }}</n-form-item-grid-item>
<n-form-item-grid-item :span="12" label="登录信息">
{{ form.operName }} / {{ form.operIp }}
</n-form-item-grid-item>
<n-form-item-grid-item :span="12" label="请求方式">{{ form.requestMethod }}</n-form-item-grid-item>
<n-form-item-grid-item :span="12" label="操作方法">{{ form.method }}</n-form-item-grid-item>
<n-form-item-grid-item v-if="form.operParam" :span="24" label="请求参数">
{{ form.operParam }}
</n-form-item-grid-item>
<n-form-item-grid-item :span="12" label="返回参数">{{ form.jsonResult }}</n-form-item-grid-item>
<n-form-item-grid-item :span="12" label="操作状态">
<div v-if="form.status === 0"></div>
<div v-else-if="form.status === 1">失败</div>
</n-form-item-grid-item>
<n-form-item-grid-item :span="12" label="消耗时间">{{ form.costTime }}毫秒</n-form-item-grid-item>
<n-form-item-grid-item :span="12" label="操作时间">
{{ form.operTime }}
</n-form-item-grid-item>
<n-form-item-grid-item v-if="form.status === 1" :span="12" label="操作时间">
{{ form.errorMsg }}
</n-form-item-grid-item>
</n-grid>
</n-form>
</n-drawer-content>
</n-drawer>
</my-card>
</div>
</template>
<script lang="tsx" setup>
import { ref } from 'vue';
import type { Ref } from 'vue';
import { NButton, useMessage, NTag } from 'naive-ui';
import type { DataTableColumns, DataTableRowKey } from 'naive-ui';
// import { ReorderThreeOutline, Sync } from '@vicons/ionicons5';
import { operaBusinessLabels, operaStatusLabels } from '@/constants';
import { operaList, delOperlog, cleanOperlog } from '@/service';
import { useLoading } from '@/hooks';
import { formatDate, addDateRange } from '@/utils';
import { download } from '@/service/request/helpers';
import { dataTableConfig } from '@/config/dataTableConfig';
const operaData = ref();
const format = 'yyyy-MM-dd hh:mm:ss';
const message = useMessage();
const rowKey = row => row.operId;
const showModal = ref(false);
const isBacthDelete = ref(true);
const form = ref();
const flag = ref(1);
const delIds = ref();
const total = ref();
const page = ref(1);
const showExport = ref(false);
const daterange = ref();
const detailId = ref();
const show = ref(false);
const formValue = ref({
pageNum: 1,
pageSize: 10,
title: undefined,
operName: undefined,
businessType: undefined,
status: undefined
});
const statusSelect = ref({
options: [
{
label: '成功',
value: 0
},
{
label: '失败',
value: 1
}
]
});
const businessTypeSelect = ref({
options: [
{
label: '新增',
value: 1
},
{
label: '删除',
value: 3
},
{
label: '修改',
value: 2
},
{
label: '授权',
value: 4
},
{
label: '导出',
value: 5
},
{
label: '退出',
value: 7
},
{
label: '清空数据',
value: 9
},
{
label: '其他',
value: 0
}
]
});
const columns: Ref<DataTableColumns<OperaManagement.OperaType>> = ref([
{
type: 'selection',
align: 'center',
width: 60
},
{
title: '日志编号',
key: 'operId',
align: 'center',
width: 140
},
{
title: '系统模块',
key: 'title',
align: 'center',
width: 200
},
{
title: '操作类型',
key: 'businessType',
width: 100,
render: row => {
if (row.businessType) {
const tagTypes: Record<OperaManagement.OperaBusinessTypeKey, NaiveUI.ThemeColor> = {
1: 'success',
2: 'warning',
3: 'error',
4: 'info',
5: 'primary',
6: 'info',
7: 'error',
9: 'success'
};
return <NTag type={tagTypes[row.businessType]}>{operaBusinessLabels[row.businessType]}</NTag>;
}
return <span></span>;
},
align: 'center'
},
{
title: '请求方式',
key: 'requestMethod',
align: 'center',
width: 120
},
{
title: '操作人员',
key: 'operName',
align: 'center',
width: 100
},
{
title: '主机',
key: 'operIp',
align: 'center',
width: 140
},
{
title: '操作状态',
key: 'status',
width: 120,
render: row => {
if (row.status.toString()) {
const tagTypes: Record<OperaManagement.OperaStatusKey, NaiveUI.ThemeColor> = {
0: 'success',
1: 'error'
};
return <NTag type={tagTypes[row.status]}>{operaStatusLabels[row.status]}</NTag>;
}
return <span></span>;
},
align: 'center'
},
{
title: '消耗时间',
key: 'costTime',
align: 'center',
width: 120
},
{
title: '操作日期',
key: 'operTime',
align: 'center',
defaultSortOrder: 'ascend',
sorter: 'default',
width: 180
},
{
title: '操作',
key: 'action',
align: 'center',
render: row => {
return (
<n-space justify={'center'}>
<n-button
size={'small'}
type="primary"
onClick={() => {
show.value = true;
detailId.value = row.operId;
form.value = row;
}}
>
<icon-tdesign-file-import class="mr-2px text-15px " />
详情
</n-button>
</n-space>
);
}
}
]) as Ref<DataTableColumns<OperaManagement.OperaType>>;
const { loading, startLoading, endLoading } = useLoading(false);
// 搜索
function handleValidateClick() {
getOperaData();
}
// 重置
function reset() {
formValue.value.pageNum = 1;
page.value = 1;
daterange.value = null;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { pageNum, pageSize, ...rest } = formValue.value;
for (const key in rest) {
if (formValue.value[key] !== '') {
formValue.value[key] = '';
}
}
getOperaData();
}
// 获取用户信息
async function getOperaData() {
startLoading();
let dateArr;
if (daterange.value) {
dateArr = daterange.value.map(item => {
return formatDate(new Date(item), format);
});
}
await operaList({ params: addDateRange(formValue.value, dateArr, undefined) }).then(res => {
operaData.value = res.rows;
total.value = res.total;
if (operaData.value) {
setTimeout(() => {
endLoading();
}, 200);
}
});
}
// 确认删除
async function onPositiveClick() {
if (!flag.value) {
await delOperlog(delIds.value).then(() => {
message.success('删除成功');
});
} else {
cleanOperlog();
}
getOperaData();
}
// 改变页签
function changePage(pageNum) {
formValue.value.pageNum = pageNum;
page.value = pageNum;
getOperaData();
}
function changePageSize(pageSize) {
formValue.value.pageSize = pageSize;
getOperaData();
}
// 导出数据
function handleExport() {
showExport.value = true;
download('system/logininfor/export', formValue.value, `operlog_${new Date().getTime()}.xlsx`, undefined)
.then(() => {
showExport.value = false;
})
.catch(() => {
showExport.value = false;
});
}
// 弹出确认框
function handleCheck(rowKeys: DataTableRowKey[]) {
if (rowKeys.length > 0) {
isBacthDelete.value = false;
delIds.value = rowKeys;
} else {
isBacthDelete.value = true;
}
}
// function typeFormat(row, column) {
// return this.selectDictLabel(form, row.businessType);
// }
function init() {
getOperaData();
}
init();
</script>
<style scoped>
.n-form {
display: flex;
flex-wrap: wrap;
}
.n-form .n-form-item-grid-item {
color: aqua;
}
</style>