mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 20:08:18 +08:00
fix(activity): mobile adaptation + fix blank detail drawer
This commit is contained in:
parent
895b96a840
commit
9ff15bc9a7
@ -108,8 +108,9 @@
|
|||||||
v-model:page-size="pageSize"
|
v-model:page-size="pageSize"
|
||||||
:total="total"
|
:total="total"
|
||||||
:page-sizes="[20, 50, 100]"
|
:page-sizes="[20, 50, 100]"
|
||||||
|
:small="isMobile"
|
||||||
background
|
background
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
:layout="paginationLayout"
|
||||||
@size-change="onPageSizeChange"
|
@size-change="onPageSizeChange"
|
||||||
@current-change="loadEvents"
|
@current-change="loadEvents"
|
||||||
/>
|
/>
|
||||||
@ -122,10 +123,9 @@
|
|||||||
JSON dump replaced by structured field rendering. -->
|
JSON dump replaced by structured field rendering. -->
|
||||||
<el-drawer
|
<el-drawer
|
||||||
v-model="detailVisible"
|
v-model="detailVisible"
|
||||||
:show-close="true"
|
:title="t('security.activity.detailTitle')"
|
||||||
:with-header="false"
|
|
||||||
direction="rtl"
|
direction="rtl"
|
||||||
size="720px"
|
:size="drawerSize"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
>
|
>
|
||||||
<div v-if="detailEvent" class="detail-shell">
|
<div v-if="detailEvent" class="detail-shell">
|
||||||
@ -206,7 +206,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, reactive, ref } from 'vue'
|
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { activityApi } from '@/api'
|
import { activityApi } from '@/api'
|
||||||
|
|
||||||
@ -227,6 +227,24 @@ interface ActivityEvent {
|
|||||||
detail?: Record<string, any>
|
detail?: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mobile detection — drives the el-drawer size and el-pagination
|
||||||
|
* layout dynamically. CSS @media handles static layout, but EP
|
||||||
|
* component props need a reactive value to switch from a 720px
|
||||||
|
* drawer to full-width sheet, and to drop the size-switcher /
|
||||||
|
* jumper when there's no horizontal room for them.
|
||||||
|
*/
|
||||||
|
const isMobile = ref(false)
|
||||||
|
let mq: MediaQueryList | null = null
|
||||||
|
function syncMobile(e: MediaQueryListEvent | MediaQueryList) {
|
||||||
|
isMobile.value = e.matches
|
||||||
|
}
|
||||||
|
|
||||||
|
const drawerSize = computed(() => isMobile.value ? '100%' : '720px')
|
||||||
|
const paginationLayout = computed(() =>
|
||||||
|
isMobile.value ? 'prev, pager, next' : 'total, sizes, prev, pager, next, jumper',
|
||||||
|
)
|
||||||
|
|
||||||
const events = ref<ActivityEvent[]>([])
|
const events = ref<ActivityEvent[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
@ -460,8 +478,15 @@ function absoluteTime(event: ActivityEvent): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
mq = window.matchMedia('(max-width: 768px)')
|
||||||
|
syncMobile(mq)
|
||||||
|
mq.addEventListener('change', syncMobile)
|
||||||
loadEvents()
|
loadEvents()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
mq?.removeEventListener('change', syncMobile)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -689,11 +714,11 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.detail-hero {
|
.detail-hero {
|
||||||
/* Indented so the absolute-positioned dot sits inside, not behind
|
/* Glass hero block. No negative margin — el-drawer body has
|
||||||
the drawer's left edge. */
|
overflow constraints and a -8px outdent caused the rounded
|
||||||
|
border to clip on the sides. */
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 18px 18px 22px;
|
padding: 18px 18px 22px;
|
||||||
margin: -8px -8px 0;
|
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
border: 1px solid var(--mc-border-light);
|
border: 1px solid var(--mc-border-light);
|
||||||
background: rgba(255, 255, 255, 0.55);
|
background: rgba(255, 255, 255, 0.55);
|
||||||
@ -791,4 +816,85 @@ onMounted(() => {
|
|||||||
color: var(--mc-text-secondary);
|
color: var(--mc-text-secondary);
|
||||||
white-space: pre-wrap; word-break: break-word;
|
white-space: pre-wrap; word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============ Mobile / narrow viewport (≤ 768px) ============ */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
/* Header — stack title and refresh button vertically */
|
||||||
|
.activity-page :deep(.mc-page-header) {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.header-actions { justify-content: flex-end; }
|
||||||
|
|
||||||
|
/* Filter chips — drop the "More filters" link out of one row */
|
||||||
|
.filter-chips { padding: 8px 10px; gap: 6px; }
|
||||||
|
.filter-chip { padding: 5px 10px; font-size: 11px; }
|
||||||
|
.filter-spacer { flex-basis: 100%; height: 0; }
|
||||||
|
|
||||||
|
/* Event row — let target wrap naturally; time on a second line */
|
||||||
|
.event-row {
|
||||||
|
padding: 12px 14px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.event-body { flex: 1 1 100%; min-width: 0; }
|
||||||
|
.event-sentence {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
.target-name {
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.event-time {
|
||||||
|
/* Push time to the right edge of its own row under the body */
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 11px;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
.event-chevron { display: none; }
|
||||||
|
.event-dot { margin-top: 5px; }
|
||||||
|
|
||||||
|
/* Day divider — tighter rhythm on mobile */
|
||||||
|
.day-divider { padding: 14px 2px 6px; }
|
||||||
|
|
||||||
|
/* Detail drawer — drawer goes full-width via :size binding;
|
||||||
|
here we collapse the inner padding and headline scale. */
|
||||||
|
.detail-shell { padding: 16px 16px 24px; gap: 18px; }
|
||||||
|
.detail-hero { padding: 14px 14px 18px; }
|
||||||
|
.detail-dot { left: 14px; top: 20px; }
|
||||||
|
.detail-headline {
|
||||||
|
font-size: 18px;
|
||||||
|
padding-left: 18px;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
.detail-verb { font-size: 13px; padding: 1px 8px; }
|
||||||
|
.detail-target-type { font-size: 14px; }
|
||||||
|
.detail-target-name {
|
||||||
|
font-size: 13px;
|
||||||
|
margin-left: 18px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.detail-meta { padding-left: 18px; flex-wrap: wrap; }
|
||||||
|
.change-key { width: 100px; font-size: 11px; }
|
||||||
|
.change-item { gap: 10px; padding: 8px 0; font-size: 12px; }
|
||||||
|
|
||||||
|
/* Pagination — :small=true on EP shrinks; container loosens padding */
|
||||||
|
.pagination { padding: 6px 8px; margin-top: 10px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============ Very small phones (≤ 480px) ============ */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.filter-chips { padding: 6px 8px; }
|
||||||
|
.event-row { padding: 10px 12px; }
|
||||||
|
.event-sentence { font-size: 12px; gap: 4px; }
|
||||||
|
.event-actor { font-weight: 700; }
|
||||||
|
.target-name { font-size: 12px; }
|
||||||
|
.detail-headline { font-size: 16px; }
|
||||||
|
.detail-target-name { font-size: 12px; }
|
||||||
|
.change-key { width: 84px; }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user