index.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. <template>
  2. <div class="store-page">
  3. <Breadcrumb />
  4. <a-card title="请求日志">
  5. <AppQueryFilter>
  6. <a-row class="query-row app-query-form">
  7. <a-col :flex="1">
  8. <a-form :model="queryData" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
  9. label-align="left">
  10. <a-row :gutter="16">
  11. <a-col :span="8">
  12. <a-form-item field="begin_time" label="开始时间">
  13. <a-date-picker show-time format="YYYY-MM-DD HH:mm:ss"
  14. v-model="queryData.begin_time" />
  15. </a-form-item>
  16. </a-col>
  17. <a-col :span="8">
  18. <a-form-item field="end_time" label="结束时间">
  19. <a-date-picker show-time format="YYYY-MM-DD HH:mm:ss"
  20. v-model="queryData.end_time" />
  21. </a-form-item>
  22. </a-col>
  23. <a-col :span="8">
  24. <a-form-item field="create_user" label="用户UUID">
  25. <a-input v-model="queryData.create_user" />
  26. </a-form-item>
  27. </a-col>
  28. <a-col :span="8">
  29. <a-form-item field="url" label="请求URL">
  30. <a-input v-model="queryData.url" />
  31. </a-form-item>
  32. </a-col>
  33. <a-col :span="8">
  34. <a-form-item field="ip" label="请求IP">
  35. <a-input v-model="queryData.ip" />
  36. </a-form-item>
  37. </a-col>
  38. </a-row>
  39. </a-form>
  40. </a-col>
  41. <a-divider style="height: 84px" direction="vertical" />
  42. <a-col :flex="'86px'" style="text-align: right">
  43. <a-space direction="vertical" :size="18">
  44. <a-button type="primary" @click="search">
  45. <template #icon>
  46. <icon-search />
  47. </template>
  48. 搜索
  49. </a-button>
  50. <a-button @click="reset">
  51. <template #icon>
  52. <icon-refresh />
  53. </template>
  54. 重置
  55. </a-button>
  56. </a-space>
  57. </a-col>
  58. </a-row>
  59. </AppQueryFilter>
  60. <a-table :bordered="false" :data="data" stripe hoverable column-resizable class="table" :loading="loading" :columns="columns"
  61. :pagination="{
  62. showPageSize: true,
  63. showJumper: true,
  64. showTotal: true,
  65. pageSize: pagination.pagesize,
  66. current: pagination.current,
  67. total: pagination.total
  68. }" @page-change="handlePageChange" @page-size-change="handlePageSizeChange">
  69. <template #username="{ record }">
  70. <a-avatar :size="30">
  71. <img :alt="record.username ?? ''"
  72. :src="record.avatar ?? 'https://lepao-cloud.xxoo365.top/view.php/25aa126dc406974ff3579a99a2c6501a.png'" />
  73. </a-avatar>
  74. {{ record.username }}
  75. </template>
  76. <template #time="{ record }">
  77. {{ stramptoTime(record.create_time) }}
  78. </template>
  79. <template #optional="{ record }">
  80. <a-button @click="showDetail(record)">查看详情</a-button>
  81. </template>
  82. </a-table>
  83. </a-card>
  84. </div>
  85. <a-modal v-model:visible="showModal" hide-cancel width="auto" style="min-width: 500px;" draggable>
  86. <template #title>
  87. 日志详情
  88. </template>
  89. <div>
  90. <a-descriptions :data="modalData" :column="1" />
  91. </div>
  92. </a-modal>
  93. </template>
  94. <script setup>
  95. import { ref, reactive, onMounted } from 'vue'
  96. import { adminGetReqLog, adminGetReqLogDetail } from '@/api/user'
  97. import { Notification } from '@arco-design/web-vue'
  98. const showModal = ref(false)
  99. const modalData = ref([])
  100. const queryData = reactive({
  101. create_user: '',
  102. url: '',
  103. ip: '',
  104. begin_time: null,
  105. end_time: null
  106. })
  107. const pagination = reactive({
  108. total: 0,
  109. current: 1, // 默认从第1页开始
  110. pagesize: 20
  111. })
  112. const loading = ref(false)
  113. const data = ref([])
  114. const columns = [{
  115. title: 'ID',
  116. dataIndex: 'id',
  117. }, {
  118. title: '操作人',
  119. slotName: 'username'
  120. }, {
  121. title: '请求路径',
  122. dataIndex: 'url',
  123. }, {
  124. title: '请求方法',
  125. dataIndex: 'method',
  126. }, {
  127. title: '请求IP',
  128. dataIndex: 'ip',
  129. }, {
  130. title: 'IP属地',
  131. dataIndex: 'location',
  132. }, {
  133. title: '客户端类型',
  134. dataIndex: 'deviceType'
  135. }, {
  136. title: '操作时间',
  137. slotName: 'time',
  138. }, {
  139. title: '状态码',
  140. dataIndex: 'code'
  141. }, {
  142. title: '操作',
  143. slotName: 'optional'
  144. }
  145. ]
  146. const search = () => {
  147. pagination.current = 1
  148. getLogList()
  149. }
  150. const reset = () => {
  151. queryData.create_user = ''
  152. queryData.url = ''
  153. queryData.ip = ''
  154. queryData.begin_time = null
  155. queryData.end_time = null
  156. getLogList()
  157. }
  158. const showDetail = async (record) => {
  159. try {
  160. loading.value = true
  161. showModal.value = false
  162. const res = await adminGetReqLogDetail({ id: record.id })
  163. if (!res || res.code !== 0)
  164. return Notification.error({
  165. title: '获取日志数据失败!',
  166. content: res?.msg ?? '请稍后再试'
  167. })
  168. const data = [{
  169. label: '操作人',
  170. value: res.data.username,
  171. }, {
  172. label: '操作人UUID',
  173. value: res.data.create_user,
  174. }, {
  175. label: '请求方式',
  176. value: res.data.method.toUpperCase()
  177. }, {
  178. label: '请求路径',
  179. value: res.data.url,
  180. }, {
  181. label: '请求IP',
  182. value: res.data.ip
  183. }, {
  184. label: 'IP属地',
  185. value: res.data.location
  186. }, {
  187. label: 'UA',
  188. value: res.data.ua
  189. },
  190. {
  191. label: '客户端类型',
  192. value: res.data.deviceType
  193. }, {
  194. label: '请求数据',
  195. value: JSON.stringify(res.data.reqData, null, 4)
  196. }, {
  197. label: '响应数据',
  198. value: JSON.stringify(res.data.resData, null, 4)
  199. },
  200. {
  201. label: '状态码',
  202. value: res.data.code
  203. },
  204. {
  205. label: '操作时间',
  206. value: stramptoTime(res.data.create_time)
  207. },
  208. ]
  209. modalData.value = data
  210. showModal.value = true
  211. } catch (error) {
  212. Notification.error({
  213. title: '获取日志数据失败!',
  214. content: error.message || '请稍后再试'
  215. })
  216. } finally {
  217. loading.value = false
  218. }
  219. }
  220. const getLogList = async () => {
  221. try {
  222. loading.value = true
  223. const reqData = {
  224. create_user: queryData.create_user,
  225. begin_time: new Date(queryData.begin_time).getTime(),
  226. end_time: new Date(queryData.end_time).getTime(),
  227. url: queryData.url,
  228. ip: queryData.ip,
  229. pagesize: pagination.pagesize,
  230. current: pagination.current
  231. }
  232. const res = await adminGetReqLog(reqData)
  233. if (!res || res.code !== 0)
  234. return Notification.error({
  235. title: '获取日志数据失败!',
  236. content: res?.msg ?? '请稍后再试'
  237. })
  238. data.value = res.data
  239. pagination.total = res.pagination.total
  240. } catch (error) {
  241. Notification.error({
  242. title: '获取日志数据失败!',
  243. content: error.message || '请稍后再试'
  244. })
  245. } finally {
  246. loading.value = false
  247. }
  248. }
  249. // 分页 - 页码变化
  250. const handlePageChange = (page) => {
  251. pagination.current = page
  252. getLogList()
  253. }
  254. // 分页 - 每页条数变化
  255. const handlePageSizeChange = (size) => {
  256. pagination.pagesize = size
  257. pagination.current = 1 // 页大小变化后回到第一页
  258. getLogList()
  259. }
  260. onMounted(() => {
  261. getLogList()
  262. })
  263. const stramptoTime = (time) => {
  264. return new Date(time).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })
  265. }
  266. </script>
  267. <style scoped>
  268. .table {
  269. margin-top: 15px;
  270. }
  271. </style>