mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-01 16:07:17 +00:00
feat(frontend): Phase 5c-iv (b) — cpu-history / xray-logs / xray-version modals
Wires up the three remaining dashboard buttons that were stubbed in
5c-iv (a): the CPU history button on StatusCard, the xray-logs button
in XrayStatusCard's error popover and ipLimitEnable action, and the
"Switch xray" button in XrayStatusCard's action footer.
- Sparkline.vue: shared SVG line chart (composition-API port of the
inline Vue 2 component). Per-instance gradient id avoids defs
collisions between sparklines on the same page.
- CpuHistoryModal.vue: bucket dropdown (2m/30m/1h/2h/3h/5h) drives
GET /panel/api/server/cpuHistory/{bucket}; renders via Sparkline.
- XrayLogModal.vue: rows + filter + direct/blocked/proxy checkboxes;
POST /panel/api/server/xraylogs/{rows} returns access-log entries
rendered as a colored HTML table; download button serializes to text.
- VersionModal.vue: collapse with Xray panel (radio list of versions
from getXrayVersion, install via installXray/{version}) and Geofiles
panel (per-file reload + Update all). CustomGeo collapse panel is
Phase 5c-v.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { HttpUtil } from '@/utils';
|
||||
import Sparkline from '@/components/Sparkline.vue';
|
||||
|
||||
const props = defineProps({
|
||||
open: { type: Boolean, default: false },
|
||||
status: { type: Object, required: true },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:open']);
|
||||
|
||||
// Bucket size in seconds per data point — matches legacy options.
|
||||
const bucket = ref(2);
|
||||
const points = ref([]);
|
||||
const labels = ref([]);
|
||||
|
||||
async function fetchBucket() {
|
||||
try {
|
||||
const msg = await HttpUtil.get(`/panel/api/server/cpuHistory/${bucket.value}`);
|
||||
if (msg?.success && Array.isArray(msg.obj)) {
|
||||
const vals = [];
|
||||
const labs = [];
|
||||
for (const p of msg.obj) {
|
||||
const d = new Date(p.t * 1000);
|
||||
const hh = String(d.getHours()).padStart(2, '0');
|
||||
const mm = String(d.getMinutes()).padStart(2, '0');
|
||||
const ss = String(d.getSeconds()).padStart(2, '0');
|
||||
labs.push(bucket.value >= 60 ? `${hh}:${mm}` : `${hh}:${mm}:${ss}`);
|
||||
vals.push(Math.max(0, Math.min(100, p.cpu)));
|
||||
}
|
||||
labels.value = labs;
|
||||
points.value = vals;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch bucketed cpu history', e);
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
emit('update:open', false);
|
||||
}
|
||||
|
||||
watch(() => props.open, (next) => { if (next) fetchBucket(); });
|
||||
watch(bucket, () => { if (props.open) fetchBucket(); });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal :open="open" :closable="true" :footer="null" width="900px" @cancel="close">
|
||||
<template #title>
|
||||
CPU history
|
||||
<a-select v-model:value="bucket" size="small" class="bucket-select">
|
||||
<a-select-option :value="2">2m</a-select-option>
|
||||
<a-select-option :value="30">30m</a-select-option>
|
||||
<a-select-option :value="60">1h</a-select-option>
|
||||
<a-select-option :value="120">2h</a-select-option>
|
||||
<a-select-option :value="180">3h</a-select-option>
|
||||
<a-select-option :value="300">5h</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<div class="cpu-chart-wrap">
|
||||
<Sparkline
|
||||
:data="points"
|
||||
:labels="labels"
|
||||
:vb-width="840"
|
||||
:height="220"
|
||||
:stroke="status?.cpu?.color || '#008771'"
|
||||
:stroke-width="2.2"
|
||||
:show-grid="true"
|
||||
:show-axes="true"
|
||||
:tick-count-x="5"
|
||||
:max-points="points.length || 1"
|
||||
:fill-opacity="0.18"
|
||||
:marker-radius="3.2"
|
||||
:show-tooltip="true"
|
||||
/>
|
||||
<div class="cpu-chart-meta">
|
||||
Timeframe: {{ bucket }} sec per point (total {{ points.length }} points)
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.bucket-select {
|
||||
width: 80px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.cpu-chart-wrap {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.cpu-chart-meta {
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
opacity: 0.65;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user