feat: 完成订单履约菜单重构与业务优化

1. 调整token有效期从30分钟改为60分钟
2. 重构订单履约菜单结构,拆分出供应商履约子目录
3. 修复发货单状态校验逻辑,允许confirmed状态发货
4. 调整页面表格样式与列宽适配
5. 新增供应商履约相关路由与菜单权限
6. 替换首页仪表盘为福安德平台首页
7. 新增批量客户初始化SQL与重复菜单清理脚本
8. 移除顶部导航栏的源码和文档地址入口
This commit is contained in:
2026-06-16 19:35:52 +08:00
parent 7ffc140cf8
commit f5b91c3bd0
13 changed files with 368 additions and 253 deletions

View File

@@ -123,8 +123,8 @@ token:
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期(默认30分钟
expireTime: 30
# 令牌有效期(默认60分钟
expireTime: 60
# MyBatis配置
mybatis:

View File

@@ -98,7 +98,7 @@ public class BizDeliveryOrderServiceImpl implements IBizDeliveryOrderService {
public int ship(Long id) {
BizDeliveryOrder d = mapper.selectBizDeliveryOrderById(id);
if (d == null) throw new RuntimeException("发货单不存在");
if (!"pending".equals(d.getDeliveryStatus()))
if (!"pending".equals(d.getDeliveryStatus()) && !"confirmed".equals(d.getDeliveryStatus()))
throw new RuntimeException("当前状态不允许发货确认");
return mapper.updateDeliveryStatus(id, "transit", null, null, "");
}

View File

@@ -435,12 +435,12 @@ body {
/* 小按钮紧凑 */
.el-button--mini {
padding: 5px 10px !important;
padding: 4px 7px !important;
font-size: 12px !important;
}
.el-button--small {
padding: 7px 12px !important;
padding: 6px 10px !important;
font-size: 12px !important;
}
@@ -451,7 +451,6 @@ body {
border: 1px solid var(--border-gray) !important;
border-bottom: none !important;
font-size: 12px !important;
table-layout: fixed !important;
/* 表头 */
thead th {
@@ -470,21 +469,21 @@ body {
td {
color: var(--text-dark) !important;
font-size: 12px !important;
padding: 4px 6px !important;
padding: 3px 4px !important;
border-bottom: 1px solid var(--border-light) !important;
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
}
/* cell 内部换行 */
/* cell 内部(操作列允许换行 */
.cell {
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
line-height: 1.4 !important;
padding-left: 2px !important;
padding-right: 2px !important;
line-height: 1.3 !important;
padding-left: 1px !important;
padding-right: 1px !important;
}
/* 行悬停 */

View File

@@ -12,14 +12,6 @@
<template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" />
<el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip content="文档地址" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip>
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="布局大小" effect="dark" placement="bottom">

View File

@@ -189,50 +189,51 @@ export const dynamicRoutes = [
}]
},
// ── 订单履约 ──
// ── 订单履约(供应商履约) ──
{
path: '/bid/order/pending',
path: '/bid/order',
component: Layout,
permissions: ['bid:order:pending'],
children: [{
path: '',
component: () => import('@/views/bid/order/pending'),
name: 'OrderPending',
meta: { title: '待发订单', activeMenu: '/bid/order' }
}]
},
{
path: '/bid/order/transit',
component: Layout,
permissions: ['bid:order:transit'],
children: [{
path: '',
component: () => import('@/views/bid/order/transit'),
name: 'OrderTransit',
meta: { title: '在途订单', activeMenu: '/bid/order' }
}]
},
{
path: '/bid/order/history',
component: Layout,
permissions: ['bid:order:history'],
children: [{
path: '',
component: () => import('@/views/bid/order/history'),
name: 'OrderHistory',
meta: { title: '历史订单', activeMenu: '/bid/order' }
}]
},
{
path: '/bid/order/closeDate',
component: Layout,
permissions: ['bid:order:closeDate'],
children: [{
path: '',
component: () => import('@/views/bid/order/closeDate'),
name: 'CloseDate',
meta: { title: '结单时间管理', activeMenu: '/bid/order' }
}]
redirect: '/bid/order/pending',
alwaysShow: true,
hidden: true,
meta: { title: '供应商履约' },
children: [
{
path: 'pending',
component: () => import('@/views/bid/order/pending'),
name: 'OrderPending',
permissions: ['bid:order:pending'],
meta: { title: '待发订单', activeMenu: '/bid/order' }
},
{
path: 'transit',
component: () => import('@/views/bid/order/transit'),
name: 'OrderTransit',
permissions: ['bid:order:transit'],
meta: { title: '在途订单', activeMenu: '/bid/order' }
},
{
path: 'history',
component: () => import('@/views/bid/order/history'),
name: 'OrderHistory',
permissions: ['bid:order:history'],
meta: { title: '历史订单', activeMenu: '/bid/order' }
},
{
path: 'closeDate',
component: () => import('@/views/bid/order/closeDate'),
name: 'CloseDate',
permissions: ['bid:order:closeDate'],
meta: { title: '结单时间管理', activeMenu: '/bid/order' }
},
{
path: 'objection',
component: () => import('@/views/bid/objection/index'),
name: 'OrderObjection',
permissions: ['bid:objection:list'],
meta: { title: '订单异议', activeMenu: '/bid/order' }
}
]
},
// ── 操作记录 ──

View File

@@ -30,7 +30,7 @@
<el-button size="mini" type="text" style="color:#E6A23C" @click="handleSubmitApproval(s.row)" v-if="s.row.deliveryStatus==='pending' || s.row.deliveryStatus==='rejected'">提交审批</el-button>
<el-button size="mini" type="text" style="color:#67C23A" @click="handleApprove(s.row)" v-if="s.row.deliveryStatus==='10'">通过</el-button>
<el-button size="mini" type="text" style="color:#F56C6C" @click="handleReject(s.row)" v-if="s.row.deliveryStatus==='10'">驳回</el-button>
<el-button size="mini" type="text" style="color:#67C23A" @click="handleShip(s.row)" v-if="s.row.deliveryStatus==='pending' || s.row.deliveryStatus==='confirmed'">发货确认</el-button>
<el-button size="mini" type="text" style="color:#67C23A" @click="handleShip(s.row)" v-if="s.row.deliveryStatus==='confirmed'">发货确认</el-button>
<el-button size="mini" type="text" style="color:#f56c6c" @click="handleDelete(s.row)">删除</el-button>
</template>
</el-table-column>
@@ -72,7 +72,7 @@ export default {
}},
created() { this.getList() },
methods: {
getList() { this.loading=true; listDelivery(this.q).then(r=>{this.list=(r.rows||[]).map(d=>({...d,deliveryDate:d.deliveryDate?d.deliveryDate.substring(0,10):''}));this.total=r.total||0;this.loading=false}).catch(()=>{this.loading=false}) },
getList() { this.loading=true; listDelivery(this.q).then(r=>{const all=r.rows||[];const filtered=all.filter(d=>d.deliveryStatus!=='transit'&&d.deliveryStatus!=='history');this.list=filtered.map(d=>({...d,deliveryDate:d.deliveryDate?d.deliveryDate.substring(0,10):''}));this.total=filtered.length;this.loading=false}).catch(()=>{this.loading=false}) },
search() { this.q.pageNum=1; this.getList() }, resetSearch() { this.q.doNo=""; this.q.clientName=""; this.search() },
handleView(row) { getDelivery(row.doId).then(r=>{this.detailData=r.data;this.detailOpen=true}).catch(()=>{}) },
handleShip(row) { this.$modal.confirm("确认发货?").then(()=>shipDelivery(row.doId)).then(()=>{this.$modal.msgSuccess("已发货");this.getList()}).catch(()=>{}) },

View File

@@ -38,27 +38,27 @@
<!-- 订单表格 -->
<div class="jd-table-wrap">
<el-table v-loading="loading" :data="list" border stripe size="small" class="jd-table" style="width:100%">
<el-table-column label="发货单号" width="155">
<el-table-column label="发货单号" width="115">
<template slot-scope="s">
<span class="order-link" @click="handleView(s.row)">{{ s.row.doNo }}</span>
</template>
</el-table-column>
<el-table-column label="供应商" prop="supplierName" min-width="140" show-overflow-tooltip />
<el-table-column label="金额" width="120" align="right">
<el-table-column label="供应商" prop="supplierName" min-width="120" show-overflow-tooltip />
<el-table-column label="金额" width="85" align="right">
<template slot-scope="s"><span class="amount">¥{{ s.row.totalAmount }}</span></template>
</el-table-column>
<el-table-column label="交货期" prop="deliveryDate" width="95" align="center" />
<el-table-column label="收货" prop="actualCloseDate" width="95" align="center" />
<el-table-column label="交期差异" width="100" align="center">
<el-table-column label="交货期" prop="deliveryDate" width="85" align="center" />
<el-table-column label="收货" prop="actualCloseDate" width="85" align="center" />
<el-table-column label="交期差异" width="80" align="center">
<template slot-scope="s"><span :class="diffClass(s.row)">{{ diffLabel(s.row) }}</span></template>
</el-table-column>
<el-table-column label="物料" prop="itemCount" width="55" align="center" />
<el-table-column label="状态" width="90" align="center">
<el-table-column label="物料" prop="itemCount" width="50" align="center" />
<el-table-column label="状态" width="70" align="center">
<template slot-scope="s">
<el-tag :type="tagType(s.row)" size="small" effect="dark">{{ tagLabel(s.row) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="170" align="center">
<el-table-column label="操作" width="210" align="center">
<template slot-scope="s">
<el-button size="mini" type="text" @click="handleView(s.row)">详情</el-button>
<el-button size="mini" type="text" @click="handleReOrder(s.row)">再次下单</el-button>

View File

@@ -26,23 +26,23 @@
<!-- 订单表格 -->
<div class="jd-table-wrap">
<el-table v-loading="loading" :data="list" border stripe size="small" class="jd-table" style="width:100%">
<el-table-column label="发货单号" width="155">
<el-table-column label="发货单号" width="115">
<template slot-scope="s">
<span class="order-link" @click="handleView(s.row)">{{ s.row.doNo }}</span>
</template>
</el-table-column>
<el-table-column label="供应商" prop="supplierName" min-width="140" show-overflow-tooltip />
<el-table-column label="金额" width="120" align="right">
<el-table-column label="供应商" prop="supplierName" min-width="120" show-overflow-tooltip />
<el-table-column label="金额" width="85" align="right">
<template slot-scope="s"><span class="amount">¥{{ s.row.totalAmount }}</span></template>
</el-table-column>
<el-table-column label="交货期" prop="deliveryDate" width="95" align="center" />
<el-table-column label="延期" prop="delayDate" width="90" align="center">
<el-table-column label="交货期" prop="deliveryDate" width="85" align="center" />
<el-table-column label="延期" prop="delayDate" width="80" align="center">
<template slot-scope="s">{{ s.row.delayDate || '-' }}</template>
</el-table-column>
<el-table-column label="逾期" width="100" align="center">
<el-table-column label="逾期" width="80" align="center">
<template slot-scope="s"><span v-html="getUrgentBadge(s.row)" /></template>
</el-table-column>
<el-table-column label="物料" prop="itemCount" width="55" align="center" />
<el-table-column label="物料" prop="itemCount" width="50" align="center" />
<el-table-column label="操作" width="210" align="center">
<template slot-scope="s">
<el-button size="mini" type="text" @click="handleView(s.row)">详情</el-button>

View File

@@ -54,31 +54,31 @@
<!-- 订单表格 -->
<div class="jd-table-wrap">
<el-table v-loading="loading" :data="list" border stripe size="small" class="jd-table" style="width:100%">
<el-table-column label="发货单号" width="155">
<el-table-column label="发货单号" width="115">
<template slot-scope="s">
<span class="order-link" @click="handleView(s.row)">{{ s.row.doNo }}</span>
</template>
</el-table-column>
<el-table-column label="供应商" prop="supplierName" min-width="140" show-overflow-tooltip />
<el-table-column label="金额" width="120" align="right">
<el-table-column label="供应商" prop="supplierName" min-width="120" show-overflow-tooltip />
<el-table-column label="金额" width="85" align="right">
<template slot-scope="s"><span class="amount">¥{{ s.row.totalAmount }}</span></template>
</el-table-column>
<el-table-column label="交货期" width="95" align="center">
<el-table-column label="交货期" width="85" align="center">
<template slot-scope="s"><span :class="getUrgentClass(s.row)">{{ s.row.deliveryDate }}</span></template>
</el-table-column>
<el-table-column label="延期至" prop="delayDate" width="90" align="center">
<el-table-column label="延期至" prop="delayDate" width="80" align="center">
<template slot-scope="s">{{ s.row.delayDate || '-' }}</template>
</el-table-column>
<el-table-column label="逾期" width="90" align="center">
<el-table-column label="逾期" width="80" align="center">
<template slot-scope="s"><span v-html="getUrgentBadge(s.row)" /></template>
</el-table-column>
<el-table-column label="物料" prop="itemCount" width="55" align="center" />
<el-table-column label="状态" width="85" align="center">
<el-table-column label="物料" prop="itemCount" width="50" align="center" />
<el-table-column label="状态" width="70" align="center">
<template slot-scope="s">
<el-tag :type="transitTagType(s.row)" size="small" effect="dark">{{ transitStatusLabel(s.row) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="220" align="center">
<el-table-column label="操作" width="230" align="center">
<template slot-scope="s">
<el-button size="mini" type="text" @click="handleView(s.row)">详情</el-button>
<el-button size="mini" type="text" style="color:#67C23A" @click="handleComplete(s.row)">收货完成</el-button>

View File

@@ -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>

12
sql/fix_dup_menu.sql Normal file
View File

@@ -0,0 +1,12 @@
-- 清理重复的供应商履约菜单
-- 旧的 menu_id=2023 是之前从"订单履约"重命名来的,与新的 2121 重复
UPDATE sys_menu SET parent_id = 2121 WHERE parent_id = 2023;
DELETE FROM sys_role_menu WHERE menu_id = 2023;
DELETE FROM sys_menu WHERE menu_id = 2023;
-- 验证
SELECT m.menu_id, m.menu_name, m.parent_id, p.menu_name AS parent_name, m.order_num, m.perms
FROM sys_menu m
LEFT JOIN sys_menu p ON m.parent_id = p.menu_id
WHERE m.parent_id IN (2120, 2121) OR m.menu_id IN (2120, 2121)
ORDER BY m.parent_id, m.order_num;

View File

@@ -0,0 +1,43 @@
-- ═══════════════════════════════════════════════════════════
-- 菜单重组:订单履约 → 供应商履约(子目录)+ 甲方履约
-- 目标结构:
-- 订单履约 (2120)
-- ├── 供应商履约 (新目录)
-- │ ├── 待发订单
-- │ ├── 在途订单
-- │ ├── 历史订单
-- │ ├── 结单时间管理
-- │ └── 订单异议
-- └── 甲方履约 (2044)
-- ├── 甲方待发
-- ├── 甲方在途
-- └── 甲方历史
-- ═══════════════════════════════════════════════════════════
SET NAMES utf8mb4;
-- 1. 创建「供应商履约」目录菜单M = 目录),挂在订单履约(2120)下
INSERT IGNORE INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
VALUES (2121, '供应商履约', 2120, 1, 'supplierFulfill', NULL, 1, 0, 'M', '0', '0', '', 'user', 'admin', NOW());
-- 2. 将 5 个子菜单移到「供应商履约」下
UPDATE sys_menu SET parent_id = 2121, order_num = 1 WHERE perms = 'bid:order:pending';
UPDATE sys_menu SET parent_id = 2121, order_num = 2 WHERE perms = 'bid:order:transit';
UPDATE sys_menu SET parent_id = 2121, order_num = 3 WHERE perms = 'bid:order:history';
UPDATE sys_menu SET parent_id = 2121, order_num = 4 WHERE perms = 'bid:order:closeDate';
UPDATE sys_menu SET parent_id = 2121, order_num = 5 WHERE perms = 'bid:objection:list';
-- 3. 甲方履约保持直接挂在订单履约下,排序调整到 2
UPDATE sys_menu SET parent_id = 2120, order_num = 2 WHERE perms = 'bid:clientDelivery:list';
-- 4. 给 admin 角色授权新菜单
INSERT IGNORE INTO sys_role_menu(role_id, menu_id)
SELECT 1, menu_id FROM sys_menu WHERE menu_id = 2121;
-- 5. 验证结果
SELECT m.menu_id, m.menu_name, m.parent_id, p.menu_name AS parent_name, m.order_num, m.perms
FROM sys_menu m
LEFT JOIN sys_menu p ON m.parent_id = p.menu_id
WHERE m.parent_id IN (2120, 2121)
OR m.menu_id IN (2120, 2121)
ORDER BY m.parent_id, m.order_num;

23
sql/seed_clients.sql Normal file
View File

@@ -0,0 +1,23 @@
/* ═══════════════════════════════════════════════════
甲方客户批量数据SQL 直接执行)
运行方式:用 Navicat / DataGrip / SQLyog 连上数据库执行
或者mysql -h 49.232.154.205 -P 13306 -u root -p ryvue < seed_clients.sql
═══════════════════════════════════════════════════ */
TRUNCATE TABLE biz_client;
INSERT INTO biz_client (client_no, client_name, contact, phone, email, city, address, grade, source, status, create_time) VALUES
('CU-001', '山东福安德信息科技有限公司', '张经理', '0531-88880001', 'zhang@fuande.com', '济南', '山东省济南市高新区齐鲁软件园A座5层', 'A', '直客', '0', NOW()),
('CU-002', '青岛海尔智能家电有限公司', '李工', '0532-88990001', 'ligong@haier.com', '青岛', '山东省青岛市海尔路1号海尔工业园', 'A', '招标', '0', NOW()),
('CU-003', '烟台万华化学集团股份有限公司', '王主管', '0535-3388001', 'wang@whchem.com', '烟台', '山东省烟台市经济技术开发区万华工业园', 'A', '长期合作', '0', NOW()),
('CU-004', '济南二机床集团有限公司', '赵工', '0531-81620001', 'zhao@jier.com', '济南', '山东省济南市市中区机床二厂路4号', 'B', '招标', '0', NOW()),
('CU-005', '潍柴动力股份有限公司', '孙部长', '0536-8197001', 'sun@weichai.com', '潍坊', '山东省潍坊市高新区福寿东街197号甲', 'A', '长期合作', '0', NOW()),
('CU-006', '山东钢铁集团有限公司', '刘工', '0531-67606001', 'liu@shangang.com', '济南', '山东省济南市历下区工业北路21号', 'B', '招标', '0', NOW()),
('CU-007', '中车青岛四方机车车辆股份有限公司', '陈经理', '0532-86088001', 'chen@crrc.com', '青岛', '山东省青岛市城阳区锦宏东路88号', 'A', '战略合作', '0', NOW()),
('CU-008', '山东黄金矿业股份有限公司', '黄工', '0535-2099001', 'huang@sd-gold.com', '烟台', '山东省烟台市莱州市金城镇', 'B', '招标', '0', NOW()),
('CU-009', '山东魏桥创业集团有限公司', '马主管', '0543-4305001', 'ma@weiqiao.com', '滨州', '山东省滨州市邹平市经济开发区魏纺路12号', 'B', '长期合作', '0', NOW()),
('CU-010', '浪潮集团有限公司', '周经理', '0531-85106001', 'zhou@inspur.com', '济南', '山东省济南市高新区浪潮路1036号', 'A', '直客', '0', NOW());
SELECT CONCAT('已插入 ', COUNT(*), ' 条甲方客户数据') AS result FROM biz_client;
SELECT CONCAT('已插入 ', COUNT(*), ' 条甲方客户数据') AS result FROM biz_client;