|
|
@@ -1,13 +1,15 @@
|
|
|
<template>
|
|
|
<div class="app-container">
|
|
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
|
|
- <el-form-item label="商品分类;0:推荐,1:种子,2:肥料,3:农药,4:农膜,5:农技配件" prop="productCategory">
|
|
|
- <el-input
|
|
|
- v-model="queryParams.productCategory"
|
|
|
- placeholder="请输入商品分类;0:推荐,1:种子,2:肥料,3:农药,4:农膜,5:农技配件"
|
|
|
- clearable
|
|
|
- @keyup.enter.native="handleQuery"
|
|
|
- />
|
|
|
+ <el-form-item label="商品分类" prop="productCategory">
|
|
|
+ <el-select v-model="queryParams.productCategory" placeholder="请选择商品分类" clearable>
|
|
|
+ <el-option
|
|
|
+ v-for="dict in categoryOptions"
|
|
|
+ :key="dict.dictValue"
|
|
|
+ :label="dict.dictLabel"
|
|
|
+ :value="parseInt(dict.dictValue)"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="商品标题" prop="title">
|
|
|
<el-input
|
|
|
@@ -17,29 +19,43 @@
|
|
|
@keyup.enter.native="handleQuery"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="商品描述" prop="description">
|
|
|
- <el-input
|
|
|
- v-model="queryParams.description"
|
|
|
- placeholder="请输入商品描述"
|
|
|
- clearable
|
|
|
- @keyup.enter.native="handleQuery"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="当前价格" prop="price">
|
|
|
- <el-input
|
|
|
- v-model="queryParams.price"
|
|
|
- placeholder="请输入当前价格"
|
|
|
- clearable
|
|
|
- @keyup.enter.native="handleQuery"
|
|
|
- />
|
|
|
+ <el-form-item label="价格" prop="originalPrice">
|
|
|
+ <div style="display: flex; align-items: center; gap: 8px;">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.originalPriceMin"
|
|
|
+ placeholder="最低价格"
|
|
|
+ clearable
|
|
|
+ style="width: 120px;"
|
|
|
+ @keyup.enter.native="handleQuery"
|
|
|
+ />
|
|
|
+ <span>-</span>
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.originalPriceMax"
|
|
|
+ placeholder="最高价格"
|
|
|
+ clearable
|
|
|
+ style="width: 120px;"
|
|
|
+ @keyup.enter.native="handleQuery"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="原价" prop="originalPrice">
|
|
|
- <el-input
|
|
|
- v-model="queryParams.originalPrice"
|
|
|
- placeholder="请输入原价"
|
|
|
- clearable
|
|
|
- @keyup.enter.native="handleQuery"
|
|
|
- />
|
|
|
+ <el-form-item label="优惠价格" prop="price">
|
|
|
+ <div style="display: flex; align-items: center; gap: 8px;">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.priceMin"
|
|
|
+ placeholder="最低优惠价"
|
|
|
+ clearable
|
|
|
+ style="width: 120px;"
|
|
|
+ @keyup.enter.native="handleQuery"
|
|
|
+ />
|
|
|
+ <span>-</span>
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.priceMax"
|
|
|
+ placeholder="最高优惠价"
|
|
|
+ clearable
|
|
|
+ style="width: 120px;"
|
|
|
+ @keyup.enter.native="handleQuery"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="商品规格" prop="specifications">
|
|
|
<el-input
|
|
|
@@ -49,21 +65,12 @@
|
|
|
@keyup.enter.native="handleQuery"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="轮播图URL数组" prop="swiperImages">
|
|
|
- <el-input
|
|
|
- v-model="queryParams.swiperImages"
|
|
|
- placeholder="请输入轮播图URL数组"
|
|
|
- clearable
|
|
|
- @keyup.enter.native="handleQuery"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="详情图URL数组" prop="detailImages">
|
|
|
- <el-input
|
|
|
- v-model="queryParams.detailImages"
|
|
|
- placeholder="请输入详情图URL数组"
|
|
|
- clearable
|
|
|
- @keyup.enter.native="handleQuery"
|
|
|
- />
|
|
|
+ <el-form-item label="上架状态" prop="status">
|
|
|
+ <el-select v-model="queryParams.status" placeholder="请选择上架状态" clearable>
|
|
|
+ <el-option label="已上架" :value="1"></el-option>
|
|
|
+ <el-option label="待上架" :value="2"></el-option>
|
|
|
+ <el-option label="已下架" :value="0"></el-option>
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item>
|
|
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
|
|
@@ -71,6 +78,57 @@
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
|
|
|
+ <!-- 统计面板 -->
|
|
|
+ <div class="statistics-panel">
|
|
|
+ <el-row :gutter="12">
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="4" :xl="4">
|
|
|
+ <div class="stat-card total-card">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-title">商品总数</div>
|
|
|
+ <div class="stat-number">{{ statistics.totalCount || 0 }}</div>
|
|
|
+ <div class="stat-desc">累计商品数量</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="4" :xl="4">
|
|
|
+ <div class="stat-card on-sale-card">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-title">已上架</div>
|
|
|
+ <div class="stat-number">{{ statistics.onSaleCount || 0 }}</div>
|
|
|
+ <div class="stat-desc">正在销售中</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="4" :xl="4">
|
|
|
+ <div class="stat-card pending-card">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-title">待上架</div>
|
|
|
+ <div class="stat-number">{{ statistics.pendingCount || 0 }}</div>
|
|
|
+ <div class="stat-desc">等待上架商品</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="4" :xl="4">
|
|
|
+ <div class="stat-card off-sale-card">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-title">已下架</div>
|
|
|
+ <div class="stat-number">{{ statistics.offSaleCount || 0 }}</div>
|
|
|
+ <div class="stat-desc">暂停销售商品</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="24" :sm="12" :md="8" :lg="4" :xl="4">
|
|
|
+ <div class="stat-card recommended-card">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-title">推荐商品</div>
|
|
|
+ <div class="stat-number">{{ statistics.recommendedCount || 0 }}</div>
|
|
|
+ <div class="stat-desc">重点推荐商品</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
<el-row :gutter="10" class="mb8">
|
|
|
<el-col :span="1.5">
|
|
|
<el-button
|
|
|
@@ -119,27 +177,86 @@
|
|
|
|
|
|
<el-table v-loading="loading" :data="mallList" @selection-change="handleSelectionChange">
|
|
|
<el-table-column type="selection" width="55" align="center" />
|
|
|
- <el-table-column label="商品ID" align="center" prop="id" />
|
|
|
- <el-table-column label="商品分类;0:推荐,1:种子,2:肥料,3:农药,4:农膜,5:农技配件" align="center" prop="productCategory" />
|
|
|
+ <el-table-column label="序号" align="center" width="80" type="index">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span>{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="商品分类" align="center" prop="productCategory">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span>{{ getCategoryText(scope.row.productCategory) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column label="商品标题" align="center" prop="title" />
|
|
|
- <el-table-column label="商品描述" align="center" prop="description" />
|
|
|
- <el-table-column label="当前价格" align="center" prop="price" />
|
|
|
- <el-table-column label="原价" align="center" prop="originalPrice" />
|
|
|
+ <el-table-column label="商品描述" align="center" prop="description" width="200" show-overflow-tooltip>
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <div class="description-cell">
|
|
|
+ {{ scope.row.description && scope.row.description.length > 35 ? scope.row.description.substring(0, 35) + '...' : scope.row.description }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="价格" align="center" prop="originalPrice" />
|
|
|
+ <el-table-column label="优惠价格" align="center" prop="price" />
|
|
|
+ <el-table-column label="单位" align="center" prop="unit" />
|
|
|
<el-table-column label="商品规格" align="center" prop="specifications" />
|
|
|
- <el-table-column label="产品特点(JSON数组)" align="center" prop="features" />
|
|
|
- <el-table-column label="使用说明" align="center" prop="usageGuide" />
|
|
|
- <el-table-column label="轮播图URL数组" align="center" prop="swiperImages" />
|
|
|
- <el-table-column label="详情图URL数组" align="center" prop="detailImages" />
|
|
|
- <el-table-column label="状态 (1上架, 0下架)" align="center" prop="status" />
|
|
|
- <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
|
|
+ <el-table-column label="商品缩略图" align="center" prop="swiperImages" width="120">
|
|
|
<template slot-scope="scope">
|
|
|
+ <el-image
|
|
|
+ v-if="getFirstImage(scope.row.swiperImages)"
|
|
|
+ :src="getFirstImage(scope.row.swiperImages)"
|
|
|
+ :preview-src-list="[getFirstImage(scope.row.swiperImages)]"
|
|
|
+ style="width: 80px; height: 80px; object-fit: cover; cursor: pointer;"
|
|
|
+ fit="cover"
|
|
|
+ >
|
|
|
+ <div slot="error" class="image-slot">
|
|
|
+ <i class="el-icon-picture-outline"></i>
|
|
|
+ </div>
|
|
|
+ </el-image>
|
|
|
+ <span v-else>暂无图片</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="上架状态" align="center" prop="status">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag
|
|
|
+ :type="scope.row.status === 1 ? 'success' : scope.row.status === 2 ? 'warning' : 'danger'"
|
|
|
+ size="mini"
|
|
|
+ >
|
|
|
+ {{ scope.row.status === 1 ? '已上架' : scope.row.status === 2 ? '待上架' : '已下架' }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <!-- 修改按钮:只有待上架(2)和已下架(0)可以修改 -->
|
|
|
<el-button
|
|
|
+ v-if="scope.row.status === 2 || scope.row.status === 0"
|
|
|
size="mini"
|
|
|
type="text"
|
|
|
icon="el-icon-edit"
|
|
|
@click="handleUpdate(scope.row)"
|
|
|
v-hasPermi="['base:mall:edit']"
|
|
|
>修改</el-button>
|
|
|
+
|
|
|
+ <!-- 上架按钮:待上架(2)和已下架(0)都可以上架 -->
|
|
|
+ <el-button
|
|
|
+ v-if="scope.row.status === 2 || scope.row.status === 0"
|
|
|
+ size="mini"
|
|
|
+ type="text"
|
|
|
+ icon="el-icon-top"
|
|
|
+ @click="handleToggleStatus(scope.row, 1)"
|
|
|
+ v-hasPermi="['base:mall:edit']"
|
|
|
+ >上架</el-button>
|
|
|
+
|
|
|
+ <!-- 下架按钮:只有已上架(1)可以下架 -->
|
|
|
+ <el-button
|
|
|
+ v-if="scope.row.status === 1"
|
|
|
+ size="mini"
|
|
|
+ type="text"
|
|
|
+ icon="el-icon-bottom"
|
|
|
+ @click="handleToggleStatus(scope.row, 0)"
|
|
|
+ v-hasPermi="['base:mall:edit']"
|
|
|
+ >下架</el-button>
|
|
|
+
|
|
|
<el-button
|
|
|
size="mini"
|
|
|
type="text"
|
|
|
@@ -160,36 +277,87 @@
|
|
|
/>
|
|
|
|
|
|
<!-- 添加或修改农资商城对话框 -->
|
|
|
- <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
|
|
- <el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
|
|
- <el-form-item label="商品分类;0:推荐,1:种子,2:肥料,3:农药,4:农膜,5:农技配件" prop="productCategory">
|
|
|
- <el-input v-model="form.productCategory" placeholder="请输入商品分类;0:推荐,1:种子,2:肥料,3:农药,4:农膜,5:农技配件" />
|
|
|
+ <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
|
|
|
+ <el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
|
|
+ <el-form-item label="商品分类" prop="productCategory">
|
|
|
+ <el-select v-model="form.productCategory" placeholder="请选择商品分类" style="width: 100%">
|
|
|
+ <el-option
|
|
|
+ v-for="dict in categoryOptions"
|
|
|
+ :key="dict.dictValue"
|
|
|
+ :label="dict.dictLabel"
|
|
|
+ :value="parseInt(dict.dictValue)"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
|
+
|
|
|
<el-form-item label="商品标题" prop="title">
|
|
|
<el-input v-model="form.title" placeholder="请输入商品标题" />
|
|
|
</el-form-item>
|
|
|
+
|
|
|
<el-form-item label="商品描述" prop="description">
|
|
|
- <el-input v-model="form.description" placeholder="请输入商品描述" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="当前价格" prop="price">
|
|
|
- <el-input v-model="form.price" placeholder="请输入当前价格" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="原价" prop="originalPrice">
|
|
|
- <el-input v-model="form.originalPrice" placeholder="请输入原价" />
|
|
|
+ <el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入商品描述" />
|
|
|
</el-form-item>
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="价格" prop="originalPrice">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.originalPrice"
|
|
|
+ :min="0"
|
|
|
+ :precision="2"
|
|
|
+ placeholder="请输入价格"
|
|
|
+ style="width: 100%"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="优惠价格" prop="price">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.price"
|
|
|
+ :min="0"
|
|
|
+ :precision="2"
|
|
|
+ placeholder="请输入优惠价格"
|
|
|
+ style="width: 100%"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="单位" prop="unit">
|
|
|
+ <el-input v-model="form.unit" placeholder="如:元/斤、元/袋" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
<el-form-item label="商品规格" prop="specifications">
|
|
|
<el-input v-model="form.specifications" placeholder="请输入商品规格" />
|
|
|
</el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="产品特点" prop="features">
|
|
|
+ <el-input v-model="form.features" type="textarea" :rows="3" placeholder="请输入产品特点" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
<el-form-item label="使用说明" prop="usageGuide">
|
|
|
- <el-input v-model="form.usageGuide" type="textarea" placeholder="请输入内容" />
|
|
|
+ <el-input v-model="form.usageGuide" type="textarea" :rows="4" placeholder="请输入使用说明" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="商品缩略图" prop="swiperImages">
|
|
|
+ <image-upload v-model="form.swiperImages" :limit="5" />
|
|
|
+ <div style="color: #999; font-size: 12px; margin-top: 5px;">用于商品列表展示,建议上传3-5张</div>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="轮播图URL数组" prop="swiperImages">
|
|
|
- <el-input v-model="form.swiperImages" placeholder="请输入轮播图URL数组" />
|
|
|
+
|
|
|
+ <el-form-item label="商品详情图" prop="detailImages">
|
|
|
+ <image-upload v-model="form.detailImages" :limit="10" />
|
|
|
+ <div style="color: #999; font-size: 12px; margin-top: 5px;">用于商品详情页展示,建议上传5-10张</div>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="详情图URL数组" prop="detailImages">
|
|
|
- <el-input v-model="form.detailImages" placeholder="请输入详情图URL数组" />
|
|
|
+
|
|
|
+ <el-form-item label="是否推荐" prop="isRecommended">
|
|
|
+ <el-radio-group v-model="form.isRecommended">
|
|
|
+ <el-radio :label="1">是</el-radio>
|
|
|
+ <el-radio :label="0">否</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
+
|
|
|
<div slot="footer" class="dialog-footer">
|
|
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
|
|
<el-button @click="cancel">取 消</el-button>
|
|
|
@@ -200,6 +368,7 @@
|
|
|
|
|
|
<script>
|
|
|
import { listMall, getMall, delMall, addMall, updateMall } from "@/api/base/mall"
|
|
|
+import { getDicts } from "@/api/system/dict/data"
|
|
|
|
|
|
export default {
|
|
|
name: "Mall",
|
|
|
@@ -219,6 +388,16 @@ export default {
|
|
|
total: 0,
|
|
|
// 农资商城表格数据
|
|
|
mallList: [],
|
|
|
+ // 商品分类字典数据
|
|
|
+ categoryOptions: [],
|
|
|
+ // 统计数据
|
|
|
+ statistics: {
|
|
|
+ totalCount: 0,
|
|
|
+ onSaleCount: 0,
|
|
|
+ offSaleCount: 0,
|
|
|
+ pendingCount: 0,
|
|
|
+ recommendedCount: 0
|
|
|
+ },
|
|
|
// 弹出层标题
|
|
|
title: "",
|
|
|
// 是否显示弹出层
|
|
|
@@ -230,8 +409,10 @@ export default {
|
|
|
productCategory: null,
|
|
|
title: null,
|
|
|
description: null,
|
|
|
- price: null,
|
|
|
- originalPrice: null,
|
|
|
+ priceMin: null,
|
|
|
+ priceMax: null,
|
|
|
+ originalPriceMin: null,
|
|
|
+ originalPriceMax: null,
|
|
|
specifications: null,
|
|
|
features: null,
|
|
|
usageGuide: null,
|
|
|
@@ -244,7 +425,7 @@ export default {
|
|
|
// 表单校验
|
|
|
rules: {
|
|
|
productCategory: [
|
|
|
- { required: true, message: "商品分类;0:推荐,1:种子,2:肥料,3:农药,4:农膜,5:农技配件不能为空", trigger: "blur" }
|
|
|
+ { required: true, message: "商品分类不能为空", trigger: "change" }
|
|
|
],
|
|
|
title: [
|
|
|
{ required: true, message: "商品标题不能为空", trigger: "blur" }
|
|
|
@@ -252,31 +433,68 @@ export default {
|
|
|
description: [
|
|
|
{ required: true, message: "商品描述不能为空", trigger: "blur" }
|
|
|
],
|
|
|
+ originalPrice: [
|
|
|
+ { required: true, message: "价格不能为空", trigger: "blur" }
|
|
|
+ ],
|
|
|
price: [
|
|
|
- { required: true, message: "当前价格不能为空", trigger: "blur" }
|
|
|
+ { required: true, message: "优惠价格不能为空", trigger: "blur" }
|
|
|
+ ],
|
|
|
+ unit: [
|
|
|
+ { required: true, message: "单位不能为空", trigger: "blur" }
|
|
|
],
|
|
|
specifications: [
|
|
|
{ required: true, message: "商品规格不能为空", trigger: "blur" }
|
|
|
],
|
|
|
features: [
|
|
|
- { required: true, message: "产品特点(JSON数组)不能为空", trigger: "blur" }
|
|
|
+ { required: true, message: "产品特点不能为空", trigger: "blur" }
|
|
|
],
|
|
|
usageGuide: [
|
|
|
{ required: true, message: "使用说明不能为空", trigger: "blur" }
|
|
|
],
|
|
|
swiperImages: [
|
|
|
- { required: true, message: "轮播图URL数组不能为空", trigger: "blur" }
|
|
|
+ { required: true, message: "商品缩略图不能为空", trigger: "change" }
|
|
|
],
|
|
|
- status: [
|
|
|
- { required: true, message: "状态 (1上架, 0下架)不能为空", trigger: "change" }
|
|
|
+ detailImages: [
|
|
|
+ { required: true, message: "商品详情图不能为空", trigger: "change" }
|
|
|
],
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
created() {
|
|
|
this.getList()
|
|
|
+ this.getDictData()
|
|
|
+ this.getStatistics()
|
|
|
},
|
|
|
methods: {
|
|
|
+ /** 获取字典数据 */
|
|
|
+ getDictData() {
|
|
|
+ getDicts("mall_product_category").then(response => {
|
|
|
+ this.categoryOptions = response.data
|
|
|
+ })
|
|
|
+ },
|
|
|
+ /** 获取统计数据 */
|
|
|
+ getStatistics() {
|
|
|
+ // 这里可以调用专门的统计API,目前先用列表数据计算
|
|
|
+ listMall({}).then(response => {
|
|
|
+ const data = response.rows || []
|
|
|
+
|
|
|
+ this.statistics = {
|
|
|
+ totalCount: data.length,
|
|
|
+ onSaleCount: data.filter(item => item.status === 1).length,
|
|
|
+ offSaleCount: data.filter(item => item.status === 0).length,
|
|
|
+ pendingCount: data.filter(item => item.status === 2).length,
|
|
|
+ recommendedCount: data.filter(item => item.isRecommended === 1).length
|
|
|
+ }
|
|
|
+ }).catch(() => {
|
|
|
+ this.statistics = {
|
|
|
+ totalCount: 0,
|
|
|
+ onSaleCount: 0,
|
|
|
+ offSaleCount: 0,
|
|
|
+ pendingCount: 0,
|
|
|
+ recommendedCount: 0
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
/** 查询农资商城列表 */
|
|
|
getList() {
|
|
|
this.loading = true
|
|
|
@@ -300,12 +518,14 @@ export default {
|
|
|
description: null,
|
|
|
price: null,
|
|
|
originalPrice: null,
|
|
|
+ unit: null,
|
|
|
specifications: null,
|
|
|
features: null,
|
|
|
usageGuide: null,
|
|
|
swiperImages: null,
|
|
|
detailImages: null,
|
|
|
- status: null,
|
|
|
+ status: 2, // 默认待上架状态
|
|
|
+ isRecommended: 0, // 默认否
|
|
|
createBy: null,
|
|
|
createTime: null,
|
|
|
updateBy: null,
|
|
|
@@ -354,12 +574,14 @@ export default {
|
|
|
this.$modal.msgSuccess("修改成功")
|
|
|
this.open = false
|
|
|
this.getList()
|
|
|
+ this.getStatistics()
|
|
|
})
|
|
|
} else {
|
|
|
addMall(this.form).then(response => {
|
|
|
this.$modal.msgSuccess("新增成功")
|
|
|
this.open = false
|
|
|
this.getList()
|
|
|
+ this.getStatistics()
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
@@ -372,6 +594,7 @@ export default {
|
|
|
return delMall(ids)
|
|
|
}).then(() => {
|
|
|
this.getList()
|
|
|
+ this.getStatistics()
|
|
|
this.$modal.msgSuccess("删除成功")
|
|
|
}).catch(() => {})
|
|
|
},
|
|
|
@@ -380,7 +603,258 @@ export default {
|
|
|
this.download('base/mall/export', {
|
|
|
...this.queryParams
|
|
|
}, `mall_${new Date().getTime()}.xlsx`)
|
|
|
+ },
|
|
|
+ /** 获取第一张图片 */
|
|
|
+ getFirstImage(swiperImages) {
|
|
|
+ if (!swiperImages) return null
|
|
|
+
|
|
|
+ // 如果是字符串,尝试解析为JSON数组
|
|
|
+ if (typeof swiperImages === 'string') {
|
|
|
+ try {
|
|
|
+ const images = JSON.parse(swiperImages)
|
|
|
+ return Array.isArray(images) && images.length > 0 ? images[0] : null
|
|
|
+ } catch (e) {
|
|
|
+ // 如果解析失败,按逗号分割处理
|
|
|
+ const images = swiperImages.split(',').map(url => url.trim()).filter(url => url)
|
|
|
+ return images.length > 0 ? images[0] : null
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是数组,直接返回第一个元素
|
|
|
+ if (Array.isArray(swiperImages) && swiperImages.length > 0) {
|
|
|
+ return swiperImages[0]
|
|
|
+ }
|
|
|
+
|
|
|
+ return null
|
|
|
+ },
|
|
|
+ /** 获取商品分类文字 */
|
|
|
+ getCategoryText(category) {
|
|
|
+ const categoryItem = this.categoryOptions.find(item => parseInt(item.dictValue) === category)
|
|
|
+ return categoryItem ? categoryItem.dictLabel : category
|
|
|
+ },
|
|
|
+ /** 切换商品上架状态 */
|
|
|
+ handleToggleStatus(row, targetStatus) {
|
|
|
+ const statusText = targetStatus === 1 ? '上架' : '下架'
|
|
|
+
|
|
|
+ this.$modal.confirm(`确认要${statusText}商品"${row.title}"吗?`).then(() => {
|
|
|
+ const updateData = {
|
|
|
+ id: row.id,
|
|
|
+ status: targetStatus
|
|
|
+ }
|
|
|
+
|
|
|
+ updateMall(updateData).then(response => {
|
|
|
+ this.$modal.msgSuccess(`${statusText}成功`)
|
|
|
+ this.getList()
|
|
|
+ this.getStatistics()
|
|
|
+ }).catch(() => {
|
|
|
+ this.$modal.msgError(`${statusText}失败`)
|
|
|
+ })
|
|
|
+ }).catch(() => {})
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.image-slot {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: #f5f7fa;
|
|
|
+ color: #909399;
|
|
|
+ font-size: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.el-image {
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #ebeef5;
|
|
|
+}
|
|
|
+
|
|
|
+.description-cell {
|
|
|
+ max-width: 180px;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ line-height: 1.5;
|
|
|
+ text-align: left;
|
|
|
+}
|
|
|
+
|
|
|
+/* 价格区间搜索样式 */
|
|
|
+.el-form .el-form-item__content > div {
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.el-form .el-form-item__content > div > span {
|
|
|
+ color: #606266;
|
|
|
+ margin: 0 4px;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+/* 统计面板样式 */
|
|
|
+.statistics-panel {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ padding: 20px;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.statistics-panel .el-row {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+}
|
|
|
+
|
|
|
+.statistics-panel .el-col {
|
|
|
+ flex: 1;
|
|
|
+ max-width: none !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 统一各部分间距 */
|
|
|
+.app-container .el-form {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.app-container .mb8 {
|
|
|
+ margin-bottom: 16px !important;
|
|
|
+}
|
|
|
+
|
|
|
+.app-container .el-table {
|
|
|
+ margin-top: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card {
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 16px;
|
|
|
+ height: 120px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: space-between;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ cursor: pointer;
|
|
|
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.05);
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+ color: #fff;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card:hover {
|
|
|
+ transform: translateY(-3px);
|
|
|
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15), 0 4px 10px rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-content {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: flex-start;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-title {
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: rgba(255, 255, 255, 0.9);
|
|
|
+ margin-bottom: 0;
|
|
|
+ line-height: 1.2;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-number {
|
|
|
+ font-size: 32px;
|
|
|
+ font-weight: 700;
|
|
|
+ line-height: 1.0;
|
|
|
+ margin: 6px 0;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-desc {
|
|
|
+ font-size: 11px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: rgba(255, 255, 255, 0.85);
|
|
|
+ line-height: 1.2;
|
|
|
+ margin: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 不同卡片的主题色 */
|
|
|
+.total-card {
|
|
|
+ background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.on-sale-card {
|
|
|
+ background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.pending-card {
|
|
|
+ background: linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.off-sale-card {
|
|
|
+ background: linear-gradient(135deg, #ef4444 0%, #f87171 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.recommended-card {
|
|
|
+ background: linear-gradient(135deg, #ec4899 0%, #f472b6 100%);
|
|
|
+}
|
|
|
+
|
|
|
+/* 响应式设计 */
|
|
|
+@media (max-width: 1200px) {
|
|
|
+ .stat-card {
|
|
|
+ padding: 14px;
|
|
|
+ height: 110px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-title {
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-number {
|
|
|
+ font-size: 28px;
|
|
|
+ margin: 4px 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-desc {
|
|
|
+ font-size: 10px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 992px) {
|
|
|
+ .stat-card {
|
|
|
+ padding: 16px;
|
|
|
+ height: 120px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-title {
|
|
|
+ font-size: 13px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-number {
|
|
|
+ font-size: 30px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-desc {
|
|
|
+ font-size: 11px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .stat-card {
|
|
|
+ padding: 20px;
|
|
|
+ height: 140px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-title {
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-number {
|
|
|
+ font-size: 36px;
|
|
|
+ margin: 8px 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-desc {
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|