fix: api
This commit is contained in:
@@ -1,52 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useProductStore, type Product } from '@/stores/product'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { TableColumn } from '@nuxt/ui'
|
||||
import { h } from 'vue'
|
||||
|
||||
interface Product {
|
||||
id: number
|
||||
image: string
|
||||
name: string
|
||||
code: string
|
||||
inStock: boolean
|
||||
priceFrom: number
|
||||
priceTo: number
|
||||
count: number
|
||||
description: string
|
||||
howToUse: string
|
||||
productDetails: string
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
const productStore = useProductStore()
|
||||
const { t } = useI18n()
|
||||
|
||||
// Mock product data (same as ProductsView)
|
||||
const products = ref<Product[]>([
|
||||
{ id: 1, image: 'https://picsum.photos/seed/product1/400/400', name: 'Laptop Pro 15', code: 'LP-001', inStock: true, priceFrom: 999, priceTo: 1299, count: 15, description: 'High-performance laptop for professionals', howToUse: 'Open the lid and press the power button', productDetails: '15-inch display, 16GB RAM, 512GB SSD' },
|
||||
{ id: 2, image: 'https://picsum.photos/seed/product2/400/400', name: 'Wireless Mouse', code: 'WM-002', inStock: true, priceFrom: 29, priceTo: 49, count: 150, description: 'Ergonomic wireless mouse with precision tracking', howToUse: 'Connect via Bluetooth or USB receiver', productDetails: '3000 DPI, 2.4GHz wireless, 12-month battery' },
|
||||
{ id: 3, image: 'https://picsum.photos/seed/product3/400/400', name: 'Mechanical Keyboard', code: 'MK-003', inStock: true, priceFrom: 89, priceTo: 159, count: 45, description: 'Premium mechanical keyboard with RGB lighting', howToUse: 'Connect via USB-C cable', productDetails: 'Cherry MX switches, RGB backlight, anti-ghosting' },
|
||||
{ id: 4, image: 'https://picsum.photos/seed/product4/400/400', name: 'USB-C Hub', code: 'UH-004', inStock: false, priceFrom: 39, priceTo: 59, count: 0, description: 'Multi-port USB-C hub for connectivity', howToUse: 'Connect to laptop USB-C port', productDetails: 'HDMI 4K, 3x USB-A, SD card reader, PD 100W' },
|
||||
{ id: 5, image: 'https://picsum.photos/seed/product5/400/400', name: 'Monitor 27 inch', code: 'MN-005', inStock: true, priceFrom: 299, priceTo: 449, count: 23, description: '27-inch 4K IPS monitor with HDR support', howToUse: 'Connect via HDMI or DisplayPort', productDetails: '3840x2160, 60Hz, HDR400, built-in speakers' },
|
||||
{ id: 6, image: 'https://picsum.photos/seed/product6/400/400', name: 'Webcam HD', code: 'WC-006', inStock: true, priceFrom: 59, priceTo: 89, count: 67, description: 'Full HD webcam for video conferencing', howToUse: 'Mount on monitor or use tripod stand', productDetails: '1080p 30fps, autofocus, noise-canceling mic' },
|
||||
{ id: 7, image: 'https://picsum.photos/seed/product7/400/400', name: 'Headphones Wireless', code: 'HW-007', inStock: true, priceFrom: 149, priceTo: 249, count: 89, description: 'Premium wireless headphones with ANC', howToUse: 'Pair via Bluetooth or use included cable', productDetails: '30-hour battery, ANC, 40mm drivers' },
|
||||
{ id: 8, image: 'https://picsum.photos/seed/product8/400/400', name: 'External SSD 1TB', code: 'ES-008', inStock: true, priceFrom: 109, priceTo: 149, count: 120, description: 'Portable external SSD with fast speeds', howToUse: 'Connect via USB-C cable', productDetails: '1TB, 1050MB/s read, compact design' },
|
||||
{ id: 9, image: 'https://picsum.photos/seed/product9/400/400', name: 'Desk Lamp LED', code: 'DL-009', inStock: false, priceFrom: 35, priceTo: 55, count: 0, description: 'Adjustable LED desk lamp with multiple brightness levels', howToUse: 'Plug in and touch controls', productDetails: '5 brightness levels, color temperature control, USB port' },
|
||||
{ id: 10, image: 'https://picsum.photos/seed/product10/400/400', name: 'Cable Organizer', code: 'CO-010', inStock: true, priceFrom: 15, priceTo: 25, count: 200, description: 'Desk cable management solution', howToUse: 'Stick to desk or use clamps', productDetails: '10 slots, adhesive backing,白色' },
|
||||
])
|
||||
|
||||
// Get product from route params
|
||||
const productId = computed(() => Number(route.params.id))
|
||||
const product = computed(() => products.value.find(p => p.id === productId.value))
|
||||
const product = computed(() => productStore.currentProduct)
|
||||
|
||||
// Fetch product on mount
|
||||
onMounted(() => {
|
||||
if (productId.value) {
|
||||
productStore.fetchProductById(productId.value)
|
||||
}
|
||||
})
|
||||
|
||||
// Clear product when leaving
|
||||
onUnmounted(() => {
|
||||
productStore.clearCurrentProduct()
|
||||
})
|
||||
|
||||
// Active tab for the four buttons
|
||||
const activeTab = ref<'description' | 'howToUse' | 'productDetails' | 'documents'>('description')
|
||||
|
||||
// Mock variants (same structure as products but with variants)
|
||||
// Mock variants (in real app, this would come from API)
|
||||
const variants = computed(() => {
|
||||
if (!product.value) return []
|
||||
// Create mock variants based on the product
|
||||
@@ -120,7 +106,7 @@ const columns = computed<TableColumn<any>[]>(() => [
|
||||
onClick: () => addToCart(row.original)
|
||||
}, t('products.add_to_cart')),
|
||||
h('button', {
|
||||
class: 'px-3 py-1.5 text-sm font-medium bg-gray-200 dark:dark:hover:bg-(--gray-dark) text-black dark:text-white rounded-lg hover:bg-(--gray) dark:hover:bg-gray-600 transition-colors',
|
||||
class: 'px-3 py-1.5 text-sm font-medium bg-gray-200 dark:bg-gray-700 text-black dark:text-white rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors',
|
||||
onClick: () => incrementCount(row.original)
|
||||
}, '+')
|
||||
])
|
||||
@@ -143,20 +129,30 @@ function goBack() {
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="p-6 bg-(--main-light) dark:bg-(--black) min-h-screen font-sans">
|
||||
<div class="p-6 bg-white dark:bg-(--black) min-h-screen font-sans">
|
||||
<!-- Back Button -->
|
||||
<button
|
||||
@click="goBack"
|
||||
class="mb-4 px-4 py-2 text-sm font-medium text-black dark:text-white bg-gray-200 dark:dark:hover:bg-(--gray-dark) rounded-lg hover:bg-(--gray) dark:hover:bg-gray-600 transition-colors"
|
||||
class="mb-4 px-4 py-2 text-sm font-medium text-black dark:text-white bg-gray-200 dark:bg-gray-700 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors"
|
||||
>
|
||||
← {{ t('products.back_to_list') }}
|
||||
</button>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div v-if="productStore.loading" class="mb-4 p-3 bg-blue-100 text-blue-700 rounded">
|
||||
{{ t('products.loading') }}...
|
||||
</div>
|
||||
|
||||
<!-- Error State -->
|
||||
<div v-if="productStore.error" class="mb-4 p-3 bg-red-100 text-red-700 rounded">
|
||||
{{ productStore.error }}
|
||||
</div>
|
||||
|
||||
<div v-if="!authStore.isAuthenticated" class="mb-4 p-3 bg-yellow-100 text-yellow-700 rounded">
|
||||
{{ t('products.login_to_view') }}
|
||||
</div>
|
||||
|
||||
<div v-if="authStore.isAuthenticated && product">
|
||||
<div v-if="authStore.isAuthenticated && product && !productStore.loading">
|
||||
<!-- Product Header: Image and Title -->
|
||||
<div class="flex flex-col md:flex-row gap-8 mb-6">
|
||||
<!-- Product Image -->
|
||||
@@ -209,7 +205,7 @@ function goBack() {
|
||||
'px-4 py-2 text-sm font-medium rounded-lg transition-colors',
|
||||
activeTab === 'description'
|
||||
? 'bg-primary text-white'
|
||||
: 'bg-gray-200 dark:dark:hover:bg-(--gray-dark) text-black dark:text-white hover:bg-(--gray) dark:hover:bg-gray-600'
|
||||
: 'bg-gray-200 dark:bg-gray-700 text-black dark:text-white hover:bg-gray-300 dark:hover:bg-gray-600'
|
||||
]"
|
||||
>
|
||||
{{ t('products.description') }}
|
||||
@@ -220,7 +216,7 @@ function goBack() {
|
||||
'px-4 py-2 text-sm font-medium rounded-lg transition-colors',
|
||||
activeTab === 'howToUse'
|
||||
? 'bg-primary text-white'
|
||||
: 'bg-gray-200 dark:dark:hover:bg-(--gray-dark) text-black dark:text-white hover:bg-(--gray) dark:hover:bg-gray-600'
|
||||
: 'bg-gray-200 dark:bg-gray-700 text-black dark:text-white hover:bg-gray-300 dark:hover:bg-gray-600'
|
||||
]"
|
||||
>
|
||||
{{ t('products.how_to_use') }}
|
||||
@@ -231,7 +227,7 @@ function goBack() {
|
||||
'px-4 py-2 text-sm font-medium rounded-lg transition-colors',
|
||||
activeTab === 'productDetails'
|
||||
? 'bg-primary text-white'
|
||||
: 'bg-gray-200 dark:dark:hover:bg-(--gray-dark) text-black dark:text-white hover:bg-(--gray) dark:hover:bg-gray-600'
|
||||
: 'bg-gray-200 dark:bg-gray-700 text-black dark:text-white hover:bg-gray-300 dark:hover:bg-gray-600'
|
||||
]"
|
||||
>
|
||||
{{ t('products.product_details') }}
|
||||
@@ -242,7 +238,7 @@ function goBack() {
|
||||
'px-4 py-2 text-sm font-medium rounded-lg transition-colors',
|
||||
activeTab === 'documents'
|
||||
? 'bg-primary text-white'
|
||||
: 'bg-gray-200 dark:dark:hover:bg-(--gray-dark) text-black dark:text-white hover:bg-(--gray) dark:hover:bg-gray-600'
|
||||
: 'bg-gray-200 dark:bg-gray-700 text-black dark:text-white hover:bg-gray-300 dark:hover:bg-gray-600'
|
||||
]"
|
||||
>
|
||||
{{ t('products.documents') }}
|
||||
@@ -300,7 +296,7 @@ function goBack() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="authStore.isAuthenticated && !product" class="text-center py-10">
|
||||
<div v-else-if="authStore.isAuthenticated && !product && !productStore.loading && !productStore.error" class="text-center py-10">
|
||||
<p class="text-gray-500 dark:text-gray-400">{{ t('products.product_not_found') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user