first commit
This commit is contained in:
146
src/pages-infra/api-access-log/components/search-form.vue
Normal file
146
src/pages-infra/api-access-log/components/search-form.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<!-- 搜索框入口 -->
|
||||
<view @click="visible = true">
|
||||
<wd-search :placeholder="placeholder" hide-cancel disabled />
|
||||
</view>
|
||||
|
||||
<!-- 搜索弹窗 -->
|
||||
<wd-popup v-model="visible" position="top" @close="visible = false">
|
||||
<view class="yd-search-form-container" :style="{ paddingTop: `${getNavbarHeight()}px` }">
|
||||
<view class="yd-search-form-item">
|
||||
<view class="yd-search-form-label">
|
||||
用户编号
|
||||
</view>
|
||||
<wd-input
|
||||
v-model="formData.userId"
|
||||
placeholder="请输入用户编号"
|
||||
clearable
|
||||
/>
|
||||
</view>
|
||||
<view class="yd-search-form-item">
|
||||
<view class="yd-search-form-label">
|
||||
应用名
|
||||
</view>
|
||||
<wd-input
|
||||
v-model="formData.applicationName"
|
||||
placeholder="请输入应用名"
|
||||
clearable
|
||||
/>
|
||||
</view>
|
||||
<view class="yd-search-form-item">
|
||||
<view class="yd-search-form-label">
|
||||
访问时间
|
||||
</view>
|
||||
<view class="yd-search-form-date-range-container">
|
||||
<view class="flex-1" @click="visibleCreateTime[0] = true">
|
||||
<view class="yd-search-form-date-range-picker">
|
||||
{{ formatDate(formData.createTime?.[0]) || '开始日期' }}
|
||||
</view>
|
||||
</view>
|
||||
-
|
||||
<view class="flex-1" @click="visibleCreateTime[1] = true">
|
||||
<view class="yd-search-form-date-range-picker">
|
||||
{{ formatDate(formData.createTime?.[1]) || '结束日期' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-datetime-picker-view v-if="visibleCreateTime[0]" v-model="tempCreateTime[0]" type="date" />
|
||||
<view v-if="visibleCreateTime[0]" class="yd-search-form-date-range-actions">
|
||||
<wd-button size="small" plain @click="visibleCreateTime[0] = false">
|
||||
取消
|
||||
</wd-button>
|
||||
<wd-button size="small" type="primary" @click="handleCreateTime0Confirm">
|
||||
确定
|
||||
</wd-button>
|
||||
</view>
|
||||
<wd-datetime-picker-view v-if="visibleCreateTime[1]" v-model="tempCreateTime[1]" type="date" />
|
||||
<view v-if="visibleCreateTime[1]" class="yd-search-form-date-range-actions">
|
||||
<wd-button size="small" plain @click="visibleCreateTime[1] = false">
|
||||
取消
|
||||
</wd-button>
|
||||
<wd-button size="small" type="primary" @click="handleCreateTime1Confirm">
|
||||
确定
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="yd-search-form-actions">
|
||||
<wd-button class="flex-1" plain @click="handleReset">
|
||||
重置
|
||||
</wd-button>
|
||||
<wd-button class="flex-1" type="primary" @click="handleSearch">
|
||||
搜索
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { getNavbarHeight } from '@/utils'
|
||||
import { formatDate, formatDateRange } from '@/utils/date'
|
||||
|
||||
const emit = defineEmits<{
|
||||
search: [data: Record<string, any>]
|
||||
reset: []
|
||||
}>()
|
||||
|
||||
const visible = ref(false)
|
||||
const formData = reactive({
|
||||
userId: undefined as number | undefined,
|
||||
applicationName: undefined as string | undefined,
|
||||
createTime: [undefined, undefined] as [number | undefined, number | undefined],
|
||||
})
|
||||
|
||||
/** 搜索条件 placeholder 拼接 */
|
||||
const placeholder = computed(() => {
|
||||
const conditions: string[] = []
|
||||
if (formData.userId) {
|
||||
conditions.push(`用户编号:${formData.userId}`)
|
||||
}
|
||||
if (formData.applicationName) {
|
||||
conditions.push(`应用名:${formData.applicationName}`)
|
||||
}
|
||||
if (formData.createTime?.[0] && formData.createTime?.[1]) {
|
||||
conditions.push(`时间:${formatDate(formData.createTime[0])}~${formatDate(formData.createTime[1])}`)
|
||||
}
|
||||
return conditions.length > 0 ? conditions.join(' | ') : '搜索日志'
|
||||
})
|
||||
|
||||
// 时间范围选择器状态
|
||||
const visibleCreateTime = ref<[boolean, boolean]>([false, false])
|
||||
const tempCreateTime = ref<[number, number]>([Date.now(), Date.now()])
|
||||
|
||||
/** 访问时间[0]确认 */
|
||||
function handleCreateTime0Confirm() {
|
||||
formData.createTime = [tempCreateTime.value[0], formData.createTime?.[1]]
|
||||
visibleCreateTime.value[0] = false
|
||||
}
|
||||
|
||||
/** 访问时间[1]确认 */
|
||||
function handleCreateTime1Confirm() {
|
||||
formData.createTime = [formData.createTime?.[0], tempCreateTime.value[1]]
|
||||
visibleCreateTime.value[1] = false
|
||||
}
|
||||
|
||||
/** 搜索 */
|
||||
function handleSearch() {
|
||||
visible.value = false
|
||||
const dateRange = formatDateRange(formData.createTime)
|
||||
emit('search', {
|
||||
userId: formData.userId,
|
||||
applicationName: formData.applicationName,
|
||||
beginTime: dateRange?.[0],
|
||||
endTime: dateRange?.[1],
|
||||
})
|
||||
}
|
||||
|
||||
/** 重置 */
|
||||
function handleReset() {
|
||||
formData.userId = undefined
|
||||
formData.applicationName = undefined
|
||||
formData.createTime = [undefined, undefined]
|
||||
visible.value = false
|
||||
emit('reset')
|
||||
}
|
||||
</script>
|
||||
133
src/pages-infra/api-access-log/detail/index.vue
Normal file
133
src/pages-infra/api-access-log/detail/index.vue
Normal file
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<view class="yd-page-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<wd-navbar
|
||||
title="访问日志详情"
|
||||
left-arrow placeholder safe-area-inset-top fixed
|
||||
@click-left="handleBack"
|
||||
/>
|
||||
|
||||
<!-- 详情内容 -->
|
||||
<view>
|
||||
<wd-cell-group border>
|
||||
<wd-cell title="日志编号" :value="formData?.id" />
|
||||
<wd-cell title="链路追踪" :value="formData?.traceId || '-'" />
|
||||
<wd-cell title="应用名" :value="formData?.applicationName" />
|
||||
<wd-cell title="用户编号" :value="formData?.userId ?? '-'" />
|
||||
<wd-cell title="用户类型" :value="getDictLabel(DICT_TYPE.USER_TYPE, formData?.userType) || '-'" />
|
||||
<wd-cell title="用户 IP" :value="formData?.userIp" />
|
||||
<wd-cell title="用户 UA" :value="formData?.userAgent" />
|
||||
<wd-cell title="请求信息" :value="getRequestInfo()" />
|
||||
<wd-cell title="请求参数" is-link @click="handleCopyText(formData?.requestParams, '请求参数')">
|
||||
<view class="max-w-400rpx truncate text-right">
|
||||
{{ formData?.requestParams || '-' }}
|
||||
</view>
|
||||
</wd-cell>
|
||||
<wd-cell title="请求结果" is-link @click="handleCopyText(formData?.responseBody, '请求结果')">
|
||||
<view class="max-w-400rpx truncate text-right">
|
||||
{{ formData?.responseBody || '-' }}
|
||||
</view>
|
||||
</wd-cell>
|
||||
<wd-cell title="请求时间" :value="getRequestTimeRange()" />
|
||||
<wd-cell title="请求耗时" :value="`${formData.duration} ms`" />
|
||||
<wd-cell title="操作结果">
|
||||
<template v-if="formData?.resultCode === 0">
|
||||
<wd-tag type="success" plain>
|
||||
正常
|
||||
</wd-tag>
|
||||
</template>
|
||||
<template v-else-if="formData?.resultCode">
|
||||
<text>失败 | {{ formData.resultCode }} | {{ formData.resultMsg }}</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text>-</text>
|
||||
</template>
|
||||
</wd-cell>
|
||||
<wd-cell title="操作模块" :value="formData?.operateModule || '-'" />
|
||||
<wd-cell title="操作名" :value="formData?.operateName || '-'" />
|
||||
<wd-cell title="操作类型" :value="getDictLabel(DICT_TYPE.INFRA_OPERATE_TYPE, formData?.operateType) || '-'" />
|
||||
</wd-cell-group>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ApiAccessLog } from '@/api/infra/api-access-log'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useToast } from 'wot-design-uni'
|
||||
import { getApiAccessLog } from '@/api/infra/api-access-log'
|
||||
import { getDictLabel } from '@/hooks/useDict'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
|
||||
const props = defineProps<{
|
||||
id: number | any
|
||||
}>()
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
})
|
||||
|
||||
const formData = ref<ApiAccessLog>() // 详情数据
|
||||
const toast = useToast()
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus('/pages-infra/api-access-log/index')
|
||||
}
|
||||
|
||||
/** 复制文本并提示 */
|
||||
function handleCopyText(text?: string, title?: string) {
|
||||
if (!text || text === '-') {
|
||||
return
|
||||
}
|
||||
uni.setClipboardData({
|
||||
data: text,
|
||||
success: () => {
|
||||
uni.hideToast()
|
||||
toast.success(`${title || '内容'}已复制`)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/** 加载详情 */
|
||||
async function getDetail() {
|
||||
if (!props.id) {
|
||||
return
|
||||
}
|
||||
toast.loading('加载中...')
|
||||
try {
|
||||
formData.value = await getApiAccessLog(props.id)
|
||||
} finally {
|
||||
toast.close()
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取请求信息 */
|
||||
function getRequestInfo() {
|
||||
if (formData.value?.requestMethod && formData.value?.requestUrl) {
|
||||
return `${formData.value.requestMethod} ${formData.value.requestUrl}`
|
||||
}
|
||||
return '-'
|
||||
}
|
||||
|
||||
/** 获取请求时间范围 */
|
||||
function getRequestTimeRange() {
|
||||
if (formData.value?.beginTime && formData.value?.endTime) {
|
||||
return `${formatDateTime(formData.value.beginTime)} ~ ${formatDateTime(formData.value.endTime)}`
|
||||
}
|
||||
return '-'
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
158
src/pages-infra/api-access-log/index.vue
Normal file
158
src/pages-infra/api-access-log/index.vue
Normal file
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<view class="yd-page-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<wd-navbar
|
||||
title="API 访问日志"
|
||||
left-arrow placeholder safe-area-inset-top fixed
|
||||
@click-left="handleBack"
|
||||
/>
|
||||
|
||||
<!-- 搜索组件 -->
|
||||
<SearchForm @search="handleQuery" @reset="handleReset" />
|
||||
|
||||
<!-- 日志列表 -->
|
||||
<view class="p-24rpx">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm"
|
||||
@click="handleDetail(item)"
|
||||
>
|
||||
<view class="p-24rpx">
|
||||
<view class="mb-16rpx flex items-center justify-between gap-16rpx">
|
||||
<view class="min-w-0 flex-1 truncate text-28rpx text-[#333] font-semibold">
|
||||
{{ item.requestMethod }} {{ item.requestUrl }}
|
||||
</view>
|
||||
<view class="flex-shrink-0">
|
||||
<wd-tag v-if="item.resultCode === 0" type="success" plain>
|
||||
成功
|
||||
</wd-tag>
|
||||
<wd-tag v-else type="danger" plain>
|
||||
失败
|
||||
</wd-tag>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-26rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">应用名:</text>
|
||||
<text>{{ item.applicationName }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-26rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">用户编号:</text>
|
||||
<text>{{ item.userId }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-26rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">执行时长:</text>
|
||||
<text>{{ item.duration }} ms</text>
|
||||
</view>
|
||||
<view v-if="item.operateName" class="mb-12rpx flex items-center text-26rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">操作名:</text>
|
||||
<text class="line-clamp-1">{{ item.operateName }}</text>
|
||||
</view>
|
||||
<view class="flex items-center text-24rpx text-[#999]">
|
||||
<text>{{ formatDateTime(item.beginTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view v-if="loadMoreState !== 'loading' && list.length === 0" class="py-100rpx text-center">
|
||||
<wd-status-tip image="content" tip="暂无日志数据" />
|
||||
</view>
|
||||
<wd-loadmore
|
||||
v-if="list.length > 0"
|
||||
:state="loadMoreState"
|
||||
@reload="loadMore"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ApiAccessLog } from '@/api/infra/api-access-log'
|
||||
import type { LoadMoreState } from '@/http/types'
|
||||
import { onReachBottom } from '@dcloudio/uni-app'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { getApiAccessLogPage } from '@/api/infra/api-access-log'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
import SearchForm from './components/search-form.vue'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
})
|
||||
|
||||
const total = ref(0)
|
||||
const list = ref<ApiAccessLog[]>([])
|
||||
const loadMoreState = ref<LoadMoreState>('loading')
|
||||
const queryParams = ref({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
})
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus()
|
||||
}
|
||||
|
||||
/** 查询日志列表 */
|
||||
async function getList() {
|
||||
loadMoreState.value = 'loading'
|
||||
try {
|
||||
const data = await getApiAccessLogPage(queryParams.value)
|
||||
list.value = [...list.value, ...data.list]
|
||||
total.value = data.total
|
||||
loadMoreState.value = list.value.length >= total.value ? 'finished' : 'loading'
|
||||
} catch {
|
||||
queryParams.value.pageNo = queryParams.value.pageNo > 1 ? queryParams.value.pageNo - 1 : 1
|
||||
loadMoreState.value = 'error'
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery(data?: Record<string, any>) {
|
||||
queryParams.value = {
|
||||
...data,
|
||||
pageNo: 1,
|
||||
pageSize: queryParams.value.pageSize,
|
||||
}
|
||||
list.value = []
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function handleReset() {
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 加载更多 */
|
||||
function loadMore() {
|
||||
if (loadMoreState.value === 'finished') {
|
||||
return
|
||||
}
|
||||
queryParams.value.pageNo++
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 查看详情 */
|
||||
function handleDetail(item: ApiAccessLog) {
|
||||
uni.navigateTo({
|
||||
url: `/pages-infra/api-access-log/detail/index?id=${item.id}`,
|
||||
})
|
||||
}
|
||||
|
||||
/** 触底加载更多 */
|
||||
onReachBottom(() => {
|
||||
loadMore()
|
||||
})
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
Reference in New Issue
Block a user