3x-ui/frontend/src/App.vue
MHSanaei 3ca644eb3d
refactor(frontend): Phase 3 — port utils, models, axios, websocket as ES modules
Ports the framework-agnostic JS from web/assets/js/ into frontend/src/
so Vue 3 pages can import what they need without relying on script-tag
globals.

- web/assets/js/util/index.js (927 lines, 21 classes) →
  frontend/src/utils/legacy.js + a barrel at utils/index.js. All
  classes are now named exports.
- Vue.prototype.$message in HttpUtil → direct import of `message`
  from ant-design-vue (Vue 3 has no Vue.prototype).
- RandomUtil.randomShadowsocksPassword previously defaulted to
  SSMethods.BLAKE3_AES_256_GCM from inbound.js, creating a circular
  import. Replaced with the literal string default.
- MediaQueryMixin (Vue 2 mixin) removed. Replaced by
  composables/useMediaQuery.js — Vue 3 composable returning reactive
  `isMobile`.
- axios-init.js wrapped as setupAxios(); Qs global → npm `qs`.
- websocket.js exported as WebSocketClient class; the implicit
  window.wsClient global is gone — pages instantiate it themselves.
- model/{inbound,outbound,dbinbound,setting,reality_targets}.js
  copied with `export` added on every top-level declaration. Imports
  between models and utils are wired up explicitly.
- subscription.js deferred to Phase 5 (it's a Vue 2 mount, not a util).
- App.vue smoke test exercises SizeFormatter / RandomUtil / Wireguard /
  useMediaQuery so the user can verify Phase 3 with `npm run dev`.

Run `cd frontend && npm install && npm run dev` — qs was added so a
fresh install is required.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 10:47:15 +02:00

70 lines
2 KiB
Vue

<script setup>
import { ref, computed } from 'vue';
import { SizeFormatter, RandomUtil, Wireguard } from '@/utils';
import { useMediaQuery } from '@/composables/useMediaQuery.js';
const message = ref('Vue 3 + Ant Design Vue 4 scaffold is alive');
const count = ref(0);
const { isMobile } = useMediaQuery();
const fakeBytes = ref(1234567890);
const formatted = computed(() => SizeFormatter.sizeFormat(fakeBytes.value));
const uuid = ref(RandomUtil.randomUUID());
const keypair = ref(Wireguard.generateKeypair());
</script>
<template>
<a-layout class="layout">
<a-layout-header class="header">
<h1>3x-ui (vue3-migration scaffold)</h1>
<a-tag color="blue">isMobile: {{ isMobile }}</a-tag>
</a-layout-header>
<a-layout-content class="content">
<a-space direction="vertical" :size="16" style="width: 100%">
<a-alert :message="message" type="success" show-icon />
<a-card title="Smoke test — toolchain">
<a-space>
<a-button type="primary" @click="count++">Clicked {{ count }} times</a-button>
<a-button @click="count = 0">Reset</a-button>
</a-space>
</a-card>
<a-card title="Smoke test — utility imports">
<p><strong>SizeFormatter:</strong> {{ formatted }}</p>
<p><strong>RandomUtil.randomUUID:</strong> <code>{{ uuid }}</code></p>
<p><strong>Wireguard public key:</strong> <code>{{ keypair.publicKey }}</code></p>
<a-button @click="uuid = RandomUtil.randomUUID()">Regenerate UUID</a-button>
</a-card>
</a-space>
</a-layout-content>
</a-layout>
</template>
<style>
.layout {
min-height: 100vh;
}
.header {
background: #001529;
color: #fff;
display: flex;
align-items: center;
gap: 16px;
padding: 0 24px;
}
.header h1 {
color: #fff;
margin: 0;
font-size: 18px;
}
.content {
padding: 24px;
background: #f0f2f5;
}
code {
background: rgba(0, 0, 0, 0.06);
padding: 1px 6px;
border-radius: 3px;
font-size: 12px;
}
</style>