|
@@ -0,0 +1,210 @@
|
|
|
+<template>
|
|
|
+ <div class="p-6">
|
|
|
+ <!-- 顶部导航栏 -->
|
|
|
+ <div class="flex justify-between items-center mb-6">
|
|
|
+ <h1 class="text-2xl font-bold">商品管理</h1>
|
|
|
+ <div class="flex space-x-4">
|
|
|
+ <button @click="openModal('add')" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">新增</button>
|
|
|
+ <button class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">查询</button>
|
|
|
+ <button class="text-gray-700 hover:text-gray-900">
|
|
|
+ <LucideSettings size="20" />
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 搜索和筛选区域 -->
|
|
|
+ <div class="mb-6">
|
|
|
+ <input v-model="searchQuery" type="text" placeholder="输入商品名称搜索" class="border p-2 rounded w-full" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 商品统计卡片 -->
|
|
|
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
|
|
+ <div class="bg-blue-100 p-4 rounded">
|
|
|
+ <h2 class="text-lg font-semibold">总商品数</h2>
|
|
|
+ <p class="text-2xl">{{ totalProducts }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="bg-green-100 p-4 rounded">
|
|
|
+ <h2 class="text-lg font-semibold">在售商品数</h2>
|
|
|
+ <p class="text-2xl">{{ activeProducts }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="bg-yellow-100 p-4 rounded">
|
|
|
+ <h2 class="text-lg font-semibold">库存预警数</h2>
|
|
|
+ <p class="text-2xl">{{ lowStockProducts }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="bg-purple-100 p-4 rounded">
|
|
|
+ <h2 class="text-lg font-semibold">热销商品数</h2>
|
|
|
+ <p class="text-2xl">{{ popularProducts }}</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 商品分析图表 -->
|
|
|
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
|
+ <div class="bg-white p-4 rounded shadow">
|
|
|
+ <h2 class="text-xl font-semibold mb-4">销量趋势图</h2>
|
|
|
+ <!-- 这里可以放置销量趋势图的组件 -->
|
|
|
+ </div>
|
|
|
+ <div class="bg-white p-4 rounded shadow">
|
|
|
+ <h2 class="text-xl font-semibold mb-4">分类分布图</h2>
|
|
|
+ <!-- 这里可以放置分类分布图的组件 -->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 商品列表 -->
|
|
|
+ <div class="overflow-x-auto">
|
|
|
+ <table class="min-w-full bg-white border rounded">
|
|
|
+ <thead>
|
|
|
+ <tr class="bg-gray-100">
|
|
|
+ <th class="py-2 px-4">商品ID</th>
|
|
|
+ <th class="py-2 px-4">商品名称</th>
|
|
|
+ <th class="py-2 px-4">价格</th>
|
|
|
+ <th class="py-2 px-4">库存</th>
|
|
|
+ <th class="py-2 px-4">供应商信息</th>
|
|
|
+ <th class="py-2 px-4">操作</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="product in paginatedProducts" :key="product.id" class="border-t">
|
|
|
+ <td class="py-2 px-4">{{ product.id }}</td>
|
|
|
+ <td class="py-2 px-4">{{ product.name }}</td>
|
|
|
+ <td class="py-2 px-4">{{ product.price }}</td>
|
|
|
+ <td class="py-2 px-4">{{ product.stock }}</td>
|
|
|
+ <td class="py-2 px-4">{{ product.supplier }}</td>
|
|
|
+ <td class="py-2 px-4 flex space-x-2">
|
|
|
+ <button @click="openModal('edit', product)" class="text-blue-500 hover:text-blue-700">
|
|
|
+ <LucideEdit size="18" />
|
|
|
+ </button>
|
|
|
+ <button @click="deleteProduct(product.id)" class="text-red-500 hover:text-red-700">
|
|
|
+ <LucideTrash size="18" />
|
|
|
+ </button>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 分页控件 -->
|
|
|
+ <div class="mt-6 flex justify-center">
|
|
|
+ <button @click="prevPage" class="mx-1 px-3 py-1 bg-gray-200 rounded">上一页</button>
|
|
|
+ <span class="mx-2">第 {{ currentPage }} 页</span>
|
|
|
+ <button @click="nextPage" class="mx-1 px-3 py-1 bg-gray-200 rounded">下一页</button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 新增/编辑弹窗 -->
|
|
|
+ <div v-if="showModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center">
|
|
|
+ <div class="bg-white p-6 rounded shadow-lg w-full max-w-md">
|
|
|
+ <h2 class="text-xl font-bold mb-4">{{ modalTitle }}</h2>
|
|
|
+ <form @submit.prevent="submitForm">
|
|
|
+ <div class="mb-4">
|
|
|
+ <label class="block text-gray-700 mb-2">商品名称</label>
|
|
|
+ <input v-model="formData.name" type="text" class="border p-2 w-full rounded" required />
|
|
|
+ </div>
|
|
|
+ <div class="mb-4">
|
|
|
+ <label class="block text-gray-700 mb-2">价格</label>
|
|
|
+ <input v-model="formData.price" type="number" class="border p-2 w-full rounded" required />
|
|
|
+ </div>
|
|
|
+ <div class="mb-4">
|
|
|
+ <label class="block text-gray-700 mb-2">库存</label>
|
|
|
+ <input v-model="formData.stock" type="number" class="border p-2 w-full rounded" required />
|
|
|
+ </div>
|
|
|
+ <div class="mb-4">
|
|
|
+ <label class="block text-gray-700 mb-2">供应商信息</label>
|
|
|
+ <input v-model="formData.supplier" type="text" class="border p-2 w-full rounded" required />
|
|
|
+ </div>
|
|
|
+ <div class="flex justify-end space-x-2">
|
|
|
+ <button type="button" @click="closeModal" class="px-4 py-2 bg-gray-300 rounded">取消</button>
|
|
|
+ <button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded">保存</button>
|
|
|
+ </div>
|
|
|
+ </form>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, computed, onMounted } from 'vue';
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
+import { LucideEdit, LucideTrash, LucideSettings } from 'lucide-vue-next';
|
|
|
+
|
|
|
+const router = useRouter();
|
|
|
+const searchQuery = ref('');
|
|
|
+const currentPage = ref(1);
|
|
|
+const products = ref([
|
|
|
+ { id: 1, name: '商品A', price: 100, stock: 50, supplier: '供应商X', status: '在售' },
|
|
|
+ { id: 2, name: '商品B', price: 200, stock: 30, supplier: '供应商Y', status: '缺货' },
|
|
|
+ // 更多商品数据...
|
|
|
+]);
|
|
|
+
|
|
|
+const totalPages = computed(() => Math.ceil(products.value.length / 10));
|
|
|
+const paginatedProducts = computed(() => {
|
|
|
+ const start = (currentPage.value - 1) * 10;
|
|
|
+ const end = start + 10;
|
|
|
+ return products.value.slice(start, end);
|
|
|
+});
|
|
|
+
|
|
|
+const totalProducts = computed(() => products.value.length);
|
|
|
+const activeProducts = computed(() => products.value.filter(p => p.status === '在售').length);
|
|
|
+const lowStockProducts = computed(() => products.value.filter(p => p.stock < 10).length);
|
|
|
+const popularProducts = computed(() => products.value.filter(p => p.stock > 50).length);
|
|
|
+
|
|
|
+const showModal = ref(false);
|
|
|
+const modalTitle = ref('');
|
|
|
+const formData = ref({
|
|
|
+ name: '',
|
|
|
+ price: '',
|
|
|
+ stock: '',
|
|
|
+ supplier: ''
|
|
|
+});
|
|
|
+
|
|
|
+const openModal = (type, product = null) => {
|
|
|
+ modalTitle.value = type === 'add' ? '新增商品' : '编辑商品';
|
|
|
+ if (product) {
|
|
|
+ formData.value = { ...product };
|
|
|
+ } else {
|
|
|
+ formData.value = { name: '', price: '', stock: '', supplier: '' };
|
|
|
+ }
|
|
|
+ showModal.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+const closeModal = () => {
|
|
|
+ showModal.value = false;
|
|
|
+};
|
|
|
+
|
|
|
+const submitForm = () => {
|
|
|
+ // 提交表单逻辑,这里只是示例
|
|
|
+ console.log('提交表单:', formData.value);
|
|
|
+ closeModal();
|
|
|
+};
|
|
|
+
|
|
|
+const deleteProduct = (id) => {
|
|
|
+ // 删除商品逻辑,这里只是示例
|
|
|
+ console.log('删除商品:', id);
|
|
|
+};
|
|
|
+
|
|
|
+const prevPage = () => {
|
|
|
+ if (currentPage.value > 1) {
|
|
|
+ currentPage.value--;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const nextPage = () => {
|
|
|
+ if (currentPage.value < totalPages.value) {
|
|
|
+ currentPage.value++;
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* 多巴胺配色 */
|
|
|
+.bg-blue-100 {
|
|
|
+ background-color: #a3bffa;
|
|
|
+}
|
|
|
+.bg-green-100 {
|
|
|
+ background-color: #a1f7ba;
|
|
|
+}
|
|
|
+.bg-yellow-100 {
|
|
|
+ background-color: #fde092;
|
|
|
+}
|
|
|
+.bg-purple-100 {
|
|
|
+ background-color: #d8b4fe;
|
|
|
+}
|
|
|
+</style>
|