Pre Merge pull request !21 from 二次元虎哥/master
commit
4444a0fb4b
|
@ -5,6 +5,7 @@ import { isArray, isString } from '@/utils/is'
|
||||||
import { DictTag } from '@/components/DictTag'
|
import { DictTag } from '@/components/DictTag'
|
||||||
import { Icon } from '@/components/Icon'
|
import { Icon } from '@/components/Icon'
|
||||||
import TableImg from '../components/TableImg.vue'
|
import TableImg from '../components/TableImg.vue'
|
||||||
|
import { JsonPreview } from '@/components/CodeEditor'
|
||||||
|
|
||||||
export const useRender = {
|
export const useRender = {
|
||||||
/**
|
/**
|
||||||
|
@ -112,5 +113,24 @@ export const useRender = {
|
||||||
if (text) {
|
if (text) {
|
||||||
return h(Icon, { icon: text })
|
return h(Icon, { icon: text })
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 使用JsonPreview组件 方便预览JSON
|
||||||
|
* @param json json字符串/obj
|
||||||
|
* @returns 能转为json返回JsonPreview 否则返回自身
|
||||||
|
*/
|
||||||
|
renderJsonPreview: (json: any) => {
|
||||||
|
if (!json) return ''
|
||||||
|
if (typeof json === 'object') {
|
||||||
|
return h(JsonPreview, { data: json })
|
||||||
|
}
|
||||||
|
if (typeof json === 'string') {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(json)
|
||||||
|
return h(JsonPreview, { data })
|
||||||
|
} catch (e) {
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,74 @@
|
||||||
import { BasicColumn, FormSchema, useRender } from '@/components/Table'
|
import { BasicColumn, FormSchema, useRender } from '@/components/Table'
|
||||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
value: '',
|
||||||
|
label: '无'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'processing',
|
||||||
|
label: '主要'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'success',
|
||||||
|
label: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'default',
|
||||||
|
label: '默认'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'warning',
|
||||||
|
label: '警告'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'error',
|
||||||
|
label: '危险'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'pink',
|
||||||
|
label: 'pink'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'red',
|
||||||
|
label: 'red'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'orange',
|
||||||
|
label: 'orange'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'green',
|
||||||
|
label: 'green'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'cyan',
|
||||||
|
label: 'cyan'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'blue',
|
||||||
|
label: 'blue'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'purple',
|
||||||
|
label: 'purple'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
function previewOptions() {
|
||||||
|
return options.map((option) => {
|
||||||
|
const { value, label } = option
|
||||||
|
if (value === '') {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
label: useRender.renderTag(label, value),
|
||||||
|
value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const dataColumns: BasicColumn[] = [
|
export const dataColumns: BasicColumn[] = [
|
||||||
{
|
{
|
||||||
title: '字典编码',
|
title: '字典编码',
|
||||||
|
@ -119,67 +187,15 @@ export const dataFormSchema: FormSchema[] = [
|
||||||
field: 'colorType',
|
field: 'colorType',
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
options: [
|
options: previewOptions()
|
||||||
{
|
|
||||||
value: '',
|
|
||||||
label: '空'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'processing',
|
|
||||||
label: '主要'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'success',
|
|
||||||
label: '成功'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'default',
|
|
||||||
label: '默认'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'warning',
|
|
||||||
label: '警告'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'error',
|
|
||||||
label: '危险'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'pink',
|
|
||||||
label: 'pink'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'red',
|
|
||||||
label: 'red'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'orange',
|
|
||||||
label: 'orange'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'green',
|
|
||||||
label: 'green'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'cyan',
|
|
||||||
label: 'cyan'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'blue',
|
|
||||||
label: 'blue'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'purple',
|
|
||||||
label: 'purple'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'CSS Class',
|
label: 'CSS Class',
|
||||||
field: 'cssClass',
|
field: 'cssClass',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
helpMessage: '输入hex模式的颜色,例如#108ee9'
|
helpMessage: '输入hex模式的颜色, 例如#108ee9',
|
||||||
|
rules: [{ required: false, message: '输入正确的16进制颜色', pattern: /^#([0-9a-fA-F]{3}){1,2}$/, trigger: 'blur' }]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '备注',
|
label: '备注',
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<template>
|
||||||
|
<BasicModal v-bind="$attrs" title="操作日志详情" @register="registerModalInner">
|
||||||
|
<Description @register="registerDescription" />
|
||||||
|
</BasicModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { BasicModal, useModalInner } from '@/components/Modal'
|
||||||
|
import { Description, useDescription } from '@/components/Description/index'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { infoSchema } from './operateLog.data'
|
||||||
|
|
||||||
|
defineOptions({ name: 'OperLogInfoModal' })
|
||||||
|
|
||||||
|
const logData = ref()
|
||||||
|
const [registerModalInner] = useModalInner((record: Recordable) => {
|
||||||
|
logData.value = record
|
||||||
|
})
|
||||||
|
|
||||||
|
const [registerDescription] = useDescription({
|
||||||
|
column: 1,
|
||||||
|
schema: infoSchema,
|
||||||
|
data: logData
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -4,16 +4,32 @@
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<a-button type="warning" :preIcon="IconEnum.EXPORT" @click="handleExport"> {{ t('action.export') }} </a-button>
|
<a-button type="warning" :preIcon="IconEnum.EXPORT" @click="handleExport"> {{ t('action.export') }} </a-button>
|
||||||
</template>
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'action'">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
icon: IconEnum.VIEW,
|
||||||
|
label: t('action.detail'),
|
||||||
|
onClick: handleShowInfo.bind(null, record)
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
</BasicTable>
|
</BasicTable>
|
||||||
|
<OperLogInfoModal @register="registerModal" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { useMessage } from '@/hooks/web/useMessage'
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
import { IconEnum } from '@/enums/appEnum'
|
import { IconEnum } from '@/enums/appEnum'
|
||||||
import { BasicTable, useTable } from '@/components/Table'
|
import { BasicTable, useTable, TableAction } from '@/components/Table'
|
||||||
import { OperateLogPageReqVO, exportOperateLog, getOperateLogPage } from '@/api/system/operatelog'
|
import { OperateLogPageReqVO, exportOperateLog, getOperateLogPage } from '@/api/system/operatelog'
|
||||||
import { columns, searchFormSchema } from './operateLog.data'
|
import { columns, searchFormSchema } from './operateLog.data'
|
||||||
|
import { useModal } from '@/components/Modal'
|
||||||
|
import OperLogInfoModal from './LogInfoModal.vue'
|
||||||
|
|
||||||
defineOptions({ name: 'SystemOperateLog' })
|
defineOptions({ name: 'SystemOperateLog' })
|
||||||
|
|
||||||
|
@ -26,7 +42,13 @@ const [registerTable, { getForm }] = useTable({
|
||||||
formConfig: { labelWidth: 120, schemas: searchFormSchema },
|
formConfig: { labelWidth: 120, schemas: searchFormSchema },
|
||||||
useSearchForm: true,
|
useSearchForm: true,
|
||||||
showTableSetting: true,
|
showTableSetting: true,
|
||||||
showIndexColumn: false
|
showIndexColumn: false,
|
||||||
|
actionColumn: {
|
||||||
|
width: 140,
|
||||||
|
title: t('common.action'),
|
||||||
|
dataIndex: 'action',
|
||||||
|
fixed: 'right'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
async function handleExport() {
|
async function handleExport() {
|
||||||
|
@ -40,4 +62,9 @@ async function handleExport() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [registerModal, { openModal }] = useModal()
|
||||||
|
function handleShowInfo(record: Recordable) {
|
||||||
|
openModal(true, record)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { BasicColumn, FormSchema, useRender } from '@/components/Table'
|
import { BasicColumn, FormSchema, useRender } from '@/components/Table'
|
||||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
|
||||||
|
import { DescItem } from '@/components/Description/index'
|
||||||
|
|
||||||
export const columns: BasicColumn[] = [
|
export const columns: BasicColumn[] = [
|
||||||
{
|
{
|
||||||
|
@ -10,7 +11,7 @@ export const columns: BasicColumn[] = [
|
||||||
{
|
{
|
||||||
title: '操作模块',
|
title: '操作模块',
|
||||||
dataIndex: 'module',
|
dataIndex: 'module',
|
||||||
width: 120
|
width: 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作名',
|
title: '操作名',
|
||||||
|
@ -20,7 +21,7 @@ export const columns: BasicColumn[] = [
|
||||||
{
|
{
|
||||||
title: '操作类型',
|
title: '操作类型',
|
||||||
dataIndex: 'type',
|
dataIndex: 'type',
|
||||||
width: 180,
|
width: 120,
|
||||||
customRender: ({ text }) => {
|
customRender: ({ text }) => {
|
||||||
return useRender.renderDict(text, DICT_TYPE.SYSTEM_OPERATE_TYPE)
|
return useRender.renderDict(text, DICT_TYPE.SYSTEM_OPERATE_TYPE)
|
||||||
}
|
}
|
||||||
|
@ -30,10 +31,14 @@ export const columns: BasicColumn[] = [
|
||||||
dataIndex: 'userNickname',
|
dataIndex: 'userNickname',
|
||||||
width: 120
|
width: 120
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// title: 'userAgent',
|
||||||
|
// dataIndex: 'userAgent',
|
||||||
|
// width: 400
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
title: 'userAgent',
|
title: '请求路径',
|
||||||
dataIndex: 'userAgent',
|
dataIndex: 'requestUrl'
|
||||||
width: 400
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作结果',
|
title: '操作结果',
|
||||||
|
@ -43,14 +48,6 @@ export const columns: BasicColumn[] = [
|
||||||
return useRender.renderTag(text === 0 ? '成功' : '失败', text === 0 ? '#87d068' : '#f50')
|
return useRender.renderTag(text === 0 ? '成功' : '失败', text === 0 ? '#87d068' : '#f50')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '操作日期',
|
|
||||||
dataIndex: 'startTime',
|
|
||||||
width: 180,
|
|
||||||
customRender: ({ text }) => {
|
|
||||||
return useRender.renderDate(text)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '执行时长',
|
title: '执行时长',
|
||||||
dataIndex: 'duration',
|
dataIndex: 'duration',
|
||||||
|
@ -58,6 +55,14 @@ export const columns: BasicColumn[] = [
|
||||||
customRender: ({ text }) => {
|
customRender: ({ text }) => {
|
||||||
return useRender.renderText(text, 'ms')
|
return useRender.renderText(text, 'ms')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作日期',
|
||||||
|
dataIndex: 'startTime',
|
||||||
|
width: 180,
|
||||||
|
customRender: ({ text }) => {
|
||||||
|
return useRender.renderDate(text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -102,3 +107,92 @@ export const searchFormSchema: FormSchema[] = [
|
||||||
colProps: { span: 8 }
|
colProps: { span: 8 }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const httpMethods = [
|
||||||
|
{ value: 'GET', color: '#108ee9' },
|
||||||
|
{ value: 'POST', color: '#2db7f5' },
|
||||||
|
{ value: 'PUT', color: 'warning' },
|
||||||
|
{ value: 'DELETE', color: '#f50' }
|
||||||
|
]
|
||||||
|
|
||||||
|
export const infoSchema: DescItem[] = [
|
||||||
|
{
|
||||||
|
field: 'module',
|
||||||
|
label: '操作模块'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
label: '操作名'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'userNickname',
|
||||||
|
label: '操作人',
|
||||||
|
render(_, data) {
|
||||||
|
const { userNickname, userId } = data
|
||||||
|
return useRender.renderText(userNickname, 'uid: ' + userId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'resultCode',
|
||||||
|
label: '请求结果',
|
||||||
|
render(value) {
|
||||||
|
return useRender.renderTag(value === 0 ? '成功' : '失败', value === 0 ? '#87d068' : '#f50')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'resultMsg',
|
||||||
|
label: '响应信息',
|
||||||
|
show(data) {
|
||||||
|
return data && data.resultMsg && data.resultMsg !== ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'userIp',
|
||||||
|
label: '请求ip'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'startTime',
|
||||||
|
label: '请求时间',
|
||||||
|
render(value) {
|
||||||
|
return useRender.renderDate(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'requestUrl',
|
||||||
|
label: '请求路径'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'requestMethod',
|
||||||
|
label: '请求方法',
|
||||||
|
render(value) {
|
||||||
|
const current = httpMethods.find((item) => item.value === value.toUpperCase())
|
||||||
|
if (current) {
|
||||||
|
return useRender.renderTag(value, current.color)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'javaMethod',
|
||||||
|
label: '操作方法',
|
||||||
|
labelMinWidth: 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'javaMethodArgs',
|
||||||
|
label: '请求参数',
|
||||||
|
render(value) {
|
||||||
|
return useRender.renderJsonPreview(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'userAgent',
|
||||||
|
label: 'userAgent'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'duration',
|
||||||
|
label: '请求耗时',
|
||||||
|
render(value) {
|
||||||
|
return useRender.renderText(value, 'ms')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
Loading…
Reference in New Issue