|
|
@@ -1,83 +1,131 @@
|
|
|
<template>
|
|
|
<div class="page-container">
|
|
|
- <PageTitle title="协议供货" />
|
|
|
- <!-- 搜索栏 -->
|
|
|
- <div class="search-bar">
|
|
|
- <el-input v-model="queryParams.keyword" placeholder="搜索" style="width: 180px" clearable>
|
|
|
- <template #prefix
|
|
|
- ><el-icon><Search /></el-icon
|
|
|
- ></template>
|
|
|
- </el-input>
|
|
|
- <div class="price-range">
|
|
|
- <el-input v-model="queryParams.minPrice" placeholder="¥ 最高价" style="width: 100px" />
|
|
|
- <span class="range-separator">—</span>
|
|
|
- <el-input v-model="queryParams.maxPrice" placeholder="¥ 最低价" style="width: 100px" />
|
|
|
+ <PageTitle title="协议供货商品" />
|
|
|
+ <!-- 搜索表单区 -->
|
|
|
+ <div class="search-form-card">
|
|
|
+ <!-- 第一行:商品编号、商品名称、商品品牌 -->
|
|
|
+ <div class="form-row">
|
|
|
+ <div class="form-item">
|
|
|
+ <span class="form-label">商品编号:</span>
|
|
|
+ <el-input v-model="queryParams.productNo" placeholder="请输入商品编号" clearable style="flex:1" @keyup.enter="handleQuery" />
|
|
|
+ </div>
|
|
|
+ <div class="form-item">
|
|
|
+ <span class="form-label">商品名称:</span>
|
|
|
+ <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable style="flex:1" @keyup.enter="handleQuery" />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <el-select v-model="queryParams.category" placeholder="商品类别" style="width: 100px" clearable>
|
|
|
- <el-option v-for="item in categoryList" :key="item.id" :label="item.categoryName" :value="item.id" />
|
|
|
- </el-select>
|
|
|
- </div>
|
|
|
- <!-- 排序栏 -->
|
|
|
- <div class="sort-bar">
|
|
|
- <el-select v-model="queryParams.sortType" placeholder="默认排序" style="width: 110px">
|
|
|
- <el-option label="默认排序" value="default" /><el-option label="销量优先" value="sales" /><el-option label="最新上架" value="newest" />
|
|
|
- </el-select>
|
|
|
- <el-select v-model="queryParams.priceSort" placeholder="价格排序" style="width: 110px">
|
|
|
- <el-option label="价格排序" value="" /><el-option label="价格从低到高" value="asc" /><el-option label="价格从高到低" value="desc" />
|
|
|
- </el-select>
|
|
|
- </div>
|
|
|
- <!-- 商品列表 -->
|
|
|
- <div class="product-grid">
|
|
|
- <div v-for="(item, index) in productList" :key="item.id || index" class="product-card">
|
|
|
- <div class="product-image">
|
|
|
- <el-image :src="item.productImage" fit="contain">
|
|
|
- <template #error
|
|
|
- ><div class="image-placeholder">
|
|
|
- <el-icon :size="40" color="#ccc"><Picture /></el-icon></div
|
|
|
- ></template>
|
|
|
- </el-image>
|
|
|
+ <!-- 第二行:售价区间、商品类别 -->
|
|
|
+ <div class="form-row">
|
|
|
+ <div class="form-item">
|
|
|
+ <span class="form-label">售价区间:</span>
|
|
|
+ <el-input v-model="queryParams.minPrice" placeholder="售价最低" style="width: 110px" />
|
|
|
+ <span class="range-sep">-</span>
|
|
|
+ <el-input v-model="queryParams.maxPrice" placeholder="售价最高" style="width: 110px" />
|
|
|
+ </div>
|
|
|
+ <div class="form-item category-item">
|
|
|
+ <span class="form-label">商品类别:</span>
|
|
|
+ <el-select v-model="queryParams.topCategoryId" placeholder="请选择" clearable @change="handleFirstCategoryChange" style="flex:1">
|
|
|
+ <el-option v-for="item in categoryList1" :key="item.id" :label="item.categoryName" :value="item.id" />
|
|
|
+ </el-select>
|
|
|
+ <el-select v-model="queryParams.middleCategoryId" placeholder="请选择" clearable :disabled="!queryParams.topCategoryId" @change="handleSecondCategoryChange" style="flex:1">
|
|
|
+ <el-option v-for="item in categoryList2" :key="item.id" :label="item.categoryName" :value="item.id" />
|
|
|
+ </el-select>
|
|
|
+ <el-select v-model="queryParams.bottomCategoryId" placeholder="请选择" clearable :disabled="!queryParams.middleCategoryId" style="flex:1">
|
|
|
+ <el-option v-for="item in categoryList3" :key="item.id" :label="item.categoryName" :value="item.id" />
|
|
|
+ </el-select>
|
|
|
</div>
|
|
|
- <div class="product-info">
|
|
|
- <div class="product-name">{{ item.itemName }}</div>
|
|
|
- <div class="product-price">
|
|
|
- <span class="price-tag">协议价</span>
|
|
|
- <span class="current-price">¥{{ formatPrice(item.agreementPrice || item.price) }}</span>
|
|
|
- <span class="original-price">¥{{ formatPrice(item.marketPrice) }}</span>
|
|
|
- <div class="add-cart" @click="handleAddCart(item)">
|
|
|
- <el-icon><Plus /></el-icon>
|
|
|
+ </div>
|
|
|
+ <!-- 第三行:操作按钮 -->
|
|
|
+ <div class="form-actions">
|
|
|
+ <el-button type="primary" @click="handleQuery">查询</el-button>
|
|
|
+ <el-button @click="handleReset">清除选择</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 商品内容区 -->
|
|
|
+ <div class="content-card">
|
|
|
+ <!-- 排序栏 -->
|
|
|
+ <div class="sort-bar">
|
|
|
+ <el-button
|
|
|
+ :class="['sort-btn', { active: queryParams.sortField === '1' }]"
|
|
|
+ size="small"
|
|
|
+ @click="handleSortToggle('1')"
|
|
|
+ >默认排序 <el-icon><Sort /></el-icon></el-button>
|
|
|
+ <el-button
|
|
|
+ :class="['sort-btn', { active: queryParams.sortField === '3' }]"
|
|
|
+ size="small"
|
|
|
+ @click="handleSortToggle('3')"
|
|
|
+ >价格排序 <el-icon><Sort /></el-icon></el-button>
|
|
|
+ </div>
|
|
|
+ <!-- 商品列表 -->
|
|
|
+ <div v-if=" productList.length > 0">
|
|
|
+ <div class="product-grid">
|
|
|
+ <div v-for="(item, index) in productList" :key="item.id || index" class="product-card">
|
|
|
+ <div class="product-image">
|
|
|
+ <el-image :src="item.productImage" fit="contain">
|
|
|
+ <template #error
|
|
|
+ ><div class="image-placeholder">
|
|
|
+ <el-icon :size="40" color="#ccc"><Picture /></el-icon></div
|
|
|
+ ></template>
|
|
|
+ </el-image>
|
|
|
+ </div>
|
|
|
+ <div class="product-info">
|
|
|
+ <div class="product-name">{{ item.itemName }}</div>
|
|
|
+ <div class="product-price">
|
|
|
+ <span class="price-tag">协议价</span>
|
|
|
+ <span class="current-price">¥{{ formatPrice(item.agreementPrice || item.price) }}</span>
|
|
|
+ <span class="original-price">¥{{ formatPrice(item.marketPrice) }}</span>
|
|
|
+ <div class="add-cart" @click="handleAddCart(item)">
|
|
|
+ <el-icon><Plus /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <TablePagination
|
|
|
+ v-model:page="queryParams.pageNum"
|
|
|
+ v-model:page-size="queryParams.pageSize"
|
|
|
+ :total="total"
|
|
|
+ @change="loadProductList"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <!-- 空状态 -->
|
|
|
+ <div v-else class="empty-area">
|
|
|
+ <el-icon :size="80" color="#d0d0d0"><Box /></el-icon>
|
|
|
+ <p class="empty-title">未收到任何系统消息</p>
|
|
|
+ <p class="empty-desc">
|
|
|
+ <a href="#" class="empty-link">还不快来建立自己的产品专区~~</a>
|
|
|
+ 与我们签订产品协议价格,您将拥有您的专属产品专区,价格更低、服务更优!<br />
|
|
|
+ 如需要建立自己专属的协议产品专区请联系您的专属客服【取客服联系方式】或致电400-111-0027。
|
|
|
+ </p>
|
|
|
+ <el-button class="policy-btn">了解协议供货政策</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <el-empty v-if="productList.length === 0" description="暂无协议供货商品" />
|
|
|
- <TablePagination
|
|
|
- v-if="productList.length > 0"
|
|
|
- v-model:page="queryParams.pageNum"
|
|
|
- v-model:page-size="queryParams.pageSize"
|
|
|
- :total="total"
|
|
|
- @change="handleQuery"
|
|
|
- />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { ref, reactive, onMounted } from 'vue';
|
|
|
-import { Search, Picture, Plus } from '@element-plus/icons-vue';
|
|
|
+import { Picture, Plus, Sort, Box } from '@element-plus/icons-vue';
|
|
|
import { ElMessage } from 'element-plus';
|
|
|
import { PageTitle, TablePagination } from '@/components';
|
|
|
-import { getAgreementSupplyProductList } from '@/api/goods/index';
|
|
|
+import { getAgreementSupplyProductList, addProductShoppingCart } from '@/api/goods/index';
|
|
|
import { getProductCategoryList } from '@/api/home/index';
|
|
|
|
|
|
const queryParams = reactive({
|
|
|
pageNum: 1,
|
|
|
pageSize: 15,
|
|
|
- keyword: '',
|
|
|
+ searchKeyword: '',
|
|
|
+ productNo: '',
|
|
|
+ itemName: '',
|
|
|
+ brand: '',
|
|
|
minPrice: '',
|
|
|
maxPrice: '',
|
|
|
- category: '',
|
|
|
- sortType: 'default',
|
|
|
- priceSort: ''
|
|
|
+ topCategoryId: '' as number | string,
|
|
|
+ middleCategoryId: '' as number | string,
|
|
|
+ bottomCategoryId: '' as number | string,
|
|
|
+ sortField: '',
|
|
|
+ sortOrder: ''
|
|
|
});
|
|
|
const total = ref(0);
|
|
|
|
|
|
@@ -100,32 +148,54 @@ interface CategoryItem {
|
|
|
[key: string]: any;
|
|
|
}
|
|
|
|
|
|
-const categoryList = ref<CategoryItem[]>([]);
|
|
|
+const categoryList1 = ref<CategoryItem[]>([]);
|
|
|
+const categoryList2 = ref<CategoryItem[]>([]);
|
|
|
+const categoryList3 = ref<CategoryItem[]>([]);
|
|
|
|
|
|
-// 加载商品类别列表
|
|
|
+// 加载一级商品类别
|
|
|
const loadCategoryList = async () => {
|
|
|
try {
|
|
|
const res = await getProductCategoryList({ classLevel: 1 });
|
|
|
if (res.data) {
|
|
|
- categoryList.value = res.data;
|
|
|
+ categoryList1.value = res.data;
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('获取商品类别失败:', error);
|
|
|
- ElMessage.error('获取商品类别失败');
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 一级类别变化:加载二级,清空二三级选中
|
|
|
+const handleFirstCategoryChange = async (val: number | string) => {
|
|
|
+ queryParams.middleCategoryId = '';
|
|
|
+ queryParams.bottomCategoryId = '';
|
|
|
+ categoryList2.value = [];
|
|
|
+ categoryList3.value = [];
|
|
|
+ if (!val) return;
|
|
|
+ try {
|
|
|
+ const res = await getProductCategoryList({ classLevel: 2, parentId: val });
|
|
|
+ if (res.data) categoryList2.value = res.data;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取二级类别失败:', error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 二级类别变化:加载三级,清空三级选中
|
|
|
+const handleSecondCategoryChange = async (val: number | string) => {
|
|
|
+ queryParams.bottomCategoryId = '';
|
|
|
+ categoryList3.value = [];
|
|
|
+ if (!val) return;
|
|
|
+ try {
|
|
|
+ const res = await getProductCategoryList({ classLevel: 3, parentId: val });
|
|
|
+ if (res.data) categoryList3.value = res.data;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取三级类别失败:', error);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 加载协议供货商品列表
|
|
|
const loadProductList = async () => {
|
|
|
try {
|
|
|
- const res = await getAgreementSupplyProductList({
|
|
|
- ...queryParams,
|
|
|
- // 将价格范围转换为后端需要的格式
|
|
|
- priceRange:
|
|
|
- queryParams.minPrice && queryParams.maxPrice
|
|
|
- ? `${queryParams.minPrice}-${queryParams.maxPrice}`
|
|
|
- : queryParams.minPrice || queryParams.maxPrice || undefined
|
|
|
- });
|
|
|
+ const res = await getAgreementSupplyProductList({ ...queryParams });
|
|
|
|
|
|
if (res.rows) {
|
|
|
productList.value = res.rows;
|
|
|
@@ -143,10 +213,46 @@ const handleQuery = () => {
|
|
|
loadProductList();
|
|
|
};
|
|
|
|
|
|
+// 重置
|
|
|
+const handleReset = () => {
|
|
|
+ queryParams.searchKeyword = '';
|
|
|
+ queryParams.productNo = '';
|
|
|
+ queryParams.itemName = '';
|
|
|
+ queryParams.brand = '';
|
|
|
+ queryParams.minPrice = '';
|
|
|
+ queryParams.maxPrice = '';
|
|
|
+ queryParams.topCategoryId = '';
|
|
|
+ queryParams.middleCategoryId = '';
|
|
|
+ queryParams.bottomCategoryId = '';
|
|
|
+ queryParams.sortField = '';
|
|
|
+ queryParams.sortOrder = '';
|
|
|
+ categoryList2.value = [];
|
|
|
+ categoryList3.value = [];
|
|
|
+ queryParams.pageNum = 1;
|
|
|
+ loadProductList();
|
|
|
+};
|
|
|
+
|
|
|
+// 排序切换
|
|
|
+const handleSortToggle = (field: string) => {
|
|
|
+ if (queryParams.sortField === field) {
|
|
|
+ queryParams.sortOrder = queryParams.sortOrder === 'Asc' ? 'Desc' : 'Asc';
|
|
|
+ } else {
|
|
|
+ queryParams.sortField = field;
|
|
|
+ queryParams.sortOrder = 'Asc';
|
|
|
+ }
|
|
|
+ queryParams.pageNum = 1;
|
|
|
+ loadProductList();
|
|
|
+};
|
|
|
+
|
|
|
// 添加购物车
|
|
|
-const handleAddCart = (item: ProductItem) => {
|
|
|
- console.log('添加到购物车:', item);
|
|
|
- ElMessage.success('已加入购物车');
|
|
|
+const handleAddCart = async (item: ProductItem) => {
|
|
|
+ try {
|
|
|
+ await addProductShoppingCart({ productId: item.id, productNum: 1 });
|
|
|
+ ElMessage.success('已加入购物车');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加入购物车失败:', error);
|
|
|
+ ElMessage.error('加入购物车失败');
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
// 格式化价格显示
|
|
|
@@ -164,38 +270,103 @@ onMounted(() => {
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
-.search-bar {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 15px;
|
|
|
- margin-bottom: 15px;
|
|
|
- .price-range {
|
|
|
+.page-container {
|
|
|
+ background: #f5f5f5;
|
|
|
+ min-height: 100%;
|
|
|
+ padding-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+// 搜索表单卡片
|
|
|
+.search-form-card {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 20px 24px 16px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ .form-row {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- gap: 5px;
|
|
|
- .range-separator {
|
|
|
- color: #999;
|
|
|
+ gap: 20px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+
|
|
|
+ .form-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ &.category-item {
|
|
|
+ flex: 2;
|
|
|
+ gap: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .form-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #333;
|
|
|
+ white-space: nowrap;
|
|
|
+ min-width: 70px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .range-sep {
|
|
|
+ margin: 0 8px;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .form-actions {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ padding-top: 4px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 商品内容卡片
|
|
|
+.content-card {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 16px 20px;
|
|
|
}
|
|
|
+
|
|
|
+// 排序栏
|
|
|
.sort-bar {
|
|
|
display: flex;
|
|
|
- gap: 10px;
|
|
|
- margin-bottom: 20px;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ padding-bottom: 12px;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+
|
|
|
+ .sort-btn {
|
|
|
+ border-color: #d9d9d9;
|
|
|
+ color: #555;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ border-color: #409eff;
|
|
|
+ color: #409eff;
|
|
|
+ background: #ecf5ff;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+// 商品网格
|
|
|
.product-grid {
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(5, 1fr);
|
|
|
gap: 15px;
|
|
|
+ margin-bottom: 16px;
|
|
|
}
|
|
|
+
|
|
|
.product-card {
|
|
|
background: #fff;
|
|
|
+ border: 1px solid #f0f0f0;
|
|
|
border-radius: 8px;
|
|
|
overflow: hidden;
|
|
|
transition: all 0.2s;
|
|
|
+
|
|
|
&:hover {
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
|
+
|
|
|
.product-image {
|
|
|
height: 160px;
|
|
|
background: #f9f9f9;
|
|
|
@@ -203,10 +374,12 @@ onMounted(() => {
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
padding: 10px;
|
|
|
+
|
|
|
.el-image {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
}
|
|
|
+
|
|
|
.image-placeholder {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
@@ -216,8 +389,10 @@ onMounted(() => {
|
|
|
background: #f5f5f5;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
.product-info {
|
|
|
padding: 12px;
|
|
|
+
|
|
|
.product-name {
|
|
|
font-size: 13px;
|
|
|
color: #333;
|
|
|
@@ -229,25 +404,30 @@ onMounted(() => {
|
|
|
-webkit-box-orient: vertical;
|
|
|
margin-bottom: 8px;
|
|
|
}
|
|
|
+
|
|
|
.product-price {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 5px;
|
|
|
position: relative;
|
|
|
+
|
|
|
.price-tag {
|
|
|
font-size: 12px;
|
|
|
color: #e60012;
|
|
|
}
|
|
|
+
|
|
|
.current-price {
|
|
|
font-size: 16px;
|
|
|
font-weight: bold;
|
|
|
color: #e60012;
|
|
|
}
|
|
|
+
|
|
|
.original-price {
|
|
|
font-size: 12px;
|
|
|
color: #999;
|
|
|
text-decoration: line-through;
|
|
|
}
|
|
|
+
|
|
|
.add-cart {
|
|
|
position: absolute;
|
|
|
right: 0;
|
|
|
@@ -262,6 +442,7 @@ onMounted(() => {
|
|
|
justify-content: center;
|
|
|
cursor: pointer;
|
|
|
transition: all 0.2s;
|
|
|
+
|
|
|
&:hover {
|
|
|
background: #e60012;
|
|
|
color: #fff;
|
|
|
@@ -270,4 +451,40 @@ onMounted(() => {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// 空状态区域
|
|
|
+.empty-area {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ padding: 60px 20px;
|
|
|
+
|
|
|
+ .empty-title {
|
|
|
+ font-size: 16px;
|
|
|
+ color: #999;
|
|
|
+ margin: 16px 0 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .empty-desc {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #666;
|
|
|
+ text-align: center;
|
|
|
+ line-height: 1.8;
|
|
|
+ margin-bottom: 24px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .empty-link {
|
|
|
+ color: #e60012;
|
|
|
+ text-decoration: none;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ text-decoration: underline;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .policy-btn {
|
|
|
+ width: 200px;
|
|
|
+ border-color: #d9d9d9;
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|