<template>
|
<div>
|
<el-card style="margin: 10px;" class="affix-container">
|
<div>
|
<div style="display: flex;justify-content: space-between">
|
<div>
|
<b style="margin-right: 20px">{{ $t('menu.AuditTrail') }}</b>
|
<el-button link size="default" type="primary" :icon="icon.Download" @click="exportData">
|
{{ $t('button.Export') }}
|
</el-button>
|
</div>
|
<div>
|
<el-input :placeholder="$t('message.PleaseInputKeyWords')" :prefix-icon="icon.Search"
|
maxlength="64"
|
clearable
|
size="default"
|
@clear="SearchData"
|
style="width: 200px;margin-right: 10px" v-model="searchValue.filter"
|
@keydown.enter.native="SearchData" :disabled="showAdvanceSearchView"></el-input>
|
<el-button :icon="icon.Search" type="primary" size="default" @click="SearchData"
|
:disabled="showAdvanceSearchView">
|
{{ $t('button.Search') }}
|
</el-button>
|
<el-button type="warning" size="default"
|
:icon="showAdvanceSearchView?icon.CaretTop:icon.CaretBottom"
|
@click="showAdvanceSearchView = !showAdvanceSearchView">
|
{{ $t('button.AdvancedSearch') }}
|
</el-button>
|
</div>
|
</div>
|
<transition name="slide-fade">
|
<div v-show="showAdvanceSearchView" class="advanced-search-box">
|
<el-form size="default" label-width="135px">
|
<el-row :style="{margin: '10px 0px 0px 0px'}">
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Module')" :style="{marginBottom: '0px'}">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="searchValue.module"/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.ActionBy')" :style="{marginBottom: '0px'}">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="searchValue.actionby"/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Type')" :style="{marginBottom: '0px'}">
|
<el-select size="default" v-model="searchValue.actiontype" filterable>
|
<el-option
|
v-for="item in actiontypes"
|
:key="item.id"
|
:label="item.name"
|
:value="item.id">
|
</el-option>
|
</el-select>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :style="{margin: '10px 0px 0px 0px'}">
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Message')" :style="{marginBottom: '0px'}">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="searchValue.message"/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="14">
|
<el-form-item :label="$t('label.ActionTime')" :style="{marginBottom: '0px'}">
|
<el-date-picker
|
size="default"
|
v-model="searchValue.datearray"
|
type="datetimerange"
|
:start-placeholder="$t('label.DateFrom')"
|
:end-placeholder="$t('label.DateTo')"
|
format="YYYY-MM-DD HH:mm:ss"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
>
|
</el-date-picker>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :style="{margin: '10px 0px 0px 0px'}">
|
<el-col :span="21" :style="{ textAlign: 'right'}" :offset="0">
|
<el-button size="default" :icon="icon.Search" type="primary"
|
@click="SearchData">
|
{{ $t('button.Search') }}
|
</el-button>
|
<el-button size="default" :icon="icon.Delete" type="info" plain
|
@click="ClearSearchData">
|
{{ $t('button.Clear') }}
|
</el-button>
|
<el-button size="default" :icon="icon.CircleClose" type="danger" plain
|
@click="showAdvanceSearchView = !showAdvanceSearchView">
|
{{ $t('button.Close') }}
|
</el-button>
|
</el-col>
|
</el-row>
|
</el-form>
|
</div>
|
</transition>
|
</div>
|
<el-divider :style="{'margin':'10px 0px'}"></el-divider>
|
<el-table v-loading="loading" :data="audittrails" stripe border size="default" highlight-current-row
|
element-loading-text="Loading..." element-loading-spinner="el-icon-loading"
|
element-loading-background="rgba(0, 0, 0, 0.8)"
|
:header-row-style="{ height: '45px', color: 'var(--el-color-primary)', background: 'var(--el-color-info-light)', }"
|
:header-cell-style="{ height: '45px', margin: '0px', padding: '0px', background: 'var(--el-color-info-light)' }"
|
@row-dblclick="showEditView">
|
<el-table-column type="index" fixed :label="$t('label.Index')" width="100"></el-table-column>
|
<el-table-column prop="module" fixed sortable align="left" :label="$t('label.Module')"
|
width="140"/>
|
<el-table-column prop="actiontype" :label="$t('label.ActionType')" width="160">
|
<template #default="scope">
|
<el-tag v-if="scope.row.actiontype==='CREATE'" type="success">
|
{{ $t('button.Create') }}
|
</el-tag>
|
<el-tag v-else-if="scope.row.actiontype==='UPDATE'" type="warning">
|
{{ $t('button.Edit') }}
|
</el-tag>
|
<el-tag v-else-if="scope.row.actiontype==='QUERY'" type="info">
|
{{ $t('button.View') }}
|
</el-tag>
|
<el-tag v-else-if="scope.row.actiontype==='DELETE'" type="danger">
|
{{ $t('button.Delete') }}
|
</el-tag>
|
<el-tag v-else type="info">
|
{{ scope.row.actiontype }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="actionby" align="left" :label="$t('label.ActionBy')"
|
width="200"/>
|
<el-table-column prop="actionip" align="left" :label="$t('label.ActionIP')"
|
width="140"/>
|
<el-table-column prop="actiontime" width="150" align="left" :label="$t('label.ActionTime')"
|
:formatter="formatterDatetime"/>
|
<el-table-column prop="message" min-width="300" width="*" align="left"
|
:label="$t('label.Message')"></el-table-column>
|
<el-table-column fixed="right" width="110" :label="$t('label.Operation')">
|
<template #default="scope">
|
<el-button @click="showEditView(scope.row)" :icon="icon.Edit"
|
size="small"
|
type="primary" circle>
|
</el-button>
|
<el-button @click="deleteItem(scope.row)" :icon="icon.Delete"
|
size="small"
|
type="danger" circle>
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
<el-affix position="bottom" :offset="10" target=".affix-container">
|
<div style="background: white;padding-bottom:10px;">
|
<el-divider :style="{'margin':'10px 0px'}"></el-divider>
|
<el-row>
|
<el-col :span="12" align="left">
|
|
</el-col>
|
<el-col :span="12" align="right">
|
<div style="display: flex;justify-content: flex-end">
|
<el-pagination
|
:currentPage="pageIndex"
|
:page-size="pageSize"
|
background
|
@current-change="currentChange"
|
@size-change="sizeChange"
|
:page-sizes="[10, 20, 50, 100]"
|
layout="sizes, prev, pager, next, jumper, ->, total, slot"
|
:total="total">
|
</el-pagination>
|
</div>
|
</el-col>
|
</el-row>
|
</div>
|
</el-affix>
|
</el-card>
|
<div v-dialogdrag>
|
<el-dialog :header="title"
|
id="detailDialog"
|
v-model="dialogVisible"
|
:close-on-click-modal="false"
|
class="el-dialog-customer"
|
width="1000px">
|
<template #header>
|
<el-row>
|
<el-col :span="12">
|
<el-icon style="font:normal bold 20px arial,sans-serif;vertical-align: -10%"
|
v-if="dialogMode==='new'">
|
<plus/>
|
</el-icon>
|
<el-icon v-else style="font:normal bold 20px arial,serif;vertical-align: -10%">
|
<edit/>
|
</el-icon>
|
<span style="margin-left: 10px; font:normal bold 20px arial,serif;">{{ title }}</span>
|
</el-col>
|
</el-row>
|
</template>
|
<el-card style="margin-top: 0px;">
|
<el-form :model="audittrail" size="default" :rules="rules" ref="dialogForm" label-width="135px">
|
<el-tabs>
|
<el-tab-pane :label="$t('label.BasicInformation')">
|
<el-row>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Module')" prop="module">
|
{{ audittrail.module }}
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.ActionType')" prop="actiontype">
|
<el-tag v-if="audittrail.actiontype==='CREATE'" type="success">
|
{{ $t('button.Create') }}
|
</el-tag>
|
<el-tag v-else-if="audittrail.actiontype==='UPDATE'" type="warning">
|
{{ $t('button.Edit') }}
|
</el-tag>
|
<el-tag v-else-if="audittrail.actiontype==='QUERY'" type="info">
|
{{ $t('button.View') }}
|
</el-tag>
|
<el-tag v-else-if="audittrail.actiontype==='DELETE'" type="danger">
|
{{ $t('button.Delete') }}
|
</el-tag>
|
<el-tag v-else type="info">
|
{{ audittrail.actiontype }}
|
</el-tag>
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.ActionBy')" prop="actionby">
|
{{ audittrail.actionby }}
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.ActionIP')" prop="actionip">
|
{{ audittrail.actionip }}
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.ActionTime')" prop="actiontime">
|
{{ formatterDatetime2(audittrail.actiontime) }}
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row>
|
<el-col :span="21">
|
<el-form-item :label="$t('label.Message')" prop="message">
|
{{ audittrail.message }}
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-tab-pane>
|
<el-tab-pane :label="$t('button.Detail')">
|
<el-row>
|
<el-col :span="21">
|
<el-row>
|
<el-col :span="12">
|
<el-form-item :label="$t('label.BeforeTarget')" prop="beforetarget">
|
<JsonViewer v-if="audittrail.beforetarget"
|
:value="audittrail.beforetarget?JSON.parse(audittrail.beforetarget):null"
|
copyable boxed sort
|
theme="jv-light"/>
|
<el-empty v-else ::imageSize="100"
|
:description="$t('message.NoData')"/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item :label="$t('label.AfterTarget')" prop="aftertarget">
|
<JsonViewer v-if="audittrail.aftertarget"
|
:value="audittrail.aftertarget?JSON.parse(audittrail.aftertarget):null"
|
copyable boxed
|
sort
|
theme="jv-light"/>
|
<el-empty v-else ::imageSize="100"
|
:description="$t('message.NoData')"/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-col>
|
</el-row>
|
</el-tab-pane>
|
</el-tabs>
|
</el-form>
|
</el-card>
|
<template #footer>
|
<el-button type="danger" @click="dialogVisible = false" :icon="icon.CircleClose" size="default"
|
plain>
|
{{ $t('button.Close') }}
|
</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
|
import * as icon from '@element-plus/icons-vue'
|
import {onMounted, onBeforeUnmount, getCurrentInstance, reactive, toRefs} from "vue";
|
import {formatDate, DownloadBlobFileStream} from '@/utils/common'
|
import {
|
GetAuditTrails,
|
UpdateAuditTrail,
|
CreateAuditTrail,
|
DeleteAuditTrail,
|
ExportAuditTrails
|
} from "@/api/admin/system"
|
import {useBasicInfoStore} from "@/store/basicInfo";
|
|
|
export default {
|
name: "audit-trail-list",
|
components: {},
|
setup() {
|
let {proxy} = getCurrentInstance();
|
const store = useBasicInfoStore()
|
const state = reactive({
|
//common properties
|
title: '',
|
total: 0,
|
pageIndex: 1,
|
pageSize: store.config.ListSize,
|
loading: false,
|
windowWidth: document.documentElement.clientWidth,
|
windowHeight: document.documentElement.clientHeight,
|
dialogVisible: false,
|
dialogMode: 'new',
|
showAdvanceSearchView: false,
|
searchValue: {
|
filter: "",
|
message: null,
|
actionby: "",
|
actiontype: null,
|
module: null,
|
datearray: '',
|
},
|
audittrails: [],
|
audittrail: {
|
id: "",
|
message: "",
|
actionby: "",
|
actiontype: "",
|
actionip: "",
|
actiontime: "",
|
module: "",
|
beforetarget: "",
|
aftertarget: "",
|
createtime: "",
|
updatetime: "",
|
isdeleted: null,
|
remark: "",
|
},
|
actiontypes: [
|
{
|
id: 'CREATE',
|
name: `${proxy.$t('button.Create')}`,
|
},
|
{
|
id: 'UPDATE',
|
name: `${proxy.$t('button.Edit')}`,
|
},
|
{
|
id: 'QUERY',
|
name: `${proxy.$t('button.View')}`,
|
},
|
{
|
id: 'DELETE',
|
name: `${proxy.$t('button.Delete')}`,
|
}],
|
rules: {}
|
});
|
|
onMounted(() => {
|
Init();
|
LoadData();
|
});
|
onBeforeUnmount(() => {
|
|
});
|
|
const Init = () => {
|
|
};
|
const sizeChange = (currentSize) => {
|
state.pageSize = currentSize;
|
store.config.ListSize = state.pageSize;
|
LoadData();
|
};
|
const currentChange = (currentPage) => {
|
state.pageIndex = currentPage;
|
LoadData();
|
};
|
const clearAddOrEditModel = () => {
|
state.audittrail = {
|
id: "",
|
message: "",
|
actionby: "",
|
actiontype: "",
|
actionip: "",
|
actiontime: "",
|
module: "",
|
beforetarget: "",
|
aftertarget: "",
|
createtime: "",
|
updatetime: "",
|
isdeleted: null,
|
remark: "",
|
}
|
};
|
const getSearchCondition = () => {
|
let condition;
|
if (!state.showAdvanceSearchView) {
|
condition = {
|
pageIndex: state.pageIndex,
|
pageSize: state.pageSize,
|
filter: state.searchValue.filter,
|
permissionLevel: store.permissionLevel,
|
}
|
} else {
|
condition = {
|
pageIndex: state.pageIndex,
|
pageSize: state.pageSize,
|
message: state.searchValue.message,
|
actionby: state.searchValue.actionby,
|
actiontype: state.searchValue.actiontype,
|
module: state.searchValue.module,
|
datearray: state.searchValue.datearray,
|
startDateTime: state.searchValue.datearray ? state.searchValue.datearray[0] : null,
|
endDateTime: state.searchValue.datearray ? state.searchValue.datearray[1] : null,
|
permissionLevel: store.permissionLevel,
|
}
|
}
|
|
return condition;
|
};
|
const LoadData = () => {
|
state.loading = true;
|
let condition = getSearchCondition();
|
|
GetAuditTrails(condition).then(resp => {
|
state.loading = false;
|
if (resp.data) {
|
state.audittrails = resp.data.data;
|
state.total = resp.data.total;
|
}
|
}).catch(err => {
|
console.error(err);
|
});
|
};
|
const SearchData = () => {
|
state.pageIndex = 1;
|
LoadData();
|
};
|
const ClearSearchData = () => {
|
state.searchValue = {
|
filter: "",
|
message: null,
|
actionby: "",
|
actiontype: null,
|
module: null,
|
datearray: '',
|
};
|
};
|
const showAddView = () => {
|
clearAddOrEditModel();
|
|
state.title = proxy.$t('button.New');
|
state.dialogMode = 'new';
|
state.dialogVisible = true;
|
};
|
const onSubmit = () => {
|
if (state.audittrail.id) {
|
proxy.$refs['dialogForm'].validate(valid => {
|
if (valid) {
|
proxy.$confirm(`${proxy.$t('message.AreYouSureToSubmit')}`, `${proxy.$t('label.SystemConfirm')}`, {
|
confirmButtonText: `${proxy.$t('button.Yes')}`,
|
cancelButtonText: `${proxy.$t('button.No')}`,
|
type: 'warning',
|
center: true
|
}).then(() => {
|
const rLoading = proxy.openLoading("#detailDialog");
|
UpdateAuditTrail(state.audittrail)
|
.then(resp => {
|
if (resp.data && resp.data.code === 200) {
|
state.dialogVisible = false
|
LoadData()
|
} else {
|
proxy.$message.error({message: `${proxy.$t('message.' + resp.data.message)}`});
|
}
|
rLoading.close();
|
})
|
.catch(error => {
|
console.error(error);
|
proxy.$message.error({message: error});
|
});
|
}).catch((error) => {
|
console.error(error)
|
});
|
}
|
});
|
} else {
|
proxy.$refs['dialogForm'].validate(valid => {
|
if (valid) {
|
proxy.$confirm(`${proxy.$t('message.AreYouSureToSubmit')}`, `${proxy.$t('label.SystemConfirm')}`, {
|
confirmButtonText: `${proxy.$t('button.Yes')}`,
|
cancelButtonText: `${proxy.$t('button.No')}`,
|
type: 'warning',
|
center: true
|
}).then(() => {
|
const rLoading = proxy.openLoading("#detailDialog");
|
CreateAuditTrail(state.audittrail)
|
.then(resp => {
|
if (resp.data && resp.data.code === 200) {
|
state.dialogVisible = false
|
LoadData()
|
} else {
|
proxy.$message.error({message: `${proxy.$t('message.' + resp.data.message)}`});
|
}
|
rLoading.close();
|
})
|
.catch(error => {
|
console.error(error);
|
proxy.$message.error({message: error});
|
});
|
}).catch((error) => {
|
console.error(error)
|
});
|
}
|
});
|
}
|
};
|
const showEditView = (data) => {
|
state.title = `${proxy.$t('button.Detail')}`;
|
state.dialogMode = 'edit';
|
state.audittrail = data;
|
|
state.dialogVisible = true;
|
};
|
const deleteItem = (data) => {
|
proxy.$confirm(`${proxy.$t('message.AreYouSureToRemove')}`, `${proxy.$t('label.SystemConfirm')}`, {
|
confirmButtonText: `${proxy.$t('button.Yes')}`,
|
cancelButtonText: `${proxy.$t('button.No')}`,
|
confirmButtonClass: 'confirmButtonClass',
|
cancelButtonClass: 'cancelButtonClass',
|
type: 'warning',
|
center: true
|
}).then(() => {
|
state.loading = true;
|
DeleteAuditTrail(data.id)
|
.then(resp => {
|
if (resp.data && resp.data.code === 200) {
|
LoadData()
|
} else {
|
state.loading = false;
|
proxy.$message.error({message: `${proxy.$t('message.' + resp.data.message)}`});
|
}
|
})
|
.catch(error => {
|
proxy.$message.error({message: error});
|
});
|
}).catch((error) => {
|
console.error(error)
|
});
|
};
|
const handleChange = (value, direction, movedKeys) => {
|
|
};
|
const exportData = () => {
|
proxy.$confirm(`${proxy.$t('message.AreYouSureToExport')}`, `${proxy.$t('label.SystemConfirm')}`, {
|
confirmButtonText: `${proxy.$t('button.Yes')}`,
|
cancelButtonText: `${proxy.$t('button.No')}`,
|
confirmButtonClass: 'confirmButtonClass',
|
cancelButtonClass: 'cancelButtonClass',
|
type: 'warning',
|
center: true,
|
}).then(() => {
|
state.loading = true;
|
let condition = getSearchCondition();
|
ExportAuditTrails(condition).then(resp => {
|
if (resp && resp.data) {
|
DownloadBlobFileStream(resp, '系统审计数据.xls');
|
}
|
state.loading = false;
|
});
|
}).catch((error) => {
|
console.error(error)
|
state.loading = false;
|
});
|
};
|
//formatter
|
const formatterDatetime = (row, column) => {
|
let date = null;
|
switch (column.property) {
|
case "createtime":
|
if (!row.createtime)
|
return null;
|
date = new Date(row.createtime);
|
break;
|
case "birthday":
|
if (!row.birthday)
|
return null;
|
date = new Date(row.birthday);
|
break;
|
case "actiontime":
|
if (!row.actiontime)
|
return null;
|
date = new Date(row.actiontime);
|
break;
|
case "validdateto":
|
if (!row.validdateto)
|
return null;
|
date = new Date(row.validdateto);
|
break;
|
case "validdatefrom":
|
if (!row.validdatefrom)
|
return null;
|
date = new Date(row.validdatefrom);
|
break;
|
}
|
|
return formatDate(date, 'yyyy-MM-dd hh:mm');
|
};
|
const formatterDatetime2 = (datetime) => {
|
let date = new Date(datetime);
|
return formatDate(date, 'yyyy-MM-dd hh:mm:ss');
|
};
|
const capitalize = (value) => {
|
if (!value) return ''
|
value = value.toString()
|
return value.charAt(0).toUpperCase() + value.slice(1)
|
};
|
|
return {
|
...toRefs(state),
|
LoadData,
|
sizeChange,
|
currentChange,
|
clearAddOrEditModel,
|
SearchData,
|
showAddView,
|
onSubmit,
|
showEditView,
|
deleteItem,
|
ClearSearchData,
|
handleChange,
|
exportData,
|
formatterDatetime,
|
formatterDatetime2,
|
capitalize,
|
icon
|
}
|
},
|
}
|
</script>
|
|
<style>
|
|
</style>
|