增加电竞手册

This commit is contained in:
yml2213
2026-07-29 10:36:44 +08:00
parent 00fa48722f
commit f4d33d19c0
11 changed files with 382 additions and 98 deletions
+3
View File
@@ -5,6 +5,7 @@ import { useProducts } from '@/composables/useProducts'
import { useAccounts } from '@/composables/useAccounts'
import { useScheduler } from '@/composables/useScheduler'
import { useLogs } from '@/composables/useLogs'
import { useActivity } from '@/composables/useActivity'
import AppShell from '@/components/layout/AppShell.vue'
import ToastContainer from '@/components/ToastContainer.vue'
@@ -14,6 +15,7 @@ const { loadAccounts, restoreSelection: restoreAccountSelection } = useAccounts(
const { clearLogs, fetchServerLogs, checkResults } = useLogs()
const { restoreSchedule, setRunState } = useScheduler()
const { clearResults } = useLogs()
const { loadActivities } = useActivity()
const version = ref('')
const serverTime = ref('--')
@@ -61,6 +63,7 @@ onMounted(async () => {
// 加载数据
await loadAccounts()
await loadActivities()
await loadProducts(false)
// 获取版本号
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { useProducts } from '@/composables/useProducts'
import { useActivity } from '@/composables/useActivity'
import ProductSearch from '@/components/product/ProductSearch.vue'
import ProductList from '@/components/product/ProductList.vue'
import ProductCard from '@/components/product/ProductCard.vue'
@@ -9,6 +10,15 @@ defineProps<{
}>()
const { loadProducts, productInfo, filteredProducts, loading } = useProducts()
const { activities, currentActivity, switching, switchActivity } = useActivity()
const onSwitchActivity = async (e: Event) => {
const id = (e.target as HTMLSelectElement).value
const ok = await switchActivity(id)
if (ok) {
await loadProducts(true)
}
}
</script>
<template>
@@ -22,6 +32,25 @@ const { loadProducts, productInfo, filteredProducts, loading } = useProducts()
<span class="stat-pill">v{{ version }}</span>
</div>
<!-- 活动选择 -->
<div class="activity-box">
<label class="activity-label">当前活动</label>
<div class="activity-row">
<select
class="activity-select"
:value="currentActivity?.id"
:disabled="switching || activities.length === 0"
@change="onSwitchActivity"
>
<option v-if="activities.length === 0" value="">加载中...</option>
<option v-for="act in activities" :key="act.id" :value="act.id">
{{ act.displayName }}
</option>
</select>
<span v-if="switching" class="activity-spin">切换中…</span>
</div>
</div>
<!-- 操作区 -->
<div class="left-header">
<div class="product-actions">
@@ -85,6 +114,64 @@ const { loadProducts, productInfo, filteredProducts, loading } = useProducts()
letter-spacing: .02em;
}
.brand + .activity-box {
border-top: 0;
}
.activity-box {
padding: 12px 18px;
border-bottom: 1px solid var(--line);
display: flex;
flex-direction: column;
gap: 6px;
}
.activity-label {
font-size: 11px;
color: var(--muted);
letter-spacing: .04em;
}
.activity-row {
display: flex;
align-items: center;
gap: 8px;
}
.activity-select {
flex: 1;
min-width: 0;
appearance: none;
background: var(--surface-2, var(--surface));
color: var(--text-strong);
border: 1px solid var(--line);
border-radius: 8px;
padding: 7px 10px;
font-size: 13px;
cursor: pointer;
transition: border-color .15s ease;
}
.activity-select:hover:not(:disabled) {
border-color: var(--primary);
}
.activity-select:focus {
outline: none;
border-color: var(--primary);
}
.activity-select:disabled {
opacity: .6;
cursor: default;
}
.activity-spin {
font-size: 11px;
color: var(--primary);
white-space: nowrap;
}
.left-header {
padding: 14px 16px;
border-bottom: 1px solid var(--line);
+64
View File
@@ -0,0 +1,64 @@
import { ref } from 'vue'
import { useToast } from './useToast'
// 活动项(与后端 types.ActivityInfo 对齐)
export interface ActivityItem {
id: string
displayName: string
}
// 模块级单例:活动列表与当前活动在所有组件中共享同一份状态
const activities = ref<ActivityItem[]>([])
const currentActivity = ref<ActivityItem | null>(null)
const switching = ref(false)
// 与项目其他 composable 一致,直接走 Wails 运行时注入的 window.go.main.App
const App = window.go.main.App
export function useActivity() {
const { showToast } = useToast()
// 拉取全部活动与当前生效活动
const loadActivities = async () => {
try {
const list = await App.GetActivities()
activities.value = list || []
try {
currentActivity.value = await App.GetCurrentActivity()
} catch {
// 当前活动读取失败不阻塞列表展示
}
} catch (e: any) {
showToast('读取活动列表失败', e?.message || '', 'error')
}
}
// 切换活动;成功返回 true,调用方可据此刷新商品等数据
const switchActivity = async (id: string): Promise<boolean> => {
if (id === currentActivity.value?.id) return true
switching.value = true
try {
const d: Record<string, any> = await App.SwitchActivity(id)
if (d && d.success) {
currentActivity.value = { id: d.activity, displayName: d.displayName }
showToast('活动已切换', (d.displayName || id) + ',正在刷新商品...', 'success')
return true
}
showToast('切换失败', d?.error || '', 'error')
return false
} catch (e: any) {
showToast('切换失败', e?.message || '', 'error')
return false
} finally {
switching.value = false
}
}
return {
activities,
currentActivity,
switching,
loadActivities,
switchActivity
}
}
+3
View File
@@ -31,6 +31,9 @@ interface Window {
ExportAccounts: (accountNames: string[], filename: string) => Promise<any>
ExportAccountsWithProduct: (accountNames: string[], commodityID: string, commodityName: string) => Promise<any>
OrganizeAccounts: (commodityID: string, commodityName: string, startTime: number, endTime: number) => Promise<any>
GetActivities: () => Promise<Array<{ id: string; displayName: string }>>
GetCurrentActivity: () => Promise<{ id: string; displayName: string }>
SwitchActivity: (id: string) => Promise<{ success: boolean; activity?: string; displayName?: string; error?: string }>
}
}
}