Files
erp-next/ruoyi-ui/src/views/index.vue

180 lines
8.5 KiB
Vue
Raw Normal View History

<template>
<div class="dashboard">
<el-row :gutter="20" class="stat-row">
<el-col :xs="12" :sm="6" v-for="item in statCards" :key="item.key">
<div class="stat-card" :style="{ borderTopColor: item.color }">
<div class="stat-icon" :style="{ background: item.color + '18', color: item.color }">
<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>
<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="/bid/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">
<el-card class="panel-card">
<div slot="header" class="panel-header">
<span><i class="el-icon-shopping-cart-full" /> 最近采购单</span>
<router-link to="/bid/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>
<el-row :gutter="20" style="margin-top:20px">
<el-col :xs="24" :lg="10">
<el-card class="panel-card">
<div slot="header" class="panel-header">
<span><i class="el-icon-s-custom" /> 供应商列表</span>
<router-link to="/bid/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 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>
</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"
export default {
name: "Dashboard",
data() {
return {
stats: { suppliers: 0, materials: 0, rfqs: 0, pos: 0 },
recentRfqs: [],
recentPos: [],
suppliers: [],
statCards: [
{ key: "suppliers", label: "供应商", icon: "el-icon-s-custom", color: "#1171c4" },
{ key: "materials", label: "物料数", icon: "el-icon-goods", color: "#67c23a" },
{ key: "rfqs", label: "询价单", icon: "el-icon-document", color: "#e6a23c" },
{ key: "pos", label: "采购单", icon: "el-icon-shopping-cart-full", color: "#f56c6c" }
],
quickActions: [
{ label: "新建询价单", icon: "el-icon-edit", color: "#1171c4", path: "/bid/rfq" },
{ label: "物料管理", icon: "el-icon-goods", color: "#67c23a", path: "/bid/material" },
{ label: "智慧比价", icon: "el-icon-data-analysis", color: "#e6a23c", path: "/bid/comparison" },
{ label: "采购单", icon: "el-icon-shopping-cart-full", color: "#f56c6c", path: "/bid/purchaseorder" },
{ label: "供应商", icon: "el-icon-s-cooperation", color: "#9b59b6", path: "/bid/supplier" },
{ label: "供应商评价", icon: "el-icon-star-off", color: "#e67e22", path: "/bid/evaluation" }
]
}
},
created() {
this.loadDashboard()
},
methods: {
loadDashboard() {
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(() => {})
},
rfqStatusType(s) { return s === "open" ? "primary" : s === "closed" ? "info" : s === "awarded" ? "success" : "warning" },
rfqStatusText(s) { return s === "open" ? "询价中" : s === "closed" ? "已关闭" : s === "awarded" ? "已定标" : (s || "-") },
poStatusType(s) { return s === "approved" ? "success" : s === "pending" ? "warning" : s === "rejected" ? "danger" : "info" },
poStatusText(s) { return s === "approved" ? "已审批" : s === "pending" ? "待审批" : s === "rejected" ? "已拒绝" : (s || "-") },
gradeType(g) { return g === "A" ? "success" : g === "B" ? "primary" : g === "C" ? "warning" : "info" }
}
}
</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-radius: 8px; border-top: 3px solid #1171c4;
padding: 20px; display: flex; align-items: center; gap: 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.05); margin-bottom: 0;
}
.stat-icon { width: 52px; height: 52px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 24px; flex-shrink: 0; }
.stat-num { font-size: 28px; font-weight: 700; color: #1a2c4e; line-height: 1; }
.stat-label { font-size: 13px; color: #8c97a8; margin-top: 4px; }
.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; }
}
.panel-header {
display: flex; align-items: center; justify-content: space-between;
font-size: 14px; font-weight: 600; color: #1a2c4e;
i { margin-right: 6px; color: #1171c4; }
}
.panel-more { font-size: 12px; color: #1171c4; 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>