feat(bid): 优化供应商报价和甲方报价页面展示
1. 重构SupplierQuoteTab移除冗余空状态样式 2. 为CompareSection添加交期最快标识样式和逻辑 3. 全新重构ClientQuoteTab页面,优化表格布局与样式
This commit is contained in:
@@ -1,30 +1,69 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="client-quote-tab">
|
||||||
<div style="margin-bottom:10px; text-align:right">
|
<!-- 操作栏 -->
|
||||||
|
<div class="tab-toolbar">
|
||||||
|
<span class="tab-title">甲方报价记录</span>
|
||||||
<el-button size="mini" icon="el-icon-download" @click="exportExcel">导出Excel</el-button>
|
<el-button size="mini" icon="el-icon-download" @click="exportExcel">导出Excel</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-table :data="list" v-loading="loading" border size="small">
|
|
||||||
<el-table-column label="报价日期" prop="create_time" width="160" />
|
<!-- 报价表格 -->
|
||||||
<el-table-column label="甲方名称" prop="client_name" width="160" />
|
<el-table
|
||||||
<el-table-column label="成本价(元)" prop="cost_price" width="120">
|
:data="list"
|
||||||
<template slot-scope="scope">¥{{ scope.row.cost_price }}</template>
|
v-loading="loading"
|
||||||
</el-table-column>
|
border
|
||||||
<el-table-column label="单价(元)" prop="unit_price" width="120">
|
size="small"
|
||||||
<template slot-scope="scope">¥{{ scope.row.unit_price }}</template>
|
class="quote-table"
|
||||||
</el-table-column>
|
:header-cell-style="headerStyle">
|
||||||
<el-table-column label="成交价(元)" prop="total_price" width="120">
|
<el-table-column label="报价日期" width="120" align="center">
|
||||||
<template slot-scope="scope">¥{{ scope.row.total_price }}</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="报价单号" prop="quote_no" width="150" />
|
|
||||||
<el-table-column label="状态" prop="quote_status" width="100">
|
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag :type="scope.row.quote_status === 'accepted' ? 'success' : 'danger'" size="small">
|
<span class="date-cell">{{ formatDate(scope.row.create_time) }}</span>
|
||||||
{{ scope.row.quote_status === 'accepted' ? '已接受' : '已拒绝' }}
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="甲方信息" min-width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div class="client-info">
|
||||||
|
<div class="client-name">{{ scope.row.client_name }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="成本价(元)" width="120" align="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="price-cell cost-price">¥{{ formatPrice(scope.row.cost_price) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="单价(元)" width="120" align="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="price-cell">¥{{ formatPrice(scope.row.unit_price) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="成交价(元)" width="120" align="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="price-cell total-price">¥{{ formatPrice(scope.row.total_price) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="报价单号" width="150" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="quote-no">{{ scope.row.quote_no }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="状态" width="100" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="getStatusType(scope.row.quote_status)"
|
||||||
|
size="small"
|
||||||
|
effect="dark"
|
||||||
|
class="status-tag">
|
||||||
|
{{ getStatusText(scope.row.quote_status) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-empty v-if="!loading && !list.length" description="暂无甲方报价" :image-size="80" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -45,9 +84,124 @@ export default {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
}).catch(() => { this.loading = false; });
|
}).catch(() => { this.loading = false; });
|
||||||
},
|
},
|
||||||
|
formatDate(dateStr) {
|
||||||
|
if (!dateStr) return '-';
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
return date.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit' });
|
||||||
|
},
|
||||||
|
formatPrice(price) {
|
||||||
|
if (!price && price !== 0) return '-';
|
||||||
|
return Number(price).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||||
|
},
|
||||||
|
getStatusType(status) {
|
||||||
|
const map = { 'accepted': 'success', 'rejected': 'danger', 'pending': 'warning', 'draft': 'info' };
|
||||||
|
return map[status] || 'info';
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
const map = { 'accepted': '已接受', 'rejected': '已拒绝', 'pending': '待处理', 'draft': '草稿' };
|
||||||
|
return map[status] || status;
|
||||||
|
},
|
||||||
|
headerStyle() {
|
||||||
|
return {
|
||||||
|
background: '#f5f7fa',
|
||||||
|
color: '#606266',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: '13px'
|
||||||
|
};
|
||||||
|
},
|
||||||
exportExcel() {
|
exportExcel() {
|
||||||
this.$message.info('导出功能开发中');
|
this.$message.info('导出功能开发中');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.client-quote-tab {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 工具栏 */
|
||||||
|
.tab-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格样式 */
|
||||||
|
.quote-table {
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
>>> .el-table__header-wrapper {
|
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 日期单元格 */
|
||||||
|
.date-cell {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 甲方信息 */
|
||||||
|
.client-info {
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 价格单元格 */
|
||||||
|
.price-cell {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-cell.cost-price {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-cell.total-price {
|
||||||
|
color: #67c23a;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 报价单号 */
|
||||||
|
.quote-no {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
background: #f5f7fa;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 状态标签 */
|
||||||
|
.status-tag {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 行悬停效果 */
|
||||||
|
>>> .el-table__row:hover {
|
||||||
|
background-color: #f5f7fa !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -107,7 +107,10 @@
|
|||||||
v-for="(quote, qIdx) in quoteMap[mat.materialId]"
|
v-for="(quote, qIdx) in quoteMap[mat.materialId]"
|
||||||
:key="qIdx"
|
:key="qIdx"
|
||||||
class="quote-row"
|
class="quote-row"
|
||||||
:class="{ 'is-lowest': isLowestPrice(mat.materialId, qIdx) }">
|
:class="{
|
||||||
|
'is-lowest': isLowestPrice(mat.materialId, qIdx),
|
||||||
|
'is-fastest': isFastestDelivery(mat.materialId, qIdx)
|
||||||
|
}">
|
||||||
<div class="qr-col supplier-col">
|
<div class="qr-col supplier-col">
|
||||||
<span class="supplier-name" :title="quote.supplier_name">{{ quote.supplier_name }}</span>
|
<span class="supplier-name" :title="quote.supplier_name">{{ quote.supplier_name }}</span>
|
||||||
<span v-if="quote.supplier_contact || quote.supplier_phone" class="supplier-contact">
|
<span v-if="quote.supplier_contact || quote.supplier_phone" class="supplier-contact">
|
||||||
@@ -115,10 +118,11 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="qr-col price-col">
|
<div class="qr-col price-col">
|
||||||
<span class="price-value">¥{{ formatPrice(quote.unit_price) }}</span>
|
<span class="price-value" :class="{ 'is-lowest-price': isLowestPrice(mat.materialId, qIdx) }">¥{{ formatPrice(quote.unit_price) }}</span>
|
||||||
<span v-if="isLowestPrice(mat.materialId, qIdx)" class="lowest-tag">最低</span>
|
</div>
|
||||||
|
<div class="qr-col delivery-col">
|
||||||
|
<span class="delivery-value" :class="{ 'is-fastest-delivery': isFastestDelivery(mat.materialId, qIdx) }">{{ quote.delivery_days || '—' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="qr-col delivery-col">{{ quote.delivery_days || '—' }}</div>
|
|
||||||
<div class="qr-col quote-no-col" :title="quote.quote_no">{{ quote.quote_no }}</div>
|
<div class="qr-col quote-no-col" :title="quote.quote_no">{{ quote.quote_no }}</div>
|
||||||
<div class="qr-col date-col">{{ formatDate(quote.submit_time) }}</div>
|
<div class="qr-col date-col">{{ formatDate(quote.submit_time) }}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -320,6 +324,17 @@ export default {
|
|||||||
const quotes = this.quoteMap[materialId];
|
const quotes = this.quoteMap[materialId];
|
||||||
if (!quotes || !quotes.length) return false;
|
if (!quotes || !quotes.length) return false;
|
||||||
return quoteIndex === 0;
|
return quoteIndex === 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
isFastestDelivery(materialId, quoteIndex) {
|
||||||
|
const quotes = this.quoteMap[materialId];
|
||||||
|
if (!quotes || !quotes.length) return false;
|
||||||
|
// 找出最小交期
|
||||||
|
const minDays = Math.min(...quotes.map(q => Number(q.delivery_days) || Infinity));
|
||||||
|
if (minDays === Infinity) return false;
|
||||||
|
// 检查当前报价的交期是否等于最小交期
|
||||||
|
const currentDays = Number(quotes[quoteIndex].delivery_days);
|
||||||
|
return currentDays === minDays;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -569,6 +584,14 @@ export default {
|
|||||||
background: linear-gradient(90deg, #f0f9eb 0%, #e8f5e0 100%);
|
background: linear-gradient(90deg, #f0f9eb 0%, #e8f5e0 100%);
|
||||||
border-left: 3px solid #67c23a;
|
border-left: 3px solid #67c23a;
|
||||||
}
|
}
|
||||||
|
.quote-row.is-fastest {
|
||||||
|
background: linear-gradient(90deg, #ecf5ff 0%, #d9ecff 100%);
|
||||||
|
border-left: 3px solid #409eff;
|
||||||
|
}
|
||||||
|
.quote-row.is-lowest.is-fastest {
|
||||||
|
background: linear-gradient(90deg, #f0f9eb 0%, #e8f5e0 50%, #ecf5ff 100%);
|
||||||
|
border-left: 3px solid #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
.qr-col {
|
.qr-col {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -597,19 +620,24 @@ export default {
|
|||||||
|
|
||||||
.price-value {
|
.price-value {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #f56c6c;
|
color: #606266;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
.lowest-tag {
|
.price-value.is-lowest-price {
|
||||||
display: inline-block;
|
color: #67c23a;
|
||||||
background: #67c23a;
|
border-bottom: 2px solid #67c23a;
|
||||||
color: #fff;
|
padding-bottom: 2px;
|
||||||
font-size: 10px;
|
}
|
||||||
padding: 1px 5px;
|
|
||||||
border-radius: 3px;
|
.delivery-value {
|
||||||
margin-left: 4px;
|
font-weight: 600;
|
||||||
vertical-align: middle;
|
color: #606266;
|
||||||
font-weight: 500;
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.delivery-value.is-fastest-delivery {
|
||||||
|
color: #e6a23c;
|
||||||
|
border-bottom: 2px solid #e6a23c;
|
||||||
|
padding-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-col.delivery-col,
|
.qr-col.delivery-col,
|
||||||
|
|||||||
@@ -69,13 +69,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
|
||||||
<el-empty v-if="!loading && !list.length" description="暂无供应商报价" :image-size="80">
|
|
||||||
<template #description>
|
|
||||||
<span class="empty-text">暂无供应商报价记录</span>
|
|
||||||
</template>
|
|
||||||
</el-empty>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -222,12 +215,6 @@ export default {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 空状态 */
|
|
||||||
.empty-text {
|
|
||||||
color: #909399;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 行悬停效果 */
|
/* 行悬停效果 */
|
||||||
>>> .el-table__row:hover {
|
>>> .el-table__row:hover {
|
||||||
background-color: #f5f7fa !important;
|
background-color: #f5f7fa !important;
|
||||||
|
|||||||
Reference in New Issue
Block a user