343 lines
10 KiB
Vue
343 lines
10 KiB
Vue
<template>
|
||
<div class="usage-monitor">
|
||
<!-- 顶部 TabPane,居中显示 -->
|
||
|
||
<el-tabs v-model="activeTab" class="tabs-wrapper">
|
||
<el-tab-pane label="在售中" name="onSale" />
|
||
<el-tab-pane label="未上架" name="offShelf" />
|
||
<el-tab-pane label="已失效" name="expired" />
|
||
</el-tabs>
|
||
|
||
<!-- 列表内容 -->
|
||
<div class="list-wrapper">
|
||
<div v-for="(p, idx) in filteredProducts" :key="p.id" class="list-item" :class="{ 'has-border': idx > 0 }">
|
||
<!-- 商品图 -->
|
||
<img class="item-img" :src="p.img" alt="商品图" />
|
||
|
||
<!-- 名称 + 月售/库存 -->
|
||
<div class="item-info">
|
||
<div class="item-name">{{ p.name }}</div>
|
||
<div class="item-stats">月售 {{ p.monthlySales }} · 库存 {{ p.stock }}</div>
|
||
<div class="item-price">¥ {{ p.price }} /kg</div>
|
||
</div>
|
||
|
||
<div class="item-buttom">
|
||
<div class="item-status" :class="statusClass(activeTab)">
|
||
{{ tabLabels[activeTab] }}
|
||
</div>
|
||
<div class="item-actions">
|
||
<el-button size="large" class="button" @click="onInspect(p.id)">抽查</el-button>
|
||
<el-button size="large" class="button" type="danger" @click="onRevoke(p)">取消授权</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 抽查弹窗 -->
|
||
<el-dialog v-model="dialogVisible" width="720px" top="20px" modal="false" :before-close="() => (dialogVisible = false)" title="追溯记录">
|
||
<div v-if="traceData" class="trace-record">
|
||
<!-- 基本信息 -->
|
||
<section class="section">
|
||
<h3>基本信息</h3>
|
||
<el-row :gutter="12">
|
||
<el-col :span="6">产品名称:</el-col><el-col :span="18">{{ traceData.productName }}</el-col> <el-col :span="6">产品数量:</el-col
|
||
><el-col :span="18">{{ traceData.quantity }}</el-col> <el-col :span="6">生产经营主体:</el-col
|
||
><el-col :span="18">{{ traceData.producer }}</el-col> <el-col :span="6">原产地:</el-col
|
||
><el-col :span="18">{{ traceData.origin }}</el-col> <el-col :span="6">生产日期:</el-col
|
||
><el-col :span="18">{{ traceData.productionDate }}</el-col> <el-col :span="6">追溯码:</el-col
|
||
><el-col :span="18">{{ traceData.traceCode }}</el-col> <el-col :span="6">追溯次数:</el-col
|
||
><el-col :span="18">{{ traceData.traceCount }} 次</el-col>
|
||
</el-row>
|
||
</section>
|
||
|
||
<!-- 基地信息 -->
|
||
<section class="section">
|
||
<h3>基地信息</h3>
|
||
<el-row :gutter="12">
|
||
<el-col :span="6">基地地址:</el-col><el-col :span="18">{{ traceData.base.address }}</el-col> <el-col :span="6">地理位置:</el-col
|
||
><el-col :span="18">{{ traceData.base.location }}</el-col> <el-col :span="6">面积:</el-col
|
||
><el-col :span="18">{{ traceData.base.area }} 亩</el-col> <el-col :span="6">气候条件:</el-col
|
||
><el-col :span="18">{{ traceData.base.climate }}</el-col> <el-col :span="6">土壤类型:</el-col
|
||
><el-col :span="18">{{ traceData.base.soil }}</el-col>
|
||
</el-row>
|
||
</section>
|
||
|
||
<!-- 农事信息 -->
|
||
<section class="section">
|
||
<h3>农事信息</h3>
|
||
<el-table :data="traceData.farmingRecords" stripe border style="width: 100%">
|
||
<el-table-column prop="date" label="日期" width="120" />
|
||
<el-table-column prop="operation" label="操作" />
|
||
<el-table-column prop="operator" label="作业人" width="120" />
|
||
</el-table>
|
||
</section>
|
||
|
||
<!-- 包装信息 -->
|
||
<section class="section">
|
||
<h3>分拣包装</h3>
|
||
<el-row :gutter="12">
|
||
<el-col :span="6">包装企业:</el-col><el-col :span="18">{{ traceData.packaging.company }}</el-col> <el-col :span="6">包装类型:</el-col
|
||
><el-col :span="18">{{ traceData.packaging.type }}</el-col> <el-col :span="6">包装人:</el-col
|
||
><el-col :span="18">{{ traceData.packaging.person }}</el-col> <el-col :span="6">包装时间:</el-col
|
||
><el-col :span="18">{{ traceData.packaging.time }}</el-col>
|
||
</el-row>
|
||
</section>
|
||
|
||
<!-- 仓储物流 -->
|
||
<section class="section">
|
||
<h3>仓储物流信息</h3>
|
||
<el-row :gutter="12">
|
||
<el-col :span="6">存储类型:</el-col><el-col :span="18">{{ traceData.logistics.storageType }}</el-col>
|
||
<el-col :span="6">存储温度:</el-col><el-col :span="18">{{ traceData.logistics.temperature }}</el-col>
|
||
<el-col :span="6">发货地址:</el-col><el-col :span="18">{{ traceData.logistics.shipFrom }}</el-col> <el-col :span="6">收货地址:</el-col
|
||
><el-col :span="18">{{ traceData.logistics.shipTo }}</el-col>
|
||
</el-row>
|
||
</section>
|
||
|
||
<!-- 交易信息 -->
|
||
<section class="section">
|
||
<h3>交易信息</h3>
|
||
<el-row :gutter="12">
|
||
<el-col :span="6">交易时间:</el-col><el-col :span="18">{{ traceData.trade.time }}</el-col> <el-col :span="6">买家:</el-col
|
||
><el-col :span="18">{{ traceData.trade.buyer }}</el-col>
|
||
</el-row>
|
||
</section>
|
||
|
||
<!-- 右侧图片 -->
|
||
<div class="trace-img">
|
||
<img :src="getAssetsFile(traceData.img)" alt="产品图" />
|
||
</div>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue';
|
||
import { getAssetsFile } from '@/utils/index.js';
|
||
import { getMonitorList } from '@/apis/brand';
|
||
|
||
const activeTab = ref('onSale');
|
||
const dialogVisible = ref(false);
|
||
const traceData = ref(null);
|
||
|
||
const tabLabels = {
|
||
onSale: '在售中',
|
||
offShelf: '未上架',
|
||
expired: '已失效',
|
||
};
|
||
|
||
// 示例数据,替换为真实接口数据
|
||
const products = ref([
|
||
{
|
||
id: 1,
|
||
name: '耿马镇沙疆西红柿',
|
||
img: getAssetsFile('images/brand/product4.png'),
|
||
monthlySales: 999,
|
||
stock: 10000,
|
||
price: 3.0,
|
||
status: 'onSale',
|
||
},
|
||
{
|
||
id: 2,
|
||
name: '耿马镇沙疆土豆',
|
||
img: getAssetsFile('images/brand/product6.png'),
|
||
monthlySales: 123,
|
||
stock: 5000,
|
||
price: 2.5,
|
||
status: 'onSale',
|
||
},
|
||
{
|
||
id: 3,
|
||
name: '彩椒南瓜混合',
|
||
img: getAssetsFile('images/brand/product1.png'),
|
||
monthlySales: 456,
|
||
stock: 8000,
|
||
price: 4.2,
|
||
status: 'offShelf',
|
||
},
|
||
// … 更多数据
|
||
]);
|
||
|
||
// 根据当前 Tab 过滤
|
||
const filteredProducts = computed(() => products.value.filter((p) => p.status === activeTab.value));
|
||
|
||
// 点击抽查后,根据 id 单独拉取或赋值 traceData
|
||
function onInspect(id) {
|
||
console.log('查看产品:', id);
|
||
// 这里用硬编码模拟请求,实际中可换成接口调用:getTraceById(id).then(res=> traceData.value = res)
|
||
const mock = {
|
||
productName: '无土栽培土豆',
|
||
quantity: '200KG',
|
||
producer: '北大荒技术有限公司',
|
||
origin: '耿马县孟定镇下坝村',
|
||
productionDate: '2025-1-2',
|
||
traceCode: '10.5487542154785XSE254.1040201',
|
||
traceCount: 30,
|
||
base: {
|
||
address: '耿马县孟定镇下坝村',
|
||
location: '东经102° · 北纬24°',
|
||
area: 12000,
|
||
climate: '亚热带高原季风型,温和多雨',
|
||
soil: '红壤',
|
||
},
|
||
farmingRecords: [
|
||
{ date: '2024/1/2', operation: '播种 20250102批土豆种', operator: '张小东' },
|
||
{ date: '2024/2/2', operation: '施肥 氮肥', operator: '张小东' },
|
||
{ date: '2024/3/2', operation: '浇水', operator: '张小东' },
|
||
{ date: '2024/4/2', operation: '采摘', operator: '张小东' },
|
||
],
|
||
packaging: {
|
||
company: '瑞禾农产品包装公司',
|
||
type: '纸箱',
|
||
person: '王大福',
|
||
time: '2025-1-2 14:00:47',
|
||
},
|
||
logistics: {
|
||
storageType: '冷藏',
|
||
temperature: '2°C',
|
||
shipFrom: '北京市朝阳区解放路24号',
|
||
shipTo: '上海市黄浦区南京路36号',
|
||
},
|
||
trade: {
|
||
time: '2025-1-2 14:00:47',
|
||
buyer: '刘小花',
|
||
},
|
||
img: 'images/brand/product6.png',
|
||
};
|
||
traceData.value = mock;
|
||
dialogVisible.value = true;
|
||
}
|
||
const onRevoke = (p) => {
|
||
console.log('取消授权', p);
|
||
};
|
||
|
||
// 状态文字的颜色
|
||
const statusClass = (tab) => {
|
||
if (tab === 'offShelf') return 'text-warning';
|
||
if (tab === 'expired') return 'text-danger';
|
||
return 'text-success';
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.usage-monitor {
|
||
padding: 20px;
|
||
background: #fff;
|
||
border-radius: 16px;
|
||
height: 100%;
|
||
|
||
.tabs-wrapper {
|
||
width: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: #fff;
|
||
|
||
:deep(.el-tabs__item) {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
// border: 1 solid #f000;
|
||
}
|
||
}
|
||
|
||
.list-wrapper {
|
||
.list-item {
|
||
display: flex;
|
||
justify-content: start;
|
||
align-items: center;
|
||
padding: 20px 0;
|
||
|
||
.item-img {
|
||
width: 120px;
|
||
height: 120px;
|
||
object-fit: cover;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.item-info {
|
||
padding-left: 16px;
|
||
// background-color: #909399;
|
||
.item-name {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
margin-bottom: 8px;
|
||
}
|
||
.item-stats {
|
||
color: #909399;
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
|
||
.item-price {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
color: #67c23a;
|
||
text-align: left;
|
||
padding-top: 8px;
|
||
}
|
||
|
||
.item-buttom {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
height: 110px;
|
||
flex: 1;
|
||
}
|
||
|
||
.item-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 8px;
|
||
|
||
.button {
|
||
font-size: 20px;
|
||
font-weight: 400;
|
||
border-radius: 8px;
|
||
}
|
||
}
|
||
|
||
.item-status {
|
||
text-align: right;
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
&.text-success {
|
||
color: #67c23a;
|
||
}
|
||
&.text-warning {
|
||
color: #e6a23c;
|
||
}
|
||
&.text-danger {
|
||
color: #f56c6c;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.trace-record {
|
||
position: relative;
|
||
padding-right: 160px; /* 给图片预留空间 */
|
||
}
|
||
.trace-record .section {
|
||
margin-bottom: 16px;
|
||
}
|
||
.trace-record h3 {
|
||
margin-bottom: 8px;
|
||
font-size: 18px;
|
||
color: #409eff;
|
||
border-left: 4px solid #409eff;
|
||
padding-left: 8px;
|
||
}
|
||
.trace-img {
|
||
position: absolute;
|
||
top: 16px;
|
||
right: 16px;
|
||
width: 128px;
|
||
text-align: center;
|
||
}
|
||
.trace-img img {
|
||
width: 100%;
|
||
border-radius: 4px;
|
||
}
|
||
}
|
||
</style>
|