Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vben
						commit
						c9cedbf7a2
					
				|  | @ -5,6 +5,7 @@ import { isArray, isString } from '@/utils/is' | |||
| import { DictTag } from '@/components/DictTag' | ||||
| import { Icon } from '@/components/Icon' | ||||
| import TableImg from '../components/TableImg.vue' | ||||
| import { JsonPreview } from '@/components/CodeEditor' | ||||
| 
 | ||||
| export const useRender = { | ||||
|   /** | ||||
|  | @ -112,5 +113,24 @@ export const useRender = { | |||
|     if (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 | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,27 @@ | |||
| <template> | ||||
|   <BasicModal v-bind="$attrs" title="访问日志详情" @register="registerModalInner" @ok="closeModal" width="800px"> | ||||
|     <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 './apiAccessLog.data' | ||||
| 
 | ||||
| defineOptions({ name: 'AcessLogModal' }) | ||||
| 
 | ||||
| const logData = ref() | ||||
| const [registerModalInner, { closeModal }] = useModalInner((record: Recordable) => { | ||||
|   logData.value = record | ||||
| }) | ||||
| 
 | ||||
| const [registerDescription] = useDescription({ | ||||
|   column: 1, | ||||
|   schema: infoSchema, | ||||
|   data: logData | ||||
| }) | ||||
| </script> | ||||
| 
 | ||||
| <style scoped></style> | ||||
|  | @ -1,5 +1,7 @@ | |||
| import { BasicColumn, FormSchema, useRender } from '@/components/Table' | ||||
| import { DICT_TYPE, getDictOptions } from '@/utils/dict' | ||||
| import { DescItem } from '@/components/Description/index' | ||||
| import { h } from 'vue' | ||||
| 
 | ||||
| export const columns: BasicColumn[] = [ | ||||
|   { | ||||
|  | @ -58,7 +60,7 @@ export const columns: BasicColumn[] = [ | |||
|     ellipsis: true, | ||||
|     customRender: ({ record }) => { | ||||
|       const success = record.resultCode === 0 | ||||
|       return useRender.renderTag(success ? '成功' : '失败(' + record.resultMsg + ')', success ? '#87d068' : '#f50') | ||||
|       return useRender.renderTag(success ? '成功' : '失败', success ? '#87d068' : '#f50') | ||||
|     } | ||||
|   } | ||||
| ] | ||||
|  | @ -110,3 +112,110 @@ export const searchFormSchema: FormSchema[] = [ | |||
|     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[] = [ | ||||
|   { | ||||
|     label: '日志id', | ||||
|     field: 'id' | ||||
|   }, | ||||
|   { | ||||
|     label: '链路id', | ||||
|     field: 'traceId', | ||||
|     show: (data) => data && data.traceId && data.traceId !== '' | ||||
|   }, | ||||
|   { | ||||
|     label: '应用名称', | ||||
|     field: 'applicationName', | ||||
|     labelMinWidth: 100 | ||||
|   }, | ||||
|   { | ||||
|     field: 'userId', | ||||
|     label: '用户id', | ||||
|     render(value, data) { | ||||
|       const tag = useRender.renderDict(data.userType, DICT_TYPE.USER_TYPE) | ||||
|       const uidTag = useRender.renderTag('uid: ' + value) | ||||
|       return h('span', {}, [tag, uidTag]) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     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 !== '' | ||||
|     }, | ||||
|     render(value) { | ||||
|       return h('span', { style: { color: 'red', fontWeight: 'bold' } }, value) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'userIp', | ||||
|     label: '请求ip' | ||||
|   }, | ||||
|   { | ||||
|     field: 'userAgent', | ||||
|     label: 'userAgent' | ||||
|   }, | ||||
|   { | ||||
|     field: 'beginTime', | ||||
|     label: '请求时间', | ||||
|     render(value) { | ||||
|       return useRender.renderDate(value) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'requestUrl', | ||||
|     label: '请求路径', | ||||
|     render(_, data) { | ||||
|       if (!data) { | ||||
|         return '' | ||||
|       } | ||||
|       const { requestMethod, requestUrl } = data | ||||
|       const current = httpMethods.find((item) => item.value === requestMethod.toUpperCase()) | ||||
|       const methodTag = current ? useRender.renderTag(requestMethod, current.color) : requestMethod | ||||
|       return h('span', {}, [methodTag, requestUrl]) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'requestParams', | ||||
|     label: '请求参数', | ||||
|     render(value) { | ||||
|       return useRender.renderJsonPreview(value) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'beginTime', | ||||
|     label: '请求开始时间', | ||||
|     render(value) { | ||||
|       return useRender.renderDate(value) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'endTime', | ||||
|     label: '请求结束时间', | ||||
|     render(value) { | ||||
|       return useRender.renderDate(value) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'duration', | ||||
|     label: '请求耗时', | ||||
|     render(value) { | ||||
|       // 为0的话需要转为string  否则不会显示
 | ||||
|       return useRender.renderText(String(value), 'ms') | ||||
|     } | ||||
|   } | ||||
| ] | ||||
|  |  | |||
|  | @ -6,16 +6,32 @@ | |||
|           {{ t('action.export') }} | ||||
|         </a-button> | ||||
|       </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> | ||||
|     <AccessLogModal @register="registerModal" /> | ||||
|   </div> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import { useI18n } from '@/hooks/web/useI18n' | ||||
| import { useMessage } from '@/hooks/web/useMessage' | ||||
| import { BasicTable, useTable } from '@/components/Table' | ||||
| import { BasicTable, useTable, TableAction } from '@/components/Table' | ||||
| import { IconEnum } from '@/enums/appEnum' | ||||
| import { getApiAccessLogPage, exportApiAccessLog, ApiAccessLogExportReqVO } from '@/api/infra/apiAccessLog' | ||||
| import { columns, searchFormSchema } from './apiAccessLog.data' | ||||
| import { useModal } from '@/components/Modal' | ||||
| import AccessLogModal from './AccessLogModal.vue' | ||||
| 
 | ||||
| defineOptions({ name: 'InfraApiErrorLog' }) | ||||
| 
 | ||||
|  | @ -28,9 +44,20 @@ const [registerTable, { getForm }] = useTable({ | |||
|   formConfig: { labelWidth: 120, schemas: searchFormSchema }, | ||||
|   useSearchForm: true, | ||||
|   showTableSetting: true, | ||||
|   showIndexColumn: false | ||||
|   showIndexColumn: false, | ||||
|   actionColumn: { | ||||
|     width: 120, | ||||
|     title: t('common.action'), | ||||
|     dataIndex: 'action', | ||||
|     fixed: 'right' | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| const [registerModal, { openModal }] = useModal() | ||||
| function handleShowInfo(record: Recordable) { | ||||
|   openModal(true, record) | ||||
| } | ||||
| 
 | ||||
| async function handleExport() { | ||||
|   createConfirm({ | ||||
|     title: t('common.exportTitle'), | ||||
|  |  | |||
|  | @ -0,0 +1,27 @@ | |||
| <template> | ||||
|   <BasicModal v-bind="$attrs" title="错误日志详情" @register="registerModalInner" @ok="closeModal" width="800px"> | ||||
|     <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 './apiErrorLog.data' | ||||
| 
 | ||||
| defineOptions({ name: 'ErrorLogModal' }) | ||||
| 
 | ||||
| const logData = ref() | ||||
| const [registerModalInner, { closeModal }] = useModalInner((record: Recordable) => { | ||||
|   logData.value = record | ||||
| }) | ||||
| 
 | ||||
| const [registerDescription] = useDescription({ | ||||
|   column: 1, | ||||
|   schema: infoSchema, | ||||
|   data: logData | ||||
| }) | ||||
| </script> | ||||
| 
 | ||||
| <style scoped></style> | ||||
|  | @ -1,5 +1,8 @@ | |||
| import { BasicColumn, FormSchema, useRender } from '@/components/Table' | ||||
| import { DICT_TYPE, getDictOptions } from '@/utils/dict' | ||||
| import { Textarea } from 'ant-design-vue' | ||||
| import { h } from 'vue' | ||||
| import { DescItem } from '@/components/Description/index' | ||||
| 
 | ||||
| export const columns: BasicColumn[] = [ | ||||
|   { | ||||
|  | @ -108,3 +111,139 @@ export const searchFormSchema: FormSchema[] = [ | |||
|     colProps: { span: 8 } | ||||
|   } | ||||
| ] | ||||
| 
 | ||||
| function renderText(value: string, color: string, bold = true) { | ||||
|   return h('span', { style: { color, fontWeight: bold ? 'bold' : 'normal' } }, value) | ||||
| } | ||||
| 
 | ||||
| const httpMethods = [ | ||||
|   { value: 'GET', color: '#108ee9' }, | ||||
|   { value: 'POST', color: '#2db7f5' }, | ||||
|   { value: 'PUT', color: 'warning' }, | ||||
|   { value: 'DELETE', color: '#f50' } | ||||
| ] | ||||
| 
 | ||||
| export const infoSchema: DescItem[] = [ | ||||
|   { | ||||
|     field: 'id', | ||||
|     label: '异常id' | ||||
|   }, | ||||
|   { | ||||
|     field: 'traceId', | ||||
|     label: '链路ID', | ||||
|     show(data) { | ||||
|       return data && data.traceId && data.traceId !== '' | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'applicationName', | ||||
|     label: '应用名称', | ||||
|     labelMinWidth: 100 | ||||
|   }, | ||||
|   { | ||||
|     field: 'processStatus', | ||||
|     label: '处理状态', | ||||
|     render(_, data) { | ||||
|       const { processStatus, processUserId } = data | ||||
|       const tag = useRender.renderDict(processStatus, DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS) | ||||
|       if (!processUserId) { | ||||
|         return tag | ||||
|       } | ||||
|       const uidTag = useRender.renderTag('uid: ' + processUserId) | ||||
|       return h('span', {}, [tag, uidTag]) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'processTime', | ||||
|     label: '处理时间', | ||||
|     show(data) { | ||||
|       return data && data.processTime && data.processTime !== '' | ||||
|     }, | ||||
|     render(value) { | ||||
|       return useRender.renderDate(value) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'userId', | ||||
|     label: '用户id', | ||||
|     render(value, data) { | ||||
|       const tag = useRender.renderDict(data.userType, DICT_TYPE.USER_TYPE) | ||||
|       const uidTag = useRender.renderTag('uid: ' + value) | ||||
|       return h('span', {}, [tag, uidTag]) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'userIp', | ||||
|     label: 'ip地址' | ||||
|   }, | ||||
|   { | ||||
|     field: 'requestUrl', | ||||
|     label: '请求地址', | ||||
|     render(_, data) { | ||||
|       if (data) { | ||||
|         const { requestMethod } = data | ||||
|         const current = httpMethods.find((item) => item.value === requestMethod) | ||||
|         const tag = current ? useRender.renderTag(requestMethod, current.color) : requestMethod | ||||
|         return h('span', {}, [tag, data.requestUrl]) | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'requestParams', | ||||
|     label: '请求参数', | ||||
|     render(value) { | ||||
|       return useRender.renderJsonPreview(value) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'userAgent', | ||||
|     label: 'userAgent' | ||||
|   }, | ||||
|   { | ||||
|     field: 'exceptionTime', | ||||
|     label: '异常时间', | ||||
|     render(value) { | ||||
|       return useRender.renderDate(value) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'exceptionClassName', | ||||
|     label: '异常类名/方法', | ||||
|     render(_, data) { | ||||
|       if (data) { | ||||
|         return renderText(data.exceptionClassName + ' / ' + data.exceptionMethodName, 'red') | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'exceptionMessage', | ||||
|     label: '异常信息', | ||||
|     render(value) { | ||||
|       return renderText(value, 'red') | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'exceptionFileName', | ||||
|     label: '异常文件名', | ||||
|     render(_, data) { | ||||
|       if (data) { | ||||
|         return useRender.renderText(data.exceptionFileName, 'Line: ' + data.exceptionLineNumber) | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'exceptionName', | ||||
|     label: '异常名称' | ||||
|   }, | ||||
|   { | ||||
|     field: 'exceptionRootCauseMessage', | ||||
|     label: '异常信息' | ||||
|   }, | ||||
|   { | ||||
|     field: 'exceptionStackTrace', | ||||
|     label: '异常堆栈', | ||||
|     render(value) { | ||||
|       return h(Textarea, { value, readonly: true, style: { minHeight: '300px' } }) | ||||
|     } | ||||
|   } | ||||
| ] | ||||
|  |  | |||
|  | @ -10,6 +10,11 @@ | |||
|         <template v-if="column.key === 'action'"> | ||||
|           <TableAction | ||||
|             :actions="[ | ||||
|               { | ||||
|                 icon: IconEnum.VIEW, | ||||
|                 label: t('action.detail'), | ||||
|                 onClick: handleShowInfo.bind(null, record) | ||||
|               }, | ||||
|               { | ||||
|                 icon: IconEnum.EDIT, | ||||
|                 label: '已处理', | ||||
|  | @ -29,6 +34,7 @@ | |||
|         </template> | ||||
|       </template> | ||||
|     </BasicTable> | ||||
|     <ErrorLogModal @register="registerModal" /> | ||||
|   </div> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
|  | @ -39,6 +45,8 @@ import { useMessage } from '@/hooks/web/useMessage' | |||
| import { BasicTable, useTable, TableAction } from '@/components/Table' | ||||
| import { updateApiErrorLogProcess, getApiErrorLogPage, exportApiErrorLog, ApiErrorLogExportReqVO } from '@/api/infra/apiErrorLog' | ||||
| import { columns, searchFormSchema } from './apiErrorLog.data' | ||||
| import { useModal } from '@/components/Modal' | ||||
| import ErrorLogModal from './ErrorLogModal.vue' | ||||
| 
 | ||||
| defineOptions({ name: 'InfraApiErrorLog' }) | ||||
| 
 | ||||
|  | @ -53,13 +61,18 @@ const [registerTable, { getForm, reload }] = useTable({ | |||
|   showTableSetting: true, | ||||
|   showIndexColumn: false, | ||||
|   actionColumn: { | ||||
|     width: 180, | ||||
|     width: 220, | ||||
|     title: t('common.action'), | ||||
|     dataIndex: 'action', | ||||
|     fixed: 'right' | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| const [registerModal, { openModal }] = useModal() | ||||
| function handleShowInfo(record: Recordable) { | ||||
|   openModal(true, record) | ||||
| } | ||||
| 
 | ||||
| function handleProcessClick(record, processStatus: number, type: string) { | ||||
|   createConfirm({ | ||||
|     iconType: 'warning', | ||||
|  |  | |||
|  | @ -1,6 +1,74 @@ | |||
| import { BasicColumn, FormSchema, useRender } from '@/components/Table' | ||||
| 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[] = [ | ||||
|   { | ||||
|     title: '字典编码', | ||||
|  | @ -119,67 +187,15 @@ export const dataFormSchema: FormSchema[] = [ | |||
|     field: 'colorType', | ||||
|     component: 'Select', | ||||
|     componentProps: { | ||||
|       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' | ||||
|         } | ||||
|       ] | ||||
|       options: previewOptions() | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     label: 'CSS Class', | ||||
|     field: 'cssClass', | ||||
|     component: 'Input', | ||||
|     helpMessage: '输入hex模式的颜色,例如#108ee9' | ||||
|     helpMessage: '输入hex模式的颜色, 例如#108ee9', | ||||
|     rules: [{ required: false, message: '输入正确的16进制颜色', pattern: /^#([0-9a-fA-F]{3}){1,2}$/, trigger: 'blur' }] | ||||
|   }, | ||||
|   { | ||||
|     label: '备注', | ||||
|  |  | |||
|  | @ -0,0 +1,27 @@ | |||
| <template> | ||||
|   <BasicModal v-bind="$attrs" title="操作日志详情" @register="registerModalInner" @ok="closeModal" width="800px"> | ||||
|     <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, { closeModal }] = 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> | ||||
|         <a-button type="warning" :preIcon="IconEnum.EXPORT" @click="handleExport"> {{ t('action.export') }} </a-button> | ||||
|       </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> | ||||
|     <OperLogInfoModal @register="registerModal" /> | ||||
|   </div> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import { useI18n } from '@/hooks/web/useI18n' | ||||
| import { useMessage } from '@/hooks/web/useMessage' | ||||
| 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 { columns, searchFormSchema } from './operateLog.data' | ||||
| import { useModal } from '@/components/Modal' | ||||
| import OperLogInfoModal from './LogInfoModal.vue' | ||||
| 
 | ||||
| defineOptions({ name: 'SystemOperateLog' }) | ||||
| 
 | ||||
|  | @ -26,7 +42,13 @@ const [registerTable, { getForm }] = useTable({ | |||
|   formConfig: { labelWidth: 120, schemas: searchFormSchema }, | ||||
|   useSearchForm: true, | ||||
|   showTableSetting: true, | ||||
|   showIndexColumn: false | ||||
|   showIndexColumn: false, | ||||
|   actionColumn: { | ||||
|     width: 140, | ||||
|     title: t('common.action'), | ||||
|     dataIndex: 'action', | ||||
|     fixed: 'right' | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| async function handleExport() { | ||||
|  | @ -40,4 +62,9 @@ async function handleExport() { | |||
|     } | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| const [registerModal, { openModal }] = useModal() | ||||
| function handleShowInfo(record: Recordable) { | ||||
|   openModal(true, record) | ||||
| } | ||||
| </script> | ||||
|  |  | |||
|  | @ -1,5 +1,7 @@ | |||
| import { BasicColumn, FormSchema, useRender } from '@/components/Table' | ||||
| import { DICT_TYPE, getDictOptions } from '@/utils/dict' | ||||
| import { DescItem } from '@/components/Description/index' | ||||
| import { h } from 'vue' | ||||
| 
 | ||||
| export const columns: BasicColumn[] = [ | ||||
|   { | ||||
|  | @ -10,7 +12,7 @@ export const columns: BasicColumn[] = [ | |||
|   { | ||||
|     title: '操作模块', | ||||
|     dataIndex: 'module', | ||||
|     width: 120 | ||||
|     width: 200 | ||||
|   }, | ||||
|   { | ||||
|     title: '操作名', | ||||
|  | @ -20,7 +22,7 @@ export const columns: BasicColumn[] = [ | |||
|   { | ||||
|     title: '操作类型', | ||||
|     dataIndex: 'type', | ||||
|     width: 180, | ||||
|     width: 120, | ||||
|     customRender: ({ text }) => { | ||||
|       return useRender.renderDict(text, DICT_TYPE.SYSTEM_OPERATE_TYPE) | ||||
|     } | ||||
|  | @ -30,10 +32,14 @@ export const columns: BasicColumn[] = [ | |||
|     dataIndex: 'userNickname', | ||||
|     width: 120 | ||||
|   }, | ||||
|   // {
 | ||||
|   //   title: 'userAgent',
 | ||||
|   //   dataIndex: 'userAgent',
 | ||||
|   //   width: 400
 | ||||
|   // },
 | ||||
|   { | ||||
|     title: 'userAgent', | ||||
|     dataIndex: 'userAgent', | ||||
|     width: 400 | ||||
|     title: '请求路径', | ||||
|     dataIndex: 'requestUrl' | ||||
|   }, | ||||
|   { | ||||
|     title: '操作结果', | ||||
|  | @ -43,14 +49,6 @@ export const columns: BasicColumn[] = [ | |||
|       return useRender.renderTag(text === 0 ? '成功' : '失败', text === 0 ? '#87d068' : '#f50') | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: '操作日期', | ||||
|     dataIndex: 'startTime', | ||||
|     width: 180, | ||||
|     customRender: ({ text }) => { | ||||
|       return useRender.renderDate(text) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: '执行时长', | ||||
|     dataIndex: 'duration', | ||||
|  | @ -58,6 +56,14 @@ export const columns: BasicColumn[] = [ | |||
|     customRender: ({ text }) => { | ||||
|       return useRender.renderText(text, 'ms') | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: '操作日期', | ||||
|     dataIndex: 'startTime', | ||||
|     width: 180, | ||||
|     customRender: ({ text }) => { | ||||
|       return useRender.renderDate(text) | ||||
|     } | ||||
|   } | ||||
| ] | ||||
| 
 | ||||
|  | @ -102,3 +108,94 @@ export const searchFormSchema: FormSchema[] = [ | |||
|     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)
 | ||||
|       return useRender.renderTags([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 !== '' | ||||
|     }, | ||||
|     render(value) { | ||||
|       return h('span', { style: { color: 'red', fontWeight: 'bold' } }, value) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'userIp', | ||||
|     label: '请求ip' | ||||
|   }, | ||||
|   { | ||||
|     field: 'startTime', | ||||
|     label: '请求时间', | ||||
|     render(value) { | ||||
|       return useRender.renderDate(value) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     field: 'requestUrl', | ||||
|     label: '请求路径', | ||||
|     render(_, data) { | ||||
|       if (!data) { | ||||
|         return '' | ||||
|       } | ||||
|       const { requestMethod, requestUrl } = data | ||||
|       const current = httpMethods.find((item) => item.value === requestMethod.toUpperCase()) | ||||
|       const methodTag = current ? useRender.renderTag(requestMethod, current.color) : requestMethod | ||||
|       return h('span', {}, [methodTag, requestUrl]) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     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
	
	 xingyu
						xingyu