feat: sync full workspace including web modules, docs, and configurations to Gitea

Optimized the root .gitignore to exclude virtual environments, node modules,
and temp folders to ensure clean and lightweight version tracking.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
王冕
2026-06-09 18:12:25 +08:00
parent 351688006e
commit a27e3b8e43
1510 changed files with 162044 additions and 1517 deletions

BIN
web端/.DS_Store vendored

Binary file not shown.

57
web端/styles/README.md Normal file
View File

@@ -0,0 +1,57 @@
# 车辆管理样式ONE-OS Web
`web端/车辆管理.jsx` 页面规范一致,可直接用于前端构建。
## 文件说明
| 文件 | 用途 |
|------|------|
| `vm-tokens.css` | 设计令牌(`:root` CSS 变量) |
| `vehicle-management.css` | 页面组件样式筛选、表格、详情、Ant 覆盖) |
| `index.css` | **推荐入口**,聚合上述文件 |
## 引入方式
### 1. 静态 HTML
```html
<link rel="stylesheet" href="https://unpkg.com/antd@5/dist/reset.css" />
<link rel="stylesheet" href="./web端/styles/index.css" />
```
页面根节点:
```html
<div class="vm-page">
<!-- 列表 / 详情内容 -->
</div>
```
### 2. Vite / Webpack
```js
import 'antd/dist/reset.css';
import '../web端/styles/index.css';
```
### 3. Axhub / JSX 原型(当前仓库)
`车辆管理.jsx` 会在运行时自动注入 `styles/vehicle-management.css`(已 `@import` tokens
若部署路径不同,可设置:
```js
window.VM_STYLESHEET_HREF = '/assets/styles/vehicle-management.css';
```
## 类名约定
- 页面容器:`.vm-page`
- 详情页:`.vm-page.vm-detail-shell`
- 筛选:`.vm-filter-card``.vm-filter-grid``.vm-filter-field`
- 表格:`.vm-table-section``.vm-table-card``.vm-list-table`
- 弹窗Modal 增加 `wrapClassName="vm-modal-wrap"`
## 定制主题
修改 `vm-tokens.css``--vm-color-primary` 等变量即可全局换色,无需改组件样式文件。

14
web端/styles/index.css Normal file
View File

@@ -0,0 +1,14 @@
/**
* ONE-OS Web 端 — 车辆管理样式入口
* 构建时在入口 HTML/JS 中引入本文件即可
*
* @example HTML
* <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/antd@5/dist/reset.css" />
* <link rel="stylesheet" href="./styles/index.css" />
*
* @example Webpack / Vite
* import 'antd/dist/reset.css';
* import './styles/index.css';
*/
@import url('./vehicle-management.css');

View File

@@ -0,0 +1,7 @@
# ONEOS Arco 主题(@arco-themes/vue-oneos
来源:[npm @arco-themes/vue-oneos](https://www.npmjs.com/package/@arco-themes/vue-oneos)
`web端/styles/oneos-arco-theme.css` 为官方 theme 副本,供后续接入 Vue/Arco 时参考。
**站点信息页**当前使用列表页绿色规范(`H2_PAGE_STYLE`),未启用 OneOS 蓝主题。

View File

@@ -0,0 +1,509 @@
body {
font-family: Inter, -apple-system, BlinkMacSystemFont, PingFang SC, Hiragino Sans GB, noto sans, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif;
}
body {
--color-white: #ffffff;
--color-black: #000000;
--color-border: rgb(var(--gray-3));
--color-bg-popup: var(--color-bg-5);
--color-bg-1: #fff;
--color-bg-2: #fff;
--color-bg-3: #fff;
--color-bg-4: #fff;
--color-bg-5: #fff;
--color-bg-white: #fff;
--color-neutral-1: rgb(var(--gray-1));
--color-neutral-2: rgb(var(--gray-2));
--color-neutral-3: rgb(var(--gray-3));
--color-neutral-4: rgb(var(--gray-4));
--color-neutral-5: rgb(var(--gray-5));
--color-neutral-6: rgb(var(--gray-6));
--color-neutral-7: rgb(var(--gray-7));
--color-neutral-8: rgb(var(--gray-8));
--color-neutral-9: rgb(var(--gray-9));
--color-neutral-10: rgb(var(--gray-10));
--color-text-1: #121314;
--color-text-2: #5c5c5c;
--color-text-3: #909399;
--color-text-4: #c2c2c2;
--color-fill-1: #fafafa;
--color-fill-2: var(--color-neutral-2);
--color-fill-3: var(--color-neutral-3);
--color-fill-4: var(--color-neutral-4);
--color-border-1: #ebeef5;
--color-border-2: #e5e6eb;
--color-border-3: #dcdfe6;
--color-border-4: #cdd0d6;
--color-primary-light-1: rgb(var(--primary-1));
--color-primary-light-2: rgb(var(--primary-2));
--color-primary-light-3: rgb(var(--primary-3));
--color-primary-light-4: rgb(var(--primary-4));
--color-secondary: var(--color-neutral-2);
--color-secondary-hover: var(--color-neutral-3);
--color-secondary-active: var(--color-neutral-4);
--color-secondary-disabled: var(--color-neutral-1);
--color-danger-light-1: rgb(var(--danger-1));
--color-danger-light-2: rgb(var(--danger-2));
--color-danger-light-3: rgb(var(--danger-3));
--color-danger-light-4: rgb(var(--danger-4));
--color-success-light-1: rgb(var(--success-1));
--color-success-light-2: rgb(var(--success-2));
--color-success-light-3: rgb(var(--success-3));
--color-success-light-4: rgb(var(--success-4));
--color-warning-light-1: rgb(var(--warning-1));
--color-warning-light-2: rgb(var(--warning-2));
--color-warning-light-3: rgb(var(--warning-3));
--color-warning-light-4: rgb(var(--warning-4));
--color-link-light-1: rgb(var(--link-1));
--color-link-light-2: rgb(var(--link-2));
--color-link-light-3: rgb(var(--link-3));
--color-link-light-4: rgb(var(--link-4));
--border-radius-none: 0;
--border-radius-small: 4px;
--border-radius-medium: 6px;
--border-radius-large: 8px;
--border-radius-circle: 50%;
--color-tooltip-bg: rgb(var(--gray-10));
--color-spin-layer-bg: rgba(255, 255, 255, 0.6);
--color-menu-dark-bg: #232324;
--color-menu-light-bg: #ffffff;
--color-menu-dark-hover: rgba(255, 255, 255, 0.04);
--color-mask-bg: rgba(29, 33, 41, 0.6);
}
body[arco-theme='dark'] {
--color-black: #000000;
--color-border: #333335;
--color-bg-1: #17171a;
--color-bg-2: #232324;
--color-bg-3: #2a2a2b;
--color-bg-4: #313132;
--color-bg-5: #373739;
--color-bg-white: #f6f6f6;
--color-text-1: rgba(255, 255, 255, 0.9);
--color-text-2: rgba(255, 255, 255, 0.7);
--color-text-3: rgba(255, 255, 255, 0.5);
--color-text-4: rgba(255, 255, 255, 0.3);
--color-fill-1: rgba(255, 255, 255, 0.04);
--color-fill-2: rgba(255, 255, 255, 0.08);
--color-fill-3: rgba(255, 255, 255, 0.12);
--color-fill-4: rgba(255, 255, 255, 0.16);
--color-border-1: var(--color-neutral-2);
--color-border-2: var(--color-neutral-3);
--color-border-3: var(--color-neutral-4);
--color-border-4: var(--color-neutral-6);
--color-primary-light-1: rgba(var(--primary-6), 0.2);
--color-primary-light-2: rgba(var(--primary-6), 0.35);
--color-primary-light-3: rgba(var(--primary-6), 0.5);
--color-primary-light-4: rgba(var(--primary-6), 0.65);
--color-secondary: rgba(var(--gray-9), 0.08);
--color-secondary-hover: rgba(var(--gray-8), 0.16);
--color-secondary-active: rgba(var(--gray-7), 0.24);
--color-secondary-disabled: rgba(var(--gray-9), 0.08);
--color-danger-light-1: rgba(var(--danger-6), 0.2);
--color-danger-light-2: rgba(var(--danger-6), 0.35);
--color-danger-light-3: rgba(var(--danger-6), 0.5);
--color-danger-light-4: rgba(var(--danger-6), 0.65);
--color-success-light-1: rgba(var(--success-6), 0.2);
--color-success-light-2: rgba(var(--success-6), 0.35);
--color-success-light-3: rgba(var(--success-6), 0.5);
--color-success-light-4: rgba(var(--success-6), 0.65);
--color-warning-light-1: rgba(var(--warning-6), 0.2);
--color-warning-light-2: rgba(var(--warning-6), 0.35);
--color-warning-light-3: rgba(var(--warning-6), 0.5);
--color-warning-light-4: rgba(var(--warning-6), 0.65);
--color-link-light-1: rgba(var(--link-6), 0.2);
--color-link-light-2: rgba(var(--link-6), 0.35);
--color-link-light-3: rgba(var(--link-6), 0.5);
--color-link-light-4: rgba(var(--link-6), 0.65);
--color-tooltip-bg: #373739;
--color-spin-layer-bg: rgba(51, 51, 51, 0.6);
--color-menu-dark-bg: #232324;
--color-menu-light-bg: #232324;
--color-menu-dark-hover: var(--color-fill-2);
--color-mask-bg: rgba(23, 23, 26, 0.6);
}
body {
--red-1: 255, 236, 232;
--red-2: 253, 205, 197;
--red-3: 251, 172, 163;
--red-4: 249, 137, 129;
--red-5: 247, 101, 96;
--red-6: 245, 63, 63;
--red-7: 203, 39, 45;
--red-8: 161, 21, 30;
--red-9: 119, 8, 19;
--red-10: 77, 0, 10;
--orangered-1: 255, 243, 232;
--orangered-2: 253, 221, 195;
--orangered-3: 252, 197, 159;
--orangered-4: 250, 172, 123;
--orangered-5: 249, 144, 87;
--orangered-6: 247, 114, 52;
--orangered-7: 204, 81, 32;
--orangered-8: 162, 53, 17;
--orangered-9: 119, 31, 6;
--orangered-10: 77, 14, 0;
--orange-1: 255, 247, 232;
--orange-2: 255, 228, 186;
--orange-3: 255, 207, 139;
--orange-4: 255, 182, 93;
--orange-5: 255, 154, 46;
--orange-6: 255, 125, 0;
--orange-7: 210, 95, 0;
--orange-8: 166, 69, 0;
--orange-9: 121, 46, 0;
--orange-10: 77, 27, 0;
--gold-1: 255, 252, 232;
--gold-2: 253, 244, 191;
--gold-3: 252, 233, 150;
--gold-4: 250, 220, 109;
--gold-5: 249, 204, 69;
--gold-6: 247, 186, 30;
--gold-7: 204, 146, 19;
--gold-8: 162, 109, 10;
--gold-9: 119, 75, 4;
--gold-10: 77, 45, 0;
--yellow-1: 254, 255, 232;
--yellow-2: 254, 254, 190;
--yellow-3: 253, 250, 148;
--yellow-4: 252, 242, 107;
--yellow-5: 251, 232, 66;
--yellow-6: 250, 220, 25;
--yellow-7: 207, 175, 15;
--yellow-8: 163, 132, 8;
--yellow-9: 120, 93, 3;
--yellow-10: 77, 56, 0;
--lime-1: 252, 255, 232;
--lime-2: 237, 248, 187;
--lime-3: 220, 241, 144;
--lime-4: 201, 233, 104;
--lime-5: 181, 226, 65;
--lime-6: 159, 219, 29;
--lime-7: 126, 183, 18;
--lime-8: 95, 148, 10;
--lime-9: 67, 112, 4;
--lime-10: 42, 77, 0;
--green-1: 232, 255, 234;
--green-2: 175, 240, 181;
--green-3: 123, 225, 136;
--green-4: 76, 210, 99;
--green-5: 35, 195, 67;
--green-6: 0, 180, 42;
--green-7: 0, 154, 41;
--green-8: 0, 128, 38;
--green-9: 0, 102, 34;
--green-10: 0, 77, 28;
--cyan-1: 232, 255, 251;
--cyan-2: 183, 244, 236;
--cyan-3: 137, 233, 224;
--cyan-4: 94, 223, 214;
--cyan-5: 55, 212, 207;
--cyan-6: 20, 201, 201;
--cyan-7: 13, 165, 170;
--cyan-8: 7, 130, 139;
--cyan-9: 3, 97, 108;
--cyan-10: 0, 66, 77;
--blue-1: 232, 247, 255;
--blue-2: 195, 231, 254;
--blue-3: 159, 212, 253;
--blue-4: 123, 192, 252;
--blue-5: 87, 169, 251;
--blue-6: 52, 145, 250;
--blue-7: 32, 108, 207;
--blue-8: 17, 75, 163;
--blue-9: 6, 48, 120;
--blue-10: 0, 26, 77;
--arcoblue-1: 232, 243, 255;
--arcoblue-2: 190, 218, 255;
--arcoblue-3: 148, 191, 255;
--arcoblue-4: 106, 161, 255;
--arcoblue-5: 64, 128, 255;
--arcoblue-6: 22, 93, 255;
--arcoblue-7: 14, 66, 210;
--arcoblue-8: 7, 44, 166;
--arcoblue-9: 3, 26, 121;
--arcoblue-10: 0, 13, 77;
--purple-1: 245, 232, 255;
--purple-2: 221, 190, 246;
--purple-3: 195, 150, 237;
--purple-4: 168, 113, 227;
--purple-5: 141, 78, 218;
--purple-6: 114, 46, 209;
--purple-7: 85, 29, 176;
--purple-8: 60, 16, 143;
--purple-9: 39, 6, 110;
--purple-10: 22, 0, 77;
--pinkpurple-1: 255, 232, 251;
--pinkpurple-2: 247, 186, 239;
--pinkpurple-3: 240, 142, 230;
--pinkpurple-4: 232, 101, 223;
--pinkpurple-5: 225, 62, 219;
--pinkpurple-6: 217, 26, 217;
--pinkpurple-7: 176, 16, 182;
--pinkpurple-8: 138, 9, 147;
--pinkpurple-9: 101, 3, 112;
--pinkpurple-10: 66, 0, 77;
--magenta-1: 255, 232, 241;
--magenta-2: 253, 194, 219;
--magenta-3: 251, 157, 199;
--magenta-4: 249, 121, 183;
--magenta-5: 247, 84, 168;
--magenta-6: 245, 49, 157;
--magenta-7: 203, 30, 131;
--magenta-8: 161, 16, 105;
--magenta-9: 119, 6, 79;
--magenta-10: 77, 0, 52;
--gray-1: 247, 248, 250;
--gray-2: 242, 243, 245;
--gray-3: 229, 230, 235;
--gray-4: 201, 205, 212;
--gray-5: 169, 174, 184;
--gray-6: 134, 144, 156;
--gray-7: 107, 119, 133;
--gray-8: 78, 89, 105;
--gray-9: 39, 46, 59;
--gray-10: 29, 33, 41;
--primary-1: var(--arcoblue-1);
--primary-2: var(--arcoblue-2);
--primary-3: var(--arcoblue-3);
--primary-4: var(--arcoblue-4);
--primary-5: var(--arcoblue-5);
--primary-6: var(--arcoblue-6);
--primary-7: var(--arcoblue-7);
--primary-8: var(--arcoblue-8);
--primary-9: var(--arcoblue-9);
--primary-10: var(--arcoblue-10);
--link-1: var(--arcoblue-1);
--link-2: var(--arcoblue-2);
--link-3: var(--arcoblue-3);
--link-4: var(--arcoblue-4);
--link-5: var(--arcoblue-5);
--link-6: var(--arcoblue-6);
--link-7: var(--arcoblue-7);
--link-8: var(--arcoblue-8);
--link-9: var(--arcoblue-9);
--link-10: var(--arcoblue-10);
--success-1: var(--green-1);
--success-2: var(--green-2);
--success-3: var(--green-3);
--success-4: var(--green-4);
--success-5: var(--green-5);
--success-6: var(--green-6);
--success-7: var(--green-7);
--success-8: var(--green-8);
--success-9: var(--green-9);
--success-10: var(--green-10);
--danger-1: var(--red-1);
--danger-2: var(--red-2);
--danger-3: var(--red-3);
--danger-4: var(--red-4);
--danger-5: var(--red-5);
--danger-6: var(--red-6);
--danger-7: var(--red-7);
--danger-8: var(--red-8);
--danger-9: var(--red-9);
--danger-10: var(--red-10);
--warning-1: var(--orange-1);
--warning-2: var(--orange-2);
--warning-3: var(--orange-3);
--warning-4: var(--orange-4);
--warning-5: var(--orange-5);
--warning-6: var(--orange-6);
--warning-7: var(--orange-7);
--warning-8: var(--orange-8);
--warning-9: var(--orange-9);
--warning-10: var(--orange-10);
}
body[arco-theme='dark'] {
--red-1: 77, 0, 10;
--red-2: 119, 6, 17;
--red-3: 161, 22, 31;
--red-4: 203, 46, 52;
--red-5: 245, 78, 78;
--red-6: 247, 105, 101;
--red-7: 249, 141, 134;
--red-8: 251, 176, 167;
--red-9: 253, 209, 202;
--red-10: 255, 240, 236;
--orangered-1: 77, 14, 0;
--orangered-2: 119, 30, 5;
--orangered-3: 162, 55, 20;
--orangered-4: 204, 87, 41;
--orangered-5: 247, 126, 69;
--orangered-6: 249, 146, 90;
--orangered-7: 250, 173, 125;
--orangered-8: 252, 198, 161;
--orangered-9: 253, 222, 197;
--orangered-10: 255, 244, 235;
--orange-1: 77, 27, 0;
--orange-2: 121, 48, 4;
--orange-3: 166, 75, 10;
--orange-4: 210, 105, 19;
--orange-5: 255, 141, 31;
--orange-6: 255, 150, 38;
--orange-7: 255, 179, 87;
--orange-8: 255, 205, 135;
--orange-9: 255, 227, 184;
--orange-10: 255, 247, 232;
--gold-1: 77, 45, 0;
--gold-2: 119, 75, 4;
--gold-3: 162, 111, 15;
--gold-4: 204, 150, 31;
--gold-5: 247, 192, 52;
--gold-6: 249, 204, 68;
--gold-7: 250, 220, 108;
--gold-8: 252, 233, 149;
--gold-9: 253, 244, 190;
--gold-10: 255, 252, 232;
--yellow-1: 77, 56, 0;
--yellow-2: 120, 94, 7;
--yellow-3: 163, 134, 20;
--yellow-4: 207, 179, 37;
--yellow-5: 250, 225, 60;
--yellow-6: 251, 233, 75;
--yellow-7: 252, 243, 116;
--yellow-8: 253, 250, 157;
--yellow-9: 254, 254, 198;
--yellow-10: 254, 255, 240;
--lime-1: 42, 77, 0;
--lime-2: 68, 112, 6;
--lime-3: 98, 148, 18;
--lime-4: 132, 183, 35;
--lime-5: 168, 219, 57;
--lime-6: 184, 226, 75;
--lime-7: 203, 233, 112;
--lime-8: 222, 241, 152;
--lime-9: 238, 248, 194;
--lime-10: 253, 255, 238;
--green-1: 0, 77, 28;
--green-2: 4, 102, 37;
--green-3: 10, 128, 45;
--green-4: 18, 154, 55;
--green-5: 29, 180, 64;
--green-6: 39, 195, 70;
--green-7: 80, 210, 102;
--green-8: 126, 225, 139;
--green-9: 178, 240, 183;
--green-10: 235, 255, 236;
--cyan-1: 0, 66, 77;
--cyan-2: 6, 97, 108;
--cyan-3: 17, 131, 139;
--cyan-4: 31, 166, 170;
--cyan-5: 48, 201, 201;
--cyan-6: 63, 212, 207;
--cyan-7: 102, 223, 215;
--cyan-8: 144, 233, 225;
--cyan-9: 190, 244, 237;
--cyan-10: 240, 255, 252;
--blue-1: 0, 26, 77;
--blue-2: 5, 47, 120;
--blue-3: 19, 76, 163;
--blue-4: 41, 113, 207;
--blue-5: 70, 154, 250;
--blue-6: 90, 170, 251;
--blue-7: 125, 193, 252;
--blue-8: 161, 213, 253;
--blue-9: 198, 232, 254;
--blue-10: 234, 248, 255;
--arcoblue-1: 0, 13, 77;
--arcoblue-2: 4, 27, 121;
--arcoblue-3: 14, 50, 166;
--arcoblue-4: 29, 77, 210;
--arcoblue-5: 48, 111, 255;
--arcoblue-6: 60, 126, 255;
--arcoblue-7: 104, 159, 255;
--arcoblue-8: 147, 190, 255;
--arcoblue-9: 190, 218, 255;
--arcoblue-10: 234, 244, 255;
--purple-1: 22, 0, 77;
--purple-2: 39, 6, 110;
--purple-3: 62, 19, 143;
--purple-4: 90, 37, 176;
--purple-5: 123, 61, 209;
--purple-6: 142, 81, 218;
--purple-7: 169, 116, 227;
--purple-8: 197, 154, 237;
--purple-9: 223, 194, 246;
--purple-10: 247, 237, 255;
--pinkpurple-1: 66, 0, 77;
--pinkpurple-2: 101, 3, 112;
--pinkpurple-3: 138, 13, 147;
--pinkpurple-4: 176, 27, 182;
--pinkpurple-5: 217, 46, 217;
--pinkpurple-6: 225, 61, 219;
--pinkpurple-7: 232, 102, 223;
--pinkpurple-8: 240, 146, 230;
--pinkpurple-9: 247, 193, 240;
--pinkpurple-10: 255, 242, 253;
--magenta-1: 77, 0, 52;
--magenta-2: 119, 8, 80;
--magenta-3: 161, 23, 108;
--magenta-4: 203, 43, 136;
--magenta-5: 245, 69, 166;
--magenta-6: 247, 86, 169;
--magenta-7: 249, 122, 184;
--magenta-8: 251, 158, 200;
--magenta-9: 253, 195, 219;
--magenta-10: 255, 232, 241;
--gray-1: 23, 23, 26;
--gray-2: 46, 46, 48;
--gray-3: 72, 72, 73;
--gray-4: 95, 95, 96;
--gray-5: 120, 120, 122;
--gray-6: 146, 146, 147;
--gray-7: 171, 171, 172;
--gray-8: 197, 197, 197;
--gray-9: 223, 223, 223;
--gray-10: 246, 246, 246;
--primary-1: var(--arcoblue-1);
--primary-2: var(--arcoblue-2);
--primary-3: var(--arcoblue-3);
--primary-4: var(--arcoblue-4);
--primary-5: var(--arcoblue-5);
--primary-6: var(--arcoblue-6);
--primary-7: var(--arcoblue-7);
--primary-8: var(--arcoblue-8);
--primary-9: var(--arcoblue-9);
--primary-10: var(--arcoblue-10);
--link-1: var(--arcoblue-1);
--link-2: var(--arcoblue-2);
--link-3: var(--arcoblue-3);
--link-4: var(--arcoblue-4);
--link-5: var(--arcoblue-5);
--link-6: var(--arcoblue-6);
--link-7: var(--arcoblue-7);
--link-8: var(--arcoblue-8);
--link-9: var(--arcoblue-9);
--link-10: var(--arcoblue-10);
--success-1: var(--green-1);
--success-2: var(--green-2);
--success-3: var(--green-3);
--success-4: var(--green-4);
--success-5: var(--green-5);
--success-6: var(--green-6);
--success-7: var(--green-7);
--success-8: var(--green-8);
--success-9: var(--green-9);
--success-10: var(--green-10);
--danger-1: var(--red-1);
--danger-2: var(--red-2);
--danger-3: var(--red-3);
--danger-4: var(--red-4);
--danger-5: var(--red-5);
--danger-6: var(--red-6);
--danger-7: var(--red-7);
--danger-8: var(--red-8);
--danger-9: var(--red-9);
--danger-10: var(--red-10);
--warning-1: var(--orange-1);
--warning-2: var(--orange-2);
--warning-3: var(--orange-3);
--warning-4: var(--orange-4);
--warning-5: var(--orange-5);
--warning-6: var(--orange-6);
--warning-7: var(--orange-7);
--warning-8: var(--orange-8);
--warning-9: var(--orange-9);
--warning-10: var(--orange-10);
}

View File

@@ -0,0 +1,726 @@
/**
* ONE-OS 车辆管理模块样式
* 页面根节点: .vm-page
* 构建引用: <link rel="stylesheet" href="styles/vehicle-management.css" />
* 或: @import './styles/vehicle-management.css';
*
* 依赖: Ant Design v5antd
* 建议顺序: antd.css → vm-tokens.css → vehicle-management.css
*/
@import url('./vm-tokens.css');
/* ==========================================================================
Page shell
========================================================================== */
.vm-page {
box-sizing: border-box;
min-height: 100vh;
padding: var(--vm-page-padding-y) var(--vm-page-padding-x) var(--vm-page-padding-bottom);
font-family: var(--vm-font-family);
font-size: var(--vm-font-size-base);
line-height: var(--vm-line-height-normal);
color: var(--vm-color-text);
background: linear-gradient(
165deg,
var(--vm-color-bg-page-alt) 0%,
var(--vm-color-bg-page) 50%,
var(--vm-color-bg-page-alt) 100%
);
}
.vm-page *,
.vm-page *::before,
.vm-page *::after {
box-sizing: border-box;
}
.vm-page.vm-detail-shell {
padding: var(--vm-page-padding-y) var(--vm-page-padding-x);
}
/* ==========================================================================
Page header
========================================================================== */
.vm-page .vm-page-header {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: var(--vm-space-3);
margin-bottom: var(--vm-space-4);
}
.vm-page .vm-page-header .ant-breadcrumb {
font-size: var(--vm-font-size-base);
}
.vm-page .vm-page-header .ant-breadcrumb a,
.vm-page .vm-page-header .ant-breadcrumb button {
color: var(--vm-color-primary);
cursor: pointer;
transition: color var(--vm-duration-fast) var(--vm-ease-default);
}
.vm-page .vm-page-header .ant-breadcrumb a:hover,
.vm-page .vm-page-header .ant-breadcrumb button:hover {
color: var(--vm-color-primary-hover);
}
/* ==========================================================================
Filter card
========================================================================== */
.vm-page .vm-filter-card.ant-card {
margin-bottom: var(--vm-space-4);
border: 1px solid var(--vm-color-border) !important;
border-radius: var(--vm-radius-2xl) !important;
box-shadow: var(--vm-shadow-card) !important;
}
.vm-page .vm-filter-card > .ant-card-head {
min-height: auto;
padding: var(--vm-space-3) var(--vm-space-5) !important;
border-bottom: 1px solid var(--vm-color-border-light) !important;
}
.vm-page .vm-filter-card > .ant-card-head .ant-card-head-title {
padding: 0 !important;
font-size: var(--vm-font-size-md) !important;
font-weight: var(--vm-font-weight-bold) !important;
color: var(--vm-color-text) !important;
}
.vm-page .vm-filter-card > .ant-card-body {
padding: var(--vm-space-4) var(--vm-space-5) var(--vm-space-5) !important;
}
.vm-page .vm-filter-grid {
display: grid;
grid-template-columns: repeat(var(--vm-filter-columns), minmax(0, 1fr));
gap: 14px var(--vm-space-5);
align-items: center;
}
.vm-page .vm-filter-field {
display: flex;
gap: 10px;
align-items: center;
min-width: 0;
}
.vm-page .vm-filter-field-label {
flex: 0 0 var(--vm-filter-label-width);
font-size: var(--vm-font-size-sm);
font-weight: var(--vm-font-weight-medium);
line-height: var(--vm-line-height-tight);
color: var(--vm-color-text-secondary);
text-align: right;
white-space: nowrap;
}
.vm-page .vm-filter-field-control {
flex: 1;
min-width: 0;
}
.vm-page .vm-filter-field-control .ant-input,
.vm-page .vm-filter-field-control .ant-input-affix-wrapper,
.vm-page .vm-filter-field-control .ant-select,
.vm-page .vm-filter-field-control .ant-select-selector,
.vm-page .vm-filter-field-control .ant-cascader,
.vm-page .vm-filter-field-control .ant-picker {
width: 100%;
border-radius: var(--vm-radius-md) !important;
}
.vm-page .vm-filter-actions {
display: flex;
flex-wrap: wrap;
gap: var(--vm-space-2);
align-items: center;
justify-content: flex-end;
margin-top: var(--vm-space-4);
padding-top: var(--vm-space-4);
border-top: 1px solid var(--vm-color-border-light);
}
/* ==========================================================================
Table section
========================================================================== */
.vm-page .vm-table-section {
display: flex;
flex-direction: column;
min-height: 0;
}
.vm-page .vm-table-toolbar {
display: flex;
flex-wrap: wrap;
gap: 10px var(--vm-space-4);
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
.vm-page .vm-table-toolbar-meta {
font-size: var(--vm-font-size-sm);
color: var(--vm-color-text-muted);
}
.vm-page .vm-table-toolbar-meta strong {
font-weight: var(--vm-font-weight-bold);
font-variant-numeric: tabular-nums;
color: var(--vm-color-text);
}
.vm-page .vm-table-toolbar-actions {
display: flex;
flex-wrap: wrap;
gap: var(--vm-space-2);
align-items: center;
}
.vm-page .vm-plate-search {
width: var(--vm-table-toolbar-search-width);
max-width: 100%;
}
.vm-page .vm-plate-search .ant-input,
.vm-page .vm-plate-search .ant-input-affix-wrapper,
.vm-page .vm-plate-search .ant-input-search-button {
border-radius: var(--vm-radius-md) !important;
}
.vm-page .vm-table-card {
overflow: hidden;
background: var(--vm-color-bg-card);
border: 1px solid var(--vm-color-border);
border-radius: var(--vm-radius-2xl);
box-shadow: var(--vm-shadow-card);
}
/* List table */
.vm-page .vm-list-table .ant-table-content table {
table-layout: fixed;
}
.vm-page .vm-list-table .ant-table-thead > tr > th {
padding: var(--vm-space-3) var(--vm-space-4) !important;
font-size: var(--vm-font-size-sm) !important;
font-weight: var(--vm-font-weight-bold) !important;
color: var(--vm-color-text-secondary) !important;
background: var(--vm-color-bg-muted) !important;
border-bottom: 1px solid var(--vm-color-border) !important;
}
.vm-page .vm-list-table .ant-table-tbody > tr:not(.ant-table-measure-row) > td {
padding: var(--vm-space-3) var(--vm-space-4) !important;
font-size: var(--vm-font-size-sm);
vertical-align: middle !important;
white-space: nowrap;
overflow: hidden;
}
.vm-page .vm-list-table .ant-table-tbody > tr:not(.ant-table-measure-row):hover > td {
background: var(--vm-color-bg-muted) !important;
}
.vm-page .vm-list-table .ant-table-tbody > tr:not(.ant-table-measure-row) > td.ant-table-cell-fix-right {
overflow: visible;
}
.vm-page .vm-list-table .ant-table-tbody > tr.ant-table-row-selected > td {
background: var(--vm-color-primary-light) !important;
}
.vm-page .vm-list-table .ant-pagination {
margin: 0 !important;
padding: var(--vm-space-3) var(--vm-space-4) !important;
border-top: 1px solid var(--vm-color-border-light);
}
/* ==========================================================================
Table cell components
========================================================================== */
.vm-page .vm-action-btn {
min-height: var(--vm-action-btn-min-height);
padding: 0 var(--vm-space-1) !important;
font-weight: var(--vm-font-weight-semibold) !important;
color: var(--vm-color-primary) !important;
}
.vm-page .vm-action-btn:hover {
color: var(--vm-color-primary-hover) !important;
}
.vm-page .vm-action-more-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--vm-action-more-size);
height: var(--vm-action-more-size);
color: var(--vm-color-text-muted);
cursor: pointer;
border-radius: var(--vm-radius-md);
transition:
background var(--vm-duration-fast) var(--vm-ease-default),
color var(--vm-duration-fast) var(--vm-ease-default);
}
.vm-page .vm-action-more-btn:hover {
color: #334155;
background: var(--vm-color-bg-hover);
}
.vm-page .vm-action-more-btn:focus-visible {
outline: 2px solid var(--vm-color-primary);
outline-offset: 2px;
}
.vm-page .vm-plate-link {
padding: 0;
font-size: var(--vm-font-size-sm);
font-weight: var(--vm-font-weight-bold);
color: var(--vm-color-text);
cursor: pointer;
background: none;
border: none;
}
.vm-page .vm-plate-link:hover {
color: var(--vm-color-primary);
text-decoration: underline;
text-underline-offset: 2px;
}
.vm-page .vm-plate-link:focus-visible {
border-radius: var(--vm-space-1);
outline: 2px solid var(--vm-color-primary);
outline-offset: 2px;
}
.vm-page .vm-vin-text {
font-family: var(--vm-font-mono);
font-size: var(--vm-font-size-xs);
letter-spacing: 0.02em;
color: var(--vm-color-text-secondary);
}
.vm-page .vm-online-pill {
display: inline-flex;
gap: var(--vm-space-2);
align-items: center;
padding: 2px var(--vm-space-2);
font-size: var(--vm-font-size-xs);
font-weight: var(--vm-font-weight-semibold);
border-radius: var(--vm-radius-pill);
}
.vm-page .vm-online-pill--on {
color: var(--vm-color-success-text);
background: var(--vm-color-success-bg);
}
.vm-page .vm-online-pill--off {
color: var(--vm-color-text-muted);
background: var(--vm-color-bg-hover);
}
.vm-page .vm-online-dot {
flex-shrink: 0;
width: 6px;
height: 6px;
border-radius: 50%;
}
.vm-page .vm-online-pill--on .vm-online-dot {
background: var(--vm-color-success);
}
.vm-page .vm-online-pill--off .vm-online-dot {
background: var(--vm-color-text-placeholder);
}
.vm-page .vm-status-tag {
margin: 0 !important;
font-weight: var(--vm-font-weight-semibold) !important;
line-height: 20px !important;
border: none !important;
border-radius: var(--vm-radius-sm) !important;
}
.vm-page .vm-empty-wrap {
padding: 48px 0;
color: var(--vm-color-text-placeholder);
text-align: center;
}
.vm-page .vm-empty-wrap svg {
display: block;
margin: 0 auto;
}
/* ==========================================================================
Detail page
========================================================================== */
.vm-page .vm-detail-card {
margin-bottom: var(--vm-space-4);
padding: var(--vm-space-6);
background: var(--vm-color-bg-card);
border: 1px solid var(--vm-color-border);
border-radius: var(--vm-radius-2xl);
box-shadow: 0 4px 20px -4px rgba(15, 23, 42, 0.05);
}
.vm-page .vm-detail-hero {
display: flex;
flex-wrap: wrap;
gap: var(--vm-space-3) var(--vm-space-4);
align-items: center;
margin-bottom: var(--vm-space-5);
padding-bottom: var(--vm-space-5);
border-bottom: 1px solid var(--vm-color-border-light);
}
.vm-page .vm-detail-plate {
font-size: var(--vm-font-size-xl);
font-weight: var(--vm-font-weight-extrabold);
letter-spacing: 0.04em;
color: var(--vm-color-text);
}
.vm-page .vm-section-title {
display: flex;
gap: var(--vm-space-2);
align-items: center;
margin-bottom: var(--vm-space-4);
font-size: var(--vm-font-size-md);
font-weight: var(--vm-font-weight-bold);
color: var(--vm-color-text);
}
.vm-page .vm-section-title::before {
flex-shrink: 0;
width: 3px;
height: 14px;
content: '';
background: linear-gradient(180deg, var(--vm-color-primary), #60a5fa);
border-radius: 2px;
}
.vm-page .vm-detail-kv-row {
display: flex;
gap: var(--vm-space-6);
margin-bottom: var(--vm-space-4);
}
.vm-page .vm-detail-kv-col {
flex: 1;
min-width: 180px;
}
.vm-page .vm-detail-kv-item {
display: flex;
gap: var(--vm-space-2);
align-items: center;
min-height: 22px;
margin-bottom: var(--vm-space-4);
}
.vm-page .vm-kv-label {
flex-shrink: 0;
min-width: var(--vm-kv-label-width);
font-size: var(--vm-font-size-sm);
color: var(--vm-color-text-muted);
text-align: right;
}
.vm-page .vm-kv-value {
min-width: 0;
font-size: var(--vm-font-size-sm);
color: var(--vm-color-text);
}
.vm-page .vm-kv-link {
font-weight: var(--vm-font-weight-semibold);
color: var(--vm-color-primary);
cursor: pointer;
background: none;
border: none;
}
.vm-page .vm-kv-link:hover {
text-decoration: underline;
}
.vm-page .vm-kv-link:focus-visible {
border-radius: var(--vm-space-1);
outline: 2px solid var(--vm-color-primary);
outline-offset: 2px;
}
.vm-page .vm-expand-bar {
margin-top: var(--vm-space-2);
padding-top: var(--vm-space-4);
text-align: center;
border-top: 1px solid var(--vm-color-border-light);
}
.vm-page .vm-tab-card {
padding: var(--vm-space-6);
margin-top: 0;
background: var(--vm-color-bg-card);
border-radius: var(--vm-radius-md);
box-shadow: var(--vm-shadow-card);
}
.vm-page .vm-tab-placeholder {
padding: var(--vm-space-2) 0;
font-size: var(--vm-font-size-base);
color: var(--vm-color-text-muted);
}
.vm-page .vm-detail-tabs .ant-tabs-nav {
margin-bottom: var(--vm-space-4);
}
.vm-page .vm-detail-tabs .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
font-weight: var(--vm-font-weight-semibold);
color: var(--vm-color-primary);
}
.vm-page .vm-detail-tabs .ant-tabs-ink-bar {
background: var(--vm-color-primary);
}
/* ==========================================================================
Blocks: hint / banner / import fail
========================================================================== */
.vm-page .vm-modal-hint,
.vm-modal-hint {
margin-bottom: var(--vm-space-4);
padding: 10px 14px;
font-size: var(--vm-font-size-sm);
line-height: var(--vm-line-height-relaxed);
color: var(--vm-color-text-secondary);
background: var(--vm-color-primary-light);
border: 1px solid var(--vm-color-primary-border);
border-radius: var(--vm-radius-lg);
}
.vm-page .vm-edit-banner,
.vm-edit-banner {
margin-bottom: var(--vm-space-4);
padding: var(--vm-space-3) 14px;
font-size: var(--vm-font-size-sm);
color: var(--vm-color-text-secondary);
background: var(--vm-color-bg-muted);
border: 1px solid var(--vm-color-border);
border-radius: var(--vm-radius-lg);
}
.vm-page .vm-edit-banner strong,
.vm-edit-banner strong {
font-weight: var(--vm-font-weight-semibold);
color: var(--vm-color-text);
}
.vm-page .vm-import-fail,
.vm-import-fail {
margin-top: var(--vm-space-4);
padding: var(--vm-space-3) 14px;
font-size: var(--vm-font-size-sm);
background: var(--vm-color-error-bg);
border: 1px solid var(--vm-color-error-border);
border-radius: var(--vm-radius-lg);
}
.vm-page .vm-import-fail a,
.vm-import-fail a {
font-weight: var(--vm-font-weight-medium);
color: var(--vm-color-primary);
}
.vm-page .vm-prd-content,
.vm-prd-content {
max-height: 560px;
overflow: auto;
font-size: var(--vm-font-size-sm);
line-height: var(--vm-line-height-relaxed);
color: var(--vm-color-text-secondary);
white-space: pre-wrap;
}
/* ==========================================================================
Ant Design — scoped under .vm-page
========================================================================== */
.vm-page .ant-btn {
border-radius: var(--vm-radius-md);
transition:
transform var(--vm-duration-fast) var(--vm-ease-default),
box-shadow var(--vm-duration-fast) var(--vm-ease-default),
background var(--vm-duration-fast) var(--vm-ease-default),
border-color var(--vm-duration-fast) var(--vm-ease-default);
}
.vm-page .ant-btn-primary:not(:disabled) {
background: var(--vm-color-primary) !important;
border-color: var(--vm-color-primary) !important;
box-shadow: var(--vm-shadow-btn-primary);
}
.vm-page .ant-btn-primary:not(:disabled):hover {
background: var(--vm-color-primary-hover) !important;
border-color: var(--vm-color-primary-hover) !important;
}
.vm-page .ant-btn-default.vm-btn-ghost,
.vm-page .vm-btn-req {
color: var(--vm-color-text-secondary) !important;
border-color: var(--vm-color-border) !important;
}
.vm-page .ant-input:focus,
.vm-page .ant-input-focused,
.vm-page .ant-input-affix-wrapper-focused,
.vm-page .ant-select-focused .ant-select-selector,
.vm-page .ant-picker-focused {
border-color: var(--vm-color-primary) !important;
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.15) !important;
}
.vm-page .ant-upload-wrapper .ant-upload-drag {
border-radius: var(--vm-radius-xl) !important;
border-color: var(--vm-color-border) !important;
background: var(--vm-color-bg-muted) !important;
transition:
border-color var(--vm-duration-fast) var(--vm-ease-default),
background var(--vm-duration-fast) var(--vm-ease-default);
}
.vm-page .ant-upload-wrapper .ant-upload-drag:hover {
border-color: var(--vm-color-primary-border) !important;
background: var(--vm-color-primary-light) !important;
}
/* ==========================================================================
Modal (portal — use Modal wrapClassName="vm-modal-wrap")
========================================================================== */
.vm-modal-wrap .ant-modal-content {
border-radius: var(--vm-radius-xl);
}
.vm-modal-wrap .ant-modal-header {
border-bottom: 1px solid var(--vm-color-border-light);
}
.vm-modal-wrap .ant-modal-footer {
border-top: 1px solid var(--vm-color-border-light);
}
.vm-modal-wrap .ant-btn-primary:not(:disabled) {
background: var(--vm-color-primary);
border-color: var(--vm-color-primary);
box-shadow: var(--vm-shadow-btn-primary);
}
.vm-modal-wrap .ant-btn-primary:not(:disabled):hover {
background: var(--vm-color-primary-hover);
border-color: var(--vm-color-primary-hover);
}
.vm-modal-wrap .ant-upload-drag {
border-radius: var(--vm-radius-xl);
}
/* ==========================================================================
Utilities
========================================================================== */
.vm-u-sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.vm-u-text-muted {
color: var(--vm-color-text-muted);
}
.vm-u-tabular {
font-variant-numeric: tabular-nums;
}
/* ==========================================================================
Responsive
========================================================================== */
@media (max-width: 1280px) {
.vm-page .vm-filter-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 768px) {
.vm-page {
padding: var(--vm-space-4) var(--vm-space-4) var(--vm-space-6);
}
.vm-page .vm-filter-field {
flex-direction: column;
align-items: stretch;
gap: var(--vm-space-2);
}
.vm-page .vm-filter-field-label {
flex: none;
text-align: left;
}
.vm-page .vm-table-toolbar {
flex-direction: column;
align-items: stretch;
}
.vm-page .vm-plate-search {
width: 100%;
}
.vm-page .vm-detail-kv-row {
flex-direction: column;
gap: 0;
}
}
@media (max-width: 640px) {
.vm-page .vm-filter-grid {
grid-template-columns: 1fr;
}
}
/* ==========================================================================
Accessibility — reduced motion
========================================================================== */
@media (prefers-reduced-motion: reduce) {
.vm-page .ant-btn,
.vm-page .vm-action-more-btn,
.vm-page .vm-plate-link {
transition: none;
}
}

View File

@@ -0,0 +1,93 @@
/**
* ONE-OS 车辆管理 / 中后台列表页 — 设计令牌
* 与 web端/车辆管理.jsx 规范一致,可供其他运维模块复用
*/
:root {
/* Brand */
--vm-color-primary: #2563eb;
--vm-color-primary-hover: #1d4ed8;
--vm-color-primary-light: #eff6ff;
--vm-color-primary-border: #bfdbfe;
--vm-color-primary-shadow: rgba(37, 99, 235, 0.45);
/* Semantic */
--vm-color-success: #10b981;
--vm-color-success-bg: #ecfdf5;
--vm-color-success-text: #047857;
--vm-color-warning: #f59e0b;
--vm-color-error: #ef4444;
--vm-color-error-bg: #fef2f2;
--vm-color-error-border: #fecaca;
/* Neutral */
--vm-color-bg-page: #f8fafc;
--vm-color-bg-page-alt: #f1f5f9;
--vm-color-bg-card: #ffffff;
--vm-color-bg-muted: #f8fafc;
--vm-color-bg-hover: #f1f5f9;
--vm-color-border: #e2e8f0;
--vm-color-border-light: #f1f5f9;
--vm-color-text: #0f172a;
--vm-color-text-secondary: #475569;
--vm-color-text-muted: #64748b;
--vm-color-text-placeholder: #94a3b8;
/* Typography */
--vm-font-family: Inter, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
--vm-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
--vm-font-size-xs: 12px;
--vm-font-size-sm: 13px;
--vm-font-size-base: 14px;
--vm-font-size-md: 15px;
--vm-font-size-lg: 16px;
--vm-font-size-xl: 22px;
--vm-font-weight-medium: 500;
--vm-font-weight-semibold: 600;
--vm-font-weight-bold: 700;
--vm-font-weight-extrabold: 800;
--vm-line-height-tight: 1.35;
--vm-line-height-normal: 1.5;
--vm-line-height-relaxed: 1.65;
/* Spacing */
--vm-space-1: 4px;
--vm-space-2: 8px;
--vm-space-3: 12px;
--vm-space-4: 16px;
--vm-space-5: 20px;
--vm-space-6: 24px;
--vm-space-8: 32px;
/* Radius */
--vm-radius-sm: 6px;
--vm-radius-md: 8px;
--vm-radius-lg: 10px;
--vm-radius-xl: 12px;
--vm-radius-2xl: 16px;
--vm-radius-pill: 999px;
/* Shadow */
--vm-shadow-card: 0 4px 20px -4px rgba(15, 23, 42, 0.04);
--vm-shadow-card-hover: 0 8px 28px -6px rgba(15, 23, 42, 0.08);
--vm-shadow-btn-primary: 0 2px 8px -2px var(--vm-color-primary-shadow);
/* Layout */
--vm-page-padding-x: 24px;
--vm-page-padding-y: 24px;
--vm-page-padding-bottom: 32px;
--vm-filter-label-width: 88px;
--vm-filter-columns: 4;
--vm-table-toolbar-search-width: 240px;
--vm-action-btn-min-height: 32px;
--vm-action-more-size: 32px;
--vm-kv-label-width: 100px;
/* Motion */
--vm-duration-fast: 150ms;
--vm-duration-normal: 200ms;
--vm-ease-default: ease;
/* Z-index */
--vm-z-dropdown: 1050;
--vm-z-modal: 1100;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -161,7 +161,7 @@ const loadOperateDraftsForDisplay = () => {
} catch {
/* ignore */
}
return merged;
return merged;
};
const DEFAULT_FILTER = {
@@ -1094,7 +1094,7 @@ const Component = function AnnualReviewWebList() {
<a className="action-link" onClick={() => goHandlePage(row)} role="button" tabIndex={0}>
办理
</a>
),
),
},
];

View File

@@ -26,6 +26,7 @@ const Component = function () {
id: 'pair_' + pairIdRef.current,
replaceType: undefined,
replaceReason: undefined,
replaceFee: '',
replaceReasonDesc: '',
originalPlate: undefined,
originalVin: '',
@@ -289,10 +290,12 @@ const Component = function () {
return;
}
var incomplete = pairs.find(function (p) {
return !p.originalPlate || !p.replacePlate || !p.replaceType || !p.replaceReason;
if (!p.originalPlate || !p.replacePlate || !p.replaceType || !p.replaceReason) return true;
if (p.replaceReason === '客户原因' && !(p.replaceFee || '').toString().trim()) return true;
return false;
});
if (incomplete) {
message.warning('请完善每条替换的新车、替换类型替换原因');
message.warning('请完善每条替换的新车、替换类型替换原因及客户原因下的替换费用');
return;
}
message.success('已提交 ' + pairs.length + ' 条替换车申请(原型)');
@@ -584,7 +587,12 @@ const Component = function () {
placeholder: '请选择',
style: { width: '100%' },
value: pair.replaceReason,
onChange: function (v) { updatePair(pair.id, { replaceReason: v }); },
onChange: function (v) {
updatePair(pair.id, {
replaceReason: v,
replaceFee: v === '客户原因' ? pair.replaceFee : ''
});
},
allowClear: true,
options: [
{ value: '客户原因', label: '客户原因' },
@@ -592,6 +600,24 @@ const Component = function () {
]
})
),
pair.replaceReason === '客户原因'
? renderField(
'替换费用',
true,
React.createElement(Input, {
placeholder: '请输入替换费用',
style: { width: '100%' },
value: pair.replaceFee || '',
addonBefore: '¥',
onChange: function (e) {
var val = e.target.value.replace(/[^\d.]/g, '');
var parts = val.split('.');
if (parts.length > 2) val = parts[0] + '.' + parts.slice(1).join('');
updatePair(pair.id, { replaceFee: val });
}
})
)
: null,
renderField(
'替换原因说明',
false,

View File

@@ -54,6 +54,7 @@ const Component = function () {
id: 'pair_2',
replaceType: '临时替换',
replaceReason: '客户原因',
replaceFee: '500.00',
replaceReasonDesc: '',
originalPlate: '浙A55555',
originalBrand: '重汽',
@@ -163,7 +164,7 @@ const Component = function () {
React.createElement('div', { className: 'vr-req-doc__meta' }, '数字化资产 ONEOS 运管平台 · 运维管理 · 车辆业务 · 替换车管理 · 查看'),
specSection('1. 页面定位', ['只读查看替换车申请详情,布局与新增/编辑一致,不可修改任何字段。']),
specSection('2. 展示内容', [
'2.1 车辆替换明细:每条被替换车一张卡片,含被替换车信息、替换说明、替换车辆;卡片标题展示被替换与替换车牌。',
'2.1 车辆替换明细:每条被替换车一张卡片,含被替换车信息、替换说明、替换车辆;卡片标题展示被替换与替换车牌;替换原因为「客户原因」时展示替换费用(只读)。',
'2.2 项目信息:客户名称、项目名称、项目类型(全单一份)。',
'2.3 审批情况:竖向步骤条展示审批节点、审批人、审批时间。'
]),
@@ -220,6 +221,15 @@ const Component = function () {
{ className: 'vr-form-grid vr-form-grid--reason' },
renderField('替换类型', React.createElement(Input, { value: pair.replaceType, disabled: true })),
renderField('替换原因', React.createElement(Input, { value: pair.replaceReason, disabled: true })),
pair.replaceReason === '客户原因'
? renderField(
'替换费用',
React.createElement(Input, {
value: pair.replaceFee ? '¥' + pair.replaceFee : '—',
disabled: true
})
)
: null,
renderField(
'替换原因说明',
React.createElement(Input.TextArea, {
@@ -302,7 +312,11 @@ const Component = function () {
{ title: '查看' }
]
}),
React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { setRequirementModalVisible(true); } }, '查看需求说明')
React.createElement(
Button,
{ type: 'link', style: { padding: 0, flexShrink: 0 }, onClick: function () { setRequirementModalVisible(true); } },
'查看需求说明'
)
),
React.createElement(
Card,

View File

@@ -41,6 +41,7 @@ const Component = function () {
id: 'pair_2',
replaceType: '临时替换',
replaceReason: '客户原因',
replaceFee: '500.00',
replaceReasonDesc: '',
originalPlate: '浙A55555',
originalVin: 'LGHXCAE28M5555555',
@@ -61,6 +62,7 @@ const Component = function () {
id: 'pair_' + pairIdRef.current,
replaceType: undefined,
replaceReason: undefined,
replaceFee: '',
replaceReasonDesc: '',
originalPlate: undefined,
originalVin: '',
@@ -324,10 +326,12 @@ const Component = function () {
return;
}
var incomplete = pairs.find(function (p) {
return !p.originalPlate || !p.replacePlate || !p.replaceType || !p.replaceReason;
if (!p.originalPlate || !p.replacePlate || !p.replaceType || !p.replaceReason) return true;
if (p.replaceReason === '客户原因' && !(p.replaceFee || '').toString().trim()) return true;
return false;
});
if (incomplete) {
message.warning('请完善每条替换的新车、替换类型替换原因');
message.warning('请完善每条替换的新车、替换类型替换原因及客户原因下的替换费用');
return;
}
message.success('已提交 ' + pairs.length + ' 条替换车申请(原型)');
@@ -455,7 +459,7 @@ const Component = function () {
'4.2 被替换车辆信息(只读):车牌号、品牌、型号;品牌/型号占位「选择车辆后自动显示」。',
'4.3 替换说明',
' 4.3.1 替换类型:必填,单选——永久替换、临时替换。',
' 4.3.2 替换原因:必填,单选——客户原因、车辆原因。',
' 4.3.2 替换原因:必填,单选——客户原因、车辆原因;选「客户原因」时右侧展示「替换费用」必填输入框(¥ 前缀,仅数字与小数点)。',
' 4.3.3 替换原因说明:选填,多行文本,最多 500 字,占位「请说明替换原因」,显示字数统计。',
'4.4 替换车辆',
' 4.4.1 新车:必填,单选下拉,支持搜索;未选被替换车时禁用,占位「请先选择被替换车辆」。',
@@ -487,7 +491,7 @@ const Component = function () {
]),
specSection('9. 校验与提示汇总', [
'请选择被替换车辆并完善替换信息',
'请完善每条替换的新车、替换类型替换原因',
'请完善每条替换的新车、替换类型替换原因及客户原因下的替换费用',
'多台替换须为同一客户、同一项目,已忽略不同项目的车辆',
'该新车已在其他替换项中选择',
'请先选择被替换车辆'
@@ -623,7 +627,12 @@ const Component = function () {
placeholder: '请选择',
style: { width: '100%' },
value: pair.replaceReason,
onChange: function (v) { updatePair(pair.id, { replaceReason: v }); },
onChange: function (v) {
updatePair(pair.id, {
replaceReason: v,
replaceFee: v === '客户原因' ? pair.replaceFee : ''
});
},
allowClear: true,
options: [
{ value: '客户原因', label: '客户原因' },
@@ -631,6 +640,24 @@ const Component = function () {
]
})
),
pair.replaceReason === '客户原因'
? renderField(
'替换费用',
true,
React.createElement(Input, {
placeholder: '请输入替换费用',
style: { width: '100%' },
value: pair.replaceFee || '',
addonBefore: '¥',
onChange: function (e) {
var val = e.target.value.replace(/[^\d.]/g, '');
var parts = val.split('.');
if (parts.length > 2) val = parts[0] + '.' + parts.slice(1).join('');
updatePair(pair.id, { replaceFee: val });
}
})
)
: null,
renderField(
'替换原因说明',
false,

View File

@@ -1,6 +1,42 @@
// 【重要】必须使用 const Component 作为组件变量名
// 运维管理 - 车辆业务 - 替换车管理2026年3月3日版本
var VR_KPI_STYLE = ''
+ '.vr-kpi-stats-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin-bottom:16px;}'
+ '@media (max-width:768px){.vr-kpi-stats-row{grid-template-columns:repeat(1,minmax(0,1fr));}}'
+ '.lc-alert-card{display:flex;align-items:flex-start;gap:12px;padding:14px 30px 14px 16px;border-radius:12px;border:1px solid #e2e8f0;background:#fff;position:relative;overflow:hidden;min-width:0;}'
+ '.lc-alert-card-main{flex:1;min-width:0;}'
+ '.lc-alert-card-icon{flex-shrink:0;width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;}'
+ '.lc-alert-card-val{font-size:26px;font-weight:800;line-height:1.1;color:#0f172a;font-variant-numeric:tabular-nums;}'
+ '.lc-alert-card-title{font-size:13px;font-weight:600;color:#334155;margin-top:2px;}'
+ '.lc-alert-card-tip-anchor{position:absolute;top:8px;right:8px;z-index:2;line-height:0;}'
+ '.lc-alert-card-tip{width:18px;height:18px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;color:#94a3b8;background:rgba(255,255,255,.92);border:1px solid #e2e8f0;cursor:help;line-height:0;}'
+ '.lc-alert-card-tip:hover{color:#64748b;border-color:#cbd5e1;background:#fff;}'
+ '.lc-alert-card--total{background:linear-gradient(135deg,#f8fafc 0%,#fff 100%);}'
+ '.lc-alert-card--total .lc-alert-card-icon{background:#e2e8f0;color:#475569;}'
+ '.lc-alert-card--progress{background:linear-gradient(135deg,#fff7ed 0%,#fff 55%);border-color:#fed7aa;}'
+ '.lc-alert-card--progress .lc-alert-card-icon{background:#ffedd5;color:#ea580c;}'
+ '.lc-alert-card--progress .lc-alert-card-val{color:#c2410c;}'
+ '.lc-alert-card--completed{background:linear-gradient(135deg,#ecfdf5 0%,#fff 55%);border-color:#bbf7d0;}'
+ '.lc-alert-card--completed .lc-alert-card-icon{background:#d1fae5;color:#059669;}'
+ '.lc-alert-card--completed .lc-alert-card-val{color:#047857;}'
+ '.lc-alert-card-clickable{cursor:pointer;transition:box-shadow .2s ease,border-color .2s ease,transform .2s ease;}'
+ '.lc-alert-card-clickable:hover{box-shadow:0 4px 14px rgba(15,23,42,.08);}'
+ '.lc-alert-card-active{box-shadow:0 0 0 2px rgba(22,93,255,.2)!important;border-color:#165dff!important;}';
var VR_KPI_ICONS = {
total: React.createElement('svg', { width: 18, height: 18, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
React.createElement('rect', { x: 3, y: 3, width: 7, height: 7 }), React.createElement('rect', { x: 14, y: 3, width: 7, height: 7 }),
React.createElement('rect', { x: 14, y: 14, width: 7, height: 7 }), React.createElement('rect', { x: 3, y: 14, width: 7, height: 7 })),
progress: React.createElement('svg', { width: 18, height: 18, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
React.createElement('circle', { cx: 12, cy: 12, r: 10 }), React.createElement('polyline', { points: '12 6 12 12 16 14' })),
completed: React.createElement('svg', { width: 18, height: 18, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
React.createElement('path', { d: 'M22 11.08V12a10 10 0 1 1-5.93-9.14' }), React.createElement('polyline', { points: '22 4 12 14.01 9 11.01' }))
};
var VR_KPI_TIP_SVG = React.createElement('svg', { width: 12, height: 12, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2.2, strokeLinecap: 'round', strokeLinejoin: 'round' },
React.createElement('circle', { cx: 12, cy: 12, r: 10 }), React.createElement('line', { x1: 12, y1: 16, x2: 12, y2: 12 }), React.createElement('line', { x1: 12, y1: 8, x2: 12.01, y2: 8 }));
const Component = function () {
var useState = React.useState;
var useCallback = React.useCallback;
@@ -13,19 +49,17 @@ const Component = function () {
var Select = antd.Select;
var Button = antd.Button;
var Table = antd.Table;
var Tabs = antd.Tabs;
var Modal = antd.Modal;
var Input = antd.Input;
var message = antd.message;
var Tag = antd.Tag;
var Empty = antd.Empty;
var App = antd.App;
var Badge = antd.Badge;
var Popover = antd.Popover;
var Tooltip = antd.Tooltip;
var RangePicker = DatePicker.RangePicker;
// 筛选展开(默认一行 3 列)
// 筛选展开(默认一行 4,与交车管理一致
var _filterExpanded = useState(false);
var _replaceDateRange = useState(null);
var _replaceType = useState(undefined);
@@ -49,8 +83,12 @@ const Component = function () {
createTimeRange: null
});
var _activeTab = useState('ongoing');
var _selectedRowKeys = useState([]);
var _kpiFilter = useState('ongoing');
var _listPlateQuick = useState(undefined);
var _page = useState(1);
var _pageSize = useState(10);
var setPage = _page[1];
var setPageSize = _pageSize[1];
var _withdrawModalVisible = useState(false);
var _withdrawModalRecord = useState(null);
var _toPermanentModalVisible = useState(false);
@@ -83,9 +121,9 @@ const Component = function () {
{ value: '临时替换', label: '临时替换' }
];
var projectNameOptions = [
{ value: 'p1', label: '嘉兴氢能示范项目' },
{ value: 'p2', label: '上海物流租赁项目' },
{ value: 'p3', label: '杭州城配租赁项目' }
{ value: '嘉兴氢能示范项目', label: '嘉兴氢能示范项目' },
{ value: '上海物流租赁项目', label: '上海物流租赁项目' },
{ value: '杭州城配租赁项目', label: '杭州城配租赁项目' }
];
var approvalStatusOptions = [
{ value: '全部', label: '全部' },
@@ -171,64 +209,59 @@ const Component = function () {
var deletedIds = _deletedIds[0];
var appliedFilter = _appliedFilter[0];
var listPlateQuick = _listPlateQuick[0];
var kpiFilter = _kpiFilter[0];
var filteredOngoing = useMemo(function () {
var list = ongoingList.filter(function (r) {
if (deletedIds.indexOf(r.id) !== -1) return false;
if (appliedFilter.replaceDateRange && appliedFilter.replaceDateRange.length === 2) {
var start = appliedFilter.replaceDateRange[0] && appliedFilter.replaceDateRange[0].format ? appliedFilter.replaceDateRange[0].format('YYYY-MM-DD') : '';
var end = appliedFilter.replaceDateRange[1] && appliedFilter.replaceDateRange[1].format ? appliedFilter.replaceDateRange[1].format('YYYY-MM-DD') : '';
var rd = (r.replaceDate || '').slice(0, 10);
if (start && rd < start) return false;
if (end && rd > end) return false;
}
if (appliedFilter.replaceType && r.replaceType !== appliedFilter.replaceType) return false;
if (appliedFilter.projectName && r.projectName !== appliedFilter.projectName) return false;
var approval = appliedFilter.approvalStatus;
if (approval && approval.length > 0 && approval.indexOf('全部') === -1 && approval.indexOf(r.approvalStatus) === -1) return false;
if (appliedFilter.originalPlate && (r.originalPlate || '').indexOf(appliedFilter.originalPlate) === -1) return false;
if (appliedFilter.replacePlate && (r.replacePlate || '').indexOf(appliedFilter.replacePlate) === -1) return false;
if (appliedFilter.replaceReason && appliedFilter.replaceReason !== '全部' && r.replaceReason !== appliedFilter.replaceReason) return false;
if (appliedFilter.creator && r.creator !== appliedFilter.creator) return false;
if (appliedFilter.createTimeRange && appliedFilter.createTimeRange.length === 2) {
var cs = appliedFilter.createTimeRange[0] && appliedFilter.createTimeRange[0].format ? appliedFilter.createTimeRange[0].format('YYYY-MM-DD') : '';
var ce = appliedFilter.createTimeRange[1] && appliedFilter.createTimeRange[1].format ? appliedFilter.createTimeRange[1].format('YYYY-MM-DD') : '';
var ct = (r.createTime || '').slice(0, 10);
if (cs && ct < cs) return false;
if (ce && ct > ce) return false;
}
return true;
});
return list;
}, [appliedFilter, deletedIds]);
function matchAppliedFilters(r, filter, quickPlate) {
if (deletedIds.indexOf(r.id) !== -1) return false;
if (filter.replaceDateRange && filter.replaceDateRange.length === 2) {
var start = filter.replaceDateRange[0] && filter.replaceDateRange[0].format ? filter.replaceDateRange[0].format('YYYY-MM-DD') : '';
var end = filter.replaceDateRange[1] && filter.replaceDateRange[1].format ? filter.replaceDateRange[1].format('YYYY-MM-DD') : '';
var rd = (r.replaceDate || '').slice(0, 10);
if (start && rd < start) return false;
if (end && rd > end) return false;
}
if (filter.replaceType && r.replaceType !== filter.replaceType) return false;
if (filter.projectName && r.projectName !== filter.projectName) return false;
var approval = filter.approvalStatus;
if (approval && approval.length > 0 && approval.indexOf('全部') === -1 && approval.indexOf(r.approvalStatus) === -1) return false;
if (filter.originalPlate && (r.originalPlate || '').indexOf(filter.originalPlate) === -1) return false;
if (filter.replacePlate && (r.replacePlate || '').indexOf(filter.replacePlate) === -1) return false;
if (filter.replaceReason && filter.replaceReason !== '全部' && r.replaceReason !== filter.replaceReason) return false;
if (filter.creator && r.creator !== filter.creator) return false;
if (filter.createTimeRange && filter.createTimeRange.length === 2) {
var cs = filter.createTimeRange[0] && filter.createTimeRange[0].format ? filter.createTimeRange[0].format('YYYY-MM-DD') : '';
var ce = filter.createTimeRange[1] && filter.createTimeRange[1].format ? filter.createTimeRange[1].format('YYYY-MM-DD') : '';
var ct = (r.createTime || '').slice(0, 10);
if (cs && ct < cs) return false;
if (ce && ct > ce) return false;
}
if (quickPlate && (r.originalPlate || '').indexOf(quickPlate) === -1 && (r.replacePlate || '').indexOf(quickPlate) === -1) return false;
return true;
}
var filteredHistory = useMemo(function () {
var list = historyList.filter(function (r) {
if (deletedIds.indexOf(r.id) !== -1) return false;
if (appliedFilter.replaceDateRange && appliedFilter.replaceDateRange.length === 2) {
var start = appliedFilter.replaceDateRange[0] && appliedFilter.replaceDateRange[0].format ? appliedFilter.replaceDateRange[0].format('YYYY-MM-DD') : '';
var end = appliedFilter.replaceDateRange[1] && appliedFilter.replaceDateRange[1].format ? appliedFilter.replaceDateRange[1].format('YYYY-MM-DD') : '';
var rd = (r.replaceDate || '').slice(0, 10);
if (start && rd < start) return false;
if (end && rd > end) return false;
}
if (appliedFilter.replaceType && r.replaceType !== appliedFilter.replaceType) return false;
if (appliedFilter.projectName && r.projectName !== appliedFilter.projectName) return false;
if (appliedFilter.originalPlate && (r.originalPlate || '').indexOf(appliedFilter.originalPlate) === -1) return false;
if (appliedFilter.replacePlate && (r.replacePlate || '').indexOf(appliedFilter.replacePlate) === -1) return false;
if (appliedFilter.replaceReason && appliedFilter.replaceReason !== '全部' && r.replaceReason !== appliedFilter.replaceReason) return false;
if (appliedFilter.creator && r.creator !== appliedFilter.creator) return false;
if (appliedFilter.createTimeRange && appliedFilter.createTimeRange.length === 2) {
var cs = appliedFilter.createTimeRange[0] && appliedFilter.createTimeRange[0].format ? appliedFilter.createTimeRange[0].format('YYYY-MM-DD') : '';
var ce = appliedFilter.createTimeRange[1] && appliedFilter.createTimeRange[1].format ? appliedFilter.createTimeRange[1].format('YYYY-MM-DD') : '';
var ct = (r.createTime || '').slice(0, 10);
if (cs && ct < cs) return false;
if (ce && ct > ce) return false;
}
return true;
var filteredBuckets = useMemo(function () {
var ongoing = ongoingList.filter(function (r) { return matchAppliedFilters(r, appliedFilter, listPlateQuick); });
var history = historyList.filter(function (r) { return matchAppliedFilters(r, appliedFilter, listPlateQuick); });
var all = ongoing.concat(history).sort(function (a, b) {
return String(b.replaceDate || '').localeCompare(String(a.replaceDate || ''));
});
return list;
}, [appliedFilter, deletedIds]);
return { ongoing: ongoing, history: history, all: all };
}, [appliedFilter, deletedIds, listPlateQuick]);
var kpiStats = useMemo(function () {
return {
total: filteredBuckets.all.length,
ongoing: filteredBuckets.ongoing.length,
history: filteredBuckets.history.length
};
}, [filteredBuckets]);
var currentList = useMemo(function () {
if (kpiFilter === 'history') return filteredBuckets.history;
if (kpiFilter === 'total') return filteredBuckets.all;
return filteredBuckets.ongoing;
}, [filteredBuckets, kpiFilter]);
var handleQuery = useCallback(function () {
_appliedFilter[1]({
@@ -242,6 +275,7 @@ const Component = function () {
creator: _creator[0],
createTimeRange: _createTimeRange[0]
});
setPage(1);
}, []);
var handleReset = useCallback(function () {
@@ -265,6 +299,7 @@ const Component = function () {
creator: undefined,
createTimeRange: null
});
setPage(1);
}, []);
var handleApprovalStatusChange = useCallback(function (v) {
@@ -278,29 +313,18 @@ const Component = function () {
_approvalStatus[1](v);
}, []);
var filterLabelStyle = { marginBottom: 6, fontSize: 13, fontWeight: 500, color: '#475569', lineHeight: 1.4 };
var filterItemStyle = { marginBottom: 0 };
var filterLabelStyle = { display: 'block', marginBottom: 4, color: '#333', fontSize: 14 };
var filterItemStyle = { minWidth: 0 };
var filterControlStyle = { width: '100%' };
var styles = {
page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontSize: 14 },
cardBody: { padding: '20px 24px' },
filterGrid: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: '16px 24px', alignItems: 'start' },
filterActions: { display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: 8, marginTop: 16, paddingTop: 16, borderTop: '1px solid #f1f5f9' }
};
var pageStyles =
'.vr-list-page{font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif}' +
'.vr-list-page .vr-page-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:20px;flex-wrap:wrap}' +
'.vr-list-page .vr-filter-card,.vr-list-page .vr-list-card{border-radius:16px;border:none;box-shadow:0 4px 24px -6px rgba(15,23,42,0.08),0 0 0 1px rgba(15,23,42,0.05);margin-bottom:16px}' +
'.vr-list-page .vr-filter-card>.ant-card-head,.vr-list-page .vr-list-card>.ant-card-head{border-bottom:1px solid #f1f5f9;min-height:auto;padding:14px 20px}' +
'.vr-list-page .vr-filter-card>.ant-card-head .ant-card-head-title,.vr-list-page .vr-list-card>.ant-card-head .ant-card-head-title{font-size:15px;font-weight:600;color:#0f172a;padding:0}' +
'.vr-list-page .vr-filter-card>.ant-card-body{padding:16px 20px 20px}' +
'.vr-list-page .vr-list-card>.ant-card-body{padding:12px 16px 16px}' +
'.vr-list-page .vr-filter-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px 20px;align-items:start}' +
'@media(max-width:900px){.vr-list-page .vr-filter-grid{grid-template-columns:1fr}}' +
'.vr-list-page .vr-filter-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px;padding-top:16px;border-top:1px solid #f1f5f9}' +
'.vr-list-page .vr-swap-arrow{color:#94a3b8;font-size:12px;margin:0 4px}' +
'.vr-list-page .vr-reason-text{display:block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#475569}' +
'.vr-list-page .vr-list-table .ant-table-thead>tr>th{background:#f8fafc!important;color:#475569;font-weight:600;font-size:13px}' +
'.vr-list-page .vr-list-table .ant-table-tbody>tr:hover>td{background:#f0f9ff!important}' +
'.vr-list-page .vr-list-table .ant-table-thead th,.vr-list-page .vr-list-table .ant-table-tbody td{white-space:nowrap}' +
'.vr-list-page .vr-list-table .ant-table-tbody>tr.ant-table-row-selected>td{background:#eff6ff!important}' +
'.vr-list-page .vr-tabs .ant-tabs-nav{margin-bottom:0}' +
'.vr-list-page .vr-empty{padding:48px 16px}' +
'.vr-approval-flow-popover .ant-popover-inner{padding:14px 16px;border-radius:8px}' +
'.vr-approval-flow{width:300px;max-width:min(340px,92vw)}' +
'.vr-approval-flow__item{display:flex;gap:12px;position:relative;padding-bottom:22px}' +
@@ -312,8 +336,46 @@ const Component = function () {
'.vr-approval-flow__head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:4px}' +
'.vr-approval-flow__role{font-size:14px;font-weight:600;color:rgba(0,0,0,0.88);line-height:1.4}' +
'.vr-approval-flow__meta{font-size:12px;color:rgba(0,0,0,0.45);line-height:1.5}' +
'.vr-list-page .vr-approval-status-trigger{display:inline-flex;cursor:pointer;border-radius:4px;transition:opacity .15s ease}' +
'.vr-list-page .vr-approval-status-trigger:hover{opacity:.88}';
'.vr-approval-status-trigger{display:inline-flex;cursor:pointer;border-radius:4px;transition:opacity .15s ease}' +
'.vr-approval-status-trigger:hover{opacity:.88}' +
'@media(max-width:900px){.vr-list-filter-grid{grid-template-columns:1fr 1fr!important}}' +
'@media(max-width:640px){.vr-list-filter-grid{grid-template-columns:1fr!important}}';
var cellLineMainStyle = { lineHeight: 1.45, color: '#333', wordBreak: 'break-all' };
var cellLineSubStyle = { lineHeight: 1.4, fontSize: 12, color: '#8c8c8c', marginTop: 2, wordBreak: 'break-all' };
var solidTagBaseStyle = { margin: 0, border: 'none', fontWeight: 600, color: '#fff', lineHeight: '20px', flexShrink: 0 };
function renderCellLines(mainText, subLines) {
var subs = subLines || [];
return React.createElement('div', null,
React.createElement('div', { style: cellLineMainStyle }, mainText || '—'),
subs.map(function (line, i) {
return React.createElement('div', { key: i, style: cellLineSubStyle }, line || '—');
})
);
}
function displayBrandModel(brand, model) {
var b = brand && brand !== '—' ? String(brand).trim() : '';
var m = model && model !== '—' ? String(model).trim() : '';
if (b && m) return b + ' · ' + m;
if (b) return b;
if (m) return m;
return '—';
}
function parseDateTimeParts(raw) {
if (!raw) return { date: '—', time: '—' };
var s = String(raw).trim();
if (s.indexOf(' ') >= 0) {
var parts = s.split(/\s+/);
var timePart = parts[1] || '—';
if (timePart.length >= 8) timePart = timePart.slice(0, 8);
else if (timePart.length >= 5) timePart = timePart.slice(0, 5);
return { date: parts[0] || '—', time: timePart };
}
return { date: s.slice(0, 10), time: '—' };
}
function formatFlowTime(timeStr) {
if (!timeStr) return '—';
@@ -419,20 +481,72 @@ const Component = function () {
}, React.createElement('span', { className: 'vr-approval-status-trigger' }, tag));
}
function renderSolidTag(text, bgColor) {
if (!text || text === '—') return '—';
return React.createElement(Tag, {
style: Object.assign({}, solidTagBaseStyle, { backgroundColor: bgColor || '#64748b' })
}, text);
}
function renderApprovalTag(status) {
var color = 'default';
if (status === '待审批') color = 'processing';
else if (status === '审批中') color = 'blue';
else if (status === '审批驳回') color = 'error';
else if (status === '未提交') color = 'default';
else if (status === '撤回') color = 'warning';
else if (status === '审批完成') color = 'success';
return React.createElement(Tag, { color: color, style: { margin: 0, fontWeight: 500 } }, status || '—');
var bg = '#8c8c8c';
if (status === '待审批') bg = '#2563eb';
else if (status === '审批中') bg = '#1677ff';
else if (status === '审批驳回') bg = '#dc2626';
else if (status === '未提交') bg = '#64748b';
else if (status === '撤回') bg = '#d97706';
else if (status === '审批完成') bg = '#16a34a';
return renderSolidTag(status || '—', bg);
}
function renderReplaceTypeTag(type) {
var color = type === '永久替换' ? 'geekblue' : type === '临时替换' ? 'gold' : 'default';
return React.createElement(Tag, { color: color, style: { margin: 0 } }, type || '—');
var bg = '#64748b';
if (type === '永久替换') bg = '#4f46e5';
else if (type === '临时替换') bg = '#ea580c';
return renderSolidTag(type || '—', bg);
}
function renderReplaceVehicleCell(record) {
return React.createElement('div', null,
React.createElement('div', { style: Object.assign({}, cellLineMainStyle, { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }) },
React.createElement('span', { style: { fontWeight: 600 } }, record.originalPlate || '—'),
React.createElement('span', { style: { color: '#94a3b8', fontWeight: 600 } }, '→'),
React.createElement('span', { style: { fontWeight: 600, color: '#165dff' } }, record.replacePlate || '—')
),
React.createElement('div', { style: cellLineSubStyle }, displayBrandModel(record.originalBrand, record.originalModel)),
React.createElement('div', { style: cellLineSubStyle }, displayBrandModel(record.replaceBrand, record.replaceModel))
);
}
function renderProjectInfoCell(record) {
return React.createElement('div', { style: { minWidth: 0 } },
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap', minWidth: 0 } },
React.createElement('span', {
style: { flex: '1 1 auto', minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: '#333', fontWeight: 600 }
}, record.projectName || '—'),
renderReplaceTypeTag(record.replaceType)
),
React.createElement('div', { style: cellLineSubStyle }, record.replaceReason || '—')
);
}
function renderApprovalInfoCell(status, record) {
var tag = renderApprovalStatusCell(status, record);
var approver = record.currentApprover && record.currentApprover !== '—' ? record.currentApprover : '—';
return React.createElement('div', null,
React.createElement('div', null, tag),
React.createElement('div', { style: cellLineSubStyle }, '当前审批人:' + approver)
);
}
function renderReplaceDateCell(record) {
var parsed = parseDateTimeParts(record.replaceDate);
return renderCellLines(parsed.date, [parsed.time]);
}
function renderCreateInfoCell(record) {
var parsed = parseDateTimeParts(record.createTime);
return renderCellLines(record.creator || '—', [parsed.date + (parsed.time !== '—' ? ' ' + parsed.time : '')]);
}
function getOperationButtons(record, isHistory) {
@@ -468,50 +582,55 @@ const Component = function () {
}
var tableColumns = [
{ title: '替换日期', dataIndex: 'replaceDate', key: 'replaceDate', width: 168, fixed: 'left' },
{
title: '替换车辆',
key: 'replaceVehicle',
width: 196,
fixed: 'left',
render: function (_, record) { return renderReplaceVehicleCell(record); }
},
{
title: '项目信息',
key: 'projectInfo',
width: 220,
render: function (_, record) { return renderProjectInfoCell(record); }
},
{
title: '替换日期',
key: 'replaceDate',
width: 118,
render: function (_, record) { return renderReplaceDateCell(record); }
},
{
title: '审批状态',
dataIndex: 'approvalStatus',
key: 'approvalStatus',
width: 108,
render: function (v, record) { return renderApprovalStatusCell(v, record); }
width: 132,
render: function (v, record) { return renderApprovalInfoCell(v, record); }
},
{
title: '当前审批人',
dataIndex: 'currentApprover',
key: 'currentApprover',
width: 110,
render: function (v) {
return React.createElement('span', { style: { color: v && v !== '—' ? '#334155' : '#94a3b8' } }, v || '—');
}
},
{ title: '被替换车(旧车)', dataIndex: 'originalPlate', key: 'originalPlate', width: 130 },
{ title: '品牌', dataIndex: 'originalBrand', key: 'originalBrand', width: 88 },
{ title: '型号', dataIndex: 'originalModel', key: 'originalModel', width: 100 },
{ title: '新车', dataIndex: 'replacePlate', key: 'replacePlate', width: 110 },
{ title: '品牌', dataIndex: 'replaceBrand', key: 'replaceBrandNew', width: 88 },
{ title: '型号', dataIndex: 'replaceModel', key: 'replaceModelNew', width: 100 },
{
title: '替换类型',
dataIndex: 'replaceType',
key: 'replaceType',
width: 108,
render: function (v) { return renderReplaceTypeTag(v); }
},
{ title: '替换原因', dataIndex: 'replaceReason', key: 'replaceReason', width: 100 },
{
title: '替换原因说明',
dataIndex: 'replaceReasonDesc',
key: 'replaceReasonDesc',
width: 140,
width: 160,
ellipsis: true,
render: function (v) {
return React.createElement('span', { className: 'vr-reason-text', title: v || '' }, v || '—');
return React.createElement('span', { title: v || '', style: { color: '#475569' } }, v || '—');
}
},
{ title: '创建人', dataIndex: 'creator', key: 'creator', width: 90 },
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 168 },
{ title: '操作', key: 'action', width: 200, fixed: 'right', render: function (_, record) { return getOperationButtons(record, _activeTab[0] === 'history'); } }
{
title: '创建信息',
key: 'createInfo',
width: 148,
render: function (_, record) { return renderCreateInfoCell(record); }
},
{
title: '操作',
key: 'action',
width: 168,
fixed: 'right',
render: function (_, record) { return getOperationButtons(record, kpiFilter === 'history'); }
}
];
var filterItems = [
@@ -544,63 +663,105 @@ const Component = function () {
React.createElement(RangePicker, { style: filterControlStyle, placeholder: ['请选择开始时间', '请选择结束时间'], value: _createTimeRange[0], onChange: function (v) { _createTimeRange[1](v); } }))
];
var filterCount = _filterExpanded[0] ? 9 : 3;
var filterCount = _filterExpanded[0] ? 9 : 4;
var filterNodes = [];
for (var i = 0; i < filterCount && i < filterItems.length; i++) {
filterNodes.push(filterItems[i]);
}
var _page = useState(1);
var _pageSize = useState(10);
var page = _page[0];
var setPage = _page[1];
var pageSize = _pageSize[0];
var setPageSize = _pageSize[1];
var currentList = _activeTab[0] === 'ongoing' ? filteredOngoing : filteredHistory;
var displayList = useMemo(function () {
var start = (page - 1) * pageSize;
return currentList.slice(start, start + pageSize);
}, [currentList, page, pageSize]);
var listStats = useMemo(function () {
return {
ongoing: filteredOngoing.length,
history: filteredHistory.length,
selected: (_selectedRowKeys[0] || []).length
};
}, [filteredOngoing.length, filteredHistory.length, _selectedRowKeys[0]]);
var kpiExportLabelMap = { total: '全部替换车', ongoing: '进行中的替换车', history: '历史替换车' };
var rowSelection = {
selectedRowKeys: _selectedRowKeys[0],
onChange: function (keys) { _selectedRowKeys[1](keys); },
fixed: true
};
var handleKpiCardClick = useCallback(function (key) {
_kpiFilter[1](key);
setPage(1);
}, []);
var handleListPlateQuickChange = useCallback(function (v) {
_listPlateQuick[1](v);
setPage(1);
}, []);
var handleExport = useCallback(function () {
if (!currentList.length) {
message.warning('当前「' + (kpiExportLabelMap[kpiFilter] || '替换车') + '」无数据可导出');
return;
}
message.success('已导出 ' + currentList.length + ' 条(' + (kpiExportLabelMap[kpiFilter] || '替换车') + ',原型)');
}, [currentList.length, kpiFilter]);
var kpiCards = useMemo(function () {
return [
{ key: 'total', type: 'total', title: '全部替换车', desc: '当前筛选条件下的全部替换车记录(含进行中与历史)', val: kpiStats.total, icon: VR_KPI_ICONS.total },
{ key: 'ongoing', type: 'progress', title: '进行中的替换车', desc: '审批状态为待审批、审批中、审批驳回、未提交、撤回的记录', val: kpiStats.ongoing, icon: VR_KPI_ICONS.progress },
{ key: 'history', type: 'completed', title: '历史替换车', desc: '审批流程已结束、状态为审批完成的记录', val: kpiStats.history, icon: VR_KPI_ICONS.completed }
];
}, [kpiStats]);
function renderKpiCard(card) {
var active = kpiFilter === card.key;
return React.createElement('div', {
key: card.key,
role: 'button',
tabIndex: 0,
className: 'lc-alert-card lc-alert-card--' + card.type + ' lc-alert-card-clickable' + (active ? ' lc-alert-card-active' : ''),
onClick: function () { handleKpiCardClick(card.key); },
onKeyDown: function (e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleKpiCardClick(card.key);
}
}
},
React.createElement('div', { className: 'lc-alert-card-tip-anchor' },
React.createElement(Tooltip, { title: card.desc, placement: 'topRight', overlayStyle: { maxWidth: 360 } },
React.createElement('span', {
className: 'lc-alert-card-tip',
role: 'img',
'aria-label': card.title + '说明',
onClick: function (e) { e.stopPropagation(); },
onMouseDown: function (e) { e.stopPropagation(); }
}, VR_KPI_TIP_SVG)
)
),
React.createElement('div', { className: 'lc-alert-card-icon' }, card.icon),
React.createElement('div', { className: 'lc-alert-card-main' },
React.createElement('div', { className: 'lc-alert-card-val' }, card.val),
React.createElement('div', { className: 'lc-alert-card-title' }, card.title)
)
);
}
var tablePagination = {
current: page,
pageSize: pageSize,
total: currentList.length,
showSizeChanger: true,
showQuickJumper: true,
showTotal: function (t) { return '共 ' + t + ' 条'; },
pageSizeOptions: ['10', '20', '50'],
onChange: function (p, ps) { setPage(p); if (ps) setPageSize(ps); }
};
function renderTableBody() {
if (displayList.length === 0) {
return React.createElement(Empty, {
className: 'vr-empty',
image: Empty.PRESENTED_IMAGE_SIMPLE,
description: '暂无符合条件的替换车记录,请调整筛选条件后重试'
});
}
return React.createElement(Table, {
rowKey: 'id',
rowSelection: rowSelection,
columns: tableColumns,
dataSource: displayList,
size: 'small',
scroll: { x: 1900 },
size: 'middle',
tableLayout: 'fixed',
scroll: { x: 1180 },
pagination: tablePagination
});
}
@@ -614,7 +775,7 @@ const Component = function () {
筛选与列表分为2个卡片
2.筛选:
支持通过替换日期、替换类型、项目名称、审批状态、被替换车车牌号、替换车车牌号、替换原因、创建人、创建时间进行筛选,右侧为重置、查询、展开/收起(筛选条件以3列显示,默认显示一行,点击展开/收起对筛选栏卡片进行展开/收起所有筛选条件),点击查询后,筛选条件与列表内容联动。点击重置会回到默认筛选条件并在列表展示结果:
支持通过替换日期、替换类型、项目名称、审批状态、被替换车车牌号、替换车车牌号、替换原因、创建人、创建时间进行筛选,右侧为重置、搜索、展开/收起(筛选条件以4列显示,默认显示一行,点击展开/收起对筛选栏进行展开/收起所有筛选条件),点击搜索后,筛选条件与列表及 KPI 统计联动。点击重置会回到默认筛选条件并在列表展示结果:
2.1.替换日期:日期选择器,支持单输入框内双日历选择开始-结束时间,默认提示文本为:请选择开始时间、请选择结束时间;
2.2.替换类型:选择器,分为永久替换、临时替换两种方式;
@@ -626,10 +787,10 @@ const Component = function () {
2.8.创建人:选择器,下拉选择所有创建人;
2.9.创建时间:日期选择器,支持单输入框内双日历选择开始-结束时间,默认提示文本为:请选择开始时间、请选择结束时间;
3.列表:列表右上角为新增、导出,首列为多选,支持多选后导出对应条目
列表展示所有替换车记录分为进行中、历史记录两个tab字段依次为替换日期、审批状态、当前审批人、被替换车旧车、品牌、型号、新车、品牌、型号、替换类型、替换原因、替换原因说明、创建人、创建时间、操作;
3.列表:顶部为 KPI 统计卡片(全部替换车 / 进行中的替换车 / 历史替换车),点击切换列表范围;工具栏左侧为车牌号快捷筛选,右侧为新增、导出(导出与当前 KPI 标签及筛选结果一致)
列表字段合并展示:替换车辆、项目信息、替换日期、审批状态、替换原因说明、创建信息、操作;
3.1.进行中:显示替换车申请流程未结束、暂存的记录;
3.1.进行中的替换车:显示替换车申请流程未结束、暂存的记录;
3.1.1.替换日期显示格式为YYYY-MM-DD HH:MM:SS
3.1.2.审批状态:分为待审批、审批中、审批驳回、未提交、撤回;
3.1.3.当前审批人:显示当前待审批节点审批人,未提交/撤回等为「—」;
@@ -642,7 +803,7 @@ const Component = function () {
3.1.7.3.撤回:审批状态为审批中时显示,二次确认;
3.1.7.4.删除:审批状态为撤回/审批驳回时显示,逻辑删除,二次确认;
3.2.历史记录:显示替换车申请流程已结束的记录;
3.2.历史替换车:显示替换车申请流程已结束的记录;
3.2.1.替换日期显示格式为YYYY-MM-DD显示替换车申请表单中设置的替换日期
3.2.2.替换类型:分为:临时替换、永久替换两种,根据替换车申请表单中设置的替换类型显示;
3.2.3.项目名称:显示替换车申请表单中设置的项目名称;
@@ -663,13 +824,10 @@ const Component = function () {
列表右下方为分页符。`;
var activeTab = _activeTab[0];
var selectedCount = (_selectedRowKeys[0] || []).length;
return React.createElement(App, null,
React.createElement('div', { className: 'vr-list-page', style: { minHeight: '100vh', padding: '20px 24px 32px', background: 'linear-gradient(180deg,#f8fafc 0%,#f1f5f9 100%)' } },
React.createElement('style', null, pageStyles),
React.createElement('div', { className: 'vr-page-header' },
React.createElement('div', { style: styles.page },
React.createElement('style', null, VR_KPI_STYLE + pageStyles),
React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } },
React.createElement(Breadcrumb, {
items: [
{ title: '运维管理' },
@@ -677,36 +835,64 @@ const Component = function () {
{ title: '替换车管理' }
]
}),
React.createElement(Button, { type: 'link', style: { padding: 0, color: '#2563eb', fontWeight: 500 }, onClick: function () { _requirementModalVisible[1](true); } }, '查看需求说明')
React.createElement(Button, { type: 'link', onClick: function () { _requirementModalVisible[1](true); } }, '查看需求说明')
),
React.createElement(Card, { className: 'vr-filter-card', title: '筛选条件' },
React.createElement('div', { className: 'vr-filter-grid' }, filterNodes),
React.createElement('div', { className: 'vr-filter-actions' },
React.createElement(Button, { onClick: handleReset }, '重置'),
React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询'),
React.createElement(Button, { type: 'link', size: 'small', onClick: function () { _filterExpanded[1](!_filterExpanded[0]); } }, _filterExpanded[0] ? '收起' : '展开')
React.createElement(Card, { style: { marginBottom: 16 } },
React.createElement('div', { style: styles.cardBody },
React.createElement('div', { className: 'vr-list-filter-grid', style: styles.filterGrid }, filterNodes),
React.createElement('div', { style: styles.filterActions },
React.createElement(Button, { onClick: handleReset }, '重置'),
React.createElement(Button, { type: 'primary', onClick: handleQuery }, '搜索'),
React.createElement(Button, {
type: 'link',
size: 'small',
onClick: function () { _filterExpanded[1](!_filterExpanded[0]); },
style: { display: 'inline-flex', alignItems: 'center', gap: 4, padding: '0 4px' }
},
_filterExpanded[0] ? '收起' : '展开',
React.createElement('svg', {
width: 12,
height: 12,
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
strokeWidth: 2,
strokeLinecap: 'round',
strokeLinejoin: 'round',
style: { transform: _filterExpanded[0] ? 'rotate(180deg)' : 'none', transition: 'transform 0.2s ease' }
}, React.createElement('polyline', { points: '6 9 12 15 18 9' }))
)
)
)
),
React.createElement(Card, { className: 'vr-list-card', title: '替换车列表' },
React.createElement('div', { className: 'vr-list-table' },
React.createElement(Tabs, {
className: 'vr-tabs',
activeKey: activeTab,
onChange: function (k) { _activeTab[1](k); _selectedRowKeys[1]([]); setPage(1); },
tabBarExtraContent: React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' } },
React.createElement(Button, { type: 'primary', onClick: function () { message.info('新增替换车申请(原型)'); } }, '新增'),
selectedCount > 0
? React.createElement(Badge, { count: selectedCount, size: 'small', offset: [-4, 4] },
React.createElement(Button, { onClick: function () { message.info('导出选中 ' + selectedCount + ' 条(原型)'); } }, '导出')
)
: React.createElement(Button, { onClick: function () { message.info('请先勾选需要导出的记录'); } }, '导出')
React.createElement(Card, null,
React.createElement('div', { style: styles.cardBody },
React.createElement('div', { className: 'vr-kpi-stats-row' }, kpiCards.map(renderKpiCard)),
React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12, flexWrap: 'wrap', gap: 12 } },
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, flexWrap: 'wrap' } },
React.createElement('span', { style: { color: '#333', fontSize: 14, whiteSpace: 'nowrap' } }, '车牌号'),
React.createElement(Select, {
placeholder: '请输入或选择车牌号',
allowClear: true,
showSearch: true,
style: { width: 220 },
value: listPlateQuick,
onChange: handleListPlateQuickChange,
options: plateOptions,
filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; }
})
),
destroyInactiveTabPane: true,
items: [
{ key: 'ongoing', label: '进行中 (' + listStats.ongoing + ')', children: activeTab === 'ongoing' ? renderTableBody() : null },
{ key: 'history', label: '历史记录 (' + listStats.history + ')', children: activeTab === 'history' ? renderTableBody() : null }
]
})
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', marginLeft: 'auto' } },
React.createElement('span', { style: { fontSize: 13, color: '#64748b' } },
'当前标签:',
React.createElement('span', { style: { color: '#334155', fontWeight: 600 } }, kpiExportLabelMap[kpiFilter] || '—'),
' · 导出与列表一致'
),
React.createElement(Button, { type: 'primary', onClick: function () { message.info('新增替换车申请(原型)'); } }, '新增'),
React.createElement(Button, { onClick: handleExport }, '导出')
)
),
renderTableBody()
)
),
React.createElement(Modal, {
@@ -736,7 +922,6 @@ const Component = function () {
if (prev.indexOf(rec.id) !== -1) return prev;
return prev.concat(rec.id);
});
_selectedRowKeys[1](function (prev) { return prev.filter(function (k) { return k !== rec.id; }); });
}
message.success('已逻辑删除(原型)');
_deleteModalVisible[1](false);