chore: sync various improvements and fixes

- Update gitignore and serena config
- Improve connection and voting stores
- Enhance admin routes and socket handling
- Update client-screen views
- Add auth middleware

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
empty
2026-02-03 23:31:38 +08:00
parent 39caecdd95
commit 83bf1d3a43
25 changed files with 284 additions and 122 deletions

View File

@@ -12,7 +12,6 @@ declare module 'vue' {
ProgramCard: typeof import('./components/ProgramCard.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
StampDock: typeof import('./components/StampDock.vue')['default']
VanButton: typeof import('vant/es')['Button']
VanCell: typeof import('vant/es')['Cell']
VanCellGroup: typeof import('vant/es')['CellGroup']

View File

@@ -18,7 +18,7 @@ type GalaSocket = Socket<ServerToClientEvents, ClientToServerEvents>;
const STORAGE_KEYS = {
USER_ID: 'gala_user_id',
USER_NAME: 'gala_user_name',
DEPARTMENT: 'gala_department',
SESSION_TOKEN: 'gala_session_token',
};
// Helper functions for localStorage
@@ -50,7 +50,7 @@ export const useConnectionStore = defineStore('connection', () => {
const reconnectAttempts = ref(0);
const userId = ref<string | null>(loadFromStorage(STORAGE_KEYS.USER_ID, null));
const userName = ref<string | null>(loadFromStorage(STORAGE_KEYS.USER_NAME, null));
const department = ref<string | null>(loadFromStorage(STORAGE_KEYS.DEPARTMENT, null));
const sessionToken = ref<string | null>(loadFromStorage(STORAGE_KEYS.SESSION_TOKEN, null));
const votedCategories = ref<VoteCategory[]>([]);
// Computed
@@ -85,6 +85,7 @@ export const useConnectionStore = defineStore('connection', () => {
reconnectionDelayMax: CONFIG.RECONNECTION_DELAY_MAX_MS,
timeout: CONFIG.HEARTBEAT_TIMEOUT_MS,
transports: ['websocket', 'polling'],
auth: sessionToken.value ? { token: sessionToken.value } : undefined,
});
// Connection events
@@ -193,8 +194,8 @@ export const useConnectionStore = defineStore('connection', () => {
{
userId: userId.value,
userName: userName.value || 'Guest',
department: department.value || '未知部门',
role: 'user',
sessionToken: sessionToken.value || undefined,
},
(response: any) => {
if (response.success) {
@@ -264,15 +265,17 @@ export const useConnectionStore = defineStore('connection', () => {
/**
* Set user info (and persist to localStorage)
*/
function setUser(id: string, name: string, dept: string) {
function setUser(id: string, name: string, token?: string) {
userId.value = id;
userName.value = name;
department.value = dept;
if (token) {
sessionToken.value = token;
saveToStorage(STORAGE_KEYS.SESSION_TOKEN, token);
}
// Persist to localStorage
saveToStorage(STORAGE_KEYS.USER_ID, id);
saveToStorage(STORAGE_KEYS.USER_NAME, name);
saveToStorage(STORAGE_KEYS.DEPARTMENT, dept);
// Rejoin if already connected
if (socket.value?.connected) {
@@ -287,13 +290,13 @@ export const useConnectionStore = defineStore('connection', () => {
// Clear state
userId.value = null;
userName.value = null;
department.value = null;
votedCategories.value = [];
sessionToken.value = null;
// Clear localStorage
localStorage.removeItem(STORAGE_KEYS.USER_ID);
localStorage.removeItem(STORAGE_KEYS.USER_NAME);
localStorage.removeItem(STORAGE_KEYS.DEPARTMENT);
localStorage.removeItem(STORAGE_KEYS.SESSION_TOKEN);
// Disconnect socket
disconnect();
@@ -333,7 +336,7 @@ export const useConnectionStore = defineStore('connection', () => {
reconnectAttempts,
userId,
userName,
department,
sessionToken,
votedCategories,
// Computed

View File

@@ -152,6 +152,10 @@ export const useVotingStore = defineStore('voting', () => {
showToast({ message: '请先选择一个奖项', position: 'bottom' });
return false;
}
if (!connectionStore.sessionToken) {
showToast({ message: '请先扫码登录', position: 'bottom' });
return false;
}
// 检查是否已为该节目投过票(任何奖项)
const existingAward = getProgramAward(programId);

View File

@@ -14,7 +14,6 @@ const isValidating = ref<boolean>(true);
const isValid = ref<boolean>(false);
const isSubmitting = ref<boolean>(false);
const userName = ref<string>('');
const department = ref<string>('技术部');
const errorMessage = ref<string>('');
// API base URL - use LAN IP for mobile access
@@ -22,7 +21,7 @@ const apiUrl = import.meta.env.VITE_API_URL || 'http://192.168.1.5:3000';
// Computed
const canSubmit = computed(() => {
return userName.value.trim().length > 0 && department.value.trim().length > 0;
return userName.value.trim().length > 0;
});
// Validate token on mount
@@ -92,7 +91,6 @@ async function handleSubmit() {
body: JSON.stringify({
scanToken: token.value,
userName: userName.value.trim(),
department: department.value.trim(),
}),
});
@@ -101,8 +99,9 @@ async function handleSubmit() {
if (result.success) {
// Set user info in connection store
const userId = result.data?.sessionToken || `user_${Date.now()}`;
connectionStore.setUser(userId, userName.value.trim(), department.value.trim());
const userId = result.data?.userId || `user_${Date.now()}`;
const sessionToken = result.data?.sessionToken;
connectionStore.setUser(userId, userName.value.trim(), sessionToken);
showToast({ message: '登录成功!', type: 'success' });
@@ -156,13 +155,6 @@ async function handleSubmit() {
:rules="[{ required: true, message: '请输入姓名' }]"
maxlength="20"
/>
<van-field
v-model="department"
label="部门"
placeholder="请输入您的部门"
:rules="[{ required: true, message: '请输入部门' }]"
maxlength="20"
/>
</van-cell-group>
<div class="button-wrapper">