Files
dygou_go/frontend/src/components/layout/LeftSidebar.vue
T

224 lines
4.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { computed } from 'vue'
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'
import XiaodianTabs from '@/components/product/XiaodianTabs.vue'
defineProps<{
version: string
}>()
const { loadProducts, productInfo, filteredProducts, loading } = useProducts()
const { activities, currentActivity, switching, switchActivity } = useActivity()
const isXiaodian = computed(() => currentActivity.value?.id === 'xiaodian')
const onSwitchActivity = async (e: Event) => {
const id = (e.target as HTMLSelectElement).value
const ok = await switchActivity(id)
if (ok) {
await loadProducts(true)
}
}
</script>
<template>
<aside class="left">
<!-- 品牌区 -->
<div class="brand">
<div>
<h1>斗鱼抢购</h1>
<div class="brand-sub">实时库存 · 多账号 · 定时触发</div>
</div>
<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">
<button
class="btn btn-primary"
:disabled="loading"
@click="loadProducts(true)"
>
{{ loading ? '刷新中...' : '刷新商品' }}
</button>
</div>
<ProductSearch />
<XiaodianTabs v-if="isXiaodian" />
<ProductCard />
<div class="product-meta" :class="{ good: productInfo && !productInfo.stale, warn: productInfo?.stale }">
<template v-if="productInfo?.fetchedAt">
{{ isXiaodian ? '商品' : '实时库存' }} {{ filteredProducts.length }} 个,刷新于 {{ productInfo.fetchedAt }}
</template>
<template v-else>{{ isXiaodian ? '商品未刷新' : '库存未刷新' }}</template>
</div>
</div>
<!-- 商品列表 -->
<ProductList />
</aside>
</template>
<style scoped>
.left {
background: var(--surface);
border-right: 1px solid var(--line);
display: flex;
flex-direction: column;
min-width: 0;
min-height: 0;
overflow: hidden;
}
.brand {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 16px 18px;
border-bottom: 1px solid var(--line);
}
.brand h1 {
font-size: 15px;
font-weight: 600;
color: var(--text-strong);
letter-spacing: -.01em;
}
.brand-sub {
font-size: 11px;
color: var(--muted);
margin-top: 3px;
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);
display: flex;
flex-direction: column;
gap: 10px;
}
.product-actions {
display: flex;
gap: 6px;
align-items: center;
}
.product-actions .btn-primary {
flex: 1;
}
.product-meta {
font-size: 11px;
color: var(--muted);
padding: 0 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.product-meta.good {
color: var(--success);
}
.product-meta.warn {
color: var(--warning);
}
@media (max-width: 1180px) {
.left {
height: auto;
overflow: visible;
border-right: 0;
border-bottom: 1px solid var(--line);
}
}
</style>