style: 完成京东风格整体改造
1. 启用京东红主题,替换全局主色调为#e4393c 2. 调整侧边栏宽度为200px,优化导航样式 3. 重构顶部导航、侧边菜单、表格、卡片等全局组件样式 4. 新增JdStatusTabs和JdFilterBar通用组件 5. 统一业务页面配色风格为红白灰配色方案 6. 修复物料列表空数据展示问题,优化表格样式 7. 新增京东风格改造文档说明
This commit is contained in:
347
doc/京东风格改造计划.md
Normal file
347
doc/京东风格改造计划.md
Normal file
@@ -0,0 +1,347 @@
|
||||
# 京东风格前端改造计划
|
||||
|
||||
## 一、设计目标
|
||||
将现有系统改造为京东订单中心风格,采用红白灰配色,提升专业感和用户体验。
|
||||
|
||||
## 二、配色方案
|
||||
|
||||
| 颜色名称 | 色值 | 用途 |
|
||||
|---------|------|------|
|
||||
| 京东红 | `#e4393c` | 主按钮、选中态、强调色 |
|
||||
| 深红 | `#c81623` | 悬停状态 |
|
||||
| 背景灰 | `#f5f5f5` | 页面背景、表头背景 |
|
||||
| 边框灰 | `#e5e5e5` | 分割线、边框 |
|
||||
| 文字深 | `#333333` | 主标题文字 |
|
||||
| 文字中 | `#666666` | 正文、表头文字 |
|
||||
| 文字浅 | `#999999` | 辅助文字 |
|
||||
| 白色 | `#ffffff` | 卡片背景、内容区 |
|
||||
| 链接蓝 | `#005ea7` | 可点击链接 |
|
||||
|
||||
## 三、改造范围
|
||||
|
||||
### 3.1 全局样式(影响所有页面)
|
||||
- [ ] 顶部导航栏(蓝色 → 京东红)
|
||||
- [ ] 左侧菜单(深色 → 白色)
|
||||
- [ ] 页面背景(白色 → 灰色)
|
||||
- [ ] 按钮样式(Element默认 → 京东风格)
|
||||
- [ ] 表格样式(标准 → 京东订单风格)
|
||||
- [ ] 分页器样式
|
||||
|
||||
### 3.2 订单模块页面(重点改造)
|
||||
- [ ] 待发订单页面
|
||||
- [ ] 在途订单页面
|
||||
- [ ] 历史订单页面
|
||||
- [ ] 结单时间管理页面
|
||||
|
||||
### 3.3 其他业务页面(同步调整)
|
||||
- [ ] 物料管理页面
|
||||
- [ ] 甲方报价页面
|
||||
- [ ] 供应商报价页面
|
||||
- [ ] 客户管理页面
|
||||
|
||||
## 四、详细改造清单
|
||||
|
||||
### 第一阶段:全局基础样式(1-2天)
|
||||
|
||||
#### 4.1.1 创建主题变量文件
|
||||
**文件**: `ruoyi-ui/src/styles/jd-variables.scss`
|
||||
|
||||
```scss
|
||||
// 京东配色
|
||||
$jd-red: #e4393c;
|
||||
$jd-red-dark: #c81623;
|
||||
$jd-red-light: #fff5f5;
|
||||
$bg-gray: #f5f5f5;
|
||||
$border-gray: #e5e5e5;
|
||||
$border-light: #f0f0f0;
|
||||
$text-dark: #333333;
|
||||
$text-medium: #666666;
|
||||
$text-light: #999999;
|
||||
$link-blue: #005ea7;
|
||||
|
||||
// 覆盖Element变量
|
||||
$--color-primary: $jd-red;
|
||||
$--color-primary-light-1: $jd-red-light;
|
||||
$--color-primary-light-9: $jd-red-light;
|
||||
$--background-color-base: $bg-gray;
|
||||
$--color-text-primary: $text-dark;
|
||||
$--color-text-regular: $text-medium;
|
||||
$--color-text-secondary: $text-light;
|
||||
$--border-color-base: $border-gray;
|
||||
$--border-color-light: $border-light;
|
||||
```
|
||||
|
||||
#### 4.1.2 顶部导航栏改造
|
||||
**文件**: `ruoyi-ui/src/layout/components/Navbar.vue` 或全局样式
|
||||
|
||||
**改造点**:
|
||||
- 背景色: `#e4393c` (京东红)
|
||||
- 高度: 50px
|
||||
- Logo区域: 白色文字,加粗
|
||||
- 导航菜单: 白色文字,透明背景
|
||||
- 选中/悬停: 深色背景 `rgba(0,0,0,0.1)`
|
||||
- 右侧工具栏: 白色图标
|
||||
|
||||
#### 4.1.3 左侧菜单改造
|
||||
**文件**: `ruoyi-ui/src/layout/components/Sidebar/index.vue` 或全局样式
|
||||
|
||||
**改造点**:
|
||||
- 背景: 白色 `#ffffff`
|
||||
- 宽度: 160px (紧凑)
|
||||
- 一级菜单:
|
||||
- 高度: 40px
|
||||
- 文字: `#666666`
|
||||
- 悬停: 文字变红 `#e4393c`,背景 `#fff5f5`
|
||||
- 选中: 文字红色,左边框3px红色
|
||||
- 二级菜单:
|
||||
- 高度: 32px
|
||||
- 缩进: 35px
|
||||
- 字号: 13px
|
||||
|
||||
#### 4.1.4 页面内容区改造
|
||||
**文件**: 全局样式
|
||||
|
||||
**改造点**:
|
||||
- 背景: `#f5f5f5` (灰色)
|
||||
- 内边距: 15px
|
||||
- 卡片容器: 白色背景,1px边框 `#e5e5e5`
|
||||
|
||||
#### 4.1.5 按钮样式覆盖
|
||||
**文件**: 全局样式
|
||||
|
||||
**改造点**:
|
||||
- 主按钮:
|
||||
- 背景: `#e4393c`
|
||||
- 边框: `#e4393c`
|
||||
- 圆角: 2px
|
||||
- 悬停: `#c81623`
|
||||
- 次按钮:
|
||||
- 边框: `#e5e5e5`
|
||||
- 文字: `#666666`
|
||||
- 悬停: 边框和文字变红
|
||||
|
||||
#### 4.1.6 表格样式覆盖
|
||||
**文件**: 全局样式
|
||||
|
||||
**改造点**:
|
||||
- 表头:
|
||||
- 背景: `#f5f5f5`
|
||||
- 文字: `#666666`
|
||||
- 高度: 32px
|
||||
- 字重: normal (不加粗)
|
||||
- 表格行:
|
||||
- 悬停: 背景 `#fff5f5` (浅红)
|
||||
- 边框: 底部1px `#f0f0f0`
|
||||
- 链接:
|
||||
- 颜色: `#005ea7` (蓝)
|
||||
- 悬停: 红色+下划线
|
||||
- 金额:
|
||||
- 颜色: `#e4393c` (红)
|
||||
- 加粗
|
||||
|
||||
#### 4.1.7 分页器样式
|
||||
**文件**: 全局样式
|
||||
|
||||
**改造点**:
|
||||
- 页码按钮:
|
||||
- 边框: 1px `#e5e5e5`
|
||||
- 选中: 红色背景+白色文字
|
||||
- 悬停: 红色边框+红色文字
|
||||
|
||||
---
|
||||
|
||||
### 第二阶段:订单模块页面改造(2-3天)
|
||||
|
||||
#### 4.2.1 页面布局结构调整
|
||||
|
||||
**改造前结构**:
|
||||
```
|
||||
统计卡片 (4个彩色卡片)
|
||||
搜索表单 (多行筛选条件)
|
||||
操作按钮栏
|
||||
数据表格
|
||||
分页器
|
||||
```
|
||||
|
||||
**改造后结构** (京东风格):
|
||||
```
|
||||
页面标题 (如"我的订单")
|
||||
状态Tab (全部/待发/在途/历史 - 下划线式)
|
||||
筛选栏 (灰底,单行)
|
||||
- 时间范围下拉
|
||||
- 搜索输入框+搜索按钮
|
||||
- 高级筛选
|
||||
数据表格 (白底)
|
||||
分页器
|
||||
```
|
||||
|
||||
#### 4.2.2 状态Tab组件
|
||||
|
||||
**样式要求**:
|
||||
- 横向排列,底部边框
|
||||
- 默认: 文字 `#666666`
|
||||
- 悬停: 文字变红
|
||||
- 选中:
|
||||
- 文字红色加粗
|
||||
- 底部2px红色下划线
|
||||
- 数量标记: 灰色小字 `(99)`
|
||||
|
||||
#### 4.2.3 筛选栏样式
|
||||
|
||||
**样式要求**:
|
||||
- 背景: `#f5f5f5`
|
||||
- 内边距: 15px
|
||||
- 元素间距: 15px
|
||||
- 下拉框: 白色背景,高度32px
|
||||
- 搜索框:
|
||||
- 宽度300px
|
||||
- 右侧红色搜索按钮
|
||||
- 高级筛选: 灰色文字,带下拉箭头
|
||||
|
||||
#### 4.2.4 表格内容优化
|
||||
|
||||
**字段展示优化**:
|
||||
- 订单号: 蓝色链接,可点击
|
||||
- 客户名称: 普通文字
|
||||
- 物料信息: 名称+规格(换行)
|
||||
- 金额: 红色加粗,右对齐
|
||||
- 状态: 彩色标签
|
||||
- 操作: 蓝色文字链接
|
||||
|
||||
#### 4.2.5 空状态样式
|
||||
|
||||
**样式要求**:
|
||||
- 居中显示
|
||||
- 图标: 120px
|
||||
- 文字: `#999999`,14px
|
||||
- 按钮: 红色主按钮
|
||||
|
||||
---
|
||||
|
||||
### 第三阶段:其他页面同步(1-2天)
|
||||
|
||||
#### 4.3.1 物料管理页面
|
||||
- 统计卡片改为京东风格(白底+顶部彩色边框)
|
||||
- 搜索栏改为灰底单行
|
||||
- 表格应用新样式
|
||||
|
||||
#### 4.3.2 报价页面(甲方/供应商)
|
||||
- 统计卡片改造
|
||||
- 表格样式统一
|
||||
- 按钮样式统一
|
||||
|
||||
#### 4.3.3 客户管理页面
|
||||
- 列表样式统一
|
||||
- 详情页卡片样式
|
||||
|
||||
---
|
||||
|
||||
## 五、文件创建清单
|
||||
|
||||
### 5.1 新建文件
|
||||
1. `ruoyi-ui/src/styles/jd-variables.scss` - 变量定义
|
||||
2. `ruoyi-ui/src/styles/jd-theme.scss` - 全局样式覆盖
|
||||
3. `ruoyi-ui/src/components/JdStatusTabs/index.vue` - 状态Tab组件
|
||||
4. `ruoyi-ui/src/components/JdFilterBar/index.vue` - 筛选栏组件
|
||||
|
||||
### 5.2 修改文件
|
||||
1. `ruoyi-ui/src/styles/index.scss` - 引入新主题
|
||||
2. `ruoyi-ui/src/layout/components/Navbar.vue` - 顶部导航
|
||||
3. `ruoyi-ui/src/layout/components/Sidebar/index.vue` - 侧边菜单
|
||||
4. `ruoyi-ui/src/views/bid/order/*.vue` - 订单页面
|
||||
5. `ruoyi-ui/src/views/bid/material/*.vue` - 物料页面
|
||||
6. `ruoyi-ui/src/views/bid/clientquote/*.vue` - 甲方报价页面
|
||||
7. `ruoyi-ui/src/views/bid/quotation/*.vue` - 供应商报价页面
|
||||
|
||||
---
|
||||
|
||||
## 六、改造顺序建议
|
||||
|
||||
```
|
||||
Day 1: 全局基础样式
|
||||
- 创建变量文件
|
||||
- 顶部导航改造
|
||||
- 左侧菜单改造
|
||||
- 按钮样式覆盖
|
||||
|
||||
Day 2: 表格和分页
|
||||
- 表格样式覆盖
|
||||
- 分页器样式
|
||||
- 页面背景调整
|
||||
|
||||
Day 3: 订单模块
|
||||
- 创建Tab组件
|
||||
- 创建筛选栏组件
|
||||
- 改造待发订单页面
|
||||
- 改造在途订单页面
|
||||
|
||||
Day 4: 订单模块+其他
|
||||
- 改造历史订单页面
|
||||
- 改造结单时间管理
|
||||
- 物料管理页面同步
|
||||
|
||||
Day 5: 收尾
|
||||
- 报价页面同步
|
||||
- 客户管理页面同步
|
||||
- 细节调整
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、验收标准
|
||||
|
||||
### 7.1 视觉验收
|
||||
- [ ] 顶部导航为京东红 `#e4393c`
|
||||
- [ ] 左侧菜单为白色背景
|
||||
- [ ] 页面背景为灰色 `#f5f5f5`
|
||||
- [ ] 主按钮为红色,悬停变深红
|
||||
- [ ] 表格表头为灰色背景 `#f5f5f5`
|
||||
- [ ] 表格行悬停为浅红背景 `#fff5f5`
|
||||
- [ ] 金额为红色加粗
|
||||
- [ ] 链接为蓝色,悬停变红
|
||||
|
||||
### 7.2 功能验收
|
||||
- [ ] 所有页面正常显示
|
||||
- [ ] 菜单点击正常
|
||||
- [ ] 表格排序/筛选正常
|
||||
- [ ] 分页功能正常
|
||||
- [ ] 按钮点击正常
|
||||
|
||||
### 7.3 兼容性验收
|
||||
- [ ] Chrome浏览器正常
|
||||
- [ ] Edge浏览器正常
|
||||
- [ ] 不同分辨率适配
|
||||
|
||||
---
|
||||
|
||||
## 八、注意事项
|
||||
|
||||
1. **渐进式改造**: 先改全局样式,再改页面结构
|
||||
2. **保留原文件**: 修改前备份原样式文件
|
||||
3. **变量优先**: 尽量使用变量,方便后续调整
|
||||
4. **测试验证**: 每改完一个模块就刷新验证
|
||||
5. **性能考虑**: 避免过多嵌套选择器
|
||||
|
||||
---
|
||||
|
||||
## 九、快速开始命令
|
||||
|
||||
```bash
|
||||
# 1. 进入前端目录
|
||||
cd ruoyi-ui
|
||||
|
||||
# 2. 安装依赖(如有新组件)
|
||||
npm install
|
||||
|
||||
# 3. 启动开发服务器
|
||||
npm run serve
|
||||
|
||||
# 4. 浏览器访问
|
||||
http://localhost:80
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**计划制定日期**: 2026-06-15
|
||||
**预计工期**: 5天
|
||||
**负责人**: 前端开发工程师
|
||||
@@ -1,31 +1,42 @@
|
||||
/**
|
||||
* I think element-ui's default theme color is too light for long-term use.
|
||||
* So I modified the default color and you can modify it to your liking.
|
||||
**/
|
||||
* 京东风格 — Element UI 主题变量
|
||||
* 主色改为京东红 #e4393c
|
||||
*/
|
||||
|
||||
/* theme color */
|
||||
$--color-primary: #4A6FA5;
|
||||
$--color-success: #13ce66;
|
||||
$--color-warning: #ffba00;
|
||||
$--color-danger: #ff4949;
|
||||
// $--color-info: #1E1E1E;
|
||||
/* theme color - JD Red */
|
||||
$--color-primary: #e4393c;
|
||||
$--color-success: #67c23a;
|
||||
$--color-warning: #e6a23c;
|
||||
$--color-danger: #f56c6c;
|
||||
$--color-info: #909399;
|
||||
|
||||
$--button-font-weight: 400;
|
||||
|
||||
// $--color-text-regular: #1f2d3d;
|
||||
$--color-text-primary: #333333;
|
||||
$--color-text-regular: #666666;
|
||||
$--color-text-secondary: #999999;
|
||||
|
||||
$--border-color-light: #dfe4ed;
|
||||
$--border-color-lighter: #e6ebf5;
|
||||
$--border-color-base: #e5e5e5;
|
||||
$--border-color-light: #f0f0f0;
|
||||
$--border-color-lighter: #f0f0f0;
|
||||
|
||||
$--table-border: 1px solid #dfe6ec;
|
||||
$--table-border: 1px solid #e5e5e5;
|
||||
$--table-header-background: #f5f5f5;
|
||||
$--table-header-font-color: #666666;
|
||||
$--table-row-hover-background: #fff5f5;
|
||||
|
||||
/* 按钮 */
|
||||
$--button-default-border-color: #e5e5e5;
|
||||
$--button-default-font-color: #666666;
|
||||
|
||||
/* 背景 */
|
||||
$--background-color-base: #f5f5f5;
|
||||
|
||||
/* icon font path, required */
|
||||
$--font-path: '~element-ui/lib/theme-chalk/fonts';
|
||||
|
||||
@import "~element-ui/packages/theme-chalk/src/index";
|
||||
|
||||
// the :export directive is the magic sauce for webpack
|
||||
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
||||
:export {
|
||||
theme: $--color-primary;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
/* ═══════════════════════════════════════════
|
||||
主题系统 — 取消注释即可切换
|
||||
═══════════════════════════════════════════ */
|
||||
// @import './theme-jd.scss'; /* 京东红主题 */
|
||||
@import './theme-jd.scss'; /* 京东红主题 */
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
|
||||
71
ruoyi-ui/src/assets/styles/jd-variables.scss
Normal file
71
ruoyi-ui/src/assets/styles/jd-variables.scss
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ═══════════════════════════════════════════════════════
|
||||
京东风格 — SCSS 变量定义
|
||||
供 element-ui 主题编译和组件内引用
|
||||
═══════════════════════════════════════════════════════ */
|
||||
|
||||
// ═══ 京东品牌色 ═══
|
||||
$jd-red: #e4393c;
|
||||
$jd-red-dark: #c81623;
|
||||
$jd-red-light: #fff5f5;
|
||||
$jd-red-bg: #fff0f0;
|
||||
|
||||
// ═══ 灰色系 ═══
|
||||
$bg-gray: #f5f5f5;
|
||||
$bg-dark: #333;
|
||||
$border-gray: #e5e5e5;
|
||||
$border-light: #f0f0f0;
|
||||
|
||||
// ═══ 文字色 ═══
|
||||
$text-dark: #333333;
|
||||
$text-medium: #666666;
|
||||
$text-light: #999999;
|
||||
|
||||
// ═══ 功能色 ═══
|
||||
$link-blue: #005ea7;
|
||||
$color-success: #67c23a;
|
||||
$color-warning: #e6a23c;
|
||||
$color-danger: #f56c6c;
|
||||
$color-info: #909399;
|
||||
|
||||
// ═══ Element 主色(覆盖默认蓝色 → 京东红) ═══
|
||||
$--color-primary: $jd-red;
|
||||
$--color-primary-light-1: #fba0a2;
|
||||
$--color-primary-light-2: #f88;
|
||||
$--color-primary-light-3: #fad;
|
||||
$--color-primary-light-4: #fbcecf;
|
||||
$--color-primary-light-5: $jd-red-light;
|
||||
$--color-primary-light-6: $jd-red-light;
|
||||
$--color-primary-light-7: $jd-red-light;
|
||||
$--color-primary-light-8: $jd-red-light;
|
||||
$--color-primary-light-9: $jd-red-light;
|
||||
|
||||
// ═══ 覆盖 Element 变量 ═══
|
||||
$--color-text-primary: $text-dark;
|
||||
$--color-text-regular: $text-medium;
|
||||
$--color-text-secondary: $text-light;
|
||||
$--color-text-placeholder: $text-light;
|
||||
|
||||
$--border-color-base: $border-gray;
|
||||
$--border-color-light: $border-light;
|
||||
$--border-color-lighter: $border-light;
|
||||
$--border-color-extra-light: #fafafa;
|
||||
|
||||
$--background-color-base: $bg-gray;
|
||||
|
||||
// ═══ 表格变量 ═══
|
||||
$--table-header-background: $bg-gray;
|
||||
$--table-header-font-color: $text-medium;
|
||||
$--table-row-hover-background: $jd-red-light;
|
||||
|
||||
// ═══ 按钮变量 ═══
|
||||
$--button-default-border-color: $border-gray;
|
||||
$--button-default-font-color: $text-medium;
|
||||
|
||||
// ═══ 导出供 JS 使用 ═══
|
||||
:export {
|
||||
jdRed: $jd-red;
|
||||
jdRedDark: $jd-red-dark;
|
||||
bgGray: $bg-gray;
|
||||
textDark: $text-dark;
|
||||
textMedium: $text-medium;
|
||||
}
|
||||
@@ -115,20 +115,22 @@
|
||||
|
||||
.el-menu-item.is-active {
|
||||
position: relative;
|
||||
font-weight: 700 !important;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: var(--current-color-dark-bg, rgba(64, 158, 255, 0.2));
|
||||
border-right: 3px solid var(--current-color, #409eff);
|
||||
background-color: var(--current-color-dark-bg, rgba(228, 57, 60, 0.2));
|
||||
border-right: 3px solid var(--current-color, #e4393c);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.el-submenu.is-active > .el-submenu__title {
|
||||
color: var(--current-color, #409eff) !important;
|
||||
color: var(--current-color, #e4393c) !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.el-menu-item:not(.is-active),
|
||||
@@ -147,7 +149,7 @@
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
background-color: var(--current-color-dark-bg, rgba(64, 158, 255, 0.2));
|
||||
background-color: var(--current-color-dark-bg, rgba(228, 57, 60, 0.2));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,29 +165,31 @@
|
||||
}
|
||||
|
||||
.el-menu-item.is-active {
|
||||
color: var(--current-color, #409eff) !important;
|
||||
color: var(--current-color, #e4393c) !important;
|
||||
font-weight: 700 !important;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: var(--current-color-light, #ecf5ff);
|
||||
border-right: 3px solid var(--current-color, #409eff);
|
||||
background-color: var(--current-color-light, #fff5f5);
|
||||
border-right: 3px solid var(--current-color, #e4393c);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.el-submenu.is-active > .el-submenu__title {
|
||||
color: var(--current-color, #409eff) !important;
|
||||
color: var(--current-color, #e4393c) !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.el-menu-item:not(.is-active):hover,
|
||||
.submenu-title-noDropdown:hover,
|
||||
.el-submenu__title:hover {
|
||||
background-color: #f5f7fa !important;
|
||||
color: rgba(0, 0, 0, 0.85) !important;
|
||||
background-color: #fff5f5 !important;
|
||||
color: #e4393c !important;
|
||||
}
|
||||
|
||||
.nest-menu .el-submenu > .el-submenu__title,
|
||||
@@ -193,7 +197,8 @@
|
||||
background-color: #fafafa !important;
|
||||
|
||||
&:hover {
|
||||
background-color: #f0f5ff !important;
|
||||
background-color: #fff5f5 !important;
|
||||
color: #e4393c !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@ $base-logo-light-title-color: #4A6FA5;
|
||||
$base-sub-menu-background: #111a27;
|
||||
$base-sub-menu-hover: rgba(17,113,196,.08);
|
||||
|
||||
$base-sidebar-width: 210px;
|
||||
$base-sidebar-width: 200px;
|
||||
|
||||
:export {
|
||||
menuColor: $base-menu-color;
|
||||
|
||||
@@ -250,7 +250,7 @@ export default {
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: red;
|
||||
color: var(--brand-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -258,6 +258,15 @@ export default {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.el-input__inner {
|
||||
border: 1px solid var(--silver-border) !important;
|
||||
border-radius: var(--radius-base) !important;
|
||||
transition: all 0.2s;
|
||||
&:focus {
|
||||
border-color: var(--brand-primary) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-search {
|
||||
@@ -265,16 +274,18 @@ export default {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
transition: color 0.2s;
|
||||
&:hover { color: var(--brand-primary); }
|
||||
}
|
||||
}
|
||||
|
||||
.result-count {
|
||||
padding: 6px 16px 0;
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
color: var(--text-muted);
|
||||
|
||||
strong {
|
||||
color: red;
|
||||
color: var(--brand-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
@@ -288,7 +299,7 @@ export default {
|
||||
height: 48px;
|
||||
align-items: center;
|
||||
padding-right: 10px;
|
||||
border-radius: 4px;
|
||||
border-radius: var(--radius-base);
|
||||
transition: background 0.15s;
|
||||
|
||||
.left {
|
||||
@@ -321,15 +332,13 @@ export default {
|
||||
}
|
||||
|
||||
.menu-path {
|
||||
color: #ccc;
|
||||
color: var(--silver-text);
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-item:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.search-item:hover { cursor: pointer; }
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
@@ -338,27 +347,9 @@ export default {
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
|
||||
.empty-icon {
|
||||
font-size: 42px;
|
||||
color: #e0e0e0;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin: 0 0 6px;
|
||||
|
||||
strong {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
font-size: 12px;
|
||||
color: #bbb;
|
||||
margin: 0;
|
||||
}
|
||||
.empty-icon { font-size: 42px; color: var(--silver-border); margin-bottom: 14px; }
|
||||
.empty-text { font-size: 14px; color: var(--text-muted); margin: 0 0 6px; strong { color: var(--text-secondary); } }
|
||||
.empty-tip { font-size: 12px; color: var(--silver-border); margin: 0; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,15 +358,11 @@ export default {
|
||||
align-items: center;
|
||||
gap: 28px;
|
||||
padding: 10px 20px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
color: #999;
|
||||
border-top: 1px solid var(--silver-border);
|
||||
color: var(--text-muted);
|
||||
font-size: 12px;
|
||||
|
||||
.shortcut-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.shortcut-item { display: flex; align-items: center; gap: 5px; }
|
||||
|
||||
kbd {
|
||||
display: inline-flex;
|
||||
@@ -384,14 +371,14 @@ export default {
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
padding: 0 5px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background: #f7f7f7;
|
||||
color: #555;
|
||||
border: 1px solid var(--silver-border);
|
||||
border-radius: var(--radius-base);
|
||||
background: var(--silver-bg);
|
||||
color: var(--text-secondary);
|
||||
font-size: 11px;
|
||||
font-family: inherit;
|
||||
line-height: 1;
|
||||
box-shadow: 0 1px 0 #ccc;
|
||||
box-shadow: 0 1px 0 var(--silver-border);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
135
ruoyi-ui/src/components/JdFilterBar/index.vue
Normal file
135
ruoyi-ui/src/components/JdFilterBar/index.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div class="jd-filter-bar">
|
||||
<div class="filter-row">
|
||||
<!-- 左侧筛选条件 -->
|
||||
<div class="filter-fields">
|
||||
<slot name="fields" />
|
||||
</div>
|
||||
|
||||
<!-- 搜索框 + 搜索按钮 -->
|
||||
<div class="filter-search" v-if="$slots.search || searchPlaceholder">
|
||||
<slot name="search">
|
||||
<el-input
|
||||
v-model="searchText"
|
||||
:placeholder="searchPlaceholder"
|
||||
clearable
|
||||
size="small"
|
||||
class="search-input"
|
||||
@keyup.enter.native="$emit('search', searchText)"
|
||||
>
|
||||
<el-button slot="append" class="search-btn" icon="el-icon-search" @click="$emit('search', searchText)" />
|
||||
</el-input>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<!-- 右侧操作区 -->
|
||||
<div class="filter-actions">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 扩展筛选区域 -->
|
||||
<div v-if="$slots.extend" class="filter-extend">
|
||||
<slot name="extend" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'JdFilterBar',
|
||||
props: {
|
||||
searchPlaceholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchText: this.value
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
this.searchText = val
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.jd-filter-bar {
|
||||
background: var(--bg-gray, #f5f5f5);
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--radius-base, 2px);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filter-fields {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.filter-search {
|
||||
flex-shrink: 0;
|
||||
|
||||
.search-input {
|
||||
width: 280px;
|
||||
|
||||
::v-deep .el-input__inner {
|
||||
border-radius: var(--radius-base, 2px) 0 0 var(--radius-base, 2px) !important;
|
||||
height: 32px !important;
|
||||
line-height: 32px !important;
|
||||
border-color: var(--border-gray, #e5e5e5) !important;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--jd-red, #e4393c) !important;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-input-group__append {
|
||||
background: var(--jd-red, #e4393c);
|
||||
border-color: var(--jd-red, #e4393c);
|
||||
border-radius: 0 var(--radius-base, 2px) var(--radius-base, 2px) 0 !important;
|
||||
padding: 0 14px;
|
||||
|
||||
.el-icon-search {
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--jd-red-dark, #c81623);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-extend {
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid var(--border-light, #f0f0f0);
|
||||
}
|
||||
</style>
|
||||
104
ruoyi-ui/src/components/JdStatusTabs/index.vue
Normal file
104
ruoyi-ui/src/components/JdStatusTabs/index.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="jd-status-tabs">
|
||||
<div class="tabs-header">
|
||||
<div
|
||||
v-for="tab in tabs"
|
||||
:key="tab.key"
|
||||
class="tab-item"
|
||||
:class="{ active: activeKey === tab.key }"
|
||||
@click="$emit('change', tab.key)"
|
||||
>
|
||||
<span class="tab-label">{{ tab.label }}</span>
|
||||
<span v-if="tab.count !== undefined" class="tab-count">({{ tab.count }})</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabs-body">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'JdStatusTabs',
|
||||
props: {
|
||||
tabs: {
|
||||
type: Array,
|
||||
required: true,
|
||||
validator(arr) {
|
||||
return arr.every(t => t.key && t.label)
|
||||
}
|
||||
},
|
||||
activeKey: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.jd-status-tabs {
|
||||
background: var(--bg-white, #ffffff);
|
||||
border-radius: var(--radius-base, 2px);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.tabs-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--border-gray, #e5e5e5);
|
||||
padding: 0 16px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
position: relative;
|
||||
padding: 12px 18px;
|
||||
font-size: 13px;
|
||||
color: var(--text-medium, #666666);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: color 0.2s;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
color: var(--jd-red, #e4393c);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--jd-red, #e4393c);
|
||||
font-weight: 700;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 18px;
|
||||
right: 18px;
|
||||
height: 2px;
|
||||
background: var(--jd-red, #e4393c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-label {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.tab-count {
|
||||
margin-left: 4px;
|
||||
font-size: 12px;
|
||||
color: var(--text-light, #999999);
|
||||
font-weight: 400;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.tab-item.active .tab-count {
|
||||
color: var(--text-light, #999999);
|
||||
}
|
||||
|
||||
.tabs-body {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,254 +1,261 @@
|
||||
<template>
|
||||
<div class="navbar" :class="'nav' + navType">
|
||||
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||
|
||||
<breadcrumb v-if="navType == 1" id="breadcrumb-container" class="breadcrumb-container" />
|
||||
<top-nav v-if="navType == 2" id="topmenu-container" class="topmenu-container" />
|
||||
<template v-if="navType == 3">
|
||||
<logo v-show="showLogo" :collapse="false"></logo>
|
||||
<top-bar id="topbar-container" class="topbar-container" />
|
||||
</template>
|
||||
<div class="right-menu">
|
||||
<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">
|
||||
<size-select id="size-select" class="right-menu-item hover-effect" />
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip content="消息通知" effect="dark" placement="bottom">
|
||||
<header-notice id="header-notice" class="right-menu-item hover-effect" />
|
||||
</el-tooltip>
|
||||
|
||||
</template>
|
||||
|
||||
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover">
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="avatar" class="user-avatar">
|
||||
<span class="user-nickname"> {{ nickName }} </span>
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<router-link to="/user/profile">
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
</router-link>
|
||||
<el-dropdown-item @click.native="setLayout" v-if="setting">
|
||||
<span>布局设置</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="lockScreen">
|
||||
<span>锁定屏幕</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided @click.native="logout">
|
||||
<span>退出登录</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import Breadcrumb from '@/components/Breadcrumb'
|
||||
import TopNav from './TopNav'
|
||||
import TopBar from './TopBar'
|
||||
import Logo from './Sidebar/Logo'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import Screenfull from '@/components/Screenfull'
|
||||
import SizeSelect from '@/components/SizeSelect'
|
||||
import Search from '@/components/HeaderSearch'
|
||||
import RuoYiGit from '@/components/RuoYi/Git'
|
||||
import RuoYiDoc from '@/components/RuoYi/Doc'
|
||||
import HeaderNotice from './HeaderNotice'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Breadcrumb,
|
||||
Logo,
|
||||
TopNav,
|
||||
TopBar,
|
||||
Hamburger,
|
||||
Screenfull,
|
||||
SizeSelect,
|
||||
Search,
|
||||
RuoYiGit,
|
||||
RuoYiDoc,
|
||||
HeaderNotice
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'sidebar',
|
||||
'avatar',
|
||||
'device',
|
||||
'nickName'
|
||||
]),
|
||||
setting: {
|
||||
get() {
|
||||
return this.$store.state.settings.showSettings
|
||||
}
|
||||
},
|
||||
navType: {
|
||||
get() {
|
||||
return this.$store.state.settings.navType
|
||||
}
|
||||
},
|
||||
showLogo: {
|
||||
get() {
|
||||
return this.$store.state.settings.sidebarLogo
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleSideBar() {
|
||||
this.$store.dispatch('app/toggleSideBar')
|
||||
},
|
||||
setLayout(event) {
|
||||
this.$emit('setLayout')
|
||||
},
|
||||
lockScreen() {
|
||||
const currentPath = this.$route.fullPath
|
||||
this.$store.dispatch('lock/lockScreen', currentPath).then(() => {
|
||||
this.$router.push('/lock')
|
||||
})
|
||||
},
|
||||
logout() {
|
||||
this.$confirm('确定注销并退出系统吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
location.href = '/index'
|
||||
})
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar.nav3 {
|
||||
.hamburger-container {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar {
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 4px rgba(0,21,41,.08);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// padding: 0 8px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.hamburger-container {
|
||||
line-height: 46px;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
transition: background .3s;
|
||||
-webkit-tap-highlight-color:transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, .025)
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.topmenu-container {
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
.topbar-container {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.right-menu {
|
||||
height: 100%;
|
||||
line-height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.right-menu-item {
|
||||
display: inline-block;
|
||||
padding: 0 8px;
|
||||
height: 100%;
|
||||
font-size: 18px;
|
||||
color: #5a5e66;
|
||||
vertical-align: text-bottom;
|
||||
|
||||
&.hover-effect {
|
||||
cursor: pointer;
|
||||
transition: background .3s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, .025)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
margin-right: 0px;
|
||||
padding-right: 0px;
|
||||
|
||||
.avatar-wrapper {
|
||||
margin-top: 10px;
|
||||
right: 8px;
|
||||
position: relative;
|
||||
|
||||
.user-avatar {
|
||||
cursor: pointer;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.user-nickname{
|
||||
position: relative;
|
||||
bottom: 10px;
|
||||
left: 2px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.el-icon-caret-bottom {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: -20px;
|
||||
top: 25px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="navbar" :class="'nav' + navType">
|
||||
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||
|
||||
<breadcrumb v-if="navType == 1" id="breadcrumb-container" class="breadcrumb-container" />
|
||||
<top-nav v-if="navType == 2" id="topmenu-container" class="topmenu-container" />
|
||||
<template v-if="navType == 3">
|
||||
<logo v-show="showLogo" :collapse="false"></logo>
|
||||
<top-bar id="topbar-container" class="topbar-container" />
|
||||
</template>
|
||||
<div class="right-menu">
|
||||
<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">
|
||||
<size-select id="size-select" class="right-menu-item hover-effect" />
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip content="消息通知" effect="dark" placement="bottom">
|
||||
<header-notice id="header-notice" class="right-menu-item hover-effect" />
|
||||
</el-tooltip>
|
||||
|
||||
</template>
|
||||
|
||||
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover">
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="avatar" class="user-avatar">
|
||||
<span class="user-nickname"> {{ nickName }} </span>
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<router-link to="/user/profile">
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
</router-link>
|
||||
<el-dropdown-item @click.native="setLayout" v-if="setting">
|
||||
<span>布局设置</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="lockScreen">
|
||||
<span>锁定屏幕</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided @click.native="logout">
|
||||
<span>退出登录</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import Breadcrumb from '@/components/Breadcrumb'
|
||||
import TopNav from './TopNav'
|
||||
import TopBar from './TopBar'
|
||||
import Logo from './Sidebar/Logo'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import Screenfull from '@/components/Screenfull'
|
||||
import SizeSelect from '@/components/SizeSelect'
|
||||
import Search from '@/components/HeaderSearch'
|
||||
import RuoYiGit from '@/components/RuoYi/Git'
|
||||
import RuoYiDoc from '@/components/RuoYi/Doc'
|
||||
import HeaderNotice from './HeaderNotice'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Breadcrumb,
|
||||
Logo,
|
||||
TopNav,
|
||||
TopBar,
|
||||
Hamburger,
|
||||
Screenfull,
|
||||
SizeSelect,
|
||||
Search,
|
||||
RuoYiGit,
|
||||
RuoYiDoc,
|
||||
HeaderNotice
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'sidebar',
|
||||
'avatar',
|
||||
'device',
|
||||
'nickName'
|
||||
]),
|
||||
setting: {
|
||||
get() {
|
||||
return this.$store.state.settings.showSettings
|
||||
}
|
||||
},
|
||||
navType: {
|
||||
get() {
|
||||
return this.$store.state.settings.navType
|
||||
}
|
||||
},
|
||||
showLogo: {
|
||||
get() {
|
||||
return this.$store.state.settings.sidebarLogo
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleSideBar() {
|
||||
this.$store.dispatch('app/toggleSideBar')
|
||||
},
|
||||
setLayout(event) {
|
||||
this.$emit('setLayout')
|
||||
},
|
||||
lockScreen() {
|
||||
const currentPath = this.$route.fullPath
|
||||
this.$store.dispatch('lock/lockScreen', currentPath).then(() => {
|
||||
this.$router.push('/lock')
|
||||
})
|
||||
},
|
||||
logout() {
|
||||
this.$confirm('确定注销并退出系统吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
location.href = '/index'
|
||||
})
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar.nav3 {
|
||||
.hamburger-container {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar {
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: #f5f5f5;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
box-shadow: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
color: #333333;
|
||||
|
||||
.hamburger-container {
|
||||
line-height: 46px;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
color: #666;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.topmenu-container {
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
.topbar-container {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.right-menu {
|
||||
height: 100%;
|
||||
line-height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
background: #ffffff;
|
||||
border-left: 1px solid #e5e5e5;
|
||||
padding: 0 4px;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.right-menu-item {
|
||||
display: inline-block;
|
||||
padding: 0 8px;
|
||||
height: 100%;
|
||||
font-size: 18px;
|
||||
color: #666;
|
||||
vertical-align: text-bottom;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.hover-effect {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: #333;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
margin-right: 0px;
|
||||
padding-right: 0px;
|
||||
|
||||
.avatar-wrapper {
|
||||
margin-top: 8px;
|
||||
right: 8px;
|
||||
position: relative;
|
||||
|
||||
.user-avatar {
|
||||
cursor: pointer;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.user-nickname {
|
||||
position: relative;
|
||||
bottom: 8px;
|
||||
left: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.el-icon-caret-bottom {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: -20px;
|
||||
top: 25px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -66,7 +66,7 @@ export default {
|
||||
}
|
||||
|
||||
.el-menu--horizontal .el-menu--popup .el-menu-item:hover {
|
||||
background-color: #f5f7fa !important;
|
||||
background-color: #f0f0f0 !important;
|
||||
}
|
||||
|
||||
/* submenu item */
|
||||
@@ -74,11 +74,18 @@ export default {
|
||||
float: left;
|
||||
height: 47px !important;
|
||||
line-height: 50px !important;
|
||||
color: #303133;
|
||||
color: #666 !important;
|
||||
margin: 0 15px !important;
|
||||
background: transparent !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
/* topbar more arrow */
|
||||
.topbar-menu.el-menu--horizontal > .el-submenu .el-submenu__title:hover {
|
||||
color: #e4393c !important;
|
||||
background: rgba(0, 0, 0, 0.03) !important;
|
||||
}
|
||||
|
||||
/* topbar more arrow */
|
||||
.topbar-menu .el-submenu .el-submenu__icon-arrow {
|
||||
position: static;
|
||||
vertical-align: middle;
|
||||
@@ -91,6 +98,22 @@ export default {
|
||||
height: 55px;
|
||||
}
|
||||
|
||||
.topbar-menu.el-menu--horizontal > .el-menu-item {
|
||||
color: #666 !important;
|
||||
background: transparent !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.topbar-menu.el-menu--horizontal > .el-menu-item:hover {
|
||||
color: #e4393c !important;
|
||||
background: rgba(0, 0, 0, 0.03) !important;
|
||||
}
|
||||
|
||||
.topbar-menu.el-menu--horizontal > .el-menu-item.is-active {
|
||||
color: #e4393c !important;
|
||||
background: rgba(0, 0, 0, 0.03) !important;
|
||||
}
|
||||
|
||||
.el-menu--horizontal .el-menu .el-menu-item, .el-menu--horizontal .el-menu .el-submenu__title{
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
@@ -171,14 +171,21 @@ export default {
|
||||
float: left;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
color: #303133 !important;
|
||||
color: #666 !important;
|
||||
padding: 0 5px !important;
|
||||
margin: 0 10px !important;
|
||||
background: transparent !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item:hover {
|
||||
color: #e4393c !important;
|
||||
background: rgba(0, 0, 0, 0.03) !important;
|
||||
}
|
||||
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-submenu.is-active .el-submenu__title {
|
||||
border-bottom: 2px solid #{'var(--theme)'} !important;
|
||||
color: #303133;
|
||||
border-bottom: 2px solid #e4393c !important;
|
||||
color: #e4393c !important;
|
||||
}
|
||||
|
||||
/* submenu item */
|
||||
@@ -186,8 +193,15 @@ export default {
|
||||
float: left;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
color: #303133 !important;
|
||||
color: #666 !important;
|
||||
padding: 0 5px !important;
|
||||
margin: 0 10px !important;
|
||||
background: transparent !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.topmenu-container.el-menu--horizontal > .el-submenu .el-submenu__title:hover {
|
||||
color: #e4393c !important;
|
||||
background: rgba(0, 0, 0, 0.03) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="dl">总金额</span>
|
||||
<span class="dv" style="color:#409EFF;font-weight:700">¥{{ detailData.totalAmount }}</span>
|
||||
<span class="dv" style="color:#e4393c;font-weight:700">¥{{ detailData.totalAmount }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="dl">状态</span>
|
||||
@@ -277,21 +277,22 @@ export default {
|
||||
/* ═══════ 整体布局 ═══════ */
|
||||
.client-manage {
|
||||
padding: 12px;
|
||||
background: #f5f7fa;
|
||||
background: #f5f5f5;
|
||||
min-height: calc(100vh - 84px);
|
||||
}
|
||||
.client-manage ::v-deep .el-tabs__header {
|
||||
background: #fff;
|
||||
padding: 0 16px;
|
||||
margin: 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.05);
|
||||
border: 1px solid #e5e5e5;
|
||||
border-bottom: none;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
.client-manage ::v-deep .el-tabs__content {
|
||||
background: #fff;
|
||||
padding: 16px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.05);
|
||||
border: 1px solid #e5e5e5;
|
||||
border-radius: 0 0 2px 2px;
|
||||
}
|
||||
|
||||
/* ═══════ 工具栏 ═══════ */
|
||||
@@ -305,12 +306,12 @@ export default {
|
||||
.order-hint {
|
||||
margin-left: 12px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* ═══════ 金额 ═══════ */
|
||||
.amount {
|
||||
color: #409EFF;
|
||||
color: #e4393c;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@@ -328,37 +329,37 @@ export default {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e5e5e5;
|
||||
border-radius: 2px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.detail-item {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
.detail-item:nth-last-child(-n+2) { border-bottom: none; }
|
||||
.detail-item:nth-child(odd) { border-right: 1px solid #ebeef5; }
|
||||
.detail-item:nth-child(odd) { border-right: 1px solid #f0f0f0; }
|
||||
.dl {
|
||||
width: 90px;
|
||||
flex-shrink: 0;
|
||||
background: #f5f7fa;
|
||||
background: #f5f5f5;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
color: #666;
|
||||
font-weight: 600;
|
||||
border-right: 1px solid #ebeef5;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
}
|
||||
.dv {
|
||||
padding: 10px 12px;
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
.detail-remark {
|
||||
padding: 8px 12px;
|
||||
background: #fdf6ec;
|
||||
border: 1px solid #faecd8;
|
||||
border-radius: 4px;
|
||||
background: #fff5f5;
|
||||
border: 1px solid #fce4e4;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
color: #e6a23c;
|
||||
margin-bottom: 16px;
|
||||
@@ -366,11 +367,10 @@ export default {
|
||||
.section-bar {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #1a2c4e;
|
||||
padding: 8px 0;
|
||||
color: #333;
|
||||
padding: 6px 0 6px 10px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 2px solid #1171c4;
|
||||
padding-left: 8px;
|
||||
border-left: 4px solid #e4393c;
|
||||
}
|
||||
|
||||
/* ═══════ 弹窗统一样式 ═══════ */
|
||||
|
||||
@@ -303,37 +303,21 @@ export default {
|
||||
.stat-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20px 24px;
|
||||
border-radius: 8px;
|
||||
padding: 14px 18px;
|
||||
border: 1px solid #e5e5e5;
|
||||
border-radius: 2px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.12);
|
||||
}
|
||||
}
|
||||
.stat-icon {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
margin-right: 16px;
|
||||
font-size: 22px;
|
||||
opacity: 0.35;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.stat-card-total .stat-icon { background: linear-gradient(135deg, #409EFF, #2d7ed9); }
|
||||
.stat-card-client .stat-icon { background: linear-gradient(135deg, #67C23A, #529b2e); }
|
||||
.stat-card-amount .stat-icon { background: linear-gradient(135deg, #E6A23C, #cf9236); }
|
||||
.stat-card-avg .stat-icon { background: linear-gradient(135deg, #909399, #73767a); }
|
||||
|
||||
.stat-body { flex: 1; }
|
||||
.stat-value { font-size: 26px; font-weight: 700; color: #303133; line-height: 1.2; }
|
||||
.stat-label { font-size: 13px; color: #909399; margin-top: 4px; }
|
||||
.stat-value { font-size: 22px; font-weight: 400; color: #333; line-height: 1.2; }
|
||||
.stat-label { font-size: 12px; color: #999; margin-top: 2px; }
|
||||
|
||||
/* ========== 搜索卡片 ========== */
|
||||
.search-card {
|
||||
|
||||
@@ -3,39 +3,39 @@
|
||||
<!-- ═══ 顶部统计卡片 ═══ -->
|
||||
<el-row :gutter="12" class="stat-row">
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#1171c4">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body">
|
||||
<div class="stat-num">{{ stats.total_count || 0 }}</div>
|
||||
<div class="stat-lbl">报价单总数</div>
|
||||
</div>
|
||||
<i class="el-icon-document-copy stat-icon" style="color:#1171c4"></i>
|
||||
<i class="el-icon-document-copy stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#30B08F">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body">
|
||||
<div class="stat-num">{{ stats.client_count || 0 }}</div>
|
||||
<div class="stat-lbl">客户数量</div>
|
||||
</div>
|
||||
<i class="el-icon-user stat-icon" style="color:#30B08F"></i>
|
||||
<i class="el-icon-user stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#409EFF">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body">
|
||||
<div class="stat-num">¥{{ (stats.total_amount_sum || 0) | money }}</div>
|
||||
<div class="stat-lbl">报价总金额</div>
|
||||
</div>
|
||||
<i class="el-icon-money stat-icon" style="color:#409EFF"></i>
|
||||
<i class="el-icon-money stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#FEC171">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body">
|
||||
<div class="stat-num">¥{{ (stats.avg_amount || 0) | money }}</div>
|
||||
<div class="stat-lbl">平均金额</div>
|
||||
</div>
|
||||
<i class="el-icon-s-data stat-icon" style="color:#FEC171"></i>
|
||||
<i class="el-icon-s-data stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -343,52 +343,44 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
/* ═══════ 页面容器 ═══════ */
|
||||
.cq-page { background: #f5f7fa; padding: 12px; min-height: calc(100vh - 84px); }
|
||||
.cq-page { background: #f5f5f5; padding: 12px; min-height: calc(100vh - 84px); }
|
||||
|
||||
/* ═══════ 统计卡片 ═══════ */
|
||||
.stat-row { margin-bottom: 12px !important; }
|
||||
.stat-card {
|
||||
background: #fff; border-radius: 4px; border-top: 3px solid #1171c4;
|
||||
padding: 16px 20px; display: flex; align-items: center; justify-content: space-between;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
|
||||
}
|
||||
.stat-num { font-size: 26px; font-weight: 700; color: #1a2c4e; line-height: 1.2; }
|
||||
.stat-lbl { font-size: 12px; color: #8c97a8; margin-top: 4px; }
|
||||
.stat-icon { font-size: 28px; opacity: 0.5; }
|
||||
|
||||
/* ═══════ 搜索栏 ═══════ */
|
||||
.search-bar {
|
||||
background: #fff; padding: 12px 16px; border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06); margin-bottom: 12px;
|
||||
background: #f5f5f5; padding: 10px 16px; border-radius: 2px;
|
||||
margin-bottom: 12px;
|
||||
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
|
||||
}
|
||||
.search-right { margin-left: auto; display: flex; gap: 8px; }
|
||||
|
||||
/* ═══════ 表格 ═══════ */
|
||||
.cq-table { box-shadow: 0 1px 4px rgba(0,0,0,0.06); }
|
||||
.link-text { font-weight: 600; color: #303133; cursor: pointer; }
|
||||
.link-text:hover { color: #1171c4; }
|
||||
.amount { color: #409EFF; font-weight: 700; }
|
||||
.cq-table { }
|
||||
.link-text { color: #005ea7; cursor: pointer; }
|
||||
.link-text:hover { color: #e4393c; text-decoration: underline; }
|
||||
.amount { color: #e4393c; font-weight: 700; }
|
||||
|
||||
/* ═══════ 标题装饰条 ═══════ */
|
||||
.section-bar {
|
||||
font-size: 13px; font-weight: 700; color: #1a2c4e;
|
||||
font-size: 13px; font-weight: 700; color: #333;
|
||||
padding: 6px 0 6px 10px; margin-bottom: 10px;
|
||||
border-left: 4px solid #1171c4;
|
||||
border-left: 4px solid #e4393c;
|
||||
}
|
||||
|
||||
/* ═══════ 弹窗样式 ═══════ */
|
||||
.cq-dialog ::v-deep .el-dialog__body { padding: 16px 24px; max-height: 70vh; overflow-y: auto; }
|
||||
.items-table-wrap { border: 1px solid #e4e7ed; border-radius: 4px; overflow-x: auto; }
|
||||
.items-table-wrap { border: 1px solid #e5e5e5; border-radius: 2px; overflow-x: auto; }
|
||||
.items-table-wrap .el-table { border: none !important; }
|
||||
.items-table-wrap .el-table::before { display: none; }
|
||||
.items-table { margin-bottom: 0; }
|
||||
.form-total-bar {
|
||||
text-align: right; padding: 10px 16px; background: #f9fbff;
|
||||
border: 1px solid #e4e7ed; border-top: none; font-size: 14px; color: #606266;
|
||||
strong { font-size: 20px; color: #409eff; margin-left: 6px; }
|
||||
text-align: right; padding: 10px 16px; background: #fafafa;
|
||||
border: 1px solid #e5e5e5; border-top: none; font-size: 14px; color: #666;
|
||||
strong { font-size: 20px; color: #e4393c; margin-left: 6px; }
|
||||
}
|
||||
.form-total-meta { margin-left: 16px; font-size: 12px; color: #909399; }
|
||||
.form-total-meta { margin-left: 16px; font-size: 12px; color: #999; }
|
||||
|
||||
/* ═══════ 详情状态流程 ═══════ */
|
||||
.steps-bar {
|
||||
@@ -399,12 +391,12 @@ export default {
|
||||
display: flex; flex-direction: column; align-items: center; gap: 4px;
|
||||
color: #c0c4cc; font-size: 12px;
|
||||
i { font-size: 22px; }
|
||||
&.active { color: #1171c4; }
|
||||
&.active { color: #e4393c; }
|
||||
&.rejected { color: #f56c6c; }
|
||||
}
|
||||
.step-line {
|
||||
flex: 1; max-width: 80px; height: 2px; background: #e4e7ed; margin: 0 8px; margin-top: -12px;
|
||||
&.active { background: #1171c4; }
|
||||
flex: 1; max-width: 80px; height: 2px; background: #e5e5e5; margin: 0 8px; margin-top: -12px;
|
||||
&.active { background: #e4393c; }
|
||||
}
|
||||
|
||||
.empty-tip { text-align: center; padding: 16px; color: #c0c4cc; font-size: 13px; }
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
:data="materialList"
|
||||
@selection-change="handleSelectionChange"
|
||||
border stripe style="width:100%"
|
||||
:header-cell-style="{ background: '#f5f7fa', color: '#303133', fontWeight: 700, fontSize: '13px' }"
|
||||
:cell-style="{ fontSize: '12px', color: '#606266' }"
|
||||
:header-cell-style="{ background: '#f5f5f5', color: '#666', fontWeight: 500, fontSize: '12px' }"
|
||||
:cell-style="{ fontSize: '12px', color: '#333' }"
|
||||
size="small">
|
||||
<el-table-column type="selection" width="44" align="center" />
|
||||
<el-table-column label="物料编码" prop="materialCode" width="120" header-align="center" align="center" />
|
||||
@@ -66,6 +66,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!loading && total === 0" description="暂无物料数据(请检查后端接口是否正常)" />
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
</el-tab-pane>
|
||||
|
||||
@@ -253,8 +254,12 @@ export default {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listMaterial(this.queryParams).then(res => {
|
||||
this.materialList = res.rows;
|
||||
this.total = res.total;
|
||||
this.materialList = res.rows || [];
|
||||
this.total = res.total || 0;
|
||||
this.loading = false;
|
||||
}).catch(() => {
|
||||
this.materialList = [];
|
||||
this.total = 0;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,35 +1,47 @@
|
||||
<template>
|
||||
<div class="cd-page">
|
||||
<!-- ═══ 统计卡片 ═══ -->
|
||||
<div class="jd-cd-page">
|
||||
<!-- ═══ JD 统计卡片 ═══ -->
|
||||
<el-row :gutter="12" class="stat-row">
|
||||
<el-col :span="6" v-for="c in statCards" :key="c.key">
|
||||
<div class="stat-card" :style="{ borderTopColor: c.color }">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body"><div class="stat-num">{{ stats[c.key] != null ? stats[c.key] : '-' }}</div><div class="stat-lbl">{{ c.label }}</div></div>
|
||||
<i :class="c.icon" class="stat-icon" :style="{ color: c.color }"></i>
|
||||
<i :class="c.icon" class="stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- ═══ JD 筛选栏 ═══ -->
|
||||
<div class="jd-filter">
|
||||
<div class="filter-left">
|
||||
<el-input v-model="q.doNo" placeholder="搜索单号" clearable size="small" class="filter-input" @keyup.enter.native="handleSearch" />
|
||||
<el-select v-model="q.deliveryStatus" placeholder="状态" clearable size="small" style="width:100px" @change="getList">
|
||||
<el-option label="待发" value="pending" />
|
||||
<el-option label="在途" value="transit" />
|
||||
<el-option label="历史" value="history" />
|
||||
</el-select>
|
||||
<el-button type="primary" size="small" @click="handleSearch">搜索</el-button>
|
||||
<el-button size="small" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
<div class="filter-right">
|
||||
<el-button size="small" icon="el-icon-refresh" @click="getList">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cd-body">
|
||||
<!-- ═══ 左侧列表 ═══ -->
|
||||
<div class="cd-left">
|
||||
<div class="left-header">
|
||||
<span class="left-title">订单列表</span>
|
||||
<div class="left-tools">
|
||||
<el-input v-model="q.doNo" placeholder="搜索单号" clearable size="small" style="width:130px" @keyup.enter.native="handleSearch" />
|
||||
<el-select v-model="q.deliveryStatus" placeholder="状态" clearable size="small" style="width:100px" @change="getList">
|
||||
<el-option label="待发" value="pending" />
|
||||
<el-option label="在途" value="transit" />
|
||||
<el-option label="历史" value="history" />
|
||||
</el-select>
|
||||
<el-button size="small" icon="el-icon-search" @click="handleSearch">搜索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table ref="table" v-loading="loading" :data="list" border stripe size="small"
|
||||
@selection-change="onSelectionChange" class="cd-table" style="width:100%"
|
||||
:row-class-name="rowClass">
|
||||
@selection-change="onSelectionChange" class="jd-table"
|
||||
style="width:100%" :row-class-name="rowClass">
|
||||
<el-table-column type="selection" width="38" align="center" />
|
||||
<el-table-column label="单号" prop="doNo" width="125" />
|
||||
<el-table-column label="单号" width="130">
|
||||
<template slot-scope="s">
|
||||
<span class="order-link">{{ s.row.doNo }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="供应商" prop="supplierName" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="交货期" prop="deliveryDate" width="85" align="center" />
|
||||
<el-table-column label="收货日期" width="115" align="center">
|
||||
@@ -50,6 +62,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!loading && list.length === 0" description="暂无数据" style="padding:40px 0" />
|
||||
<pagination v-show="total>0" :total="total" :page.sync="q.pageNum" :limit.sync="q.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
|
||||
@@ -100,7 +113,7 @@ export default {
|
||||
batchDate: null,
|
||||
q: { pageNum: 1, pageSize: 50, doNo: "", deliveryStatus: "" },
|
||||
statCards: [
|
||||
{ key: "pendingClose", label: "已收货未结单", icon: "el-icon-document", color: "#4A6FA5" },
|
||||
{ key: "pendingClose", label: "已收货未结单", icon: "el-icon-document", color: "#e4393c" },
|
||||
{ key: "todayClosed", label: "今日结单", icon: "el-icon-circle-check", color: "#67c23a" },
|
||||
{ key: "weekClosed", label: "本周结单", icon: "el-icon-data-line", color: "#e6a23c" },
|
||||
{ key: "avgCycleDays", label: "平均周期(天)", icon: "el-icon-time", color: "#8e44ad" }
|
||||
@@ -129,6 +142,7 @@ export default {
|
||||
request({ url: '/bid/delivery/closeDate/stats', method: 'get' }).then(r => { this.stats = r.data || {} }).catch(() => {})
|
||||
},
|
||||
handleSearch() { this.q.pageNum = 1; this.getList() },
|
||||
resetSearch() { this.q.doNo = ""; this.q.deliveryStatus = ""; this.handleSearch() },
|
||||
onSelectionChange(rows) { this.selected = rows },
|
||||
rowClass({ row }) { return this.selected.includes(row) ? 'selected-row' : '' },
|
||||
|
||||
@@ -166,42 +180,40 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cd-page { background: #f5f7fa; padding: 12px; min-height: calc(100vh - 84px); }
|
||||
.jd-cd-page { padding: 12px; min-height: calc(100vh - 84px); }
|
||||
|
||||
.stat-row { margin-bottom: 12px !important; }
|
||||
.stat-card {
|
||||
background: #fff; border-radius: 4px; border-top: 3px solid #4A6FA5;
|
||||
padding: 16px 20px; display: flex; align-items: center; justify-content: space-between;
|
||||
}
|
||||
.stat-num { font-size: 26px; font-weight: 700; color: #1a2c4e; line-height: 1.2; }
|
||||
.stat-lbl { font-size: 12px; color: #8c97a8; margin-top: 4px; }
|
||||
.stat-icon { font-size: 28px; opacity: 0.5; }
|
||||
|
||||
.jd-filter { display: flex; align-items: center; background: #f5f5f5; padding: 10px 16px; border-radius: 2px; margin-bottom: 12px; flex-wrap: wrap; gap: 8px; }
|
||||
.filter-left { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.filter-right { margin-left: auto; }
|
||||
.filter-input { width: 130px; }
|
||||
|
||||
.cd-body { display: flex; gap: 12px; align-items: flex-start; }
|
||||
|
||||
/* ═══ 左侧列表 ═══ */
|
||||
.cd-left { flex: 1; background: #fff; border-radius: 4px; padding: 12px; }
|
||||
.left-header { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
|
||||
.left-title { font-size: 14px; font-weight: 700; color: #1a2c4e; white-space: nowrap; }
|
||||
.left-tools { display: flex; align-items: center; gap: 6px; margin-left: auto; }
|
||||
.cd-left { flex: 1; background: #fff; border-radius: 2px; border: 1px solid #e5e5e5; padding: 14px; }
|
||||
.left-header { display: flex; align-items: center; margin-bottom: 12px; }
|
||||
.left-title { font-size: 14px; font-weight: 700; color: #333; }
|
||||
|
||||
/* ═══ 右侧操作区 ═══ */
|
||||
.cd-right { width: 320px; flex-shrink: 0; background: #fff; border-radius: 4px; padding: 16px; }
|
||||
.right-panel { }
|
||||
.right-title { font-size: 14px; font-weight: 700; color: #1a2c4e; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 2px solid #4A6FA5; }
|
||||
.jd-table { border: none !important; }
|
||||
|
||||
.cd-right { width: 320px; flex-shrink: 0; background: #fff; border-radius: 2px; border: 1px solid #e5e5e5; padding: 16px; }
|
||||
.right-title { font-size: 14px; font-weight: 700; color: #333; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 2px solid #e4393c; }
|
||||
.right-section { margin-bottom: 20px; }
|
||||
.rs-header { font-size: 12px; color: #606266; margin-bottom: 8px; }
|
||||
.rs-list { max-height: 150px; overflow-y: auto; border: 1px solid #ebeef5; border-radius: 4px; padding: 4px; }
|
||||
.rs-item { padding: 4px 8px; font-size: 12px; color: #303133; border-bottom: 1px solid #f5f7fa; }
|
||||
.rs-header { font-size: 12px; color: #666; margin-bottom: 8px; }
|
||||
.rs-list { max-height: 150px; overflow-y: auto; border: 1px solid #e5e5e5; border-radius: 2px; padding: 4px; }
|
||||
.rs-item { padding: 4px 8px; font-size: 12px; color: #333; border-bottom: 1px solid #f5f5f5; }
|
||||
.rs-item:last-child { border-bottom: none; }
|
||||
.rs-empty { text-align: center; padding: 20px; color: #c0c4cc; font-size: 12px; }
|
||||
.rs-empty { text-align: center; padding: 20px; color: #999; font-size: 12px; }
|
||||
.rs-date-row { display: flex; gap: 6px; margin-bottom: 8px; }
|
||||
.rs-quick { display: flex; gap: 6px; }
|
||||
.rs-warn { font-size: 11px; color: #f56c6c; margin-top: 6px; }
|
||||
|
||||
/* ═══ 选中行高亮 ═══ */
|
||||
::v-deep .selected-row td { background: #ecf5ff !important; }
|
||||
.order-link { color: #005ea7; cursor: pointer; }
|
||||
.order-link:hover { color: #e4393c; }
|
||||
|
||||
::v-deep .selected-row td { background: #fff5f5 !important; }
|
||||
|
||||
/* ═══ 差异颜色 ═══ */
|
||||
.diff-early { color: #67c23a; font-weight: 600; }
|
||||
.diff-late { color: #f56c6c; font-weight: 600; }
|
||||
</style>
|
||||
|
||||
@@ -1,64 +1,76 @@
|
||||
<template>
|
||||
<div class="order-page">
|
||||
<!-- ═══ 标题栏 ═══ -->
|
||||
<div class="page-header">
|
||||
<span class="page-title">历史订单</span>
|
||||
<div class="header-right">
|
||||
<el-tag type="success" size="small" effect="dark">STATUS: HISTORY</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ 统计卡片 ═══ -->
|
||||
<div class="jd-order-page">
|
||||
<!-- ═══ JD 统计卡片 ═══ -->
|
||||
<el-row :gutter="12" class="stat-row">
|
||||
<el-col :span="6" v-for="card in statCards" :key="card.key">
|
||||
<div class="stat-card" :style="{ borderTopColor: card.color }">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body"><div class="stat-num">{{ stats[card.key] != null ? stats[card.key] : '-' }}</div><div class="stat-lbl">{{ card.label }}</div></div>
|
||||
<i :class="card.icon" class="stat-icon" :style="{ color: card.color }"></i>
|
||||
<i :class="card.icon" class="stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- ═══ 搜索栏 ═══ -->
|
||||
<div class="search-bar">
|
||||
<el-input v-model="q.doNo" placeholder="搜索发货单号" clearable size="small" style="width:150px" @keyup.enter.native="handleSearch" />
|
||||
<el-input v-model="q.supplierName" placeholder="搜索供应商名称" clearable size="small" style="width:160px" @keyup.enter.native="handleSearch" />
|
||||
<el-date-picker v-model="closeDateRange" type="daterange" range-separator="至" start-placeholder="收货开始" end-placeholder="收货结束"
|
||||
value-format="yyyy-MM-dd" size="small" style="width:210px" clearable />
|
||||
<el-button type="primary" size="small" icon="el-icon-search" @click="handleSearch">搜索</el-button>
|
||||
<el-button size="small" icon="el-icon-refresh" @click="resetSearch">重置</el-button>
|
||||
<div class="search-right">
|
||||
<!-- ═══ JD Tabs ═══ -->
|
||||
<div class="jd-tabs">
|
||||
<div class="jd-tab" v-for="t in statusTabs" :key="t.key"
|
||||
:class="{ active: activeTab === t.key }"
|
||||
@click="switchTab(t.key)">
|
||||
<span class="tab-label">{{ t.label }}</span>
|
||||
<span class="tab-count">({{ t.count }})</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ JD 筛选栏 ═══ -->
|
||||
<div class="jd-filter">
|
||||
<div class="filter-left">
|
||||
<el-input v-model="q.doNo" placeholder="搜索发货单号" clearable size="small" class="filter-input" @keyup.enter.native="handleSearch" />
|
||||
<el-input v-model="q.supplierName" placeholder="搜索供应商名称" clearable size="small" class="filter-input" @keyup.enter.native="handleSearch" />
|
||||
<el-date-picker v-model="closeDateRange" type="daterange" range-separator="至" start-placeholder="收货开始" end-placeholder="收货结束"
|
||||
value-format="yyyy-MM-dd" size="small" style="width:210px" clearable />
|
||||
<el-button type="primary" size="small" @click="handleSearch">搜索</el-button>
|
||||
<el-button size="small" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
<div class="filter-right">
|
||||
<el-button size="small" icon="el-icon-refresh" @click="getList">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ 表格 ═══ -->
|
||||
<el-table v-loading="loading" :data="list" border stripe size="small" class="order-table" style="width:100%">
|
||||
<el-table-column label="发货单号" prop="doNo" width="150" />
|
||||
<el-table-column label="供应商" prop="supplierName" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="金额" width="120" 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">
|
||||
<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">
|
||||
<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">
|
||||
<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>
|
||||
<el-button size="mini" type="text" style="color:#e6a23c" @click="handleRecall(s.row)">撤回</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- ═══ 订单表格 ═══ -->
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<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>
|
||||
<el-button size="mini" type="text" style="color:#e6a23c" @click="handleRecall(s.row)">撤回</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="q.pageNum" :limit.sync="q.pageSize" @pagination="getList" />
|
||||
<el-empty v-if="!loading && list.length === 0" description="暂无历史订单" />
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="q.pageNum" :limit.sync="q.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
|
||||
<!-- ═══ 详情弹窗 ═══ -->
|
||||
<el-dialog title="订单详情" :visible.sync="detailOpen" width="820px" append-to-body>
|
||||
@@ -70,7 +82,7 @@
|
||||
<div class="tl-content"><div class="tl-title">待发</div><div class="tl-time">{{ detailData.createTime || '-' }}</div></div>
|
||||
</div>
|
||||
<div class="tl-node">
|
||||
<div class="tl-dot" style="background:#4A6FA5"></div>
|
||||
<div class="tl-dot" style="background:#e4393c"></div>
|
||||
<div class="tl-content"><div class="tl-title">运输中</div><div class="tl-time">—</div></div>
|
||||
</div>
|
||||
<div class="tl-node">
|
||||
@@ -81,8 +93,8 @@
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item"><span class="dl">发货单号</span><span class="dv"><b>{{ detailData.doNo }}</b></span></div>
|
||||
<div class="detail-item"><span class="dl">供应商</span><span class="dv">{{ detailData.supplierName || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#409EFF;font-weight:700">¥{{ detailData.totalAmount }}</span></div>
|
||||
<div class="detail-item"><span class="dl">状态</span><span class="dv"><el-tag type="success" size="small" effect="dark">HISTORY</el-tag></span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#e4393c;font-weight:700">¥{{ detailData.totalAmount }}</span></div>
|
||||
<div class="detail-item"><span class="dl">状态</span><span class="dv"><el-tag type="success" size="small" effect="dark">已完成</el-tag></span></div>
|
||||
<div class="detail-item"><span class="dl">交货期</span><span class="dv">{{ detailData.deliveryDate || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">收货日期</span><span class="dv">{{ detailData.actualCloseDate || '-' }}</span></div>
|
||||
</div>
|
||||
@@ -93,7 +105,7 @@
|
||||
<el-table-column label="单位" prop="unit" width="60" />
|
||||
<el-table-column label="数量" prop="quantity" width="80" align="right" />
|
||||
<el-table-column label="单价" width="100" align="right"><template slot-scope="s">¥{{ s.row.unitPrice }}</template></el-table-column>
|
||||
<el-table-column label="小计" width="100" align="right"><template slot-scope="s">¥{{ s.row.totalPrice }}</template></el-table-column>
|
||||
<el-table-column label="小计" width="100" align="right"><template slot-scope="s" class="amount">¥{{ s.row.totalPrice }}</template></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div slot="footer"><el-button @click="detailOpen = false">关闭</el-button></div>
|
||||
@@ -111,10 +123,16 @@ export default {
|
||||
return {
|
||||
loading: false, list: [], total: 0, stats: {},
|
||||
closeDateRange: null,
|
||||
activeTab: 'history',
|
||||
statusTabs: [
|
||||
{ key: 'pending', label: '待发', count: 0 },
|
||||
{ key: 'transit', label: '在途', count: 0 },
|
||||
{ key: 'history', label: '历史', count: 0 },
|
||||
],
|
||||
q: { pageNum: 1, pageSize: 20, deliveryStatus: "history", doNo: "", supplierName: "" },
|
||||
detailOpen: false, detailData: null,
|
||||
statCards: [
|
||||
{ key: "totalHistory", label: "历史订单总数", icon: "el-icon-document-copy", color: "#4A6FA5" },
|
||||
{ key: "totalHistory", label: "历史订单总数", icon: "el-icon-document-copy", color: "#e4393c" },
|
||||
{ key: "monthCompleted", label: "本月完成", icon: "el-icon-circle-check", color: "#67c23a" },
|
||||
{ key: "totalAmount", label: "总金额", icon: "el-icon-money", color: "#e6a23c" },
|
||||
{ key: "avgDeliveryDays", label: "平均交期(天)", icon: "el-icon-data-line", color: "#8e44ad" }
|
||||
@@ -141,6 +159,9 @@ export default {
|
||||
getStats() {
|
||||
request({ url: '/bid/delivery/history/stats', method: 'get' }).then(r => { this.stats = r.data || {} }).catch(() => {})
|
||||
},
|
||||
switchTab(key) {
|
||||
this.$router.push({ path: '/bid/order/' + key })
|
||||
},
|
||||
handleSearch() { this.q.pageNum = 1; this.getList() },
|
||||
resetSearch() { this.q.doNo = ""; this.q.supplierName = ""; this.closeDateRange = null; this.q.params = {}; this.handleSearch() },
|
||||
|
||||
@@ -168,7 +189,6 @@ export default {
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
// 交期差异计算
|
||||
diffDays(row) {
|
||||
if (!row.deliveryDate || !row.actualCloseDate) return null
|
||||
return Math.round((new Date(row.actualCloseDate) - new Date(row.deliveryDate)) / 86400000)
|
||||
@@ -188,7 +208,6 @@ export default {
|
||||
return 'diff-late'
|
||||
},
|
||||
|
||||
// 状态标签
|
||||
tagType(row) {
|
||||
const d = this.diffDays(row)
|
||||
if (d === null) return 'success'
|
||||
@@ -208,73 +227,47 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ═══════ 页面容器 ═══════ */
|
||||
.order-page { background: #f5f7fa; padding: 12px; min-height: calc(100vh - 84px); }
|
||||
.jd-order-page { padding: 12px; min-height: calc(100vh - 84px); }
|
||||
|
||||
/* ═══════ 标题栏 ═══════ */
|
||||
.page-header {
|
||||
background: #fff; padding: 12px 16px; border-radius: 4px; margin-bottom: 12px;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
}
|
||||
.page-title { font-size: 16px; font-weight: 700; color: #1a2c4e; }
|
||||
.header-right { margin-left: auto; }
|
||||
|
||||
/* ═══════ 统计卡片 ═══════ */
|
||||
.stat-row { margin-bottom: 12px !important; }
|
||||
.stat-card {
|
||||
background: #fff; border-radius: 4px; border-top: 3px solid #4A6FA5;
|
||||
padding: 16px 20px; display: flex; align-items: center; justify-content: space-between;
|
||||
}
|
||||
.stat-num { font-size: 26px; font-weight: 700; color: #1a2c4e; line-height: 1.2; }
|
||||
.stat-lbl { font-size: 12px; color: #8c97a8; margin-top: 4px; }
|
||||
.stat-icon { font-size: 28px; opacity: 0.5; }
|
||||
|
||||
/* ═══════ 搜索栏 ═══════ */
|
||||
.search-bar {
|
||||
background: #fff; padding: 12px 16px; border-radius: 4px; margin-bottom: 12px;
|
||||
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
|
||||
}
|
||||
.search-right { margin-left: auto; }
|
||||
.jd-tabs { display: flex; align-items: center; background: #fff; border-bottom: 1px solid #e5e5e5; margin-bottom: 12px; border-radius: 2px 2px 0 0; }
|
||||
.jd-tab { padding: 12px 20px; font-size: 13px; color: #666; cursor: pointer; position: relative; transition: color 0.2s; user-select: none; }
|
||||
.jd-tab:hover { color: #e4393c; }
|
||||
.jd-tab.active { color: #e4393c; font-weight: 700; }
|
||||
.jd-tab.active::after { content: ''; position: absolute; bottom: 0; left: 20px; right: 20px; height: 2px; background: #e4393c; }
|
||||
.tab-count { margin-left: 4px; font-size: 12px; color: #999; font-weight: 400; }
|
||||
|
||||
/* ═══════ 表格 ═══════ */
|
||||
.order-table { }
|
||||
.amount { color: #409EFF; font-weight: 700; }
|
||||
.jd-filter { display: flex; align-items: center; background: #f5f5f5; padding: 10px 16px; border-radius: 2px; margin-bottom: 12px; flex-wrap: wrap; gap: 8px; }
|
||||
.filter-left { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.filter-right { margin-left: auto; }
|
||||
.filter-input { width: 150px; }
|
||||
|
||||
.jd-table-wrap { background: #fff; border-radius: 2px; border: 1px solid #e5e5e5; }
|
||||
.jd-table { border: none !important; }
|
||||
|
||||
.amount { color: #e4393c; font-weight: 700; }
|
||||
.order-link { color: #005ea7; cursor: pointer; transition: color 0.2s; }
|
||||
.order-link:hover { color: #e4393c; text-decoration: underline; }
|
||||
|
||||
/* ═══════ 交期差异颜色 ═══════ */
|
||||
.diff-early { color: #67c23a; font-weight: 600; }
|
||||
.diff-ontime { color: #909399; font-weight: 600; }
|
||||
.diff-ontime { color: #999; font-weight: 600; }
|
||||
.diff-late { color: #f56c6c; font-weight: 600; }
|
||||
|
||||
/* ═══════ 详情 ═══════ */
|
||||
.detail-grid {
|
||||
display: grid; grid-template-columns: 1fr 1fr; gap: 0;
|
||||
border: 1px solid #ebeef5; border-radius: 4px; margin-bottom: 16px;
|
||||
}
|
||||
.detail-item { display: flex; border-bottom: 1px solid #ebeef5; }
|
||||
.detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0; border: 1px solid #e5e5e5; border-radius: 2px; margin-bottom: 16px; }
|
||||
.detail-item { display: flex; border-bottom: 1px solid #f0f0f0; }
|
||||
.detail-item:nth-last-child(-n+2) { border-bottom: none; }
|
||||
.detail-item:nth-child(odd) { border-right: 1px solid #ebeef5; }
|
||||
.dl { width: 90px; flex-shrink: 0; background: #f5f7fa; padding: 10px 12px; font-size: 12px; color: #606266; font-weight: 600; border-right: 1px solid #ebeef5; }
|
||||
.dv { padding: 10px 12px; font-size: 13px; color: #303133; flex: 1; }
|
||||
.section-bar { font-size: 13px; font-weight: 700; color: #1a2c4e; padding: 6px 0 6px 10px; margin-bottom: 10px; border-left: 4px solid #4A6FA5; }
|
||||
.detail-item:nth-child(odd) { border-right: 1px solid #f0f0f0; }
|
||||
.dl { width: 90px; flex-shrink: 0; background: #f5f5f5; padding: 10px 12px; font-size: 12px; color: #666; font-weight: 600; border-right: 1px solid #f0f0f0; }
|
||||
.dv { padding: 10px 12px; font-size: 13px; color: #333; flex: 1; }
|
||||
.section-bar { font-size: 13px; font-weight: 700; color: #333; padding: 6px 0 6px 10px; margin-bottom: 10px; border-left: 4px solid #e4393c; }
|
||||
|
||||
/* ═══════ 时间轴 ═══════ */
|
||||
.timeline {
|
||||
display: flex; justify-content: center; gap: 0; padding: 16px 0 24px;
|
||||
}
|
||||
.tl-node {
|
||||
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
||||
position: relative; flex: 1; max-width: 140px;
|
||||
}
|
||||
.tl-node::after {
|
||||
content: ''; position: absolute; top: 8px; left: 50%; width: 100%;
|
||||
height: 2px; background: #e4e7ed; z-index: 0;
|
||||
}
|
||||
.timeline { display: flex; justify-content: center; gap: 0; padding: 16px 0 24px; }
|
||||
.tl-node { display: flex; flex-direction: column; align-items: center; gap: 6px; position: relative; flex: 1; max-width: 140px; }
|
||||
.tl-node::after { content: ''; position: absolute; top: 8px; left: 50%; width: 100%; height: 2px; background: #e5e5e5; z-index: 0; }
|
||||
.tl-node:last-child::after { display: none; }
|
||||
.tl-dot {
|
||||
width: 18px; height: 18px; border-radius: 50%; z-index: 1;
|
||||
border: 3px solid #fff;
|
||||
}
|
||||
.tl-dot { width: 18px; height: 18px; border-radius: 50%; z-index: 1; border: 3px solid #fff; }
|
||||
.tl-content { text-align: center; z-index: 1; }
|
||||
.tl-title { font-size: 13px; font-weight: 600; color: #303133; }
|
||||
.tl-time { font-size: 11px; color: #909399; margin-top: 2px; }
|
||||
.tl-title { font-size: 13px; font-weight: 600; color: #333; }
|
||||
.tl-time { font-size: 11px; color: #999; margin-top: 2px; }
|
||||
</style>
|
||||
|
||||
@@ -1,57 +1,71 @@
|
||||
<template>
|
||||
<div class="order-page">
|
||||
<!-- ═══ 标题栏 ═══ -->
|
||||
<div class="page-header">
|
||||
<span class="page-title">待发订单</span>
|
||||
<el-tag type="warning" size="small" effect="dark" class="status-tag">STATUS: PENDING</el-tag>
|
||||
<div class="jd-order-page">
|
||||
<!-- ═══ JD 状态标签页 ═══ -->
|
||||
<div class="jd-tabs">
|
||||
<div class="jd-tab" v-for="t in statusTabs" :key="t.key"
|
||||
:class="{ active: activeTab === t.key }"
|
||||
@click="switchTab(t.key)">
|
||||
<span class="tab-label">{{ t.label }}</span>
|
||||
<span class="tab-count">({{ t.count }})</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ 搜索栏 ═══ -->
|
||||
<div class="search-bar">
|
||||
<el-input v-model="queryParams.doNo" placeholder="搜索发货单号" clearable size="small" style="width:150px" @keyup.enter.native="handleSearch" />
|
||||
<el-input v-model="queryParams.supplierName" placeholder="搜索供应商名称" clearable size="small" style="width:160px" @keyup.enter.native="handleSearch" />
|
||||
<el-button type="primary" size="small" icon="el-icon-search" @click="handleSearch">搜索</el-button>
|
||||
<el-button size="small" icon="el-icon-refresh" @click="resetSearch">重置</el-button>
|
||||
<div class="search-right">
|
||||
<!-- ═══ JD 筛选栏 ═══ -->
|
||||
<div class="jd-filter">
|
||||
<div class="filter-left">
|
||||
<el-input v-model="queryParams.doNo" placeholder="搜索发货单号" clearable size="small" class="filter-input" @keyup.enter.native="handleSearch" />
|
||||
<el-input v-model="queryParams.supplierName" placeholder="搜索供应商名称" clearable size="small" class="filter-input" @keyup.enter.native="handleSearch" />
|
||||
<el-button type="primary" size="small" @click="handleSearch">搜索</el-button>
|
||||
<el-button size="small" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
<div class="filter-right">
|
||||
<el-button size="small" icon="el-icon-refresh" @click="getList">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ 表格 ═══ -->
|
||||
<el-table v-loading="loading" :data="list" border stripe size="small" class="order-table" style="width:100%">
|
||||
<el-table-column label="发货单号" prop="doNo" width="150" />
|
||||
<el-table-column label="供应商" prop="supplierName" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="金额" width="120" 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">
|
||||
<template slot-scope="s">{{ s.row.delayDate || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="逾期" width="100" 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="200" 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="handleEdit(s.row)" v-if="s.row.deliveryStatus==='pending'">编辑</el-button>
|
||||
<el-button size="mini" type="text" style="color:#67C23A" @click="handleShip(s.row)" v-if="s.row.deliveryStatus==='pending'">发货确认</el-button>
|
||||
<el-button size="mini" type="text" style="color:#f56c6c" @click="handleDelete(s.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- ═══ 订单表格 ═══ -->
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<template slot-scope="s">{{ s.row.delayDate || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="逾期" width="100" 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="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="handleEdit(s.row)" v-if="s.row.deliveryStatus==='pending'">编辑</el-button>
|
||||
<el-button size="mini" type="text" style="color:var(--color-success)" @click="handleShip(s.row)" v-if="s.row.deliveryStatus==='pending'">发货确认</el-button>
|
||||
<el-button size="mini" type="text" style="color:var(--color-danger)" @click="handleDelete(s.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
<el-empty v-if="!loading && list.length === 0" description="暂无待发订单" />
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
|
||||
<!-- ═══ 详情弹窗 ═══ -->
|
||||
<el-dialog title="发货单详情" :visible.sync="detailOpen" width="780px" append-to-body class="detail-dialog">
|
||||
<el-dialog title="发货单详情" :visible.sync="detailOpen" width="780px" append-to-body class="jd-dialog">
|
||||
<div v-if="detailData">
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item"><span class="dl">发货单号</span><span class="dv"><b>{{ detailData.doNo }}</b></span></div>
|
||||
<div class="detail-item"><span class="dl">供应商</span><span class="dv">{{ detailData.supplierName || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#409EFF;font-weight:700">¥{{ detailData.totalAmount }}</span></div>
|
||||
<div class="detail-item"><span class="dl">状态</span><span class="dv"><el-tag type="warning" size="small" effect="dark">PENDING</el-tag></span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#e4393c;font-weight:700">¥{{ detailData.totalAmount }}</span></div>
|
||||
<div class="detail-item"><span class="dl">状态</span><span class="dv"><el-tag type="warning" size="small" effect="dark">待发</el-tag></span></div>
|
||||
<div class="detail-item"><span class="dl">交货期</span><span class="dv">{{ detailData.deliveryDate || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">延期日期</span><span class="dv">{{ detailData.delayDate || '-' }}</span></div>
|
||||
</div>
|
||||
@@ -63,7 +77,7 @@
|
||||
<el-table-column label="单位" prop="unit" width="60" />
|
||||
<el-table-column label="数量" prop="quantity" width="80" align="right" />
|
||||
<el-table-column label="单价" width="100" align="right"><template slot-scope="s">¥{{ s.row.unitPrice }}</template></el-table-column>
|
||||
<el-table-column label="小计" width="100" align="right"><template slot-scope="s">¥{{ s.row.totalPrice }}</template></el-table-column>
|
||||
<el-table-column label="小计" width="100" align="right"><template slot-scope="s" class="amount">¥{{ s.row.totalPrice }}</template></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div slot="footer"><el-button @click="detailOpen = false">关闭</el-button></div>
|
||||
@@ -95,12 +109,18 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false, list: [], total: 0,
|
||||
activeTab: 'pending',
|
||||
statusTabs: [
|
||||
{ key: 'pending', label: '待发', count: 0 },
|
||||
{ key: 'transit', label: '在途', count: 0 },
|
||||
{ key: 'history', label: '历史', count: 0 },
|
||||
],
|
||||
queryParams: { pageNum: 1, pageSize: 20, deliveryStatus: "pending", doNo: "", supplierName: "" },
|
||||
detailOpen: false, detailData: null,
|
||||
editOpen: false, editForm: {}
|
||||
}
|
||||
},
|
||||
created() { this.getList() },
|
||||
created() { this.getList(); this.getTabCounts() },
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true
|
||||
@@ -115,10 +135,13 @@ export default {
|
||||
this.loading = false
|
||||
}).catch(() => { this.loading = false })
|
||||
},
|
||||
switchTab(key) {
|
||||
this.activeTab = key
|
||||
this.$router.push({ path: '/bid/order/' + key })
|
||||
},
|
||||
handleSearch() { this.queryParams.pageNum = 1; this.getList() },
|
||||
resetSearch() { this.queryParams.doNo = ""; this.queryParams.supplierName = ""; this.handleSearch() },
|
||||
|
||||
// 详情
|
||||
handleView(row) {
|
||||
getDelivery(row.doId).then(r => {
|
||||
this.detailData = r.data
|
||||
@@ -126,7 +149,6 @@ export default {
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
// 编辑
|
||||
handleEdit(row) {
|
||||
this.editForm = { ...row }
|
||||
this.editOpen = true
|
||||
@@ -139,7 +161,6 @@ export default {
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
// 发货确认
|
||||
handleShip(row) {
|
||||
this.$modal.confirm("确认标记该发货单为「已发货」?").then(() => {
|
||||
return shipDelivery(row.doId)
|
||||
@@ -149,7 +170,6 @@ export default {
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
// 删除
|
||||
handleDelete(row) {
|
||||
this.$modal.confirm("确认删除发货单 " + row.doNo + "?").then(() => {
|
||||
return delDelivery(row.doId)
|
||||
@@ -159,7 +179,6 @@ export default {
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
// 逾期预警
|
||||
getUrgentBadge(row) {
|
||||
if (!row.deliveryDate) return ""
|
||||
const today = new Date(); today.setHours(0, 0, 0, 0)
|
||||
@@ -175,50 +194,121 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.order-page { background: #f5f7fa; padding: 12px; min-height: calc(100vh - 84px); }
|
||||
|
||||
/* ═══ 标题栏 ═══ */
|
||||
.page-header {
|
||||
background: #fff; padding: 12px 16px; border-radius: 4px; margin-bottom: 12px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06); display: flex; align-items: center; gap: 12px;
|
||||
.jd-order-page {
|
||||
padding: 12px;
|
||||
min-height: calc(100vh - 84px);
|
||||
}
|
||||
.page-title { font-size: 16px; font-weight: 700; color: #1a2c4e; }
|
||||
.status-tag { margin-left: auto; }
|
||||
|
||||
/* ═══ 搜索栏 ═══ */
|
||||
.search-bar {
|
||||
background: #fff; padding: 12px 16px; border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06); margin-bottom: 12px;
|
||||
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
|
||||
/* ═══ JD Tabs ═══ */
|
||||
.jd-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
.search-right { margin-left: auto; }
|
||||
|
||||
/* ═══ 表格 ═══ */
|
||||
.order-table { box-shadow: 0 1px 4px rgba(0,0,0,0.06); }
|
||||
.amount { color: #409EFF; font-weight: 700; }
|
||||
.jd-tab {
|
||||
padding: 12px 20px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: color 0.2s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.jd-tab:hover {
|
||||
color: #e4393c;
|
||||
}
|
||||
|
||||
.jd-tab.active {
|
||||
color: #e4393c;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.jd-tab.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
height: 2px;
|
||||
background: #e4393c;
|
||||
}
|
||||
|
||||
.tab-count {
|
||||
margin-left: 4px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* ═══ JD Filter ═══ */
|
||||
.jd-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f5f5f5;
|
||||
padding: 10px 16px;
|
||||
border-radius: 2px;
|
||||
margin-bottom: 12px;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.filter-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
/* ═══ JD Table Wrapper ═══ */
|
||||
.jd-table-wrap {
|
||||
background: #fff;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.jd-table {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* ═══ Details ═══ */
|
||||
.detail-grid {
|
||||
display: grid; grid-template-columns: 1fr 1fr; gap: 0;
|
||||
border: 1px solid #e5e5e5; border-radius: 2px; margin-bottom: 16px;
|
||||
}
|
||||
.detail-item { display: flex; border-bottom: 1px solid #f0f0f0; }
|
||||
.detail-item:nth-last-child(-n+2) { border-bottom: none; }
|
||||
.detail-item:nth-child(odd) { border-right: 1px solid #f0f0f0; }
|
||||
.dl { width: 90px; flex-shrink: 0; background: #f5f5f5; padding: 10px 12px; font-size: 12px; color: #666; font-weight: 600; border-right: 1px solid #f0f0f0; }
|
||||
.dv { padding: 10px 12px; font-size: 13px; color: #333; flex: 1; }
|
||||
.detail-remark { padding: 8px 12px; background: #fff5f5; border: 1px solid #fce4e4; border-radius: 2px; font-size: 12px; color: #e6a23c; margin-bottom: 16px; }
|
||||
.section-bar { font-size: 13px; font-weight: 700; color: #333; padding: 6px 0 6px 10px; margin-bottom: 10px; border-left: 4px solid #e4393c; }
|
||||
|
||||
/* ═══ Amount ═══ */
|
||||
.amount { color: #e4393c; font-weight: 700; }
|
||||
.urgent-overdue { color: #f56c6c; font-weight: 700; font-size: 12px; }
|
||||
.urgent-soon { color: #e6a23c; font-weight: 700; font-size: 12px; }
|
||||
|
||||
/* ═══ 详情 ═══ */
|
||||
.detail-grid {
|
||||
display: grid; grid-template-columns: 1fr 1fr; gap: 0;
|
||||
border: 1px solid #ebeef5; border-radius: 4px; margin-bottom: 16px;
|
||||
/* ═══ Order Link ═══ */
|
||||
.order-link {
|
||||
color: #005ea7;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.detail-item { display: flex; border-bottom: 1px solid #ebeef5; }
|
||||
.detail-item:nth-last-child(-n+2) { border-bottom: none; }
|
||||
.detail-item:nth-child(odd) { border-right: 1px solid #ebeef5; }
|
||||
.dl {
|
||||
width: 90px; flex-shrink: 0; background: #f5f7fa; padding: 10px 12px;
|
||||
font-size: 12px; color: #606266; font-weight: 600; border-right: 1px solid #ebeef5;
|
||||
}
|
||||
.dv { padding: 10px 12px; font-size: 13px; color: #303133; flex: 1; }
|
||||
.detail-remark {
|
||||
padding: 8px 12px; background: #fdf6ec; border: 1px solid #faecd8;
|
||||
border-radius: 4px; font-size: 12px; color: #e6a23c; margin-bottom: 16px;
|
||||
}
|
||||
.section-bar {
|
||||
font-size: 13px; font-weight: 700; color: #1a2c4e;
|
||||
padding: 6px 0 6px 10px; margin-bottom: 10px;
|
||||
border-left: 4px solid #4A6FA5;
|
||||
.order-link:hover {
|
||||
color: #e4393c;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,83 +1,97 @@
|
||||
<template>
|
||||
<div class="order-page">
|
||||
<!-- ═══ 标题栏 ═══ -->
|
||||
<div class="page-header">
|
||||
<span class="page-title">在途订单</span>
|
||||
<el-tag type="primary" size="small" effect="dark" class="status-tag">STATUS: TRANSIT</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- ═══ 统计卡片 ═══ -->
|
||||
<div class="jd-order-page">
|
||||
<!-- ═══ JD 统计卡片 ═══ -->
|
||||
<el-row :gutter="12" class="stat-row">
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#4A6FA5">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body"><div class="stat-num">{{ stats.totalTransit != null ? stats.totalTransit : '-' }}</div><div class="stat-lbl">在途总数</div></div>
|
||||
<i class="el-icon-ship stat-icon" style="color:#4A6FA5"></i>
|
||||
<i class="el-icon-ship stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#67c23a">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body"><div class="stat-num">{{ stats.todayShipped != null ? stats.todayShipped : '-' }}</div><div class="stat-lbl">今日发货</div></div>
|
||||
<i class="el-icon-upload2 stat-icon" style="color:#67c23a"></i>
|
||||
<i class="el-icon-upload2 stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#e6a23c">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body"><div class="stat-num">{{ stats.expiringSoon != null ? stats.expiringSoon : '-' }}</div><div class="stat-lbl">即将到期</div></div>
|
||||
<i class="el-icon-time stat-icon" style="color:#e6a23c"></i>
|
||||
<i class="el-icon-time stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#f56c6c">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body"><div class="stat-num">{{ stats.overdue != null ? stats.overdue : '-' }}</div><div class="stat-lbl">已逾期</div></div>
|
||||
<i class="el-icon-warning-outline stat-icon" style="color:#f56c6c"></i>
|
||||
<i class="el-icon-warning-outline stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- ═══ 搜索栏 ═══ -->
|
||||
<div class="search-bar">
|
||||
<el-input v-model="queryParams.doNo" placeholder="搜索发货单号" clearable size="small" style="width:150px" @keyup.enter.native="handleSearch" />
|
||||
<el-input v-model="queryParams.supplierName" placeholder="搜索供应商名称" clearable size="small" style="width:160px" @keyup.enter.native="handleSearch" />
|
||||
<el-button type="primary" size="small" icon="el-icon-search" @click="handleSearch">搜索</el-button>
|
||||
<el-button size="small" icon="el-icon-refresh" @click="resetSearch">重置</el-button>
|
||||
<div class="search-right">
|
||||
<!-- ═══ JD Tabs ═══ -->
|
||||
<div class="jd-tabs">
|
||||
<div class="jd-tab" v-for="t in statusTabs" :key="t.key"
|
||||
:class="{ active: activeTab === t.key }"
|
||||
@click="switchTab(t.key)">
|
||||
<span class="tab-label">{{ t.label }}</span>
|
||||
<span class="tab-count">({{ t.count }})</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ JD 筛选栏 ═══ -->
|
||||
<div class="jd-filter">
|
||||
<div class="filter-left">
|
||||
<el-input v-model="queryParams.doNo" placeholder="搜索发货单号" clearable size="small" class="filter-input" @keyup.enter.native="handleSearch" />
|
||||
<el-input v-model="queryParams.supplierName" placeholder="搜索供应商名称" clearable size="small" class="filter-input" @keyup.enter.native="handleSearch" />
|
||||
<el-button type="primary" size="small" @click="handleSearch">搜索</el-button>
|
||||
<el-button size="small" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
<div class="filter-right">
|
||||
<el-button size="small" icon="el-icon-refresh" @click="getList">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ 表格 ═══ -->
|
||||
<el-table v-loading="loading" :data="list" border stripe size="small" class="order-table" style="width:100%">
|
||||
<el-table-column label="发货单号" prop="doNo" width="150" />
|
||||
<el-table-column label="供应商" prop="supplierName" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="金额" width="120" 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">
|
||||
<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">
|
||||
<template slot-scope="s">{{ s.row.delayDate || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="逾期" width="90" 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">
|
||||
<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">
|
||||
<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>
|
||||
<el-button size="mini" type="text" style="color:#e6a23c" @click="handleDelay(s.row)">延期</el-button>
|
||||
<el-button size="mini" type="text" @click="handleRecall(s.row)">撤回</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- ═══ 订单表格 ═══ -->
|
||||
<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">
|
||||
<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">
|
||||
<template slot-scope="s"><span class="amount">¥{{ s.row.totalAmount }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交货期" width="95" 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">
|
||||
<template slot-scope="s">{{ s.row.delayDate || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="逾期" width="90" 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">
|
||||
<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">
|
||||
<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>
|
||||
<el-button size="mini" type="text" style="color:#e6a23c" @click="handleDelay(s.row)">延期</el-button>
|
||||
<el-button size="mini" type="text" @click="handleRecall(s.row)">撤回</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
<el-empty v-if="!loading && list.length === 0" description="暂无在途订单" />
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
|
||||
<!-- ═══ 详情弹窗 ═══ -->
|
||||
<el-dialog title="发货单详情" :visible.sync="detailOpen" width="780px" top="5vh" append-to-body>
|
||||
@@ -85,8 +99,8 @@
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item"><span class="dl">发货单号</span><span class="dv"><b>{{ detailData ? detailData.doNo : '' }}</b></span></div>
|
||||
<div class="detail-item"><span class="dl">供应商</span><span class="dv">{{ (detailData && detailData.supplierName) || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#409EFF">¥{{ detailData ? detailData.totalAmount : 0 }}</span></div>
|
||||
<div class="detail-item"><span class="dl">状态</span><span class="dv"><el-tag type="primary" size="small" effect="dark">TRANSIT</el-tag></span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#e4393c;font-weight:700">¥{{ detailData ? detailData.totalAmount : 0 }}</span></div>
|
||||
<div class="detail-item"><span class="dl">状态</span><span class="dv"><el-tag type="primary" size="small" effect="dark">在途</el-tag></span></div>
|
||||
<div class="detail-item"><span class="dl">交货期</span><span class="dv">{{ (detailData && detailData.deliveryDate) || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">延期日期</span><span class="dv">{{ (detailData && detailData.delayDate) || '-' }}</span></div>
|
||||
</div>
|
||||
@@ -98,7 +112,7 @@
|
||||
<el-table-column label="单位" prop="unit" width="60" />
|
||||
<el-table-column label="数量" prop="quantity" width="80" align="right" />
|
||||
<el-table-column label="单价" width="100" align="right"><template slot-scope="s">¥{{ s.row.unitPrice }}</template></el-table-column>
|
||||
<el-table-column label="小计" width="100" align="right"><template slot-scope="s">¥{{ s.row.totalPrice }}</template></el-table-column>
|
||||
<el-table-column label="小计" width="100" align="right"><template slot-scope="s" class="amount">¥{{ s.row.totalPrice }}</template></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div slot="footer"><el-button @click="detailOpen = false">关闭</el-button></div>
|
||||
@@ -129,6 +143,12 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false, list: [], total: 0, stats: {},
|
||||
activeTab: 'transit',
|
||||
statusTabs: [
|
||||
{ key: 'pending', label: '待发', count: 0 },
|
||||
{ key: 'transit', label: '在途', count: 0 },
|
||||
{ key: 'history', label: '历史', count: 0 },
|
||||
],
|
||||
queryParams: { pageNum: 1, pageSize: 20, deliveryStatus: "transit", doNo: "", supplierName: "" },
|
||||
detailOpen: false, detailData: null,
|
||||
delayOpen: false, delayForm: {}
|
||||
@@ -146,6 +166,9 @@ export default {
|
||||
getStats() {
|
||||
request({ url: '/bid/delivery/transit/stats', method: 'get' }).then(r => { this.stats = r.data || {} }).catch(() => {})
|
||||
},
|
||||
switchTab(key) {
|
||||
this.$router.push({ path: '/bid/order/' + key })
|
||||
},
|
||||
handleSearch() { this.queryParams.pageNum = 1; this.getList() },
|
||||
resetSearch() { this.queryParams.doNo = ""; this.queryParams.supplierName = ""; this.handleSearch() },
|
||||
|
||||
@@ -179,7 +202,6 @@ export default {
|
||||
.then(() => { this.$modal.msgSuccess("已撤回"); this.getList(); this.getStats() }).catch(() => {})
|
||||
},
|
||||
|
||||
// 状态判断
|
||||
transitTagType(row) {
|
||||
if (!row.deliveryDate) return "primary"
|
||||
const diff = Math.round((new Date(row.deliveryDate) - new Date()) / 86400000)
|
||||
@@ -214,51 +236,62 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.order-page { background: #f5f7fa; padding: 12px; min-height: calc(100vh - 84px); }
|
||||
|
||||
/* ═══ 标题栏 ═══ */
|
||||
.page-header {
|
||||
background: #fff; padding: 12px 16px; border-radius: 4px; margin-bottom: 12px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06); display: flex; align-items: center; gap: 12px;
|
||||
.jd-order-page {
|
||||
padding: 12px;
|
||||
min-height: calc(100vh - 84px);
|
||||
}
|
||||
.page-title { font-size: 16px; font-weight: 700; color: #1a2c4e; }
|
||||
.status-tag { margin-left: auto; }
|
||||
|
||||
/* ═══ 统计卡片 ═══ */
|
||||
/* ═══ Stat Cards ═══ */
|
||||
.stat-row { margin-bottom: 12px !important; }
|
||||
.stat-card {
|
||||
background: #fff; border-radius: 4px; border-top: 3px solid #4A6FA5;
|
||||
padding: 16px 20px; display: flex; align-items: center; justify-content: space-between;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
|
||||
|
||||
/* ═══ JD Tabs ═══ */
|
||||
.jd-tabs {
|
||||
display: flex; align-items: center; background: #fff;
|
||||
border-bottom: 1px solid #e5e5e5; margin-bottom: 12px;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
.stat-num { font-size: 26px; font-weight: 700; color: #1a2c4e; line-height: 1.2; }
|
||||
.stat-lbl { font-size: 12px; color: #8c97a8; margin-top: 4px; }
|
||||
.stat-icon { font-size: 28px; opacity: 0.5; }
|
||||
|
||||
/* ═══ 搜索栏 ═══ */
|
||||
.search-bar {
|
||||
background: #fff; padding: 12px 16px; border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06); margin-bottom: 12px;
|
||||
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
|
||||
.jd-tab {
|
||||
padding: 12px 20px; font-size: 13px; color: #666; cursor: pointer;
|
||||
position: relative; transition: color 0.2s; user-select: none;
|
||||
}
|
||||
.search-right { margin-left: auto; }
|
||||
.jd-tab:hover { color: #e4393c; }
|
||||
.jd-tab.active { color: #e4393c; font-weight: 700; }
|
||||
.jd-tab.active::after {
|
||||
content: ''; position: absolute; bottom: 0; left: 20px; right: 20px;
|
||||
height: 2px; background: #e4393c;
|
||||
}
|
||||
.tab-count { margin-left: 4px; font-size: 12px; color: #999; font-weight: 400; }
|
||||
|
||||
/* ═══ 表格 ═══ */
|
||||
.order-table { box-shadow: 0 1px 4px rgba(0,0,0,0.06); }
|
||||
.amount { color: #409EFF; font-weight: 700; }
|
||||
.urgent-overdue { color: #f56c6c; font-weight: 700; font-size: 12px; }
|
||||
.urgent-soon { color: #e6a23c; font-weight: 700; font-size: 12px; }
|
||||
/* ═══ JD Filter ═══ */
|
||||
.jd-filter {
|
||||
display: flex; align-items: center; background: #f5f5f5;
|
||||
padding: 10px 16px; border-radius: 2px; margin-bottom: 12px; flex-wrap: wrap; gap: 8px;
|
||||
}
|
||||
.filter-left { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.filter-right { margin-left: auto; }
|
||||
.filter-input { width: 150px; }
|
||||
|
||||
/* ═══ 详情 ═══ */
|
||||
/* ═══ JD Table Wrapper ═══ */
|
||||
.jd-table-wrap { background: #fff; border-radius: 2px; border: 1px solid #e5e5e5; }
|
||||
.jd-table { border: none !important; }
|
||||
|
||||
/* ═══ Details ═══ */
|
||||
.detail-grid {
|
||||
display: grid; grid-template-columns: 1fr 1fr; gap: 0;
|
||||
border: 1px solid #ebeef5; border-radius: 4px; margin-bottom: 16px;
|
||||
border: 1px solid #e5e5e5; border-radius: 2px; margin-bottom: 16px;
|
||||
}
|
||||
.detail-item { display: flex; border-bottom: 1px solid #ebeef5; }
|
||||
.detail-item { display: flex; border-bottom: 1px solid #f0f0f0; }
|
||||
.detail-item:nth-last-child(-n+2) { border-bottom: none; }
|
||||
.detail-item:nth-child(odd) { border-right: 1px solid #ebeef5; }
|
||||
.dl { width: 90px; flex-shrink: 0; background: #f5f7fa; padding: 10px 12px; font-size: 12px; color: #606266; font-weight: 600; border-right: 1px solid #ebeef5; }
|
||||
.dv { padding: 10px 12px; font-size: 13px; color: #303133; flex: 1; }
|
||||
.detail-remark { padding: 8px 12px; background: #fdf6ec; border: 1px solid #faecd8; border-radius: 4px; font-size: 12px; color: #e6a23c; margin-bottom: 16px; }
|
||||
.section-bar { font-size: 13px; font-weight: 700; color: #1a2c4e; padding: 6px 0 6px 10px; margin-bottom: 10px; border-left: 4px solid #4A6FA5; }
|
||||
.detail-item:nth-child(odd) { border-right: 1px solid #f0f0f0; }
|
||||
.dl { width: 90px; flex-shrink: 0; background: #f5f5f5; padding: 10px 12px; font-size: 12px; color: #666; font-weight: 600; border-right: 1px solid #f0f0f0; }
|
||||
.dv { padding: 10px 12px; font-size: 13px; color: #333; flex: 1; }
|
||||
.detail-remark { padding: 8px 12px; background: #fff5f5; border: 1px solid #fce4e4; border-radius: 2px; font-size: 12px; color: #e6a23c; margin-bottom: 16px; }
|
||||
.section-bar { font-size: 13px; font-weight: 700; color: #333; padding: 6px 0 6px 10px; margin-bottom: 10px; border-left: 4px solid #e4393c; }
|
||||
|
||||
/* ═══ Urgency ═══ */
|
||||
.amount { color: #e4393c; font-weight: 700; }
|
||||
.urgent-overdue { color: #f56c6c; font-weight: 700; font-size: 12px; }
|
||||
.urgent-soon { color: #e6a23c; font-weight: 700; font-size: 12px; }
|
||||
.order-link { color: #005ea7; cursor: pointer; transition: color 0.2s; }
|
||||
.order-link:hover { color: #e4393c; text-decoration: underline; }
|
||||
</style>
|
||||
|
||||
@@ -3,45 +3,45 @@
|
||||
<!-- ═══ 顶部统计卡片 ═══ -->
|
||||
<el-row :gutter="12" class="stat-row">
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#1171c4">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body">
|
||||
<div class="stat-num">{{ stats.total || 0 }}</div>
|
||||
<div class="stat-lbl">全部报价</div>
|
||||
</div>
|
||||
<i class="el-icon-document stat-icon" style="color:#1171c4"></i>
|
||||
<i class="el-icon-document stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#909399">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body">
|
||||
<div class="stat-num">{{ stats.draft || 0 }}</div>
|
||||
<div class="stat-lbl">草稿</div>
|
||||
</div>
|
||||
<i class="el-icon-edit-outline stat-icon" style="color:#909399"></i>
|
||||
<i class="el-icon-edit-outline stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#e6a23c">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body">
|
||||
<div class="stat-num">{{ stats.submitted || 0 }}</div>
|
||||
<div class="stat-lbl">待处理</div>
|
||||
</div>
|
||||
<i class="el-icon-time stat-icon" style="color:#e6a23c"></i>
|
||||
<i class="el-icon-time stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card" style="border-top-color:#67c23a">
|
||||
<div class="stat-card">
|
||||
<div class="stat-body">
|
||||
<div class="stat-num">{{ stats.accepted || 0 }}</div>
|
||||
<div class="stat-lbl">已采纳</div>
|
||||
</div>
|
||||
<i class="el-icon-circle-check stat-icon" style="color:#67c23a"></i>
|
||||
<i class="el-icon-circle-check stat-icon"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- ── 搜索栏 ── -->
|
||||
<el-card shadow="never" class="search-card">
|
||||
<div class="jd-filter-bar">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
|
||||
<el-form-item label="报价单号">
|
||||
<el-input v-model="queryParams.quoteNo" placeholder="报价单号" clearable style="width:150px" @keyup.enter.native="handleQuery" />
|
||||
@@ -65,7 +65,7 @@
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- ── 工具栏 ── -->
|
||||
<el-row :gutter="10" class="mb8" style="margin-top:12px">
|
||||
@@ -75,25 +75,25 @@
|
||||
</el-row>
|
||||
|
||||
<!-- ── 报价列表 ── -->
|
||||
<el-table v-loading="loading" :data="list" border>
|
||||
<el-table v-loading="loading" :data="list" border stripe>
|
||||
<el-table-column label="报价单号" prop="quoteNo" width="155" />
|
||||
<el-table-column label="关联询价单" width="200">
|
||||
<template slot-scope="s">
|
||||
<div style="font-weight:600;color:#303133">{{ s.row.rfqNo }}</div>
|
||||
<div style="font-size:12px;color:#909399;margin-top:2px" v-if="s.row.rfqTitle">{{ s.row.rfqTitle }}</div>
|
||||
<div style="font-weight:600;color:#333">{{ s.row.rfqNo }}</div>
|
||||
<div style="font-size:12px;color:#999;margin-top:2px" v-if="s.row.rfqTitle">{{ s.row.rfqTitle }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="供应商" prop="supplierName" min-width="150">
|
||||
<template slot-scope="s">
|
||||
<div style="display:flex;align-items:center;gap:6px">
|
||||
<el-avatar :size="28" style="background:#1171c4;flex-shrink:0">{{ (s.row.supplierName||'?').charAt(0) }}</el-avatar>
|
||||
<!-- <el-avatar :size="28" style="background:var(--brand-primary);flex-shrink:0">{{ (s.row.supplierName||'?').charAt(0) }}</el-avatar> -->
|
||||
<span>{{ s.row.supplierName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" width="120" align="right">
|
||||
<template slot-scope="s">
|
||||
<strong style="color:#409EFF;font-size:15px">¥{{ s.row.totalAmount | money }}</strong>
|
||||
<strong style="color:#e4393c;font-size:15px">¥{{ s.row.totalAmount | money }}</strong>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交期" prop="deliveryDays" width="80" align="center">
|
||||
@@ -230,7 +230,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="金额(元)" width="110" align="right">
|
||||
<template slot-scope="s">
|
||||
<strong style="color:#409EFF">¥{{ itemTotal(s.row) }}</strong>
|
||||
<strong style="color:#e4393c">¥{{ itemTotal(s.row) }}</strong>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交期(天)" width="85">
|
||||
@@ -603,16 +603,20 @@ export default {
|
||||
|
||||
/* ── 顶部统计卡片 ── */
|
||||
.stat-row { margin-bottom: 12px !important; }
|
||||
.stat-card {
|
||||
background: #fff; border-radius: 4px; border-top: 3px solid #1171c4;
|
||||
padding: 16px 20px; display: flex; align-items: center; justify-content: space-between;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
|
||||
}
|
||||
.stat-num { font-size: 26px; font-weight: 700; color: #1a2c4e; line-height: 1.2; }
|
||||
.stat-lbl { font-size: 12px; color: #8c97a8; margin-top: 4px; }
|
||||
.stat-icon { font-size: 28px; opacity: 0.5; }
|
||||
|
||||
/* ── 搜索 ── */
|
||||
.jd-filter-bar {
|
||||
background: #f5f5f5;
|
||||
padding: 12px 16px 4px;
|
||||
border-radius: 2px;
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
::v-deep .el-form-item__label {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
.search-card { ::v-deep .el-card__body { padding: 16px 20px 8px; } }
|
||||
|
||||
/* ── 状态芯片 ── */
|
||||
@@ -630,10 +634,10 @@ export default {
|
||||
.items-table { margin-bottom: 0; }
|
||||
.form-total-bar {
|
||||
text-align: right; padding: 10px 16px;
|
||||
background: linear-gradient(90deg, #f9fbff, #f0f7ff);
|
||||
border: 1px solid #e4e7ed; border-top: none; border-radius: 0 0 4px 4px;
|
||||
font-size: 14px; color: #606266;
|
||||
strong { font-size: 20px; color: #409EFF; margin-left: 6px; }
|
||||
background: #fafafa;
|
||||
border: 1px solid #e5e5e5; border-top: none; border-radius: 0 0 2px 2px;
|
||||
font-size: 14px; color: #666;
|
||||
strong { font-size: 20px; color: #e4393c; margin-left: 6px; }
|
||||
}
|
||||
|
||||
/* ── 详情 - 状态流程 ── */
|
||||
@@ -645,12 +649,12 @@ export default {
|
||||
display: flex; flex-direction: column; align-items: center; gap: 4px;
|
||||
color: #c0c4cc; font-size: 12px;
|
||||
i { font-size: 22px; }
|
||||
&.active { color: #1171c4; }
|
||||
&.rejected { color: #f56c6c; }
|
||||
&.active { color: var(--brand-primary); }
|
||||
&.rejected { color: var(--color-danger); }
|
||||
}
|
||||
.step-line {
|
||||
flex: 1; max-width: 80px; height: 2px; background: #e4e7ed; margin: 0 8px; margin-top: -12px;
|
||||
&.active { background: #1171c4; }
|
||||
flex: 1; max-width: 80px; height: 2px; background: var(--silver-border); margin: 0 8px; margin-top: -12px;
|
||||
&.active { background: var(--brand-primary); }
|
||||
}
|
||||
|
||||
/* ── PDF ── */
|
||||
@@ -658,21 +662,21 @@ export default {
|
||||
.pdf-header { display: flex; align-items: center; padding-bottom: 14px; }
|
||||
.pdf-logo { width: 48px; height: 48px; object-fit: contain; margin-right: 16px; }
|
||||
.pdf-header-text { flex: 1; }
|
||||
.pdf-company { font-size: 20px; font-weight: 700; color: #1171c4; letter-spacing: 1px; }
|
||||
.pdf-doc-type { font-size: 13px; color: #666; margin-top: 2px; }
|
||||
.pdf-header-no { font-size: 13px; color: #888; }
|
||||
.pdf-divider { border-top: 2px solid #1171c4; margin-bottom: 16px; }
|
||||
.pdf-company { font-size: 20px; font-weight: 700; color: var(--brand-primary); letter-spacing: 1px; }
|
||||
.pdf-doc-type { font-size: 13px; color: var(--text-secondary); margin-top: 2px; }
|
||||
.pdf-header-no { font-size: 13px; color: var(--text-muted); }
|
||||
.pdf-divider { border-top: 2px solid var(--brand-primary); margin-bottom: 16px; }
|
||||
.pdf-meta-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; td { padding: 7px 10px; border: 1px solid #e4e7ed; } }
|
||||
.meta-label { background: #f5f7fa; color: #606266; font-weight: 600; width: 90px; }
|
||||
.meta-val { color: #303133; }
|
||||
.amount { color: #409EFF; font-weight: 700; font-size: 15px; }
|
||||
.pdf-section-title { font-size: 14px; font-weight: 700; color: #1a2c4e; margin: 0 0 10px; padding-left: 8px; border-left: 4px solid #1171c4; }
|
||||
.meta-label { background: var(--silver-bg); color: var(--text-secondary); font-weight: 600; width: 90px; }
|
||||
.meta-val { color: var(--text-primary); }
|
||||
.amount { color: var(--color-amount); font-weight: 700; font-size: 15px; }
|
||||
.pdf-section-title { font-size: 14px; font-weight: 700; color: var(--text-primary); margin: 0 0 10px; padding-left: 8px; border-left: 4px solid var(--brand-primary); }
|
||||
.pdf-items-table { width: 100%; border-collapse: collapse; margin-bottom: 20px;
|
||||
th { background: #1171c4; color: #fff; padding: 8px; text-align: center; font-weight: 600; font-size: 12px; }
|
||||
td { border: 1px solid #e4e7ed; padding: 7px 8px; text-align: center; font-size: 12px; }
|
||||
th { background: var(--brand-primary); color: #fff; padding: 8px; text-align: center; font-weight: 600; font-size: 12px; }
|
||||
td { border: 1px solid var(--silver-border); padding: 7px 8px; text-align: center; font-size: 12px; }
|
||||
tbody tr:nth-child(even) td { background: #f9fbff; }
|
||||
}
|
||||
.amount-cell { color: #409EFF; font-weight: 600; }
|
||||
.total-cell { font-size: 15px; background: #f0f7ff !important; font-weight: 700; }
|
||||
.amount-cell { color: #e4393c; font-weight: 600; }
|
||||
.total-cell { font-size: 15px; background: #fff5f5 !important; font-weight: 700; }
|
||||
.pdf-footer { text-align: right; font-size: 11px; color: #aaa; margin-top: 10px; border-top: 1px solid #f0f2f5; padding-top: 8px; }
|
||||
</style>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<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" :style="{ borderTopColor: item.color }">
|
||||
<div class="stat-icon" :style="{ background: item.color + '18', color: item.color }">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">
|
||||
<i :class="item.icon" />
|
||||
</div>
|
||||
<div class="stat-body">
|
||||
@@ -172,13 +172,12 @@ export default {
|
||||
.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;
|
||||
background: #fff; border: 1px solid #e5e5e5; border-radius: 2px;
|
||||
padding: 14px 18px; display: flex; align-items: center; gap: 14px;
|
||||
}
|
||||
.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; }
|
||||
.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; }
|
||||
|
||||
Reference in New Issue
Block a user