index.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. <template>
  2. <div class="new-app-container layout-vertical">
  3. <YMTableSearch :loading="loading" :search-form="searchForm" @submit="onSubmit" />
  4. <YMTable ref="table" :table-key="5" :index="false" :selection="false" :columns="columns"
  5. :rows-actions="rowsActions" :apis="apis" :custom-style="{ height: 'calc(100vh - 200px)' }"
  6. :actions-width="100"
  7. :actions-fixed="false">
  8. </YMTable>
  9. <!-- 查看详情弹窗 -->
  10. <Dialog ref="viewDialog" :dialog-actions="{
  11. width: '820px',
  12. title: '登录日志详情',
  13. confirmButtom: false,
  14. }">
  15. <template slot="content">
  16. <div style="display: flex; flex-wrap: wrap; gap: 12px; padding: 10px 0; max-height: 500px; overflow-y: auto;">
  17. <div v-for="field in viewFields" :key="field.prop"
  18. style="flex: 0 0 calc(33.33% - 12px); background: #f5f7fa; border-radius: 6px; padding: 12px 16px; box-sizing: border-box; border: 1px solid #e4e7ed;">
  19. <div style="font-size: 13px; color: #909399; margin-bottom: 4px;">{{ field.label }}</div>
  20. <div v-if="field.type === 'status'"
  21. :style="{ color: Number(field.rawValue) === 1 ? '#18C80A' : '#909399', fontSize: '15px' }">
  22. {{ field.value }}
  23. </div>
  24. <div v-else style="color: #303133; font-size: 15px; word-break: break-all;">{{ field.value }}</div>
  25. </div>
  26. </div>
  27. </template>
  28. </Dialog>
  29. </div>
  30. </template>
  31. <script>
  32. import Dialog from '@/components/Dialog/index.vue'
  33. export default {
  34. name: 'UserDiary',
  35. components: {
  36. Dialog
  37. },
  38. data() {
  39. return {
  40. loading: false,
  41. viewRow: null,
  42. searchForm: [
  43. { label: '会员ID', type: 'input', prop: 'member_id', clearable: true },
  44. { label: '会员昵称', type: 'input', prop: 'first_name', clearable: true },
  45. { label: '日期范围', type: 'daterange', prop: 'dateRange', valueFormat: 'yyyy-MM-dd',
  46. startPlaceholder: '开始日期', endPlaceholder: '结束日期' }
  47. ]
  48. }
  49. },
  50. computed: {
  51. apis() {
  52. return {
  53. list: {
  54. url: 'user/loginLog',
  55. dataParam: 'data.data',
  56. resultParam: 'data'
  57. }
  58. }
  59. },
  60. columns() {
  61. return [
  62. { label: '会员ID', prop: 'member_id', width: 100 },
  63. { label: '会员昵称', prop: 'first_name', width: 140 },
  64. { label: 'IP地址', prop: 'login_ip', width: 150, align: 'center',
  65. method: (row) => row.login_ip || '--' },
  66. { label: '国家/地区', prop: 'country', width: 120, align: 'center',
  67. method: (row) => row.country || '--' },
  68. { label: '浏览器核心', prop: 'browser', width: 120, align: 'center',
  69. method: (row) => row.browser || '--' },
  70. { label: '操作系统', prop: 'os', width: 120, align: 'center',
  71. method: (row) => row.os || '--' },
  72. { label: '状态', prop: 'status', width: 80, align: 'center',
  73. html: true,
  74. method: (row) => {
  75. if (row.status == null) return '--'
  76. const isOnline = Number(row.status) === 1
  77. const color = isOnline ? '#18C80A' : '#909399'
  78. const text = isOnline ? '在线' : '离线'
  79. return `<span style="color: ${color}">${text}</span>`
  80. } },
  81. { label: '在线时长(分)', prop: 'online_duration', width: 120, align: 'center',
  82. method: (row) => row.online_duration != null ? row.online_duration : '--' },
  83. { label: '登入时间', prop: 'created_at', width: 175, align: 'center' },
  84. { label: '登出时间', prop: 'logout_time', width: 175, align: 'center',
  85. method: (row) => row.logout_time ? row.logout_time : '--' },
  86. ]
  87. },
  88. rowsActions() {
  89. return [
  90. {
  91. key: 'view',
  92. label: '查看',
  93. method: (row) => {
  94. this.viewRow = { ...row }
  95. this.$refs.viewDialog.open()
  96. }
  97. }
  98. ]
  99. },
  100. viewFields() {
  101. if (!this.viewRow) return []
  102. const row = this.viewRow
  103. return [
  104. { label: '会员ID', prop: 'member_id', value: row.member_id ?? '--' },
  105. { label: '会员昵称', prop: 'first_name', value: row.first_name || '--' },
  106. { label: 'IP地址', prop: 'login_ip', value: row.login_ip || '--' },
  107. { label: '国家/地区', prop: 'country', value: row.country || '--' },
  108. { label: '浏览器核心', prop: 'browser', value: row.browser || '--' },
  109. { label: '操作系统', prop: 'os', value: row.os || '--' },
  110. { label: '状态', prop: 'status', value: row.status != null ? (Number(row.status) === 1 ? '在线' : '离线') : '--', type: 'status', rawValue: row.status },
  111. { label: '在线时长(分)', prop: 'online_duration', value: row.online_duration != null ? row.online_duration : '--' },
  112. { label: '登入时间', prop: 'created_at', value: row.created_at || '--' },
  113. { label: '登出时间', prop: 'logout_time', value: row.logout_time || '--' }
  114. ]
  115. }
  116. },
  117. methods: {
  118. onSubmit(params = {}) {
  119. const query = { ...params }
  120. if (params.dateRange && params.dateRange.length === 2) {
  121. query.start_time = params.dateRange[0]
  122. query.end_time = params.dateRange[1]
  123. }
  124. delete query.dateRange
  125. this.$refs.table.getList(query)
  126. }
  127. },
  128. mounted() {
  129. this.onSubmit()
  130. }
  131. }
  132. </script>
  133. <style scoped>
  134. .new-app-container { display: flex; flex-direction: column; height: 100%; padding: 16px; background: #f5f7fa; }
  135. </style>