mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-08-28 10:09:54 +08:00
fix 修复websocket在某些情况下不重连问题
fix 修复登录日志查看权限 feat 访问日志增加接口信息显示 perf 为所有orm的Insert操作增加OmitEmptyData选项
This commit is contained in:
@@ -97,7 +97,7 @@
|
||||
"stylelint-config-standard": "^29.0.0",
|
||||
"stylelint-order": "^5.0.0",
|
||||
"stylelint-scss": "^4.7.0",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"tailwindcss": "^2.2.19",
|
||||
"typescript": "^5.3.0",
|
||||
"unplugin-vue-components": "^0.22.12",
|
||||
"vite": "^4.5.3",
|
||||
|
@@ -18,15 +18,6 @@ export function Delete(params) {
|
||||
});
|
||||
}
|
||||
|
||||
// 获取登录日志指定详情
|
||||
export function View(params) {
|
||||
return http.request({
|
||||
url: '/loginLog/view',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 导出登录日志
|
||||
export function Export(params) {
|
||||
jumpExport('/loginLog/export', params);
|
||||
|
@@ -49,21 +49,29 @@ export default () => {
|
||||
let timer: ReturnType<typeof setTimeout>;
|
||||
const createSocket = () => {
|
||||
console.log('[WebSocket] createSocket...');
|
||||
if (useUserStore.token === '') {
|
||||
if (useUserStore.token === '' || useUserStore.config?.wsAddr == '') {
|
||||
console.error('[WebSocket] 用户未登录,稍后重试...');
|
||||
reconnect();
|
||||
resetReconnect();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
socket = new WebSocket(`${useUserStore.config?.wsAddr}?authorization=${useUserStore.token}`);
|
||||
init();
|
||||
if (lockReconnect) {
|
||||
lockReconnect = false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`[WebSocket] createSocket err: ${e}`);
|
||||
reconnect();
|
||||
resetReconnect();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const resetReconnect = () => {
|
||||
if (lockReconnect) {
|
||||
lockReconnect = false;
|
||||
}
|
||||
reconnect();
|
||||
};
|
||||
|
||||
const reconnect = () => {
|
||||
@@ -73,7 +81,7 @@ export default () => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
createSocket();
|
||||
}, SocketEnum.HeartBeatInterval);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const init = () => {
|
||||
|
@@ -64,9 +64,9 @@
|
||||
{
|
||||
field: 'url',
|
||||
component: 'NInput',
|
||||
label: '访问路径',
|
||||
label: '接口路径',
|
||||
componentProps: {
|
||||
placeholder: '请输入访问路径',
|
||||
placeholder: '请输入接口路径',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
|
@@ -1,84 +1,147 @@
|
||||
import { h } from 'vue';
|
||||
import { NTag } from 'naive-ui';
|
||||
import { NTag, NEllipsis, NSpace } from 'naive-ui';
|
||||
import { timestampToTime } from '@/utils/dateUtil';
|
||||
import { renderHtmlTooltip } from '@/utils';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: '模块',
|
||||
key: 'module',
|
||||
render(row) {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
type: row.module == 'admin' ? 'info' : 'success',
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => row.module,
|
||||
}
|
||||
);
|
||||
},
|
||||
title: '记录ID',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '操作人',
|
||||
key: 'memberName',
|
||||
render(row) {
|
||||
if (row.memberId === 0) {
|
||||
return row.memberName;
|
||||
}
|
||||
return row.memberName + '(' + row.memberId + ')';
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '请求方式',
|
||||
key: 'method',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '请求路径',
|
||||
key: 'url',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '访问IP',
|
||||
key: 'ip',
|
||||
width: 150,
|
||||
},
|
||||
// {
|
||||
// title: 'IP地区',
|
||||
// key: 'region',
|
||||
// },
|
||||
{
|
||||
title: '状态码',
|
||||
key: 'errorCode',
|
||||
title: '访客',
|
||||
key: 'name',
|
||||
width: 180,
|
||||
render(row) {
|
||||
const operator =
|
||||
row.memberId === 0 ? row.memberName : row.memberName + '(' + row.memberId + ')';
|
||||
|
||||
return h(
|
||||
NTag,
|
||||
NEllipsis,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
maxWidth: '180px',
|
||||
},
|
||||
type: row.errorCode == 0 ? 'success' : 'warning',
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => row.errorMsg + '(' + row.errorCode + ')',
|
||||
default: () =>
|
||||
h(
|
||||
NSpace,
|
||||
{ vertical: true },
|
||||
{
|
||||
default: () => [
|
||||
h('div', {
|
||||
innerHTML: '<div><p>' + operator + '</p></div>',
|
||||
}),
|
||||
h('div', {
|
||||
innerHTML: '<div><p>IP:' + row.ip + '</p></div>',
|
||||
}),
|
||||
row.cityLabel != ''
|
||||
? h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
type: 'primary',
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => row.cityLabel,
|
||||
}
|
||||
)
|
||||
: null,
|
||||
],
|
||||
}
|
||||
),
|
||||
}
|
||||
);
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '处理耗时',
|
||||
key: 'takeUpTime',
|
||||
title: '请求接口',
|
||||
key: 'name',
|
||||
width: 260,
|
||||
render(row) {
|
||||
return row.takeUpTime + ' ms';
|
||||
return h(
|
||||
NEllipsis,
|
||||
{
|
||||
style: {
|
||||
maxWidth: '260px',
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () =>
|
||||
h(
|
||||
NSpace,
|
||||
{ vertical: true },
|
||||
{
|
||||
default: () => [
|
||||
h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => row.method,
|
||||
}
|
||||
),
|
||||
h('div', {
|
||||
innerHTML: '<div><p>接口:' + row.url + '</p></div>',
|
||||
}),
|
||||
h('div', {
|
||||
innerHTML: '<div><p>名称:' + row.tags + ' / ' + row.summary + '</p></div>',
|
||||
}),
|
||||
],
|
||||
}
|
||||
),
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '接口响应',
|
||||
key: 'name',
|
||||
width: 260,
|
||||
render(row) {
|
||||
return h(
|
||||
NEllipsis,
|
||||
{
|
||||
style: {
|
||||
maxWidth: '260px',
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () =>
|
||||
h(
|
||||
NSpace,
|
||||
{ vertical: true },
|
||||
{
|
||||
default: () => [
|
||||
renderHtmlTooltip(
|
||||
'<div style="width: 240px"><p>状态码:' +
|
||||
row.errorMsg +
|
||||
'(' +
|
||||
row.errorCode +
|
||||
')' +
|
||||
'</p></div>'
|
||||
),
|
||||
h('div', {
|
||||
innerHTML: '<div><p>处理耗时:' + row.takeUpTime + 'ms</p></div>',
|
||||
}),
|
||||
h('div', {
|
||||
innerHTML: '<div><p>响应时间:' + timestampToTime(row.timestamp) + '</p></div>',
|
||||
}),
|
||||
],
|
||||
}
|
||||
),
|
||||
}
|
||||
);
|
||||
},
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '访问时间',
|
||||
|
@@ -39,150 +39,26 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, h, reactive, ref } from 'vue';
|
||||
import { computed, h, onMounted, reactive, ref } from 'vue';
|
||||
import { useDialog, useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
|
||||
import { BasicForm, useForm } from '@/components/Form/index';
|
||||
import { getLogList, Delete } from '@/api/log/log';
|
||||
import { columns } from './columns';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { DeleteOutlined } from '@vicons/antd';
|
||||
import { adaTableScrollX } from '@/utils/hotgo';
|
||||
import { loadOptions, schemas } from './model';
|
||||
|
||||
const dialog = useDialog();
|
||||
const batchDeleteDisabled = ref(true);
|
||||
const checkedIds = ref([]);
|
||||
|
||||
const schemas: FormSchema[] = [
|
||||
{
|
||||
field: 'member_id',
|
||||
component: 'NInput',
|
||||
label: '操作人',
|
||||
componentProps: {
|
||||
placeholder: '请输入操作人ID',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
rules: [{ trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'url',
|
||||
component: 'NInput',
|
||||
label: '访问路径',
|
||||
componentProps: {
|
||||
placeholder: '请输入访问路径',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'ip',
|
||||
component: 'NInput',
|
||||
label: '访问IP',
|
||||
componentProps: {
|
||||
placeholder: '请输入IP地址',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'method',
|
||||
component: 'NSelect',
|
||||
label: '请求方式',
|
||||
componentProps: {
|
||||
placeholder: '请选择请求方式',
|
||||
options: [
|
||||
{
|
||||
label: 'GET',
|
||||
value: 'GET',
|
||||
},
|
||||
{
|
||||
label: 'POST',
|
||||
value: 'POST',
|
||||
},
|
||||
],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'created_at',
|
||||
component: 'NDatePicker',
|
||||
label: '访问时间',
|
||||
componentProps: {
|
||||
type: 'datetimerange',
|
||||
clearable: true,
|
||||
// defaultValue: [new Date() - 86400000 * 30, new Date()],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'take_up_time',
|
||||
component: 'NSelect',
|
||||
label: '请求耗时',
|
||||
componentProps: {
|
||||
placeholder: '请选择请求耗时',
|
||||
options: [
|
||||
{
|
||||
label: '50ms内',
|
||||
value: '50',
|
||||
},
|
||||
{
|
||||
label: '100ms内',
|
||||
value: '100',
|
||||
},
|
||||
{
|
||||
label: '200ms内',
|
||||
value: '200',
|
||||
},
|
||||
{
|
||||
label: '500ms内',
|
||||
value: '500',
|
||||
},
|
||||
],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'error_code',
|
||||
component: 'NSelect',
|
||||
label: '状态码',
|
||||
componentProps: {
|
||||
placeholder: '请选择状态码',
|
||||
options: [
|
||||
{
|
||||
label: '0 成功',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
label: '-1 失败',
|
||||
value: '-1',
|
||||
},
|
||||
],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const router = useRouter();
|
||||
const message = useMessage();
|
||||
const actionRef = ref();
|
||||
const formParams = ref({});
|
||||
|
||||
const params = ref({
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const actionColumn = reactive({
|
||||
width: 160,
|
||||
title: '操作',
|
||||
@@ -233,9 +109,6 @@
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -251,14 +124,11 @@
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
return await getLogList({ ...formParams.value, ...params.value, ...res });
|
||||
return await getLogList({ ...formParams.value, ...res });
|
||||
};
|
||||
|
||||
function reloadTable() {
|
||||
@@ -278,6 +148,10 @@
|
||||
formParams.value = {};
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
134
web/src/views/log/log/model.ts
Normal file
134
web/src/views/log/log/model.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { FormSchema } from '@/components/Form';
|
||||
import { ref } from 'vue';
|
||||
import { defRangeShortcuts } from '@/utils/dateUtil';
|
||||
import { Option } from '@/utils/hotgo';
|
||||
import { Dicts } from '@/api/dict/dict';
|
||||
|
||||
export const schemas = ref<FormSchema[]>([
|
||||
{
|
||||
field: 'reqId',
|
||||
component: 'NInput',
|
||||
label: '链路ID',
|
||||
componentProps: {
|
||||
placeholder: '请输入链路ID',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'memberId',
|
||||
component: 'NInput',
|
||||
label: '操作人',
|
||||
componentProps: {
|
||||
placeholder: '请输入操作人ID',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
rules: [{ trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'url',
|
||||
component: 'NInput',
|
||||
label: '接口路径',
|
||||
componentProps: {
|
||||
placeholder: '请输入接口路径',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'ip',
|
||||
component: 'NInput',
|
||||
label: '访问IP',
|
||||
componentProps: {
|
||||
placeholder: '请输入IP地址',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'method',
|
||||
component: 'NSelect',
|
||||
label: '请求方式',
|
||||
componentProps: {
|
||||
placeholder: '请选择请求方式',
|
||||
options: [],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createdAt',
|
||||
component: 'NDatePicker',
|
||||
label: '访问时间',
|
||||
componentProps: {
|
||||
type: 'datetimerange',
|
||||
clearable: true,
|
||||
shortcuts: defRangeShortcuts(),
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'takeUpTime',
|
||||
component: 'NSelect',
|
||||
label: '请求耗时',
|
||||
componentProps: {
|
||||
placeholder: '请选择请求耗时',
|
||||
options: [],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'errorCode',
|
||||
component: 'NSelect',
|
||||
label: '状态码',
|
||||
labelMessage: '支持填入自定义状态码',
|
||||
componentProps: {
|
||||
placeholder: '请选择状态码',
|
||||
options: [],
|
||||
filterable: true,
|
||||
tag: true,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// 字典数据选项
|
||||
export const options = ref({
|
||||
HTTPMethod: [] as Option[],
|
||||
HTTPHandlerTime: [] as Option[],
|
||||
HTTPApiCode: [] as Option[],
|
||||
});
|
||||
|
||||
// 加载字典数据选项
|
||||
export function loadOptions() {
|
||||
Dicts({
|
||||
types: ['HTTPMethod', 'HTTPHandlerTime', 'HTTPApiCode'],
|
||||
}).then((res) => {
|
||||
options.value = res;
|
||||
for (const item of schemas.value) {
|
||||
switch (item.field) {
|
||||
case 'method':
|
||||
item.componentProps.options = options.value.HTTPMethod;
|
||||
break;
|
||||
case 'takeUpTime':
|
||||
item.componentProps.options = options.value.HTTPHandlerTime;
|
||||
break;
|
||||
case 'errorCode':
|
||||
item.componentProps.options = options.value.HTTPApiCode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
@@ -14,15 +14,18 @@
|
||||
<template #label>请求地址</template>
|
||||
{{ data.url }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="请求耗时">{{ data.takeUpTime }} ms</n-descriptions-item>
|
||||
<n-descriptions-item label="接口名称"
|
||||
>{{ data.tags }} / {{ data.summary }}</n-descriptions-item
|
||||
>
|
||||
<n-descriptions-item label="访问IP">{{ data.ip }}</n-descriptions-item>
|
||||
<n-descriptions-item label="IP归属地">{{ data.cityLabel }}</n-descriptions-item>
|
||||
<n-descriptions-item label="链路ID">{{ data.reqId }}</n-descriptions-item>
|
||||
<n-descriptions-item label="请求耗时">{{ data.takeUpTime }} ms</n-descriptions-item>
|
||||
<n-descriptions-item label="响应时间">{{
|
||||
timestampToTime(data.timestamp)
|
||||
data.timestamp > 0 ? timestampToTime(data.timestamp) : '--'
|
||||
}}</n-descriptions-item>
|
||||
|
||||
<n-descriptions-item label="创建时间">{{ data.createdAt }}</n-descriptions-item>
|
||||
<n-descriptions-item label="访问时间">{{ data.createdAt }}</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
</n-card>
|
||||
<n-card
|
||||
@@ -131,22 +134,13 @@
|
||||
|
||||
const message = useMessage();
|
||||
const router = useRouter();
|
||||
const logId = Number(router.currentRoute.value.params.id);
|
||||
const params = router.currentRoute.value.params;
|
||||
const loading = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
if (logId === undefined || logId < 1) {
|
||||
message.error('ID不正确,请检查!');
|
||||
return;
|
||||
}
|
||||
getInfo();
|
||||
});
|
||||
|
||||
const data = ref({});
|
||||
|
||||
const getInfo = () => {
|
||||
loading.value = true;
|
||||
View({ id: logId })
|
||||
View(params)
|
||||
.then((res) => {
|
||||
data.value = res;
|
||||
})
|
||||
@@ -154,6 +148,14 @@
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (!params.id) {
|
||||
message.error('日志ID不正确,请检查!');
|
||||
return;
|
||||
}
|
||||
getInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@@ -77,7 +77,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||
cssTarget: 'chrome80',
|
||||
outDir: OUTPUT_DIR,
|
||||
reportCompressedSize: false,
|
||||
chunkSizeWarningLimit: 2000,
|
||||
chunkSizeWarningLimit: 3000,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
Reference in New Issue
Block a user