- Set up pnpm workspace with 4 packages: shared, server, client-mobile, client-screen - Implement Redis atomic voting with Lua scripts (HINCRBY + distributed lock) - Add optimistic UI with IndexedDB queue for offline resilience - Configure Socket.io with auto-reconnection (infinite retries) - Separate mobile (Vant) and big screen (Pixi.js) dependencies Tech stack: - Frontend Mobile: Vue 3 + Vant + Socket.io-client - Frontend Screen: Vue 3 + Pixi.js + GSAP - Backend: Express + Socket.io + Redis + Prisma/MySQL Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
93 lines
1.9 KiB
Vue
93 lines
1.9 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from 'vue';
|
|
import { useConnectionStore } from '../stores/connection';
|
|
|
|
const connectionStore = useConnectionStore();
|
|
|
|
const statusClass = computed(() => {
|
|
switch (connectionStore.connectionStatus) {
|
|
case 'connected':
|
|
return 'status-connected';
|
|
case 'connecting':
|
|
return 'status-connecting';
|
|
default:
|
|
return 'status-disconnected';
|
|
}
|
|
});
|
|
|
|
const statusText = computed(() => {
|
|
switch (connectionStore.connectionStatus) {
|
|
case 'connected':
|
|
return '已连接';
|
|
case 'connecting':
|
|
return '连接中...';
|
|
default:
|
|
return '未连接';
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="connection-status" :class="statusClass">
|
|
<span class="status-dot"></span>
|
|
<span class="status-text">{{ statusText }}</span>
|
|
<span v-if="connectionStore.latency > 0" class="latency">
|
|
{{ connectionStore.latency }}ms
|
|
</span>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '../assets/styles/variables.scss';
|
|
|
|
.connection-status {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $spacing-xs;
|
|
padding: $spacing-xs $spacing-sm;
|
|
border-radius: $radius-full;
|
|
font-size: $font-size-xs;
|
|
background: rgba(255, 255, 255, 0.9);
|
|
box-shadow: $shadow-sm;
|
|
position: fixed;
|
|
top: env(safe-area-inset-top, 8px);
|
|
right: $spacing-sm;
|
|
z-index: $z-index-fixed;
|
|
|
|
.status-dot {
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 50%;
|
|
transition: background-color $transition-normal;
|
|
}
|
|
|
|
.status-text {
|
|
color: $color-text-secondary;
|
|
}
|
|
|
|
.latency {
|
|
color: $color-text-muted;
|
|
font-size: $font-size-xs;
|
|
}
|
|
|
|
&.status-connected {
|
|
.status-dot {
|
|
background-color: $color-success;
|
|
}
|
|
}
|
|
|
|
&.status-connecting {
|
|
.status-dot {
|
|
background-color: $color-warning;
|
|
animation: pulse 1s infinite;
|
|
}
|
|
}
|
|
|
|
&.status-disconnected {
|
|
.status-dot {
|
|
background-color: $color-error;
|
|
}
|
|
}
|
|
}
|
|
</style>
|