feat: 完成订单履约菜单重构与业务优化
1. 调整token有效期从30分钟改为60分钟 2. 重构订单履约菜单结构,拆分出供应商履约子目录 3. 修复发货单状态校验逻辑,允许confirmed状态发货 4. 调整页面表格样式与列宽适配 5. 新增供应商履约相关路由与菜单权限 6. 替换首页仪表盘为福安德平台首页 7. 新增批量客户初始化SQL与重复菜单清理脚本 8. 移除顶部导航栏的源码和文档地址入口
This commit is contained in:
@@ -1,197 +1,242 @@
|
||||
<template>
|
||||
<div class="dashboard">
|
||||
<el-row :gutter="20" class="stat-row">
|
||||
<el-col :xs="12" :sm="6" v-for="item in (isSupplier ? statCards.filter(c => c.key==='rfqs') : statCards)" :key="item.key">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">
|
||||
<i :class="item.icon" />
|
||||
</div>
|
||||
<div class="stat-body">
|
||||
<div class="stat-num">{{ stats[item.key] }}</div>
|
||||
<div class="stat-label">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="home-page">
|
||||
<!-- Logo -->
|
||||
<div class="logo-section">
|
||||
<div class="logo-box">
|
||||
<img src="@/assets/logo/logo.svg" alt="福安德" class="logo-img">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :lg="14">
|
||||
<el-card class="panel-card">
|
||||
<div slot="header" class="panel-header">
|
||||
<span><i class="el-icon-document" /> 最近询价单</span>
|
||||
<router-link to="/rfq" class="panel-more">查看全部</router-link>
|
||||
</div>
|
||||
<el-table :data="recentRfqs" size="small" style="width:100%">
|
||||
<el-table-column prop="rfqNo" label="单号" width="160" />
|
||||
<el-table-column prop="title" label="标题" show-overflow-tooltip />
|
||||
<el-table-column prop="deadline" label="截止日期" width="105" />
|
||||
<el-table-column label="状态" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="mini" :type="rfqStatusType(scope.row.status)">{{ rfqStatusText(scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :lg="10" v-if="!isSupplier">
|
||||
<el-card class="panel-card">
|
||||
<div slot="header" class="panel-header">
|
||||
<span><i class="el-icon-shopping-cart-full" /> 最近采购单</span>
|
||||
<router-link to="/purchaseorder" class="panel-more">查看全部</router-link>
|
||||
</div>
|
||||
<el-table :data="recentPos" size="small" style="width:100%">
|
||||
<el-table-column prop="poNo" label="单号" width="140" />
|
||||
<el-table-column prop="supplierName" label="供应商" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="mini" :type="poStatusType(scope.row.status)">{{ poStatusText(scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 系统名称 -->
|
||||
<div class="title-section">
|
||||
<h1 class="main-title">福安德智慧报价平台</h1>
|
||||
<p class="sub-title">FUANDE SMART QUOTATION PLATFORM</p>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="20" style="margin-top:20px">
|
||||
<el-col :xs="24" :lg="10" v-if="!isSupplier">
|
||||
<el-card class="panel-card">
|
||||
<div slot="header" class="panel-header">
|
||||
<span><i class="el-icon-s-custom" /> 供应商列表</span>
|
||||
<router-link to="/supplier" class="panel-more">查看全部</router-link>
|
||||
</div>
|
||||
<el-table :data="suppliers" size="small" style="width:100%">
|
||||
<el-table-column prop="supplierName" label="供应商名称" show-overflow-tooltip />
|
||||
<el-table-column prop="contactName" label="联系人" width="90" />
|
||||
<el-table-column label="等级" width="70">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="mini" :type="gradeType(scope.row.grade)">{{ scope.row.grade || 'B' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :lg="14">
|
||||
<el-card class="panel-card">
|
||||
<div slot="header" class="panel-header">
|
||||
<span><i class="el-icon-magic-stick" /> 快捷操作</span>
|
||||
</div>
|
||||
<div class="quick-actions">
|
||||
<router-link v-for="action in (isSupplier ? supplierQuickActions : quickActions)" :key="action.path" :to="action.path" class="quick-btn">
|
||||
<div class="quick-icon" :style="{ background: action.color + '18', color: action.color }">
|
||||
<i :class="action.icon" />
|
||||
</div>
|
||||
<span>{{ action.label }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 分隔线 -->
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- 欢迎语 -->
|
||||
<div class="welcome-section">
|
||||
<p class="welcome-text">{{ greeting }},{{ nickName }}!欢迎回来</p>
|
||||
</div>
|
||||
|
||||
<!-- 时间 -->
|
||||
<div class="time-section">
|
||||
<div class="time-display">{{ currentTime }}</div>
|
||||
<div class="date-display">{{ currentDate }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部标语 -->
|
||||
<div class="slogan-section">
|
||||
<p class="slogan">原料·研发·生产·销售·服务 全产业链数智运营</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listSupplier } from "@/api/bid/supplier"
|
||||
import { listMaterial } from "@/api/bid/material"
|
||||
import { listRfq } from "@/api/bid/rfq"
|
||||
import { listPurchaseorder } from "@/api/bid/purchaseorder"
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: "Dashboard",
|
||||
computed: {
|
||||
...mapGetters(['nickName', 'roles']),
|
||||
isSupplier() {
|
||||
return this.$store.getters.roles && this.$store.getters.roles.includes('supplier');
|
||||
},
|
||||
supplierQuickActions() {
|
||||
return [
|
||||
{ label: "报价请求", icon: "el-icon-document", color: "#e4393c", path: "/rfq" },
|
||||
{ label: "我的报价", icon: "el-icon-money", color: "#67c23a", path: "/quotation" }
|
||||
];
|
||||
return this.roles && this.roles.includes('supplier')
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
stats: { suppliers: 0, materials: 0, rfqs: 0, pos: 0 },
|
||||
recentRfqs: [],
|
||||
recentPos: [],
|
||||
suppliers: [],
|
||||
statCards: [
|
||||
{ key: "suppliers", label: "供应商", icon: "el-icon-s-custom", color: "#e4393c" },
|
||||
{ key: "materials", label: "物料数", icon: "el-icon-goods", color: "#e4393c" },
|
||||
{ key: "rfqs", label: "询价单", icon: "el-icon-document", color: "#e4393c" },
|
||||
{ key: "pos", label: "采购单", icon: "el-icon-shopping-cart-full", color: "#e4393c" }
|
||||
],
|
||||
currentTime: '',
|
||||
currentDate: '',
|
||||
greeting: '',
|
||||
timer: null,
|
||||
quickActions: [
|
||||
{ label: "新建询价单", icon: "el-icon-edit", color: "#e4393c", path: "/rfq" },
|
||||
{ label: "物料管理", icon: "el-icon-goods", color: "#e4393c", path: "/material" },
|
||||
{ label: "智慧比价", icon: "el-icon-data-analysis", color: "#e4393c", path: "/comparison" },
|
||||
{ label: "采购单", icon: "el-icon-shopping-cart-full", color: "#e4393c", path: "/purchaseorder" },
|
||||
{ label: "供应商", icon: "el-icon-s-cooperation", color: "#e4393c", path: "/supplier" },
|
||||
{ label: "供应商评价", icon: "el-icon-star-off", color: "#e4393c", path: "/evaluation" }
|
||||
{ label: '物料管理', icon: 'el-icon-goods', path: '/material' },
|
||||
{ label: '甲方报价', icon: 'el-icon-document-copy', path: '/clientquote' },
|
||||
{ label: '供应商报价', icon: 'el-icon-money', path: '/quotation' },
|
||||
{ label: '订单管理', icon: 'el-icon-s-order', path: '/bid/order/pending' },
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadDashboard()
|
||||
mounted() {
|
||||
this.updateTime()
|
||||
this.timer = setInterval(this.updateTime, 1000)
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.timer)
|
||||
},
|
||||
methods: {
|
||||
loadDashboard() {
|
||||
// 供应商只加载自己有权限的RFQ数据
|
||||
if (this.isSupplier) {
|
||||
listRfq({ pageNum: 1, pageSize: 5 }).then(r => {
|
||||
this.recentRfqs = r.rows || []
|
||||
this.stats.rfqs = r.total || 0
|
||||
}).catch(() => {})
|
||||
return
|
||||
}
|
||||
listSupplier({ pageNum: 1, pageSize: 5 }).then(r => {
|
||||
this.suppliers = r.rows || []
|
||||
this.stats.suppliers = r.total || 0
|
||||
}).catch(() => {})
|
||||
listMaterial({ pageNum: 1, pageSize: 1 }).then(r => {
|
||||
this.stats.materials = r.total || 0
|
||||
}).catch(() => {})
|
||||
listRfq({ pageNum: 1, pageSize: 5 }).then(r => {
|
||||
this.recentRfqs = r.rows || []
|
||||
this.stats.rfqs = r.total || 0
|
||||
}).catch(() => {})
|
||||
listPurchaseorder({ pageNum: 1, pageSize: 5 }).then(r => {
|
||||
this.recentPos = r.rows || []
|
||||
this.stats.pos = r.total || 0
|
||||
}).catch(() => {})
|
||||
updateTime() {
|
||||
const now = new Date()
|
||||
this.currentTime = now.toLocaleTimeString('zh-CN', {
|
||||
hour12: false,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
})
|
||||
this.currentDate = now.toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
weekday: 'long'
|
||||
})
|
||||
this.greeting = this.getGreeting(now.getHours())
|
||||
},
|
||||
rfqStatusType(s) { return ({ draft:"info", published:"warning", "10":"warning", closed:"", completed:"success", awarded:"success", open:"primary", rejected:"danger" })[s] || "info" },
|
||||
rfqStatusText(s) { return ({ draft:"草稿", published:"已发布", "10":"审批中", closed:"已关闭", completed:"已完成", awarded:"已定标", open:"询价中", rejected:"已驳回" })[s] || (s || "-") },
|
||||
poStatusType(s) { return ({ draft:"info", "10":"warning", rejected:"danger", confirmed:"success", delivered:"", closed:"info", disputed:"danger" })[s] || "info" },
|
||||
poStatusText(s) { return ({ draft:"草稿", "10":"审批中", rejected:"已驳回", confirmed:"已确认", delivered:"已交付", closed:"已关闭", disputed:"异议中" })[s] || (s || "-") },
|
||||
gradeType(g) { return g === "A" ? "success" : g === "B" ? "primary" : g === "C" ? "warning" : "info" }
|
||||
getGreeting(hour) {
|
||||
if (hour >= 5 && hour < 12) return '上午好'
|
||||
if (hour >= 12 && hour < 14) return '中午好'
|
||||
if (hour >= 14 && hour < 18) return '下午好'
|
||||
return '晚上好'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dashboard { padding: 20px; background: #f5f7fa; min-height: calc(100vh - 84px); }
|
||||
.stat-row { margin-bottom: 20px !important; }
|
||||
.stat-card {
|
||||
background: #fff; border: 1px solid #e5e5e5; border-radius: 2px;
|
||||
padding: 14px 18px; display: flex; align-items: center; gap: 14px;
|
||||
.home-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: calc(100vh - 84px);
|
||||
background: #ffffff;
|
||||
padding: 40px 20px 24px;
|
||||
}
|
||||
.stat-icon { font-size: 22px; opacity: 0.35; flex-shrink: 0; }
|
||||
.stat-num { font-size: 22px; font-weight: 400; color: #333; line-height: 1.2; }
|
||||
.stat-label { font-size: 12px; color: #999; margin-top: 2px; }
|
||||
.panel-card {
|
||||
border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||||
::v-deep .el-card__header { padding: 14px 20px; border-bottom: 1px solid #f0f2f5; }
|
||||
|
||||
/* ═══ Logo ═══ */
|
||||
.logo-section {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.panel-header {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
font-size: 14px; font-weight: 600; color: #333;
|
||||
i { margin-right: 6px; color: #e4393c; }
|
||||
|
||||
.logo-box {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* ═══ 标题 ═══ */
|
||||
.title-section {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
letter-spacing: 2px;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* ═══ 分隔线 ═══ */
|
||||
.divider {
|
||||
width: 60px;
|
||||
height: 2px;
|
||||
background: #e4393c;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* ═══ 欢迎语 ═══ */
|
||||
.welcome-section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: #666666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ═══ 时间 ═══ */
|
||||
.time-section {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.time-display {
|
||||
font-size: 48px;
|
||||
font-weight: 300;
|
||||
color: #e4393c;
|
||||
font-family: 'Roboto Mono', 'Courier New', monospace;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.date-display {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
/* ═══ 标语 ═══ */
|
||||
.slogan-section {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.slogan {
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #cccccc;
|
||||
margin: 0;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
/* ═══ 快捷入口 ═══ */
|
||||
.quick-section {
|
||||
width: 100%;
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
.quick-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.quick-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 16px 8px;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: #e4393c;
|
||||
background: #fff5f5;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-item-icon {
|
||||
font-size: 24px;
|
||||
color: #e4393c;
|
||||
}
|
||||
|
||||
.quick-item-label {
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
}
|
||||
.panel-more { font-size: 12px; color: #e4393c; text-decoration: none; }
|
||||
.panel-more:hover { text-decoration: underline; }
|
||||
.quick-actions { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; padding: 4px 0; }
|
||||
.quick-btn { display: flex; flex-direction: column; align-items: center; gap: 8px; text-decoration: none; padding: 16px 8px; border-radius: 8px; transition: background 0.2s; }
|
||||
.quick-btn:hover { background: #f5f7fa; }
|
||||
.quick-btn span { font-size: 13px; color: #4a5568; }
|
||||
.quick-icon { width: 44px; height: 44px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 20px; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user