Compare commits
120
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
614bb875eb | ||
|
|
c404da3520 | ||
|
|
f5fab0dd16 | ||
|
|
74ff93863e | ||
|
|
18851a2070 | ||
|
|
8cfffb7704 | ||
|
|
9fcdd55af2 | ||
|
|
e42b86e05f | ||
|
|
4277989def | ||
|
|
4888d4964b | ||
|
|
5cb0f8f44d | ||
|
|
eb696e4409 | ||
|
|
a5b07e3f40 | ||
|
|
b95f325b3e | ||
|
|
1001244f38 | ||
|
|
fb65bf728b | ||
|
|
257d7fd4c6 | ||
|
|
6c0db0163a | ||
|
|
fa53771e59 | ||
|
|
e71976ba5f | ||
|
|
fd080aa39b | ||
|
|
e7bb5a23be | ||
|
|
da844d0e24 | ||
|
|
1d7b200f76 | ||
|
|
ddb3817d6e | ||
|
|
2ff9e7684f | ||
|
|
c7327de2e5 | ||
|
|
9c324fbff3 | ||
|
|
69469ed61f | ||
|
|
3a880c0f34 | ||
|
|
798a2a60c9 | ||
|
|
0ad22527a1 | ||
|
|
f30afdd1e9 | ||
|
|
1addd5112b | ||
|
|
cd51aec32a | ||
|
|
4a2513e100 | ||
|
|
7c40fbc4c5 | ||
|
|
336bb11c42 | ||
|
|
4aae54f9a5 | ||
|
|
57f839166e | ||
|
|
e4757849dd | ||
|
|
61e8418459 | ||
|
|
80c15dcf1b | ||
|
|
8a053c5a80 | ||
|
|
d5d777afd3 | ||
|
|
0483c2d0a7 | ||
|
|
3c1f238163 | ||
|
|
384a64b2c8 | ||
|
|
c55f6b4045 | ||
|
|
0b4127a0e2 | ||
|
|
2970dca3e0 | ||
|
|
15d5c4694e | ||
|
|
1f7ce0da6d | ||
|
|
2bd56d8a9e | ||
|
|
0b388414c9 | ||
|
|
b0b1abca01 | ||
|
|
4fdcd3e72e | ||
|
|
424ef98798 | ||
|
|
37aeba20da | ||
|
|
a9382c11a4 | ||
|
|
4b94c614ad | ||
|
|
b39ae0bdf1 | ||
|
|
709f125c42 | ||
|
|
a0c1a2700a | ||
|
|
8c25f5a195 | ||
|
|
ee50c3ba19 | ||
|
|
1477f94774 | ||
|
|
35fd987f00 | ||
|
|
b6491a21d1 | ||
|
|
030417ba9c | ||
|
|
c05bdb361b | ||
|
|
e288d1e180 | ||
|
|
a48e73bdb5 | ||
|
|
56a8ba66b7 | ||
|
|
8c62b8e704 | ||
|
|
f1a3f7b45b | ||
|
|
e7344a1f16 | ||
|
|
75dd0b9740 | ||
|
|
aa5b724bed | ||
|
|
ca99e068cf | ||
|
|
aafd7ffdb5 | ||
|
|
112f42669c | ||
|
|
9bf8e1253d | ||
|
|
4f9134249f | ||
|
|
318922bddc | ||
|
|
5d0d4541a8 | ||
|
|
17aa9b54ca | ||
|
|
9d3d822227 | ||
|
|
fbee7979e4 | ||
|
|
8e55d61a9e | ||
|
|
83f36b12a8 | ||
|
|
e800ee1b7e | ||
|
|
3fbcc7a125 | ||
|
|
c90bf52b4d | ||
|
|
c9ac87b104 | ||
|
|
47fdf8f528 | ||
|
|
9192625165 | ||
|
|
5ebbce5c1f | ||
|
|
dc5327c8e4 | ||
|
|
044832a8f7 | ||
|
|
19c5809af2 | ||
|
|
d41684d7ed | ||
|
|
60445b1523 | ||
|
|
534ef90f2e | ||
|
|
f8484391b8 | ||
|
|
414f2cd320 | ||
|
|
d12ef20726 | ||
|
|
ed7ce35a91 | ||
|
|
6b5bc0494c | ||
|
|
fed4ae6edf | ||
|
|
87546848d2 | ||
|
|
3a6d0878aa | ||
|
|
d18fec1ddc | ||
|
|
62f2c166c6 | ||
|
|
14a14e2c87 | ||
|
|
8a457952fa | ||
|
|
c657f3b5ac | ||
|
|
31d1388890 | ||
|
|
dc94c6d261 | ||
|
|
97c86dbee3 |
@@ -17,6 +17,8 @@ pnpm-debug.log*
|
|||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
.claude
|
||||||
|
.kimi-code
|
||||||
*.suo
|
*.suo
|
||||||
*.ntvs*
|
*.ntvs*
|
||||||
*.njsproj
|
*.njsproj
|
||||||
|
|||||||
Generated
+3
@@ -3,4 +3,7 @@
|
|||||||
<component name="JavaScriptSettings">
|
<component name="JavaScriptSettings">
|
||||||
<option name="languageLevel" value="ES6" />
|
<option name="languageLevel" value="ES6" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="WebPackConfiguration">
|
||||||
|
<option name="mode" value="DISABLED" />
|
||||||
|
</component>
|
||||||
</project>
|
</project>
|
||||||
+2
-2
@@ -31,14 +31,14 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=2560bbf04daef66c810c5e6a97e8c508&plugin=AMap.AutoComplete,AMap.PlaceSearch,AMap.Geolocation,AMap.Geocoder,AMap.Marker,AMap.Driving"></script>
|
<script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=2560bbf04daef66c810c5e6a97e8c508&plugin=AMap.AutoComplete,AMap.PlaceSearch,AMap.Geolocation,AMap.Geocoder,AMap.Marker,AMap.Driving"></script>
|
||||||
<!-- <script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>-->
|
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.selfLink = window.location.href;
|
window.selfLink = window.location.href;
|
||||||
window.selfSearch = window.location.search;
|
window.selfSearch = window.location.search;
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
// VConsole 默认会挂载到 `window.VConsole` 上
|
// VConsole 默认会挂载到 `window.VConsole` 上
|
||||||
// let vConsole = new window.VConsole();
|
let vConsole = new window.VConsole();
|
||||||
(function (doc, win) {
|
(function (doc, win) {
|
||||||
// console.log("==window.location.pathname===",window.location.pathname)
|
// console.log("==window.location.pathname===",window.location.pathname)
|
||||||
let docEl = doc.documentElement
|
let docEl = doc.documentElement
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
# 年终总结看板 Dashboard
|
||||||
|
|
||||||
|
道路救援服务年终数据可视化看板项目。
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
dashboard-demo/
|
||||||
|
├── index.html # 主看板页面(支持URL参数动态加载数据)
|
||||||
|
├── data/
|
||||||
|
│ ├── 供应商年度KPI.xlsx # 服务商KPI数据(主数据源)
|
||||||
|
│ ├── 师傅案件top3.xlsx # 案件量TOP3师傅数据
|
||||||
|
│ ├── 师傅在线top3.xlsx # 在线时长TOP3师傅数据
|
||||||
|
│ ├── 拒单率最高地区.xlsx # 拒单率最高地区数据
|
||||||
|
│ └── 拒单率最高时段.xlsx # 拒单率最高时段数据
|
||||||
|
└── README.md # 项目说明
|
||||||
|
```
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- 深色主题数据可视化看板
|
||||||
|
- **直接读取Excel文件**,无需转换为JSON
|
||||||
|
- 支持通过URL参数 `?providerId=XXX` 切换不同服务商
|
||||||
|
- 右上角下拉框可实时切换服务商(自动从Excel读取服务商列表)
|
||||||
|
- 使用 Chart.js 实现饼状图和雷达图
|
||||||
|
- 使用 SheetJS (xlsx) 解析Excel文件
|
||||||
|
- 响应式布局,支持不同屏幕尺寸
|
||||||
|
- 数据与页面分离,便于维护
|
||||||
|
|
||||||
|
## 数据指标
|
||||||
|
|
||||||
|
看板展示以下数据指标:
|
||||||
|
|
||||||
|
1. **总案件量分布**(饼状图)
|
||||||
|
- 小修、困境、拖车三类案件占比
|
||||||
|
|
||||||
|
2. **年度聚合案件量**
|
||||||
|
|
||||||
|
3. **案件量TOP3师傅**
|
||||||
|
|
||||||
|
4. **车辆平均总在线时长**
|
||||||
|
|
||||||
|
5. **在线时长TOP3师傅**
|
||||||
|
|
||||||
|
6. **拒单率最高地区**
|
||||||
|
|
||||||
|
7. **拒单率最高时段**
|
||||||
|
|
||||||
|
8. **APP使用率**
|
||||||
|
- 年度APP使用率展示
|
||||||
|
|
||||||
|
## Excel数据结构
|
||||||
|
|
||||||
|
### 1. 供应商年度KPI.xlsx
|
||||||
|
|
||||||
|
| 列名 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 服务商id | 服务商唯一标识 |
|
||||||
|
| 服务商 | 服务商名称 |
|
||||||
|
| 完成案件量 | 总案件数量 |
|
||||||
|
| 拖车完成量 | 拖车案件数 |
|
||||||
|
| 小修完成量 | 小修案件数 |
|
||||||
|
| 困境完成量 | 困境案件数 |
|
||||||
|
| 聚合案件量 | 聚合案件数量 |
|
||||||
|
| APP使用率. | APP使用率(小数形式,如0.998) |
|
||||||
|
| 年度车辆平均总在线时长(小时) | 车辆平均在线时长 |
|
||||||
|
|
||||||
|
### 2. 师傅案件top3.xlsx
|
||||||
|
|
||||||
|
| 列名 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 服务商id | 服务商唯一标识 |
|
||||||
|
| 服务商 | 服务商名称 |
|
||||||
|
| 服务人员工号 | 师傅姓名/工号 |
|
||||||
|
| 完成案件量 | 该师傅完成的案件数 |
|
||||||
|
|
||||||
|
### 3. 师傅在线top3.xlsx
|
||||||
|
|
||||||
|
| 列名 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 服务商名称 | 服务商名称 |
|
||||||
|
| 司机姓名 | 师傅姓名 |
|
||||||
|
| 年度总在线时长(小时) | 在线时长 |
|
||||||
|
|
||||||
|
### 4. 拒单率最高地区.xlsx
|
||||||
|
|
||||||
|
| 列名 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 供应商名称 | 服务商名称 |
|
||||||
|
| 地区 | 拒单率最高的地区 |
|
||||||
|
| 拒单率 | 拒单率(小数形式) |
|
||||||
|
|
||||||
|
### 5. 拒单率最高时段.xlsx
|
||||||
|
|
||||||
|
| 列名 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 供应商名称 | 服务商名称 |
|
||||||
|
| 时段 | 拒单率最高的时段 |
|
||||||
|
| 拒单率 | 拒单率(小数形式) |
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
### 1. 启动本地服务器
|
||||||
|
|
||||||
|
由于页面使用 `fetch` 加载JSON数据,需要通过HTTP服务器访问:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd C:\Users\Administrator\dashboard-demo
|
||||||
|
|
||||||
|
# 使用 Python
|
||||||
|
python -m http.server 8080
|
||||||
|
|
||||||
|
# 或使用 Node.js
|
||||||
|
npx serve .
|
||||||
|
|
||||||
|
# 或使用 PHP
|
||||||
|
php -S localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 访问页面
|
||||||
|
|
||||||
|
- 默认服务商:`http://localhost:8080`(自动加载第一个服务商)
|
||||||
|
- 指定服务商:`http://localhost:8080?providerId=1128`(使用服务商id)
|
||||||
|
- 切换服务商:使用页面右上角的下拉框
|
||||||
|
|
||||||
|
### 3. 更新数据
|
||||||
|
|
||||||
|
直接替换 `data/` 目录下的Excel文件即可,页面会自动读取最新数据。
|
||||||
|
服务商列表从Excel自动生成,无需手动配置。
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
- HTML5 / CSS3
|
||||||
|
- JavaScript (ES6+)
|
||||||
|
- Chart.js - 图表库(饼图、雷达图)
|
||||||
|
- SheetJS (xlsx) - Excel文件解析
|
||||||
|
- CSS Grid / Flexbox - 布局
|
||||||
|
|
||||||
|
## 后续优化方向
|
||||||
|
|
||||||
|
- [ ] 添加更多图表类型(柱状图、折线图等)
|
||||||
|
- [ ] 支持时间范围筛选
|
||||||
|
- [ ] 添加数据导出功能
|
||||||
|
- [ ] 移动端适配优化
|
||||||
|
- [ ] 添加数据加载骨架屏
|
||||||
|
- [ ] 支持主题切换(深色/浅色)
|
||||||
|
|
||||||
|
## 更新日志
|
||||||
|
|
||||||
|
### 2025-01-21
|
||||||
|
- 改为直接读取Excel文件,无需JSON转换
|
||||||
|
- 服务商列表从Excel自动生成
|
||||||
|
- 将"AB段里程对比"改为"APP使用率"展示
|
||||||
|
- 新增服务商选择下拉框
|
||||||
|
|
||||||
|
### 2025-01-20
|
||||||
|
- 初始版本
|
||||||
|
- 实现基础看板布局
|
||||||
|
- 支持URL参数切换服务商
|
||||||
|
- 数据JSON化,与页面分离
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"serviceProviderId": "SP001",
|
||||||
|
"serviceProviderName": "上海道路救援服务中心",
|
||||||
|
"year": 2025,
|
||||||
|
"summary": {
|
||||||
|
"totalCases": 100,
|
||||||
|
"caseBreakdown": {
|
||||||
|
"minorRepair": 20,
|
||||||
|
"predicament": 20,
|
||||||
|
"towing": 60
|
||||||
|
},
|
||||||
|
"aggregatedCases": 15
|
||||||
|
},
|
||||||
|
"topMastersByCases": [
|
||||||
|
{ "rank": 1, "name": "小王", "cases": 18 },
|
||||||
|
{ "rank": 2, "name": "小张", "cases": 16 },
|
||||||
|
{ "rank": 3, "name": "小李", "cases": 12 }
|
||||||
|
],
|
||||||
|
"onlineHours": {
|
||||||
|
"averageTotal": 2920,
|
||||||
|
"topMasters": [
|
||||||
|
{ "rank": 1, "name": "小王", "hours": 3230 },
|
||||||
|
{ "rank": 2, "name": "小张", "hours": 3028 },
|
||||||
|
{ "rank": 3, "name": "小李", "hours": 2996 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rejectionRate": {
|
||||||
|
"highestRegion": {
|
||||||
|
"name": "崇明",
|
||||||
|
"rate": 21.2
|
||||||
|
},
|
||||||
|
"highestTimeSlot": {
|
||||||
|
"period": "18:00-22:00",
|
||||||
|
"description": "晚高峰时段"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"abMileage": {
|
||||||
|
"highest": {
|
||||||
|
"region": "崇明",
|
||||||
|
"distance": 62
|
||||||
|
},
|
||||||
|
"lowest": {
|
||||||
|
"region": "黄浦",
|
||||||
|
"distance": 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"serviceProviderId": "SP002",
|
||||||
|
"serviceProviderName": "浦东汽车救援有限公司",
|
||||||
|
"year": 2025,
|
||||||
|
"summary": {
|
||||||
|
"totalCases": 156,
|
||||||
|
"caseBreakdown": {
|
||||||
|
"minorRepair": 35,
|
||||||
|
"predicament": 41,
|
||||||
|
"towing": 80
|
||||||
|
},
|
||||||
|
"aggregatedCases": 22
|
||||||
|
},
|
||||||
|
"topMastersByCases": [
|
||||||
|
{ "rank": 1, "name": "老陈", "cases": 28 },
|
||||||
|
{ "rank": 2, "name": "大刘", "cases": 24 },
|
||||||
|
{ "rank": 3, "name": "阿强", "cases": 19 }
|
||||||
|
],
|
||||||
|
"onlineHours": {
|
||||||
|
"averageTotal": 3150,
|
||||||
|
"topMasters": [
|
||||||
|
{ "rank": 1, "name": "老陈", "hours": 3580 },
|
||||||
|
{ "rank": 2, "name": "大刘", "hours": 3420 },
|
||||||
|
{ "rank": 3, "name": "阿强", "hours": 3210 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rejectionRate": {
|
||||||
|
"highestRegion": {
|
||||||
|
"name": "奉贤",
|
||||||
|
"rate": 18.5
|
||||||
|
},
|
||||||
|
"highestTimeSlot": {
|
||||||
|
"period": "07:00-09:00",
|
||||||
|
"description": "早高峰时段"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"abMileage": {
|
||||||
|
"highest": {
|
||||||
|
"region": "金山",
|
||||||
|
"distance": 55
|
||||||
|
},
|
||||||
|
"lowest": {
|
||||||
|
"region": "静安",
|
||||||
|
"distance": 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+119
@@ -0,0 +1,119 @@
|
|||||||
|
{
|
||||||
|
"name": "data",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "data",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"xlsx": "^0.18.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/adler-32": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cfb": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"adler-32": "~1.3.0",
|
||||||
|
"crc-32": "~1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/codepage": {
|
||||||
|
"version": "1.15.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz",
|
||||||
|
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/crc-32": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"crc32": "bin/crc32.njs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/frac": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ssf": {
|
||||||
|
"version": "0.11.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz",
|
||||||
|
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"frac": "~1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wmf": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/word": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xlsx": {
|
||||||
|
"version": "0.18.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
|
||||||
|
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"adler-32": "~1.3.0",
|
||||||
|
"cfb": "~1.2.1",
|
||||||
|
"codepage": "~1.15.0",
|
||||||
|
"crc-32": "~1.2.1",
|
||||||
|
"ssf": "~0.11.2",
|
||||||
|
"wmf": "~1.0.1",
|
||||||
|
"word": "~0.3.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"xlsx": "bin/xlsx.njs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "data",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "read-excel.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "commonjs",
|
||||||
|
"dependencies": {
|
||||||
|
"xlsx": "^0.18.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
const XLSX = require('xlsx');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Excel文件路径
|
||||||
|
const excelPath = 'C:\\Users\\chenhaid\\Documents\\WXWork\\1688858118476511\\Cache\\File\\2026-02\\供应商年度KPIv5.xlsx';
|
||||||
|
|
||||||
|
// 输出JSON文件路径
|
||||||
|
const outputPath = path.join(__dirname, 'kpi.json');
|
||||||
|
|
||||||
|
// 读取Excel文件
|
||||||
|
const workbook = XLSX.readFile(excelPath);
|
||||||
|
|
||||||
|
// 获取第一个工作表名称
|
||||||
|
const sheetName = workbook.SheetNames[0];
|
||||||
|
console.log('工作表列表:', workbook.SheetNames);
|
||||||
|
|
||||||
|
// 获取工作表
|
||||||
|
const worksheet = workbook.Sheets[sheetName];
|
||||||
|
|
||||||
|
// 转换为JSON
|
||||||
|
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
||||||
|
|
||||||
|
console.log('读取到', jsonData.length, '条数据');
|
||||||
|
console.log('第一条数据示例:', JSON.stringify(jsonData[0], null, 2));
|
||||||
|
|
||||||
|
// 写入JSON文件
|
||||||
|
fs.writeFileSync(outputPath, JSON.stringify(jsonData, null, 2), 'utf8');
|
||||||
|
|
||||||
|
console.log('已保存到:', outputPath);
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 296 KiB |
@@ -0,0 +1,14 @@
|
|||||||
|
import request from '@/utils/http'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询 H5 损伤照片
|
||||||
|
* @param {Object} data - { userOrderId, taskOrderId }
|
||||||
|
*/
|
||||||
|
export function getDamagePhotos(data) {
|
||||||
|
return request({
|
||||||
|
url: '/api/h5-interface/damagePhotos',
|
||||||
|
method: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import request from '@/utils/http'
|
||||||
|
|
||||||
|
export function getBeforeDispatchImage(data) {
|
||||||
|
return request({
|
||||||
|
url: '/supplierAppV2/dispatchApp/order/getBeforeDispatchImage',
|
||||||
|
method: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
+22
-2
@@ -72,10 +72,12 @@ export function vehicleTypeList(){
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 服务商服务能力
|
// 服务商服务能力
|
||||||
export function supplierServiceTree(){
|
export function supplierServiceTree(data){
|
||||||
|
console.log('ddddddd0', data)
|
||||||
return request({
|
return request({
|
||||||
url:'/supplierAppV2/dispatchApp/user/supplierServiceTree',
|
url:'/supplierAppV2/dispatchApp/user/supplierServiceTree',
|
||||||
method:'POST'
|
method:'POST',
|
||||||
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +117,15 @@ export function getSupplierInfo(key){
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据id查询潜在服务商
|
||||||
|
export function supplierSelectById(params){
|
||||||
|
return request({
|
||||||
|
url:'/supplier/potential/supplierSelectById',
|
||||||
|
method:'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 获取企微二维码
|
// 获取企微二维码
|
||||||
export function getOrCodeInfo(data){
|
export function getOrCodeInfo(data){
|
||||||
return request({
|
return request({
|
||||||
@@ -133,6 +144,15 @@ export function getContactQrCodeResult(data){
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加企微
|
||||||
|
export function updateWechatId(data){
|
||||||
|
return request({
|
||||||
|
url:'/supplier/potential/updateWechatId',
|
||||||
|
method:'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
//司机列表
|
//司机列表
|
||||||
export function driverList(data){
|
export function driverList(data){
|
||||||
return request({
|
return request({
|
||||||
|
|||||||
+30
-1
@@ -23,7 +23,7 @@ export function toDoAudit(data){
|
|||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 工单详情
|
//
|
||||||
export function getOrderDetail(data){
|
export function getOrderDetail(data){
|
||||||
return request({
|
return request({
|
||||||
url:'/supplierAppV2/dispatchApp/order/getOrderDetail',
|
url:'/supplierAppV2/dispatchApp/order/getOrderDetail',
|
||||||
@@ -77,6 +77,15 @@ export function updateOrderSettlement(data){
|
|||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 获取司机信息
|
||||||
|
export function getDriverInfo(data){
|
||||||
|
return request({
|
||||||
|
url:'/supplierAppV2/dispatchApp/order/getDriverInfo',
|
||||||
|
method:'POST',
|
||||||
|
contentType:'application/json',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
// 电瓶数量列表
|
// 电瓶数量列表
|
||||||
export function batteryCountList(){
|
export function batteryCountList(){
|
||||||
return request({
|
return request({
|
||||||
@@ -168,3 +177,23 @@ export function getNewVersion(key){
|
|||||||
params: key
|
params: key
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getLog(data){
|
||||||
|
// 添加log
|
||||||
|
return request({
|
||||||
|
url: '/ureport/pageView/save',
|
||||||
|
method:'POST',
|
||||||
|
contentType:'application/json',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//获取最新版本
|
||||||
|
export function getUserBO(){
|
||||||
|
return request({
|
||||||
|
url: '/base/user/getUserBO',
|
||||||
|
method:'POST',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export function userFeedback(data){
|
|||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
//二手拖车信息擦亮
|
//擦亮
|
||||||
export function usedCarPolish(data){
|
export function usedCarPolish(data){
|
||||||
return request({
|
return request({
|
||||||
url: `/toc-user/car-app/usedCarPolish/${data}`,
|
url: `/toc-user/car-app/usedCarPolish/${data}`,
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import request from '@/utils/http'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前服务商分时段电话列表和审批状态
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {number} params.supplierId 服务商 ID
|
||||||
|
*/
|
||||||
|
export function getSupplierPhoneList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/supplierAppV2/dispatchApp/supplierPhone/list',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前服务商电话时段变更审批状态
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {number} params.supplierId 服务商 ID
|
||||||
|
*/
|
||||||
|
export function getSupplierPhoneApprovalStatus(params) {
|
||||||
|
return request({
|
||||||
|
url: '/supplierAppV2/dispatchApp/supplierPhone/approvalStatus',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交服务商电话时段变更审批
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {number} data.supplierId 服务商 ID
|
||||||
|
* @param {string} [data.modifyReason] 修改原因
|
||||||
|
* @param {Array} data.phoneList 电话时段草稿列表
|
||||||
|
*/
|
||||||
|
export function submitSupplierPhoneApproval(data) {
|
||||||
|
return request({
|
||||||
|
url: '/supplierAppV2/dispatchApp/supplierPhone/submit',
|
||||||
|
method: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 643 KiB After Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,646 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cameraGuide" v-if="visible">
|
||||||
|
<!-- ===== 拍摄引导页 ===== -->
|
||||||
|
<div class="cameraPage" v-show="step==='guide'">
|
||||||
|
<video ref="video" class="cameraVideo" autoplay playsinline muted></video>
|
||||||
|
|
||||||
|
<div class="cameraNav">
|
||||||
|
<van-icon name="arrow-left" class="backBtn" @click="handleClose" />
|
||||||
|
<span class="navTitle">{{ navTitle }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="guideLayer">
|
||||||
|
<!-- 顶部主指令卡 -->
|
||||||
|
<div class="guideHero">
|
||||||
|
<div class="guideHeroIcon">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect x="3" y="5" width="18" height="14" rx="2"></rect>
|
||||||
|
<path d="M3 9h18"></path>
|
||||||
|
<circle cx="8" cy="13.5" r="1.6"></circle>
|
||||||
|
<path d="M11 12.5h7M11 15h5"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="guideHeroText">
|
||||||
|
<div class="guideHeroTitle">{{ heroTitle }}</div>
|
||||||
|
<div class="guideHeroSub">从卡套内拿出,避免反光</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 取景框 -->
|
||||||
|
<div class="guideFrameOuter">
|
||||||
|
<span class="corner tl"></span>
|
||||||
|
<span class="corner tr"></span>
|
||||||
|
<span class="corner bl"></span>
|
||||||
|
<span class="corner br"></span>
|
||||||
|
<div class="guideFrameBorder"></div>
|
||||||
|
<div class="guideHole">
|
||||||
|
<!-- 主页模板:标题 + 印章 -->
|
||||||
|
<div v-if="type==='front'" class="licenseTemplate ltFrontPage">
|
||||||
|
<div class="ltInner">
|
||||||
|
<div class="ltTitle">
|
||||||
|
<div class="ltTitleLine">中华人民共和国机动车行驶证</div>
|
||||||
|
</div>
|
||||||
|
<div class="ltSeal">印章</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 副页模板:条形码 + 编号 -->
|
||||||
|
<div v-else-if="type==='back'" class="licenseTemplate ltBackPage">
|
||||||
|
<div class="ltInner">
|
||||||
|
<div class="ltBarcodeArea">
|
||||||
|
<div class="ltBarcodeStrip">
|
||||||
|
<div v-for="(bar, i) in barcodeBars" :key="i" class="ltBar" :class="bar"></div>
|
||||||
|
</div>
|
||||||
|
<div class="ltBarcodeNum">3701001234567</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 车辆照片模板:标题 + 车辆轮廓 -->
|
||||||
|
<div v-else class="licenseTemplate ltCarPage">
|
||||||
|
<div class="ltInner">
|
||||||
|
<div class="ltTitle">
|
||||||
|
<div class="ltTitleLine">车辆照片</div>
|
||||||
|
</div>
|
||||||
|
<div class="ltCarOutline">
|
||||||
|
<svg viewBox="0 0 140 70" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M14 46 L20 26 Q22 20 30 19 L88 16 Q96 16 102 22 L116 34 Q126 36 128 42 L128 48 Q128 52 124 52 L116 52"></path>
|
||||||
|
<path d="M14 46 L14 50 Q14 52 18 52 L30 52"></path>
|
||||||
|
<circle cx="42" cy="52" r="9"></circle>
|
||||||
|
<circle cx="102" cy="52" r="9"></circle>
|
||||||
|
<path d="M51 52 L93 52"></path>
|
||||||
|
<path d="M58 19 L60 33 L34 34"></path>
|
||||||
|
<path d="M92 18 L94 32 L114 33"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部取景要点 -->
|
||||||
|
<div class="guideTips">
|
||||||
|
<div class="tipItem ok">
|
||||||
|
<span class="tipIco">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M4 8V4h4M16 4h4v4M20 16v4h-4M8 20H4v-4"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="tipLabel">四角入框</span>
|
||||||
|
</div>
|
||||||
|
<div class="tipItem ok">
|
||||||
|
<span class="tipIco">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M4 12h16M7 7h10M7 17h10"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="tipLabel">平放正向</span>
|
||||||
|
</div>
|
||||||
|
<div class="tipItem warn">
|
||||||
|
<span class="tipIco">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="4"></circle>
|
||||||
|
<path d="M12 2v3M12 19v3M2 12h3M19 12h3M5 5l2 2M17 17l2 2M19 5l-2 2M5 19l2-2"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="tipLabel">避免反光</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="shootBar">
|
||||||
|
<div class="shootBtn" @click="takePhoto"></div>
|
||||||
|
<div class="shootLabel">点击拍照</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ===== 预览确认页 ===== -->
|
||||||
|
<div class="previewPage" v-show="step==='preview'">
|
||||||
|
<div class="cameraNav">
|
||||||
|
<van-icon name="arrow-left" class="backBtn" @click="handlePreviewBack" />
|
||||||
|
<span class="navTitle">照片预览</span>
|
||||||
|
</div>
|
||||||
|
<div class="previewImgWrap">
|
||||||
|
<img class="previewImg" :src="previewDataURL" alt="预览" />
|
||||||
|
</div>
|
||||||
|
<div class="previewBtns">
|
||||||
|
<button class="previewBtn btnRetake" @click="retake">重拍</button>
|
||||||
|
<button class="previewBtn btnConfirm" @click="confirmPhoto">确认使用</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 拍摄引导组件:WebRTC 取景 + 引导框 + 分页面证件模板 + 预览确认
|
||||||
|
// props: type(front/back/car), visible
|
||||||
|
// emit: confirm({blob, dataURL})、cancel、fallback(权限拒绝/相机不可用,父组件降级系统选择器)
|
||||||
|
export default {
|
||||||
|
name: 'CameraGuide',
|
||||||
|
props: {
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'front'
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const heights = ['tall', 'short', 'medium wide', 'tall', 'short', 'medium', 'tall wide', 'short',
|
||||||
|
'medium', 'tall', 'short wide', 'medium', 'tall', 'short', 'medium wide', 'tall',
|
||||||
|
'short', 'medium', 'tall wide', 'short', 'medium', 'tall', 'short wide', 'medium',
|
||||||
|
'tall', 'short', 'medium wide', 'tall', 'short', 'medium', 'tall wide', 'short',
|
||||||
|
'medium', 'tall', 'short wide', 'medium', 'tall', 'short', 'medium wide', 'tall']
|
||||||
|
return {
|
||||||
|
step: 'guide', // guide 拍摄引导 | preview 预览确认
|
||||||
|
stream: null,
|
||||||
|
previewBlob: null,
|
||||||
|
previewDataURL: '',
|
||||||
|
barcodeBars: heights
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
navTitle() {
|
||||||
|
return { front: '拍摄行驶证主页', back: '拍摄行驶证副页', car: '拍摄行驶证车辆照片' }[this.type] || '拍摄行驶证'
|
||||||
|
},
|
||||||
|
heroTitle() {
|
||||||
|
return { front: '请将主页对准取景框', back: '请将副页对准取景框', car: '请将车辆对准取景框' }[this.type] || '请将证件对准取景框'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
visible(val) {
|
||||||
|
if (val) {
|
||||||
|
this.step = 'guide'
|
||||||
|
this.previewBlob = null
|
||||||
|
this.previewDataURL = ''
|
||||||
|
this.$nextTick(() => this.startCamera())
|
||||||
|
} else {
|
||||||
|
this.stopCamera()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.stopCamera()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async startCamera() {
|
||||||
|
try {
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
|
video: { facingMode: 'environment' },
|
||||||
|
audio: false
|
||||||
|
})
|
||||||
|
// 等待期间组件已被关闭,立即释放
|
||||||
|
if (!this.visible) {
|
||||||
|
stream.getTracks().forEach(t => t.stop())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.stream = stream
|
||||||
|
await this.$nextTick()
|
||||||
|
const video = this.$refs.video
|
||||||
|
if (video) {
|
||||||
|
video.srcObject = stream
|
||||||
|
video.play().catch(() => {})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 权限拒绝或相机不可用 → 通知父组件降级为系统选择器
|
||||||
|
console.error('getUserMedia error:', e)
|
||||||
|
this.$emit('fallback')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stopCamera() {
|
||||||
|
if (this.stream) {
|
||||||
|
this.stream.getTracks().forEach(t => t.stop())
|
||||||
|
this.stream = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
takePhoto() {
|
||||||
|
const video = this.$refs.video
|
||||||
|
if (!video || !video.videoWidth) {
|
||||||
|
this.$toast('相机尚未就绪,请稍候')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const canvas = document.createElement('canvas')
|
||||||
|
canvas.width = video.videoWidth
|
||||||
|
canvas.height = video.videoHeight
|
||||||
|
canvas.getContext('2d').drawImage(video, 0, 0)
|
||||||
|
const dataURL = canvas.toDataURL('image/jpeg', 0.92)
|
||||||
|
canvas.toBlob(blob => {
|
||||||
|
if (!blob) {
|
||||||
|
this.$toast('拍摄失败,请重试')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.previewBlob = blob
|
||||||
|
this.previewDataURL = dataURL
|
||||||
|
this.step = 'preview'
|
||||||
|
}, 'image/jpeg', 0.92)
|
||||||
|
},
|
||||||
|
retake() {
|
||||||
|
// 重拍回到引导页,相机流保持
|
||||||
|
this.step = 'guide'
|
||||||
|
},
|
||||||
|
confirmPhoto() {
|
||||||
|
this.$emit('confirm', { blob: this.previewBlob, dataURL: this.previewDataURL })
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.$emit('cancel')
|
||||||
|
},
|
||||||
|
handlePreviewBack() {
|
||||||
|
// 预览页返回 = 放弃拍摄,关闭并释放相机流
|
||||||
|
this.$emit('cancel')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.cameraGuide {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 3000;
|
||||||
|
background: #F5F5F5;
|
||||||
|
}
|
||||||
|
.cameraPage {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(160deg, #F7F9FC 0%, #EEF1F6 50%, #E6EAF1 100%);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.cameraVideo {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.cameraNav {
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 52px;
|
||||||
|
padding-left: 14px;
|
||||||
|
padding-top: env(safe-area-inset-top);
|
||||||
|
background: #2C395F;
|
||||||
|
color: #fff;
|
||||||
|
.backBtn {
|
||||||
|
font-size: 22px;
|
||||||
|
width: 36px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.navTitle {
|
||||||
|
flex: 1;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bolder;
|
||||||
|
letter-spacing: .5px;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.guideLayer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 80px;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.guideHero {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border: 1px solid #E9E9EA;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px 15px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
max-width: 322px;
|
||||||
|
width: calc(100% - 48px);
|
||||||
|
box-sizing: border-box;
|
||||||
|
box-shadow: 0 2px 10px rgba(20, 30, 50, .08);
|
||||||
|
.guideHeroIcon {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(53, 77, 147, .10);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #354D93;
|
||||||
|
svg {
|
||||||
|
width: 23px;
|
||||||
|
height: 23px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.guideHeroTitle {
|
||||||
|
color: #222;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.3;
|
||||||
|
letter-spacing: .3px;
|
||||||
|
}
|
||||||
|
.guideHeroSub {
|
||||||
|
color: #5B606E;
|
||||||
|
font-size: 12.5px;
|
||||||
|
margin-top: 4px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 取景框:宽约为屏宽 80%,宽高比约 1.43:1 */
|
||||||
|
.guideFrameOuter {
|
||||||
|
position: relative;
|
||||||
|
width: 80%;
|
||||||
|
max-width: 340px;
|
||||||
|
padding-top: 69.93%; /* 宽高比约 1.43:1 */
|
||||||
|
flex-shrink: 0;
|
||||||
|
filter: drop-shadow(0 4px 16px rgba(20, 30, 50, .12));
|
||||||
|
}
|
||||||
|
.corner {
|
||||||
|
position: absolute;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-color: #354D93;
|
||||||
|
border-style: solid;
|
||||||
|
z-index: 10;
|
||||||
|
animation: cornerFadeIn .45s ease both;
|
||||||
|
&.tl { top: -4px; left: -4px; border-width: 4px 0 0 4px; border-radius: 4px 0 0 0; }
|
||||||
|
&.tr { top: -4px; right: -4px; border-width: 4px 4px 0 0; border-radius: 0 4px 0 0; }
|
||||||
|
&.bl { bottom: -4px; left: -4px; border-width: 0 0 4px 4px; border-radius: 0 0 0 4px; }
|
||||||
|
&.br { bottom: -4px; right: -4px; border-width: 0 4px 4px 0; border-radius: 0 0 4px 0; }
|
||||||
|
}
|
||||||
|
@keyframes cornerFadeIn {
|
||||||
|
from { opacity: 0; transform: scale(1.15); }
|
||||||
|
to { opacity: 1; transform: scale(1); }
|
||||||
|
}
|
||||||
|
.guideFrameBorder {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border: 3px solid #C2C8D2;
|
||||||
|
border-radius: 8px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
.guideHole {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
/* 证件模板叠层(仅视觉辅助,不拦截事件) */
|
||||||
|
.licenseTemplate {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 6;
|
||||||
|
border: 1.5px solid rgba(53, 77, 147, .55);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
background: rgba(53, 77, 147, .06);
|
||||||
|
}
|
||||||
|
.ltInner {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
left: 5px;
|
||||||
|
right: 5px;
|
||||||
|
bottom: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.ltTitle {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.ltTitleLine {
|
||||||
|
font-size: 10px;
|
||||||
|
color: rgba(53, 77, 147, .9);
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.ltSeal {
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
width: 34px;
|
||||||
|
height: 34px;
|
||||||
|
border: 1.5px dashed rgba(255, 85, 59, .85);
|
||||||
|
border-radius: 2px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 5.5px;
|
||||||
|
color: rgba(255, 85, 59, .85);
|
||||||
|
}
|
||||||
|
.ltFrontPage .ltInner {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
.ltBackPage .ltInner {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.ltBarcodeArea {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 4px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
.ltBarcodeStrip {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
height: 28px;
|
||||||
|
gap: 1px;
|
||||||
|
.ltBar {
|
||||||
|
background: rgba(53, 77, 147, .6);
|
||||||
|
width: 1.5px;
|
||||||
|
border-radius: 0.5px;
|
||||||
|
&.wide { width: 2.8px; }
|
||||||
|
&.short { height: 50%; }
|
||||||
|
&.medium { height: 70%; }
|
||||||
|
&.tall { height: 100%; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ltBarcodeNum {
|
||||||
|
font-size: 8px;
|
||||||
|
color: rgba(53, 77, 147, .9);
|
||||||
|
letter-spacing: 2px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
.ltCarPage .ltCarOutline {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
svg {
|
||||||
|
width: 60%;
|
||||||
|
opacity: .6;
|
||||||
|
color: #354D93;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.guideTips {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 22px;
|
||||||
|
padding: 0 14px;
|
||||||
|
max-width: 340px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.tipItem {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 11px 4px 9px;
|
||||||
|
border: 1px solid #E9E9EA;
|
||||||
|
box-shadow: 0 2px 8px rgba(20, 30, 50, .06);
|
||||||
|
.tipIco {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
svg {
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.ok .tipIco {
|
||||||
|
background: rgba(53, 77, 147, .10);
|
||||||
|
color: #354D93;
|
||||||
|
}
|
||||||
|
&.warn .tipIco {
|
||||||
|
background: rgba(255, 85, 59, .12);
|
||||||
|
color: #FF553B;
|
||||||
|
}
|
||||||
|
.tipLabel {
|
||||||
|
color: #5B606E;
|
||||||
|
font-size: 11.5px;
|
||||||
|
line-height: 1.3;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.shootBar {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: calc(32px + env(safe-area-inset-bottom));
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-top: 1px solid #E9E9EA;
|
||||||
|
}
|
||||||
|
.shootBtn {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 4px solid #354D93;
|
||||||
|
background: #FFFFFF;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 0 0 2px rgba(53, 77, 147, .22), 0 6px 16px rgba(20, 30, 50, .18);
|
||||||
|
&:active {
|
||||||
|
transform: scale(.92);
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 58px;
|
||||||
|
height: 58px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #354D93;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.shootLabel {
|
||||||
|
color: #5B606E;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 10px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
/* ===== 预览确认页 ===== */
|
||||||
|
.previewPage {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: #F5F5F5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
.previewImgWrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.previewImg {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
.previewBtns {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 0 20px 32px;
|
||||||
|
padding-bottom: calc(32px + env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
.previewBtn {
|
||||||
|
flex: 1;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 24px;
|
||||||
|
border: none;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
&:active {
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btnRetake {
|
||||||
|
background: #FFFFFF;
|
||||||
|
color: #354D93;
|
||||||
|
border: 1px solid #354D93;
|
||||||
|
}
|
||||||
|
.btnConfirm {
|
||||||
|
background: #354D93;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
<template>
|
||||||
|
<div class="step-bar">
|
||||||
|
<div
|
||||||
|
v-for="(step, index) in steps"
|
||||||
|
:key="index"
|
||||||
|
class="step-item"
|
||||||
|
:class="getStepClass(index)"
|
||||||
|
>
|
||||||
|
<div class="step-circle" @click="onStepClick(index)">
|
||||||
|
<span v-if="getStatus(index) === 'finish'" class="step-check">✓</span>
|
||||||
|
<span v-else>{{ index + 1 }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="step-label">{{ step }}</div>
|
||||||
|
<div v-if="index < steps.length - 1" class="step-line" :class="{'step-line-done': getStatus(index) === 'finish'}"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'StepBar',
|
||||||
|
props: {
|
||||||
|
current: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
steps: {
|
||||||
|
type: Array,
|
||||||
|
default: () => ['基本信息', '添加企微', '车辆信息', '驾驶员信息']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getStatus(index) {
|
||||||
|
const stepNum = index + 1;
|
||||||
|
if (stepNum < this.current) return 'finish';
|
||||||
|
if (stepNum === this.current) return 'process';
|
||||||
|
return 'wait';
|
||||||
|
},
|
||||||
|
getStepClass(index) {
|
||||||
|
return `step-${this.getStatus(index)}`;
|
||||||
|
},
|
||||||
|
onStepClick(index) {
|
||||||
|
const stepNum = index + 1;
|
||||||
|
if (this.getStatus(index) === 'finish') {
|
||||||
|
this.$emit('step-click', stepNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.step-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 14px 10px 10px;
|
||||||
|
background: #fff;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-circle {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: bold;
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-finish .step-circle {
|
||||||
|
background: #0E76F4;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-process .step-circle {
|
||||||
|
background: #0E76F4;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 118, 244, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-wait .step-circle {
|
||||||
|
background: #e0e0e0;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-label {
|
||||||
|
font-size: 11px;
|
||||||
|
margin-top: 5px;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-finish .step-label {
|
||||||
|
color: #0E76F4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-process .step-label {
|
||||||
|
color: #0E76F4;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-wait .step-label {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-line {
|
||||||
|
position: absolute;
|
||||||
|
top: 13px;
|
||||||
|
left: calc(50% + 13px);
|
||||||
|
right: calc(-50% + 13px);
|
||||||
|
height: 2px;
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-line-done {
|
||||||
|
background: #0E76F4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-check {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+41
-1
@@ -27,6 +27,14 @@ const routes = [
|
|||||||
title: '审核中',
|
title: '审核中',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/addWechat',
|
||||||
|
name: 'addWechat',
|
||||||
|
component: () => import('@/views/index/addWechat'),
|
||||||
|
meta:{
|
||||||
|
title: '添加微信',
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/supplierInfo',
|
path: '/supplierInfo',
|
||||||
name: 'supplierInfo',
|
name: 'supplierInfo',
|
||||||
@@ -35,6 +43,22 @@ const routes = [
|
|||||||
title: '信息查看',
|
title: '信息查看',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/supplierTimePhone',
|
||||||
|
name: 'supplierTimePhone',
|
||||||
|
component: () => import('@/views/index/supplierTimePhone'),
|
||||||
|
meta:{
|
||||||
|
title: '供应商时段电话',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/supplierTimePhoneEdit',
|
||||||
|
name: 'supplierTimePhoneEdit',
|
||||||
|
component: () => import('@/views/index/supplierTimePhoneEdit'),
|
||||||
|
meta:{
|
||||||
|
title: '编辑时段电话',
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/toDoList',
|
path: '/toDoList',
|
||||||
name: 'toDoList',
|
name: 'toDoList',
|
||||||
@@ -156,6 +180,22 @@ const routes = [
|
|||||||
title: '查看发票'
|
title: '查看发票'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/damagePhotos',
|
||||||
|
name: 'damagePhotoList',
|
||||||
|
component: () => import('@/views/damagePhoto/damagePhotoList.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '损伤照片'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/environmentPhotos',
|
||||||
|
name: 'environmentPhotoList',
|
||||||
|
component: () => import('@/views/environmentPhoto/environmentPhotoList.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '环境照片'
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/vehicleManage",
|
path: "/vehicleManage",
|
||||||
name: 'vehicleManage',
|
name: 'vehicleManage',
|
||||||
@@ -329,7 +369,7 @@ const routes = [
|
|||||||
name: 'goToApp',
|
name: 'goToApp',
|
||||||
component:()=>import('@/views/goToApp/goToApp.vue'),
|
component:()=>import('@/views/goToApp/goToApp.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title:'电瓶详情'
|
title:'小板运输司机端'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...kpiRouter,
|
...kpiRouter,
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ service.interceptors.request.use(
|
|||||||
// let token='eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJBTkNIQU5HIiwidXNlcklkIjo0NTY3MSwibmFtZSI6IuWuieeVhSIsInVzZXJOYW1lIjoiQU5DSEFORyIsInN1cHBsaWVySWQiOjExMjgsImlzWmQiOjAsImV4cCI6MTc1NTQyMjUyNX0.xzDZhaANJFnbeViIHJA0SEtOyTv7Ja3rKmXqRKRuFkc'
|
// let token='eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJBTkNIQU5HIiwidXNlcklkIjo0NTY3MSwibmFtZSI6IuWuieeVhSIsInVzZXJOYW1lIjoiQU5DSEFORyIsInN1cHBsaWVySWQiOjExMjgsImlzWmQiOjAsImV4cCI6MTc1NTQyMjUyNX0.xzDZhaANJFnbeViIHJA0SEtOyTv7Ja3rKmXqRKRuFkc'
|
||||||
// let token='eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJTSEhQWEIiLCJ1c2VySWQiOjU0NzI2LCJuYW1lIjoiI-a1i-ivleWwj-eZveeZvSIsInVzZXJOYW1lIjoiU0hIUFhCIiwic3VwcGxpZXJJZCI6MTAwMDE2NSwiZXhwIjoxNzQ0NTEwNzkwfQ.JPk0OA7slYJN3FIi_uhW4Y0CiWRvl6R1dK8MRTbyhD8'
|
// let token='eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJTSEhQWEIiLCJ1c2VySWQiOjU0NzI2LCJuYW1lIjoiI-a1i-ivleWwj-eZveeZvSIsInVzZXJOYW1lIjoiU0hIUFhCIiwic3VwcGxpZXJJZCI6MTAwMDE2NSwiZXhwIjoxNzQ0NTEwNzkwfQ.JPk0OA7slYJN3FIi_uhW4Y0CiWRvl6R1dK8MRTbyhD8'
|
||||||
// if(!(reqUrl=='/supplier/supplierTraining/trainingTask' || reqUrl=='/supplier/supplierTraining/normalList' || reqUrl=='/supplier/supplierTraining/trainingList')){
|
// if(!(reqUrl=='/supplier/supplierTraining/trainingTask' || reqUrl=='/supplier/supplierTraining/normalList' || reqUrl=='/supplier/supplierTraining/trainingList')){
|
||||||
if (reqUrl=='/supplier/supplierTraining/trainingList' && config?.params?.type==1){
|
if ((reqUrl=='/supplier/supplierTraining/trainingList' || reqUrl=='/supplier/supplierTraining/normalList') && config?.params?.type==1){
|
||||||
console.log('司机app使用该请求不挂token')
|
console.log('司机app使用该请求不挂token')
|
||||||
} else {
|
} else {
|
||||||
if(token) {
|
if(token) {
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { getLog } from "@/api/order"
|
||||||
|
|
||||||
|
let _setLogTimer = null
|
||||||
|
|
||||||
export const myMixins = {
|
export const myMixins = {
|
||||||
data() {
|
data() {
|
||||||
@@ -5,6 +8,11 @@ export const myMixins = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setLogHandler(data) {
|
||||||
|
if (_setLogTimer) return
|
||||||
|
_setLogTimer = setTimeout(() => { _setLogTimer = null }, 1000)
|
||||||
|
getLog(data)
|
||||||
|
},
|
||||||
showFun() {
|
showFun() {
|
||||||
if( localStorage.getItem('infoVerify') == 8 || localStorage.getItem('infoVerify') == 12 ) {
|
if( localStorage.getItem('infoVerify') == 8 || localStorage.getItem('infoVerify') == 12 ) {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 校验电话号码
|
||||||
|
* 支持:大陆手机号、大陆固话、400电话、港澳手机号
|
||||||
|
* @param {string} phone
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function isValidPhone(phone) {
|
||||||
|
if (!phone) return false
|
||||||
|
// 大陆手机号
|
||||||
|
const mainlandMobile = /^1[3-9]\d{9}$/
|
||||||
|
// 大陆固话:区号(可选) + 7~8位号码
|
||||||
|
const mainlandLandline = /^(0\d{2,3}-?)\d{7,8}$/
|
||||||
|
// 400电话
|
||||||
|
const phone400 = /^400-?\d{3}-?\d{4}$/
|
||||||
|
// 港澳手机号(8位,首位5/6/8/9)
|
||||||
|
const hkMacaoMobile = /^[5689]\d{7}$/
|
||||||
|
// 港澳固话
|
||||||
|
const hkMacaoLandline = /^(\d{4}-?)?\d{4}$/
|
||||||
|
|
||||||
|
return (
|
||||||
|
mainlandMobile.test(phone) ||
|
||||||
|
mainlandLandline.test(phone) ||
|
||||||
|
phone400.test(phone) ||
|
||||||
|
hkMacaoMobile.test(phone) ||
|
||||||
|
hkMacaoLandline.test(phone)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验时段格式 HH:mm 及起止关系
|
||||||
|
* @param {string} startTime
|
||||||
|
* @param {string} endTime
|
||||||
|
* @returns {{valid: boolean, message?: string}}
|
||||||
|
*/
|
||||||
|
export function validateTimeRange(startTime, endTime) {
|
||||||
|
const timeRegex = /^([0-1]\d|2[0-3]):([0-5]\d)$/
|
||||||
|
if (!startTime || !endTime) {
|
||||||
|
return { valid: false, message: '请填写完整的起止时段' }
|
||||||
|
}
|
||||||
|
if (!timeRegex.test(startTime) || !timeRegex.test(endTime)) {
|
||||||
|
return { valid: false, message: '时段格式不正确,应为 00:00 ~ 23:59' }
|
||||||
|
}
|
||||||
|
const [startH, startM] = startTime.split(':').map(Number)
|
||||||
|
const [endH, endM] = endTime.split(':').map(Number)
|
||||||
|
const start = startH * 60 + startM
|
||||||
|
const end = endH * 60 + endM
|
||||||
|
if (start > end) {
|
||||||
|
return { valid: false, message: '开始时间不得晚于结束时间' }
|
||||||
|
}
|
||||||
|
return { valid: true }
|
||||||
|
}
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wrap damage-photo-wrap">
|
||||||
|
<div class="navBar">
|
||||||
|
<van-nav-bar
|
||||||
|
title="损伤照片"
|
||||||
|
left-arrow
|
||||||
|
left-arrow-color="#FFFFFF"
|
||||||
|
:border="false"
|
||||||
|
:fixed="true"
|
||||||
|
:safe-area-inset-top="true"
|
||||||
|
@click-left="h5GoBack"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-wrap" v-if="loaded">
|
||||||
|
<!-- 案件信息 -->
|
||||||
|
<div class="info-card">
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">案件号</span>
|
||||||
|
<span class="info-value">{{ info.caseNo || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">车辆标识</span>
|
||||||
|
<span class="info-value">{{ info.vehicleIdentity || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">起止路线</span>
|
||||||
|
<span class="info-value">{{ info.route || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 照片列表 -->
|
||||||
|
<div class="photo-list" v-if="photoList.length">
|
||||||
|
<div class="photo-card" v-for="(item, index) in photoList" :key="index">
|
||||||
|
<div class="photo-title">
|
||||||
|
<span>{{ item.title || '-' }}</span>
|
||||||
|
<span
|
||||||
|
v-if="item.replaced || (item.historyList && item.historyList.length)"
|
||||||
|
class="replaced-tag"
|
||||||
|
@click="showHistory(item)"
|
||||||
|
>被替换</span>
|
||||||
|
</div>
|
||||||
|
<div class="photo-image" @click="previewCurrent(item.currentUrl)">
|
||||||
|
<van-image
|
||||||
|
:src="item.currentUrl"
|
||||||
|
fit="cover"
|
||||||
|
width="100%"
|
||||||
|
height="160"
|
||||||
|
:error-icon="defaultImg"
|
||||||
|
error-icon-size="48"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 空态 -->
|
||||||
|
<div class="empty-state" v-else>
|
||||||
|
<van-empty description="暂无损伤照片" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 历史照片弹框 -->
|
||||||
|
<van-popup
|
||||||
|
v-model="historyShow"
|
||||||
|
position="bottom"
|
||||||
|
round
|
||||||
|
closeable
|
||||||
|
:style="{ height: '70%' }"
|
||||||
|
@closed="currentHistory = []"
|
||||||
|
>
|
||||||
|
<div class="history-popup">
|
||||||
|
<div class="history-title">历史照片</div>
|
||||||
|
<div class="history-list" v-if="currentHistory.length">
|
||||||
|
<div
|
||||||
|
class="history-item"
|
||||||
|
v-for="(item, index) in currentHistory"
|
||||||
|
:key="index"
|
||||||
|
@click="previewHistory(item.url)"
|
||||||
|
>
|
||||||
|
<van-image
|
||||||
|
:src="item.url"
|
||||||
|
fit="cover"
|
||||||
|
width="100%"
|
||||||
|
height="120"
|
||||||
|
:error-icon="defaultImg"
|
||||||
|
error-icon-size="48"
|
||||||
|
/>
|
||||||
|
<div class="history-time" v-if="item.replaceTime">替换时间:{{ item.replaceTime }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="history-empty" v-else>
|
||||||
|
<van-empty description="暂无历史照片" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-popup>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getDamagePhotos } from '@/api/damagePhoto'
|
||||||
|
import { myMixins } from '@/utils/myMixins'
|
||||||
|
import { ImagePreview, Toast } from 'vant'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'damagePhotoList',
|
||||||
|
mixins: [myMixins],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loaded: false,
|
||||||
|
info: {
|
||||||
|
caseNo: '',
|
||||||
|
vehicleIdentity: '',
|
||||||
|
route: ''
|
||||||
|
},
|
||||||
|
photoList: [],
|
||||||
|
historyShow: false,
|
||||||
|
currentHistory: [],
|
||||||
|
defaultImg: require('@/assets/empty.png')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
await this.fetchDamagePhotos()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchDamagePhotos() {
|
||||||
|
const userOrderId = this.$route.query.userOrderId
|
||||||
|
const taskOrderId = this.$route.query.taskOrderId
|
||||||
|
if (!userOrderId || !taskOrderId) {
|
||||||
|
Toast('缺少必要参数')
|
||||||
|
this.loaded = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Toast.loading({
|
||||||
|
message: '加载中...',
|
||||||
|
forbidClick: true,
|
||||||
|
duration: 0
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const res = await getDamagePhotos({
|
||||||
|
userOrderId: Number(userOrderId),
|
||||||
|
taskOrderId: Number(taskOrderId)
|
||||||
|
})
|
||||||
|
this.info = {
|
||||||
|
caseNo: res.data?.caseNo || '',
|
||||||
|
vehicleIdentity: res.data?.vehicleIdentity || '',
|
||||||
|
route: res.data?.route || ''
|
||||||
|
}
|
||||||
|
this.photoList = Array.isArray(res.data?.photos) ? res.data.photos : []
|
||||||
|
} finally {
|
||||||
|
Toast.clear()
|
||||||
|
this.loaded = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
previewCurrent(url) {
|
||||||
|
if (!url) return
|
||||||
|
ImagePreview({
|
||||||
|
images: [url],
|
||||||
|
startPosition: 0,
|
||||||
|
closeable: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
showHistory(item) {
|
||||||
|
this.currentHistory = Array.isArray(item.historyList) ? item.historyList : []
|
||||||
|
this.historyShow = true
|
||||||
|
},
|
||||||
|
previewHistory(url) {
|
||||||
|
if (!url) return
|
||||||
|
ImagePreview({
|
||||||
|
images: [url],
|
||||||
|
startPosition: 0,
|
||||||
|
closeable: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "@/styles/mixin.scss";
|
||||||
|
@import "@/styles/common.scss";
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
@include wh(100%, 100%);
|
||||||
|
background-color: #F4F5F7;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navBar {
|
||||||
|
height: 46px;
|
||||||
|
::v-deep .van-nav-bar--fixed {
|
||||||
|
z-index: 9999 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrap {
|
||||||
|
padding: 10px 15px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
color: rgba(0, 0, 0, 0.5);
|
||||||
|
width: 70px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
color: #323643;
|
||||||
|
flex: 1;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-list {
|
||||||
|
.photo-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 12px 15px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.photo-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #323643;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.replaced-tag {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #ee0a24;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-image {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-popup {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.history-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #323643;
|
||||||
|
text-align: center;
|
||||||
|
padding: 15px 0;
|
||||||
|
border-bottom: 1px solid #E9E9EA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
.history-item {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.history-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(0, 0, 0, 0.5);
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-empty {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 确保图片预览及其遮罩层级高于底部弹框 */
|
||||||
|
.van-image-preview {
|
||||||
|
z-index: 10000000003 !important;
|
||||||
|
}
|
||||||
|
.van-overlay.van-image-preview__overlay {
|
||||||
|
z-index: 10000000002 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -154,7 +154,7 @@ export default {
|
|||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
},
|
},
|
||||||
initShow(){
|
initShow(){
|
||||||
this.keyword= '',
|
this.keyword= ''
|
||||||
this.getNormalList()
|
this.getNormalList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wrap environment-photo-wrap">
|
||||||
|
<div v-if="loaded && photoList.length === 0" class="empty-tip">暂无环境照片</div>
|
||||||
|
<div v-for="(item, index) in photoList" :key="index" class="photo-item">
|
||||||
|
<div class="media-wrap">
|
||||||
|
<video
|
||||||
|
v-if="item.tag && item.tag.includes('video')"
|
||||||
|
:src="item.imageUrl"
|
||||||
|
controls
|
||||||
|
playsinline
|
||||||
|
class="media"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
v-else-if="item.tag && item.tag.includes('img')"
|
||||||
|
:src="item.imageUrl"
|
||||||
|
class="media"
|
||||||
|
@click="previewImage(index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p v-if="item.title" class="photo-name">{{ item.title }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Toast, ImagePreview } from 'vant'
|
||||||
|
import { getBeforeDispatchImage } from '@/api/environmentPhoto'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'EnvironmentPhotoList',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
photoList: [],
|
||||||
|
loaded: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
await this.fetchPhotos()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchPhotos() {
|
||||||
|
const userOrderId = this.$route.query.userOrderId
|
||||||
|
if (!userOrderId) {
|
||||||
|
Toast('缺少必要参数')
|
||||||
|
this.loaded = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Toast.loading({
|
||||||
|
message: '加载中...',
|
||||||
|
forbidClick: true,
|
||||||
|
duration: 0
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const res = await getBeforeDispatchImage({ userOrderId: Number(userOrderId) })
|
||||||
|
this.photoList = Array.isArray(res.data) ? res.data : []
|
||||||
|
} finally {
|
||||||
|
Toast.clear()
|
||||||
|
this.loaded = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
previewImage(index) {
|
||||||
|
const urls = this.photoList
|
||||||
|
.filter(item => item.tag && item.tag.includes('img'))
|
||||||
|
.map(item => item.imageUrl)
|
||||||
|
ImagePreview({ images: urls, startPosition: index })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>.wrap {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #F4F5F7;
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.environment-photo-wrap {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
.empty-tip {
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 60px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.photo-item {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.media-wrap {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 56.25%;
|
||||||
|
}
|
||||||
|
.photo-item img,
|
||||||
|
.photo-item video {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
.photo-name {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #333;
|
||||||
|
padding: 8px 10px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,22 +3,57 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="goToBtn" @click="goAppHandler">立即打开</div>
|
<div class="goToBtn" @click="goAppHandler">立即打开</div>
|
||||||
<div class="content_tip">
|
<div class="content_tip">
|
||||||
<div class="font_cls">如未安装请点击下载</div>
|
<div class="font_cls">如未安装,请点击 <span @click="downLoadHandler">立即下载</span></div>
|
||||||
<div class="font_cls">如已安装未自动跳转,请 <span @click="downLoadHandler">立即下载</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 微信内置浏览器引导 -->
|
||||||
|
<div v-if="showGuide" class="guide-mask" @click.self="closeGuide">
|
||||||
|
<img class="guide-img" src="@/assets/sucai.png" alt="引导在浏览器打开" />
|
||||||
|
<div class="guide-btn" @click="closeGuide">我知道了</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getNewVersion } from "@/api/order"
|
import { getNewVersion, getLog } from "@/api/order"
|
||||||
export default {
|
export default {
|
||||||
name: "goToApp",
|
name: "goToApp",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showGuide: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
getLog({
|
||||||
|
source: 'App下载页',
|
||||||
|
event: '页面浏览',
|
||||||
|
page: '/h5/supplier/dispatch/goToApp'
|
||||||
|
})
|
||||||
|
this.showGuide = this.isWeixin()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
isWeixin() {
|
||||||
|
const ua = navigator.userAgent.toLowerCase()
|
||||||
|
return ua.indexOf('micromessenger') !== -1
|
||||||
|
},
|
||||||
|
closeGuide() {
|
||||||
|
this.showGuide = false
|
||||||
|
},
|
||||||
goAppHandler() {
|
goAppHandler() {
|
||||||
|
getLog({
|
||||||
|
source: 'App下载页',
|
||||||
|
event: '直接打开',
|
||||||
|
page: '/h5/supplier/dispatch/goToApp'
|
||||||
|
})
|
||||||
window.location.href = "rvdriver://page/pagesLogin/phoneLogin?source=h5Link";
|
window.location.href = "rvdriver://page/pagesLogin/phoneLogin?source=h5Link";
|
||||||
},
|
},
|
||||||
async downLoadHandler() {
|
async downLoadHandler() {
|
||||||
|
getLog({
|
||||||
|
source: 'App下载页',
|
||||||
|
event: '立即下载',
|
||||||
|
page: '/h5/supplier/dispatch/goToApp'
|
||||||
|
});
|
||||||
getNewVersion({
|
getNewVersion({
|
||||||
appType: 5
|
appType: 5
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
@@ -70,4 +105,32 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.guide-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 999;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(0, 0, 0, 0.85);
|
||||||
|
}
|
||||||
|
.guide-img {
|
||||||
|
width: 90%;
|
||||||
|
margin-top: 0;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
.guide-btn {
|
||||||
|
width: 140px;
|
||||||
|
height: 40px;
|
||||||
|
margin-top: 40px;
|
||||||
|
border: 1px solid #FFFFFF;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="top-wrap"></div>
|
||||||
|
<div class="center-info">
|
||||||
|
<div class="wei_code_wrap">
|
||||||
|
<div class="wei_title">请先添加企业微信,保证申请流程正常进行。</div>
|
||||||
|
<div class="wei_code_bg">
|
||||||
|
<img v-if="qrCodeUrl" :src="qrCodeUrl" alt="企业微信二维码" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="btn_wrap">
|
||||||
|
<div class="btn" @click="nextStep">下一步</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import QRCode from 'qrcode'
|
||||||
|
import {getOrCodeInfo, getContactQrCodeResult, saveSupplier} from "@/api/mine"
|
||||||
|
import {myMixins} from "@/utils/myMixins"
|
||||||
|
import {Toast, Dialog} from "vant";
|
||||||
|
export default {
|
||||||
|
name: "addWechat",
|
||||||
|
mixins: [myMixins],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
potentialFlag: '',
|
||||||
|
formPayload: null,
|
||||||
|
qrCodeUrl: '',
|
||||||
|
qrCode: '',
|
||||||
|
configId: '',
|
||||||
|
wechatId: '',
|
||||||
|
clickFlag: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
this.potentialFlag = this.$route.query.potentialFlag || urlParams.get('potentialFlag') || '';
|
||||||
|
const cached = localStorage.getItem('supplierAddForm');
|
||||||
|
if (cached) {
|
||||||
|
this.formPayload = JSON.parse(cached);
|
||||||
|
this.wechatId = this.formPayload.wechatId;
|
||||||
|
this.name = this.formPayload.name;
|
||||||
|
}
|
||||||
|
if (this.name) {
|
||||||
|
await this.QrCodeHandler();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async QrCodeHandler() {
|
||||||
|
await this.QrCodeInfo();
|
||||||
|
await this.getQrCode();
|
||||||
|
},
|
||||||
|
async QrCodeInfo() {
|
||||||
|
let res = await getOrCodeInfo({ name: this.name });
|
||||||
|
this.configId = res?.configId;
|
||||||
|
this.qrCode = res?.qrCode;
|
||||||
|
},
|
||||||
|
async getQrCode() {
|
||||||
|
try {
|
||||||
|
this.qrCodeUrl = await QRCode.toDataURL(this.qrCode, {
|
||||||
|
width: 150,
|
||||||
|
margin: 2,
|
||||||
|
color: { dark: '#000000', light: '#FFFFFF' },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成二维码失败:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async nextStep() {
|
||||||
|
if (!this.clickFlag) return;
|
||||||
|
if(!this.wechatId) {
|
||||||
|
let res = await getContactQrCodeResult({ configId: this.configId, name: this.name });
|
||||||
|
this.wechatId = res?.data?.wechatId;
|
||||||
|
}
|
||||||
|
if (!this.wechatId) {
|
||||||
|
Toast('请先添加企微再继续');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.clickFlag = false;
|
||||||
|
const res = await saveSupplier({
|
||||||
|
...this.formPayload,
|
||||||
|
wechatId: this.wechatId,
|
||||||
|
});
|
||||||
|
if (res?.noToast) {
|
||||||
|
if (res?.code != 0) {
|
||||||
|
Dialog.alert({ title: '提示', message: '该服务商已存在,查看信息' }).then(() => {
|
||||||
|
localStorage.removeItem('supplierAddForm');
|
||||||
|
const target = { name: 'supplierInfo', query: { id: res?.code, potentialFlag: this.potentialFlag } };
|
||||||
|
localStorage.setItem('lastRoute', JSON.stringify(target));
|
||||||
|
this.goPage('supplierInfo', { id: res?.code, potentialFlag: this.potentialFlag });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Toast('该服务商已存在');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('supplierAddForm');
|
||||||
|
const supplierId = res?.data;
|
||||||
|
const potentialFlag = this.potentialFlag;
|
||||||
|
const target = { name: 'supplierAddResult', query: { id: supplierId, potentialFlag } };
|
||||||
|
localStorage.setItem('lastRoute', JSON.stringify(target));
|
||||||
|
Dialog.confirm({
|
||||||
|
title: '提示',
|
||||||
|
message: '添加车辆和驾驶员会加快审核进度,是否去添加?',
|
||||||
|
confirmButtonText: '去添加',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
}).then(() => {
|
||||||
|
localStorage.setItem('lastRoute', JSON.stringify({ name: 'vehicleManage', query: { id: supplierId, potentialFlag } }));
|
||||||
|
this.goPage('vehicleManage', { id: supplierId, potentialFlag });
|
||||||
|
}).catch(() => {
|
||||||
|
localStorage.setItem('lastRoute', JSON.stringify({ name: 'supplierAddResult', query: { id: supplierId, potentialFlag } }));
|
||||||
|
this.goPage('supplierAddResult', { id: supplierId, potentialFlag });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Toast(e?.message || '提交失败');
|
||||||
|
} finally {
|
||||||
|
setTimeout(() => { this.clickFlag = true; }, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "@/styles/mixin.scss";
|
||||||
|
@import "@/styles/common.scss";
|
||||||
|
.wrap {
|
||||||
|
@include wh(100%, 100%);
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: linear-gradient(180deg, #FBFDFE 0%, #F6FBFF 73%, #F0F7FF 100%);
|
||||||
|
backdrop-filter: blur(5.602678571428572px);
|
||||||
|
}
|
||||||
|
.top-wrap {
|
||||||
|
width: 100%;
|
||||||
|
height: 175px;
|
||||||
|
background: linear-gradient(180deg, #DAECFF 0%, rgba(233, 243, 255, 0) 100%);
|
||||||
|
}
|
||||||
|
.center-info {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.wei_code_wrap {
|
||||||
|
margin-top: 10px;
|
||||||
|
.wei_title {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #FF553B;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.wei_code_bg {
|
||||||
|
width: 163px;
|
||||||
|
height: 173px;
|
||||||
|
background-image: url("~@/assets/supplier/weiCodeBg.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
|
margin: 8px auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btn_wrap {
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px 0;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 10px;
|
||||||
|
z-index: 10000 !important;
|
||||||
|
.btn {
|
||||||
|
width: calc(100% - 80px);
|
||||||
|
margin-left: 40px;
|
||||||
|
height: 46px;
|
||||||
|
background: #0E76F4;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 46px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
:border="false"
|
:border="false"
|
||||||
:fixed="true"
|
:fixed="true"
|
||||||
:safe-area-inset-top="true"
|
:safe-area-inset-top="true"
|
||||||
@click-left="h5GoBack"
|
@click-left="back"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="addContentWrap">
|
<div class="addContentWrap">
|
||||||
@@ -92,6 +92,57 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="lineBot" v-if="permissonList?.includes('hasInsuranceAudit')"></div>
|
||||||
|
<div class="itemContent" v-if="permissonList?.includes('hasInsuranceAudit')" style="align-items: center">
|
||||||
|
<div class="titleType" style="width: 60px">
|
||||||
|
<img class="startImg" src="@/assets/start.png" />
|
||||||
|
<span>保费</span>
|
||||||
|
</div>
|
||||||
|
<van-field
|
||||||
|
type="number"
|
||||||
|
class="vanIpt"
|
||||||
|
v-model="liabilityInsuranceAmount"
|
||||||
|
input-align="right"
|
||||||
|
>
|
||||||
|
<template slot="right-icon" >
|
||||||
|
<span style="white-space: nowrap;">元</span>
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
|
</div>
|
||||||
|
<div class="lineBot" v-if="permissonList?.includes('hasInsuranceAudit')"></div>
|
||||||
|
<div class="itemContent" v-if="permissonList?.includes('hasInsuranceAudit')">
|
||||||
|
<div class="titleType">
|
||||||
|
<img class="startImg" src="@/assets/start.png" />
|
||||||
|
<span>保额</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items: center;justify-content: flex-end">
|
||||||
|
<el-select
|
||||||
|
v-model="liabilityInsuranceQuota"
|
||||||
|
value-key="name"
|
||||||
|
class="elSelect"
|
||||||
|
collapse-tags="collapse-tags"
|
||||||
|
placeholder="请选择" style="width: 55%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in liabilityQuotaOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
>
|
||||||
|
</el-option>
|
||||||
|
</el-select><span style="margin-right: 16px;opacity: .5;">万元</span>
|
||||||
|
</div>
|
||||||
|
<!-- <van-field
|
||||||
|
type="number"
|
||||||
|
class="vanIpt"
|
||||||
|
v-model="liabilityInsuranceQuota"
|
||||||
|
input-align="right"
|
||||||
|
>
|
||||||
|
<template slot="right-icon" >
|
||||||
|
<span style="white-space: nowrap;">万元 </span>
|
||||||
|
</template>
|
||||||
|
</van-field>-->
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<common-btn title="保存" @submitClick="submitBtn"/>
|
<common-btn title="保存" @submitClick="submitBtn"/>
|
||||||
<van-calendar v-model="showDatePicker" :min-date="minDate"
|
<van-calendar v-model="showDatePicker" :min-date="minDate"
|
||||||
@@ -104,7 +155,7 @@
|
|||||||
import {Dialog} from "vant";
|
import {Dialog} from "vant";
|
||||||
import {myMixins} from "@/utils/myMixins"
|
import {myMixins} from "@/utils/myMixins"
|
||||||
import {formatDate1} from "@/utils/common"
|
import {formatDate1} from "@/utils/common"
|
||||||
import { uploadImage, updateInsurance, getInfoById} from "@/api/mine"
|
import { uploadImage, updateInsurance, getInfoById,userOperationPermissions} from "@/api/mine"
|
||||||
import CommonBtn from "@/components/commonBtn.vue"
|
import CommonBtn from "@/components/commonBtn.vue"
|
||||||
export default {
|
export default {
|
||||||
name: "vehicleAdd",
|
name: "vehicleAdd",
|
||||||
@@ -115,6 +166,7 @@ export default {
|
|||||||
children: 'children',
|
children: 'children',
|
||||||
label: 'name'
|
label: 'name'
|
||||||
},
|
},
|
||||||
|
liabilityQuotaOptions:[{value:10},{value:20},{value:30},{value:50},{value:70},{value:80},{value:100},{value:200}],
|
||||||
minDate: new Date(1970, 0, 1), // 设置最小可选日期(1970年1月1日)
|
minDate: new Date(1970, 0, 1), // 设置最小可选日期(1970年1月1日)
|
||||||
maxDate: new Date(2099, 11, 31), // 设置最大可选日期(2099年12月31日)
|
maxDate: new Date(2099, 11, 31), // 设置最大可选日期(2099年12月31日)
|
||||||
showDatePicker: false,
|
showDatePicker: false,
|
||||||
@@ -130,6 +182,9 @@ export default {
|
|||||||
insurancePicturePhoto: '', // 保单照片
|
insurancePicturePhoto: '', // 保单照片
|
||||||
isMultiple: false, // 是否支持多选
|
isMultiple: false, // 是否支持多选
|
||||||
insuranceCorp: '',
|
insuranceCorp: '',
|
||||||
|
liabilityInsuranceAmount:'',
|
||||||
|
liabilityInsuranceQuota:'',
|
||||||
|
permissonList:[],
|
||||||
insuranceOptions: [{
|
insuranceOptions: [{
|
||||||
name: '太平洋',
|
name: '太平洋',
|
||||||
value: 1
|
value: 1
|
||||||
@@ -183,11 +238,24 @@ export default {
|
|||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.id=this.$route.params?.id
|
this.id=this.$route.params?.id
|
||||||
|
await this.getPermissions()
|
||||||
if( this.id){
|
if( this.id){
|
||||||
// await this.vehicleInfo()
|
// await this.vehicleInfo()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
|
back() {
|
||||||
|
this.$router.push({
|
||||||
|
name:'vehicleAdd',
|
||||||
|
params:{
|
||||||
|
id: this.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async getPermissions(){
|
||||||
|
let res = await userOperationPermissions();
|
||||||
|
this.permissonList = res.data
|
||||||
|
},
|
||||||
async vehicleInfo(){
|
async vehicleInfo(){
|
||||||
let res= await getInfoById({
|
let res= await getInfoById({
|
||||||
vehicleId:this.id
|
vehicleId:this.id
|
||||||
@@ -259,6 +327,14 @@ export default {
|
|||||||
this.$toast('保单有效期不能为空')
|
this.$toast('保单有效期不能为空')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if(this.hasLiabilityInsurance == 1 && this.permissonList?.includes('hasInsuranceAudit') && !this.liabilityInsuranceAmount && this.liabilityInsuranceAmount!=0){
|
||||||
|
this.$toast('保费不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(this.hasLiabilityInsurance == 1 && !this.liabilityInsuranceQuota && this.permissonList?.includes('hasInsuranceAudit')){
|
||||||
|
this.$toast('保额不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
let timeObj;
|
let timeObj;
|
||||||
if(this.dateVal) {
|
if(this.dateVal) {
|
||||||
timeObj = this.formatDateTimeRange(this.dateVal)
|
timeObj = this.formatDateTimeRange(this.dateVal)
|
||||||
@@ -271,6 +347,8 @@ export default {
|
|||||||
insuranceCorp: this.hasLiabilityInsurance == 1 ? this.insuranceCorp : '',
|
insuranceCorp: this.hasLiabilityInsurance == 1 ? this.insuranceCorp : '',
|
||||||
liabilityInsuranceStartTime: this.hasLiabilityInsurance == 1 ? (timeObj?.startTime || '' ) : '',
|
liabilityInsuranceStartTime: this.hasLiabilityInsurance == 1 ? (timeObj?.startTime || '' ) : '',
|
||||||
liabilityInsuranceEndTime: this.hasLiabilityInsurance == 1 ? (timeObj?.endTime || '') : '',
|
liabilityInsuranceEndTime: this.hasLiabilityInsurance == 1 ? (timeObj?.endTime || '') : '',
|
||||||
|
liabilityInsuranceQuota:this.liabilityInsuranceQuota || '',
|
||||||
|
liabilityInsuranceAmount:this.liabilityInsuranceAmount || '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
await updateInsurance(params)
|
await updateInsurance(params)
|
||||||
@@ -280,7 +358,8 @@ export default {
|
|||||||
this.$toast('添加成功')
|
this.$toast('添加成功')
|
||||||
}
|
}
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
this.$router.back();
|
// this.$router.back();
|
||||||
|
this.back()
|
||||||
},2000)
|
},2000)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -122,7 +122,7 @@
|
|||||||
<span>司机状态</span>
|
<span>司机状态</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="isJoin">
|
<div class="isJoin">
|
||||||
<van-radio-group v-model="states" @change="isUse" class="joinWrap">
|
<van-radio-group v-model="states" @change="isUse" :disabled="statesDisabled" :class="{ 'disabled-tree': statesDisabled }" class="joinWrap">
|
||||||
<van-radio :name="1" style="margin-right: 26px" class="item">
|
<van-radio :name="1" style="margin-right: 26px" class="item">
|
||||||
启用
|
启用
|
||||||
<img
|
<img
|
||||||
@@ -163,6 +163,7 @@ export default {
|
|||||||
// baseURL:'',
|
// baseURL:'',
|
||||||
// licenseOcrInfo:'',
|
// licenseOcrInfo:'',
|
||||||
id:'',
|
id:'',
|
||||||
|
supplierId: '',
|
||||||
driverName:"",
|
driverName:"",
|
||||||
driverPhone:"",
|
driverPhone:"",
|
||||||
identityCardNumber:"",
|
identityCardNumber:"",
|
||||||
@@ -184,6 +185,7 @@ export default {
|
|||||||
iconList: [],
|
iconList: [],
|
||||||
driverInfo: {},
|
driverInfo: {},
|
||||||
drivingLicenceName: '',
|
drivingLicenceName: '',
|
||||||
|
potentialFlag: '',
|
||||||
idCardAuthority:'',
|
idCardAuthority:'',
|
||||||
idCardValidStartTime:'',
|
idCardValidStartTime:'',
|
||||||
idCardValidEndTime:'',
|
idCardValidEndTime:'',
|
||||||
@@ -204,6 +206,7 @@ export default {
|
|||||||
let token=localStorage.getItem('token')
|
let token=localStorage.getItem('token')
|
||||||
this.authorization.Authorization=token;*/
|
this.authorization.Authorization=token;*/
|
||||||
this.id = this.$route.params?.id;
|
this.id = this.$route.params?.id;
|
||||||
|
this.supplierId = this.$route.params?.supplierId || '';
|
||||||
this.driverName = this.$route.params?.name;
|
this.driverName = this.$route.params?.name;
|
||||||
this.driverPhone = this.$route.params?.phone;
|
this.driverPhone = this.$route.params?.phone;
|
||||||
this.drivingModel = this.$route.params?.drivingModel;
|
this.drivingModel = this.$route.params?.drivingModel;
|
||||||
@@ -220,6 +223,11 @@ export default {
|
|||||||
this.identityCardFront = this.$route.params?.identityCardFront;
|
this.identityCardFront = this.$route.params?.identityCardFront;
|
||||||
this.driverInfo.lssueDate = this.$route.params?.lssueDate;
|
this.driverInfo.lssueDate = this.$route.params?.lssueDate;
|
||||||
this.drivingLicenceName = this.$route.params?.drivingLicenceName;
|
this.drivingLicenceName = this.$route.params?.drivingLicenceName;
|
||||||
|
this.potentialFlag = this.$route.params?.potentialFlag || '';
|
||||||
|
console.log('this.potentialFlagthis.potentialFlag', this.potentialFlag)
|
||||||
|
if(!this.id && this.potentialFlag === '1') {
|
||||||
|
this.states = 0;
|
||||||
|
}
|
||||||
if( this.identityCardFront ) {
|
if( this.identityCardFront ) {
|
||||||
this.identityCardFrontList = [{ url : this.identityCardFront }];
|
this.identityCardFrontList = [{ url : this.identityCardFront }];
|
||||||
}
|
}
|
||||||
@@ -240,7 +248,7 @@ export default {
|
|||||||
const selectedValue = selectElement.value;
|
const selectedValue = selectElement.value;
|
||||||
this.drivingModel=selectedValue
|
this.drivingModel=selectedValue
|
||||||
});
|
});
|
||||||
|
console.log('statesDisabled', this.statesDisabled)
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
isUse(e){
|
isUse(e){
|
||||||
@@ -426,6 +434,7 @@ export default {
|
|||||||
}
|
}
|
||||||
await saveDriver({
|
await saveDriver({
|
||||||
driverId:this.id ? this.id : '',
|
driverId:this.id ? this.id : '',
|
||||||
|
supplierId: this.supplierId || undefined,
|
||||||
driverName:this.driverName ,
|
driverName:this.driverName ,
|
||||||
driverPhone:this.driverPhone,
|
driverPhone:this.driverPhone,
|
||||||
identityCardNumber:this.identityCardNumber,
|
identityCardNumber:this.identityCardNumber,
|
||||||
@@ -453,6 +462,11 @@ export default {
|
|||||||
},2000)
|
},2000)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
statesDisabled() {
|
||||||
|
return !this.id && this.potentialFlag === '1';
|
||||||
|
}
|
||||||
|
},
|
||||||
components:{
|
components:{
|
||||||
TwoCommonBtn,
|
TwoCommonBtn,
|
||||||
CellGroup
|
CellGroup
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
<div class="navBar">
|
<div class="navBar">
|
||||||
<van-nav-bar
|
<van-nav-bar
|
||||||
title="司机管理"
|
title="司机管理"
|
||||||
left-arrow
|
:left-arrow="!supplierId"
|
||||||
left-arrow-color="#FFFFFF"
|
left-arrow-color="#FFFFFF"
|
||||||
:border="false"
|
:border="false"
|
||||||
:fixed="true"
|
:fixed="true"
|
||||||
:safe-area-inset-top="true"
|
:safe-area-inset-top="true"
|
||||||
@click-left="goBack"
|
@click-left="goBack"
|
||||||
>
|
>
|
||||||
<template slot="right" v-if="permissonList.includes('driverAddBtn')">
|
<template slot="right" v-if="permissonList.includes('driverAddBtn') || supplierId">
|
||||||
<div class="rightWrap" @click="addDriver" >
|
<div class="rightWrap" @click="addDriver" >
|
||||||
<img src="@/assets/addImg.png" />
|
<img src="@/assets/addImg.png" />
|
||||||
<span class="addTxt">添加</span>
|
<span class="addTxt">添加</span>
|
||||||
@@ -62,14 +62,14 @@
|
|||||||
finished-text="没有更多了"
|
finished-text="没有更多了"
|
||||||
@load="onLoad"
|
@load="onLoad"
|
||||||
>
|
>
|
||||||
<div class="itemWrap" v-for="(item,index) in driverList" :key="index" @click.stop="updateDriver(item)">
|
<div class="itemWrap" v-for="(item,index) in driverList" :key="index" @click.stop="!isMiniProgram(item) && updateDriver(item)">
|
||||||
<div class="name-status">
|
<div class="name-status">
|
||||||
<div class="namephone">{{ item.driverName }} / {{ item.driverPhone }}
|
<div class="namephone">{{ item.driverName }} / {{ item.driverPhone }}
|
||||||
<span style="margin-left: 10px" :class="item.states?.code == 1 ? 'statusYes' : 'statusNo'">{{ item.states?.label }}</span>
|
<span style="margin-left: 10px" :class="item.states?.code == 1 ? 'statusYes' : 'statusNo'">{{ item.states?.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="twoBtn">
|
<div class="twoBtn">
|
||||||
<!-- 只有启用状态下才展示停用按钮-->
|
<!-- 只有启用状态下才展示停用按钮,小程序来源司机不显示-->
|
||||||
<button v-if="permissonList.includes('driverModifyBtn') && item.states?.code==1" class="del" @click.stop="handleStatus(item)">停用</button>
|
<button v-if="!isMiniProgram(item) && permissonList.includes('driverModifyBtn') && item.states?.code==1" class="del" @click.stop="handleStatus(item)">停用</button>
|
||||||
<!-- <button v-if="permissonList.includes('driverModifyBtn')" class="revise" @click="updateDriver(item)">修改</button>-->
|
<!-- <button v-if="permissonList.includes('driverModifyBtn')" class="revise" @click="updateDriver(item)">修改</button>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
<span class="halfOpci">身份证号:</span>
|
<span class="halfOpci">身份证号:</span>
|
||||||
<span class="allOpci">{{ item.identityCardNumber }}</span>
|
<span class="allOpci">{{ item.identityCardNumber }}</span>
|
||||||
</div>
|
</div>
|
||||||
<van-icon class="arrow_position" v-if="permissonList.includes('driverModifyBtn')" name="arrow" />
|
<van-icon class="arrow_position" v-if="!isMiniProgram(item) && permissonList.includes('driverModifyBtn')" name="arrow" />
|
||||||
<!-- <div>
|
<!-- <div>
|
||||||
<span class="halfOpci">状 态:</span>
|
<span class="halfOpci">状 态:</span>
|
||||||
<span :class="item.states?.code == 1 ? 'statusYes' : 'statusNo'">{{ item.states?.label }}</span>
|
<span :class="item.states?.code == 1 ? 'statusYes' : 'statusNo'">{{ item.states?.label }}</span>
|
||||||
@@ -113,6 +113,9 @@
|
|||||||
</van-pull-refresh>
|
</van-pull-refresh>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="btn_wrap" v-if="supplierId">
|
||||||
|
<div class="btn" @click="nextToResult">下一步</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -131,6 +134,10 @@ export default {
|
|||||||
total:'',
|
total:'',
|
||||||
driverList:[],
|
driverList:[],
|
||||||
states:'',
|
states:'',
|
||||||
|
supplierId: '',
|
||||||
|
routeId: '',
|
||||||
|
initialized: false,
|
||||||
|
potentialFlag: '',
|
||||||
supplierType:'',
|
supplierType:'',
|
||||||
count: 0,
|
count: 0,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
@@ -176,10 +183,24 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
this.supplierType = urlParams.get('supplierType');
|
this.supplierType = urlParams.get('supplierType');
|
||||||
|
const rawId = this.$route.query.id || urlParams.get('id');
|
||||||
|
this.supplierId = rawId ? Number(rawId) : '';
|
||||||
|
const potentialId = this.$route.query.potentialId || urlParams.get('potentialId');
|
||||||
|
this.routeId = potentialId;
|
||||||
|
this.potentialFlag = this.$route.query.potentialFlag || urlParams.get('potentialFlag') || '';
|
||||||
|
if(!rawId) {
|
||||||
this.getPermissions();
|
this.getPermissions();
|
||||||
|
}
|
||||||
// this.getDriverList()
|
// this.getDriverList()
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
|
nextToResult() {
|
||||||
|
localStorage.setItem('lastRoute', JSON.stringify({ name: 'supplierAddResult', query: { id: this.routeId, potentialFlag: this.potentialFlag } }));
|
||||||
|
this.goPage('supplierAddResult', { id: this.routeId, potentialFlag: this.potentialFlag });
|
||||||
|
},
|
||||||
|
isMiniProgram(item) {
|
||||||
|
return item.registerFrom === '小程序' || (item.jobNumber && item.jobNumber.includes('小程序'))
|
||||||
|
},
|
||||||
getClass(id) {
|
getClass(id) {
|
||||||
const ids = String(id)
|
const ids = String(id)
|
||||||
const steps = {
|
const steps = {
|
||||||
@@ -236,6 +257,7 @@ export default {
|
|||||||
}else{
|
}else{
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'driverAdd',
|
name: 'driverAdd',
|
||||||
|
params: { supplierId: this.supplierId || undefined, potentialFlag: this.potentialFlag || undefined }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -247,6 +269,7 @@ export default {
|
|||||||
idCardStatusList: this.idCardStatusList,
|
idCardStatusList: this.idCardStatusList,
|
||||||
driverLicenseStatusList: this.driverLicenseStatusList,
|
driverLicenseStatusList: this.driverLicenseStatusList,
|
||||||
authStatusList: this.authStatusList,
|
authStatusList: this.authStatusList,
|
||||||
|
supplierId: this.supplierId || undefined,
|
||||||
});
|
});
|
||||||
if(res.code == 200){
|
if(res.code == 200){
|
||||||
this.total=res.total
|
this.total=res.total
|
||||||
@@ -517,4 +540,22 @@ export default {
|
|||||||
.flex-between{
|
.flex-between{
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
.btn_wrap {
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px 0;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 10px;
|
||||||
|
z-index: 10000;
|
||||||
|
.btn {
|
||||||
|
width: calc(100% - 80px);
|
||||||
|
margin-left: 40px;
|
||||||
|
height: 46px;
|
||||||
|
background: #0E76F4;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 46px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -153,12 +153,17 @@ export default {
|
|||||||
if (this.notifyList.length >= this.total) {
|
if (this.notifyList.length >= this.total) {
|
||||||
this.finished = true;
|
this.finished = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 未开票不需要分页,直接标记加载完成
|
||||||
|
this.loading = false;
|
||||||
|
this.finished = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async changeTabHandler() {
|
async changeTabHandler() {
|
||||||
this.pageNum = 1;
|
this.pageNum = 1;
|
||||||
this.notifyList = [];
|
this.notifyList = [];
|
||||||
this.checkList = [];
|
this.checkList = [];
|
||||||
|
this.finished = false;
|
||||||
if (this.active == 1) { // 全部, 需要分页
|
if (this.active == 1) { // 全部, 需要分页
|
||||||
this.status = '';
|
this.status = '';
|
||||||
this.dispatchAppSearchInvoiced = '';
|
this.dispatchAppSearchInvoiced = '';
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
|
<div class="error_bar" v-if="errorMsg">{{ errorMsg }}</div>
|
||||||
<div class="top_banner">
|
<div class="top_banner">
|
||||||
<img src="@/assets/supplier/topBg.png" alt="">
|
<img src="@/assets/supplier/topBg.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
@@ -84,8 +85,8 @@
|
|||||||
</el-tree>
|
</el-tree>
|
||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
<van-field :border="true" v-model="form.trailCount" name="trailCount" label="拖车数量" placeholder="请输入" />
|
<van-field :border="true" v-model="form.trailCount" type="digit" class="required" name="trailCount" label="拖车数量" placeholder="请输入" :rules="countVerify" />
|
||||||
<van-field :border="true" v-model="form.minorCount" name="minorCount" label="抢修车数量" placeholder="请输入" />
|
<van-field :border="true" v-model="form.minorCount" type="digit" class="required" name="minorCount" label="抢修车数量" placeholder="请输入" :rules="countVerify" />
|
||||||
<van-field :border="true" class="required" label="服务区域" placeholder="请选择">
|
<van-field :border="true" class="required" label="服务区域" placeholder="请选择">
|
||||||
<template #input>
|
<template #input>
|
||||||
<el-cascader
|
<el-cascader
|
||||||
@@ -99,12 +100,12 @@
|
|||||||
clearable></el-cascader>
|
clearable></el-cascader>
|
||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
<div class="wei_code_wrap" v-if="form.name && !id">
|
<!-- <div class="wei_code_wrap" v-if="form.name && !id">
|
||||||
<div class="wei_title">请先添加企业微信,保证申请流程正常进行。</div>
|
<div class="wei_title">请先添加企业微信,保证申请流程正常进行。</div>
|
||||||
<div class="wei_code_bg">
|
<div class="wei_code_bg">
|
||||||
<img v-if="qrCodeUrl" :src="qrCodeUrl" alt="企业微信二维码" />
|
<img v-if="qrCodeUrl" :src="qrCodeUrl" alt="企业微信二维码" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -134,8 +135,10 @@
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
clickFlag: true,
|
clickFlag: true,
|
||||||
|
errorMsg: '',
|
||||||
phoneVerify: [{ required: true, message: '请输入联系电话' }, { validator: value => { return /^1[3456789]\d{9}$/.test(value) }, message: '联系电话格式不正确' }],
|
phoneVerify: [{ required: true, message: '请输入联系电话' }, { validator: value => { return /^1[3456789]\d{9}$/.test(value) }, message: '联系电话格式不正确' }],
|
||||||
emailVerify: [{ required: true, message: '请输入邮箱' }, { validator: value => { return /[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/.test(value) }, message: '邮箱格式不正确' }],
|
emailVerify: [{ required: true, message: '请输入邮箱' }, { validator: value => { return /[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/.test(value) }, message: '邮箱格式不正确' }],
|
||||||
|
countVerify: [{ required: true, message: '请输入数量' }, { validator: value => /^\d+$/.test(value) && Number(value) >= 0, message: '请输入大于等于0的整数' }],
|
||||||
id:'', //车辆Id
|
id:'', //车辆Id
|
||||||
idBackPhotoList: [],
|
idBackPhotoList: [],
|
||||||
idBackPhoto: '',
|
idBackPhoto: '',
|
||||||
@@ -156,6 +159,7 @@
|
|||||||
minorCount: '',
|
minorCount: '',
|
||||||
serviceAreaCode: [],
|
serviceAreaCode: [],
|
||||||
linkEmail:'',
|
linkEmail:'',
|
||||||
|
person:'',
|
||||||
accountInfoDTO:{
|
accountInfoDTO:{
|
||||||
dutyParagraph:'',
|
dutyParagraph:'',
|
||||||
accountNumber:'',
|
accountNumber:'',
|
||||||
@@ -199,7 +203,14 @@
|
|||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const lastRoute = localStorage.getItem('lastRoute');
|
||||||
this.id = this.$route.query.id || urlParams.get('id');
|
this.id = this.$route.query.id || urlParams.get('id');
|
||||||
|
if (lastRoute && !this.id) {
|
||||||
|
const { name, query } = JSON.parse(lastRoute);
|
||||||
|
this.$router.replace({ name, query });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await this.getAreaTree();
|
await this.getAreaTree();
|
||||||
if(this.id) {
|
if(this.id) {
|
||||||
await this.getInfoHandler();
|
await this.getInfoHandler();
|
||||||
@@ -218,7 +229,6 @@
|
|||||||
});
|
});
|
||||||
this.configId = res?.configId;
|
this.configId = res?.configId;
|
||||||
this.qrCode = res?.qrCode
|
this.qrCode = res?.qrCode
|
||||||
console.log('idid', res);
|
|
||||||
},
|
},
|
||||||
async getInfoHandler() {
|
async getInfoHandler() {
|
||||||
let res = await getSupplierInfo({
|
let res = await getSupplierInfo({
|
||||||
@@ -230,7 +240,11 @@
|
|||||||
this.idBackPhoto = _data?.idCardBackUrl;
|
this.idBackPhoto = _data?.idCardBackUrl;
|
||||||
this.companyPhoto = _data?.businessLicense;
|
this.companyPhoto = _data?.businessLicense;
|
||||||
this.licensePhoto = _data?.accountUrl;
|
this.licensePhoto = _data?.accountUrl;
|
||||||
this.wechatId = _data?.wechatId
|
this.wechatId = _data?.wechatId;
|
||||||
|
this.form.person = _data.legalName;
|
||||||
|
if( _data.failReason ) {
|
||||||
|
this.showError(_data.failReason)
|
||||||
|
}
|
||||||
if(this.idFrontPhoto) {
|
if(this.idFrontPhoto) {
|
||||||
this.idFrontPhotoList = [{ url : this.idFrontPhoto }];
|
this.idFrontPhotoList = [{ url : this.idFrontPhoto }];
|
||||||
}
|
}
|
||||||
@@ -248,46 +262,66 @@
|
|||||||
}
|
}
|
||||||
if(_data?.serviceAreaCode) {
|
if(_data?.serviceAreaCode) {
|
||||||
this.$set(this.form, 'serviceAreaCode', _data?.serviceAreaCode.split(',').map(item => Number(item)));
|
this.$set(this.form, 'serviceAreaCode', _data?.serviceAreaCode.split(',').map(item => Number(item)));
|
||||||
// this.form.serviceAreaCode = _data?.serviceAreaCode.split(',').map(item => Number(item));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
showError(msg) {
|
||||||
|
this.errorMsg = msg;
|
||||||
|
},
|
||||||
async applyAdd() {
|
async applyAdd() {
|
||||||
|
this.errorMsg = '';
|
||||||
if( !this.idFrontPhoto ) {
|
if( !this.idFrontPhoto ) {
|
||||||
this.$toast('法人身份证人像页未上传')
|
this.showError('法人身份证人像页未上传')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if( !this.idBackPhoto ) {
|
if( !this.idBackPhoto ) {
|
||||||
this.$toast('法人身份证国徽页未上传')
|
this.showError('法人身份证国徽页未上传')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if( !this.companyPhoto ) {
|
if( !this.companyPhoto ) {
|
||||||
this.$toast('营业执照未上传')
|
this.showError('营业执照未上传')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(this.form.person != this.form.legalName){
|
||||||
|
this.showError('身份证法人姓名和营业执照法人不一致')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if( !this.licensePhoto ) {
|
||||||
|
this.showError('开户许可证未上传')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if( !(this.$refs.tree.getCheckedKeys().length > 0) ) {
|
||||||
|
this.showError('服务能力不能为空')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if( !(this.form.serviceAreaCode.length > 0) ) {
|
if( !(this.form.serviceAreaCode.length > 0) ) {
|
||||||
this.$toast('服务区域不能为空')
|
this.showError('服务区域不能为空')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// this.wechatId='wmOTNXBwAABrvKkE_Fh8ZN8Xm2S9v2wQ'
|
|
||||||
if(!this.wechatId) {
|
|
||||||
await this.QrCodeResult();
|
|
||||||
if( !this.wechatId ) {
|
|
||||||
this.$toast('请先添加企微再申请')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( this.$refs.tree.getCheckedKeys().length > 0 ) {
|
if( this.$refs.tree.getCheckedKeys().length > 0 ) {
|
||||||
await this.saveHandler()
|
let _node = this.$refs.areaCascader.getCheckedNodes();
|
||||||
} else {
|
let checkArr = [];
|
||||||
this.$toast('服务能力不能为空')
|
_node.map(item => { checkArr.push(item.data.id) });
|
||||||
return
|
const payload = {
|
||||||
|
id: this.id,
|
||||||
|
...this.form,
|
||||||
|
accountInfoJson: JSON.stringify(this.form.accountInfoDTO),
|
||||||
|
accountInfoDTO: {},
|
||||||
|
idCardFrontUrl: this.idFrontPhoto,
|
||||||
|
idCardBackUrl: this.idBackPhoto,
|
||||||
|
businessLicense: this.companyPhoto,
|
||||||
|
accountUrl: this.licensePhoto,
|
||||||
|
service: this.$refs.tree.getCheckedKeys().join(','),
|
||||||
|
serviceAreaCode: checkArr.join(','),
|
||||||
|
wechatId: this.wechatId,
|
||||||
|
};
|
||||||
|
localStorage.setItem('supplierAddForm', JSON.stringify(payload));
|
||||||
|
this.goPage('addWechat', { potentialFlag: 1 });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async QrCodeResult() { //获取添加企微结果
|
async QrCodeResult() { //获取添加企微结果
|
||||||
let res = await getContactQrCodeResult({
|
let res = await getContactQrCodeResult({
|
||||||
configId: this.configId
|
configId: this.configId
|
||||||
});
|
});
|
||||||
console.log('resres', res)
|
|
||||||
this.wechatId = res?.data?.wechatId
|
this.wechatId = res?.data?.wechatId
|
||||||
},
|
},
|
||||||
async saveHandler() {
|
async saveHandler() {
|
||||||
@@ -310,17 +344,38 @@
|
|||||||
accountUrl:this.licensePhoto,
|
accountUrl:this.licensePhoto,
|
||||||
service: this.$refs.tree.getCheckedKeys().join(',') ,
|
service: this.$refs.tree.getCheckedKeys().join(',') ,
|
||||||
serviceAreaCode: checkArr.join(','),
|
serviceAreaCode: checkArr.join(','),
|
||||||
wechatId: this.wechatId,
|
|
||||||
});
|
});
|
||||||
|
console.log('rererere', res)
|
||||||
|
if( res?.noToast ) { // 服务商已存在
|
||||||
|
this.showError('当前服务商已存在')
|
||||||
|
if( res?.code != 0 ) {
|
||||||
|
Dialog.alert({
|
||||||
|
title: '提示',
|
||||||
|
message: "该服务商已存在,查看信息"
|
||||||
|
}).then(async () => {
|
||||||
|
const target = { name: 'supplierAddResult', query: { id: res?.code } };
|
||||||
|
localStorage.setItem('lastRoute', JSON.stringify(target));
|
||||||
|
this.goPage('supplierAddResult', { id : res?.code })
|
||||||
|
setTimeout(() => {
|
||||||
|
this.clickFlag = true
|
||||||
|
}, 1000)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Dialog.alert({
|
Dialog.alert({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
message: "操作成功"
|
message: "操作成功"
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
|
const target = { name: 'supplierAddResult', query: { id: res?.data } };
|
||||||
|
localStorage.setItem('lastRoute', JSON.stringify(target));
|
||||||
this.goPage('supplierAddResult', { id : res?.data })
|
this.goPage('supplierAddResult', { id : res?.data })
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.clickFlag = true
|
this.clickFlag = true
|
||||||
}, 1000)
|
}, 1000)
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.showError(e)
|
||||||
} finally {
|
} finally {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.clickFlag = true
|
this.clickFlag = true
|
||||||
@@ -394,6 +449,7 @@
|
|||||||
imageUrl: this.idFrontPhoto,
|
imageUrl: this.idFrontPhoto,
|
||||||
cardSide: 'FRONT'
|
cardSide: 'FRONT'
|
||||||
})
|
})
|
||||||
|
// 身份证法人名称
|
||||||
this.form.legalName = res?.data?.name
|
this.form.legalName = res?.data?.name
|
||||||
},
|
},
|
||||||
async companyPhotoHandler(file) {
|
async companyPhotoHandler(file) {
|
||||||
@@ -416,7 +472,9 @@
|
|||||||
this.form.accountInfoDTO.dutyParagraph=res?.data?.regNum
|
this.form.accountInfoDTO.dutyParagraph=res?.data?.regNum
|
||||||
this.form.accountInfoDTO.unitName = res?.data?.name
|
this.form.accountInfoDTO.unitName = res?.data?.name
|
||||||
this.form.accountInfoDTO.companyType=res?.data?.type
|
this.form.accountInfoDTO.companyType=res?.data?.type
|
||||||
await this.QrCodeHandler();
|
// 营业执照法人名称
|
||||||
|
this.form.person = res?.data?.person
|
||||||
|
// await this.QrCodeHandler();
|
||||||
},
|
},
|
||||||
async licensePhotoHandler(file){// 开户许可证
|
async licensePhotoHandler(file){// 开户许可证
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
@@ -430,8 +488,8 @@
|
|||||||
ocrType: 15,
|
ocrType: 15,
|
||||||
imageUrl: this.licensePhoto,
|
imageUrl: this.licensePhoto,
|
||||||
})
|
})
|
||||||
this.form.accountInfoDTO.accountNumber=res?.data?.accountNumber
|
this.form.accountInfoDTO.accountNumber=res?.data?.accountNumber || ''
|
||||||
this.form.accountInfoDTO.accountName = res?.data?.accountBank
|
this.form.accountInfoDTO.accountName = res?.data?.accountBank || ''
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -451,6 +509,17 @@
|
|||||||
padding-bottom: 100px;
|
padding-bottom: 100px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
.error_bar {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
background-color: #e53935;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
.top_banner {
|
.top_banner {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
img {
|
img {
|
||||||
|
|||||||
@@ -19,15 +19,26 @@
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
id: '',
|
id: '',
|
||||||
|
potentialFlag: '',
|
||||||
|
name: '',
|
||||||
|
qrCodeUrl: '',
|
||||||
|
qrCode: '',
|
||||||
|
configId: '',
|
||||||
|
wechatId: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
this.id = this.$route.query.id || urlParams.get('id');
|
this.id = this.$route.query.id || urlParams.get('id');
|
||||||
|
this.potentialFlag = this.$route.query.potentialFlag || urlParams.get('potentialFlag') || '';
|
||||||
|
if (this.id) {
|
||||||
|
localStorage.setItem('lastRoute', JSON.stringify({ name: 'supplierAddResult', query: { id: this.id, potentialFlag: this.potentialFlag } }));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
searchInfo() {
|
async searchInfo() {
|
||||||
this.goPage('supplierInfo', { id : this.id })
|
localStorage.setItem('lastRoute', JSON.stringify({ name: 'supplierInfo', query: { id: this.id, potentialFlag: this.potentialFlag } }));
|
||||||
|
this.goPage('supplierInfo', { id: this.id, potentialFlag: this.potentialFlag })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,4 +91,23 @@
|
|||||||
line-height: 46px;
|
line-height: 46px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wei_code_wrap {
|
||||||
|
margin-top: 10px;
|
||||||
|
.wei_title {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #FF553B;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.wei_code_bg {
|
||||||
|
width: 163px;
|
||||||
|
height: 173px;
|
||||||
|
background-image: url("~@/assets/supplier/weiCodeBg.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
|
margin: 8px auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -87,11 +87,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="info_item">
|
<div class="info_item">
|
||||||
<div class="label">拖车数量:</div>
|
<div class="label">拖车数量:</div>
|
||||||
<div class="content">{{supplierInfo?.trailCount || ''}}</div>
|
<div class="content">{{supplierInfo.trailCount>=0 ? supplierInfo.trailCount : ''}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info_item">
|
<div class="info_item">
|
||||||
<div class="label">抢修车数量:</div>
|
<div class="label">抢修车数量:</div>
|
||||||
<div class="content">{{supplierInfo?.minorCount || ''}}</div>
|
<div class="content">{{supplierInfo.minorCount >= 0 ? supplierInfo.minorCount : ''}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info_item">
|
<div class="info_item">
|
||||||
<div class="label">服务区域:</div>
|
<div class="label">服务区域:</div>
|
||||||
@@ -124,7 +124,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="btn_wrap" style="margin-bottom: 5px">
|
||||||
|
<div class="btn" @click="goAddVehicle">完善信息</div>
|
||||||
|
</div>-->
|
||||||
<div class="btn_wrap" v-if="type == 'show' && supplierInfo?.state == 99 && origin != 'web'">
|
<div class="btn_wrap" v-if="type == 'show' && supplierInfo?.state == 99 && origin != 'web'">
|
||||||
<div class="btn" @click="goModifyPage">修改信息</div>
|
<div class="btn" @click="goModifyPage">修改信息</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -152,6 +154,7 @@
|
|||||||
supplierInfo: {},
|
supplierInfo: {},
|
||||||
type: 'show',
|
type: 'show',
|
||||||
origin: '',
|
origin: '',
|
||||||
|
potentialFlag: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
@@ -159,9 +162,15 @@
|
|||||||
this.id=this.$route.query.id || urlParams.get('id');
|
this.id=this.$route.query.id || urlParams.get('id');
|
||||||
this.type=this.$route.query.type || urlParams.get('type') || 'show';
|
this.type=this.$route.query.type || urlParams.get('type') || 'show';
|
||||||
this.origin = this.$route.query.origin || ''
|
this.origin = this.$route.query.origin || ''
|
||||||
|
this.potentialFlag = this.$route.query.potentialFlag || urlParams.get('potentialFlag') || '';
|
||||||
await this.getInfoHandler();
|
await this.getInfoHandler();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
goAddVehicle() {
|
||||||
|
const supplierId = this.id
|
||||||
|
localStorage.setItem('lastRoute', JSON.stringify({ name: 'vehicleManage', query: { id: supplierId, potentialFlag: this.potentialFlag } }));
|
||||||
|
this.goPage('vehicleManage', { id: supplierId, potentialFlag: this.potentialFlag });
|
||||||
|
},
|
||||||
goModifyPage() {
|
goModifyPage() {
|
||||||
this.goPage('supplierAdd', { id : this.id })
|
this.goPage('supplierAdd', { id : this.id })
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,417 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wrap">
|
||||||
|
<van-nav-bar
|
||||||
|
title="供应商时段电话"
|
||||||
|
left-arrow
|
||||||
|
@click-left="onNavBack"
|
||||||
|
fixed
|
||||||
|
placeholder
|
||||||
|
class="nav-bar"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<!-- 审批状态提示条 -->
|
||||||
|
<div v-if="inApproval" class="status-tip status-tip--pending">{{approvalStateName }}</div>
|
||||||
|
<div v-else-if="rejected" class="status-tip status-tip--danger">
|
||||||
|
<div class="status-tip__title">审批被驳回</div>
|
||||||
|
<div v-if="approvalRemark" class="status-tip__desc">{{ approvalRemark }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 空态 -->
|
||||||
|
<div v-if="!phoneList.length" class="empty-wrap">
|
||||||
|
<div class="empty-text">暂无分时段电话,点击新增时段电话</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 时段卡片列表 -->
|
||||||
|
<div v-else class="card-list">
|
||||||
|
<div v-for="(item, index) in phoneList" :key="index" class="time-card" @click="goEdit(index)">
|
||||||
|
<div class="card-head">
|
||||||
|
<span class="period">{{ item.startTime }} — {{ item.endTime }}</span>
|
||||||
|
<div class="ops" @click.stop v-if="!inApproval">
|
||||||
|
<span class="op-edit" @click="goEdit(index)">编辑</span>
|
||||||
|
<span class="op-del" @click="deleteItem(index)">删除</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div class="phone-rows">
|
||||||
|
<div v-for="phone in visiblePhones(item)" :key="phone.index" class="phone-row">
|
||||||
|
<span class="phone-icon">☎</span>
|
||||||
|
<span class="phone-num">{{ phone.num }}</span>
|
||||||
|
<span v-if="phone.desc" class="tag">{{ phone.desc }}</span>
|
||||||
|
<span v-if="phone.wx" class="tag tag-wx">微:{{ phone.wx }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部操作栏 -->
|
||||||
|
<div class="footer">
|
||||||
|
<button v-if="!inApproval" class="btn btn-add" @click="goAdd">+ 新增时段</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-submit"
|
||||||
|
:disabled="inApproval || !phoneList.length"
|
||||||
|
@click="onSubmitClick"
|
||||||
|
>
|
||||||
|
{{ inApproval ? '审核中' : '提交审批' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getSupplierPhoneList, submitSupplierPhoneApproval } from '@/api/supplierPhone'
|
||||||
|
import { myMixins } from '@/utils/myMixins'
|
||||||
|
import { Dialog, Toast } from 'vant'
|
||||||
|
|
||||||
|
const DRAFT_KEY_PREFIX = 'supplierTimePhoneDraft_'
|
||||||
|
|
||||||
|
function getSupplierIdFromToken() {
|
||||||
|
const token = localStorage.getItem('token')
|
||||||
|
if (!token) return null
|
||||||
|
try {
|
||||||
|
const payload = JSON.parse(atob(token.split('.')[1]))
|
||||||
|
return payload.supplierId || null
|
||||||
|
} catch (e) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDraftKey(supplierId) {
|
||||||
|
return `${DRAFT_KEY_PREFIX}${supplierId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'supplierTimePhone',
|
||||||
|
mixins: [myMixins],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
supplierId: null,
|
||||||
|
supplierName: '',
|
||||||
|
approvalId: null,
|
||||||
|
approvalState: null,
|
||||||
|
approvalStateName: '',
|
||||||
|
inApproval: false,
|
||||||
|
rejected: false,
|
||||||
|
approvalRemark: '',
|
||||||
|
phoneList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
this.supplierId = urlParams.get('supplierId') || this.$route.query.supplierId || getSupplierIdFromToken()
|
||||||
|
if (!this.supplierId) {
|
||||||
|
Toast('未获取到服务商信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loadData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadData() {
|
||||||
|
Toast.loading({ message: '加载中...', forbidClick: true, duration: 0 })
|
||||||
|
try {
|
||||||
|
const result = await getSupplierPhoneList({ supplierId: this.supplierId })
|
||||||
|
const res=result?.data || ''
|
||||||
|
this.supplierName = res?.supplierName || ''
|
||||||
|
this.approvalId = res?.approvalId || null
|
||||||
|
this.approvalState = res?.approvalState || null
|
||||||
|
this.approvalStateName = res?.approvalStateName || ''
|
||||||
|
this.inApproval = !!res?.inApproval
|
||||||
|
this.rejected = !!res?.rejected
|
||||||
|
this.approvalRemark = res?.approvalRemark || ''
|
||||||
|
|
||||||
|
const cached = this.getCachedDraft()
|
||||||
|
if (cached && Array.isArray(cached.phoneList)) {
|
||||||
|
this.phoneList = cached.phoneList
|
||||||
|
} else {
|
||||||
|
this.phoneList = res?.phoneList || []
|
||||||
|
this.saveDraft()
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
} finally {
|
||||||
|
Toast.clear()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
visiblePhones(item) {
|
||||||
|
const list = []
|
||||||
|
for (let i = 1; i <= 3; i++) {
|
||||||
|
const num = item['phone' + i]
|
||||||
|
if (num) {
|
||||||
|
list.push({
|
||||||
|
index: i,
|
||||||
|
num,
|
||||||
|
desc: item['phone' + i + 'Desc'],
|
||||||
|
wx: item['phone' + i + 'WechatId']
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
},
|
||||||
|
getCachedDraft() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(getDraftKey(this.supplierId))
|
||||||
|
return raw ? JSON.parse(raw) : null
|
||||||
|
} catch (e) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveDraft() {
|
||||||
|
const draft = {
|
||||||
|
approvalId: this.approvalId,
|
||||||
|
approvalState: this.approvalState,
|
||||||
|
phoneList: this.phoneList
|
||||||
|
}
|
||||||
|
localStorage.setItem(getDraftKey(this.supplierId), JSON.stringify(draft))
|
||||||
|
},
|
||||||
|
onNavBack() {
|
||||||
|
localStorage.removeItem(getDraftKey(this.supplierId))
|
||||||
|
this.goBack()
|
||||||
|
},
|
||||||
|
goAdd() {
|
||||||
|
this.saveDraft()
|
||||||
|
this.goPage('supplierTimePhoneEdit', { supplierId: this.supplierId, index: -1 })
|
||||||
|
},
|
||||||
|
goEdit(index) {
|
||||||
|
this.saveDraft()
|
||||||
|
this.goPage('supplierTimePhoneEdit', { supplierId: this.supplierId, index })
|
||||||
|
},
|
||||||
|
deleteItem(index) {
|
||||||
|
Dialog.confirm({
|
||||||
|
title: '提示',
|
||||||
|
message: '确定删除该时段电话?'
|
||||||
|
}).then(() => {
|
||||||
|
this.phoneList.splice(index, 1)
|
||||||
|
this.saveDraft()
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
// 将 HH:mm 转换为分钟数,兼容中英文冒号
|
||||||
|
timeToMinutes(timeStr) {
|
||||||
|
if (!timeStr) return null
|
||||||
|
const normalized = String(timeStr).trim().replace(/:/g, ':')
|
||||||
|
const [h, m] = normalized.split(':').map(Number)
|
||||||
|
if (isNaN(h) || isNaN(m)) return null
|
||||||
|
return h * 60 + m
|
||||||
|
},
|
||||||
|
// 校验时间段:重叠、00:00 开始、23:59 结束、遗漏(允许 1 分钟误差)
|
||||||
|
validateTimePeriods(list) {
|
||||||
|
const periods = list.map(item => ({
|
||||||
|
start: this.timeToMinutes(item.startTime),
|
||||||
|
end: this.timeToMinutes(item.endTime)
|
||||||
|
}))
|
||||||
|
if (periods.some(p => p.start === null || p.end === null)) {
|
||||||
|
return '存在格式不正确的时间段'
|
||||||
|
}
|
||||||
|
periods.sort((a, b) => a.start - b.start)
|
||||||
|
if (periods[0].start !== 0) {
|
||||||
|
return '时间段未从 00:00 开始!'
|
||||||
|
}
|
||||||
|
if (periods[periods.length - 1].end !== 23 * 60 + 59) {
|
||||||
|
return '时间段未到 23:59 结束!'
|
||||||
|
}
|
||||||
|
for (let i = 1; i < periods.length; i++) {
|
||||||
|
if (periods[i].start < periods[i - 1].end) {
|
||||||
|
return '时间段重叠'
|
||||||
|
}
|
||||||
|
if (periods[i].start - periods[i - 1].end > 1) {
|
||||||
|
return '时间段有遗漏'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
async onSubmitClick() {
|
||||||
|
if (this.inApproval) return
|
||||||
|
if (!this.phoneList.length) {
|
||||||
|
Toast('请先添加至少一个时段电话')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.phoneList.every(item => item.phone1)) {
|
||||||
|
Toast('每条时段电话1均必填')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const timeError = this.validateTimePeriods(this.phoneList)
|
||||||
|
if (timeError) {
|
||||||
|
Toast(timeError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.loading({ message: '提交中...', forbidClick: true, duration: 0 })
|
||||||
|
try {
|
||||||
|
await submitSupplierPhoneApproval({
|
||||||
|
supplierId: this.supplierId,
|
||||||
|
modifyReason: '',
|
||||||
|
phoneList: this.phoneList
|
||||||
|
})
|
||||||
|
Toast.success('提交成功')
|
||||||
|
localStorage.removeItem(getDraftKey(this.supplierId))
|
||||||
|
this.loadData()
|
||||||
|
} catch (e) {
|
||||||
|
Toast(e?.message || '提交失败,请重试')
|
||||||
|
} finally {
|
||||||
|
Toast.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "@/styles/mixin.scss";
|
||||||
|
@import "@/styles/common.scss";
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
@include wh(100%, 100%);
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #f4f5f7;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .nav-bar {
|
||||||
|
.van-nav-bar {
|
||||||
|
background: #2b6cff;
|
||||||
|
}
|
||||||
|
.van-nav-bar__title,
|
||||||
|
.van-nav-bar__arrow {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tip {
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
&--pending {
|
||||||
|
background: #eef3ff;
|
||||||
|
color: #2b6cff;
|
||||||
|
}
|
||||||
|
&--danger {
|
||||||
|
background: #fff2f0;
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
&__title {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
&__desc {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-wrap {
|
||||||
|
text-align: center;
|
||||||
|
padding: 60px 0;
|
||||||
|
.empty-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-list {
|
||||||
|
.time-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
.card-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.period {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1a1a1a;
|
||||||
|
background: #eef3ff;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.ops {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #2b6cff;
|
||||||
|
span {
|
||||||
|
margin-left: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.op-del {
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.divider {
|
||||||
|
height: 1px;
|
||||||
|
background: #f0f0f0;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.phone-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 5px 0;
|
||||||
|
font-size: 13.5px;
|
||||||
|
}
|
||||||
|
.phone-icon {
|
||||||
|
color: #2b6cff;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.phone-num {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1a1a;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
}
|
||||||
|
.tag {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #f2f3f5;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.tag-wx {
|
||||||
|
background: #e8f8ee;
|
||||||
|
color: #1a8a4a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: #fff;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
.btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 46px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.btn-add {
|
||||||
|
flex: 0 0 44%;
|
||||||
|
background: #fff;
|
||||||
|
color: #2b6cff;
|
||||||
|
border: 1.5px solid #2b6cff;
|
||||||
|
}
|
||||||
|
.btn-submit {
|
||||||
|
background: #2b6cff;
|
||||||
|
color: #fff;
|
||||||
|
&:disabled {
|
||||||
|
background: #b9c8ee;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,393 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wrap">
|
||||||
|
<van-nav-bar
|
||||||
|
:title="isEdit ? '编辑时段电话' : '新增时段电话'"
|
||||||
|
left-arrow
|
||||||
|
@click-left="h5GoBack"
|
||||||
|
fixed
|
||||||
|
placeholder
|
||||||
|
class="nav-bar"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<div class="field">
|
||||||
|
<label class="field-label">时段(开始 — 结束,格式 00:00 ~ 23:59)</label>
|
||||||
|
<div class="period-row">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
readonly
|
||||||
|
:value="form.startTime"
|
||||||
|
placeholder="开始时间"
|
||||||
|
@click="openTimePicker('startTime')"
|
||||||
|
/>
|
||||||
|
<span class="sep">—</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
readonly
|
||||||
|
:value="form.endTime"
|
||||||
|
placeholder="结束时间"
|
||||||
|
@click="openTimePicker('endTime')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="periodErr" class="err-msg">{{ periodErr }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="phone-block" v-for="n in 3" :key="n">
|
||||||
|
<div class="phone-block__head">
|
||||||
|
<span>电话 {{ n }}{{ n === 1 ? '(必填)' : '(选填)' }}</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="form['phone' + n]"
|
||||||
|
:placeholder="'电话号码'"
|
||||||
|
/>
|
||||||
|
<div class="sub-grid">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="form['phone' + n + 'Desc']"
|
||||||
|
placeholder="备注/负责人"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="form['phone' + n + 'WechatId']"
|
||||||
|
placeholder="微信号"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="phoneErr[n - 1]" class="err-msg">{{ phoneErr[n - 1] }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<button class="btn btn-save" @click="save">保存</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<van-popup v-model="showTimePicker" round position="bottom">
|
||||||
|
<van-datetime-picker
|
||||||
|
v-if="showTimePicker"
|
||||||
|
v-model="pickerValue"
|
||||||
|
type="time"
|
||||||
|
:formatter="timeFormatter"
|
||||||
|
@confirm="onTimeConfirm"
|
||||||
|
@cancel="showTimePicker = false"
|
||||||
|
/>
|
||||||
|
</van-popup>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { myMixins } from '@/utils/myMixins'
|
||||||
|
import { isValidPhone, validateTimeRange } from '@/utils/validate'
|
||||||
|
import { Toast } from 'vant'
|
||||||
|
|
||||||
|
const DRAFT_KEY_PREFIX = 'supplierTimePhoneDraft_'
|
||||||
|
|
||||||
|
function getSupplierIdFromToken() {
|
||||||
|
const token = localStorage.getItem('token')
|
||||||
|
if (!token) return null
|
||||||
|
try {
|
||||||
|
const payload = JSON.parse(atob(token.split('.')[1]))
|
||||||
|
return payload.supplierId || null
|
||||||
|
} catch (e) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDraftKey(supplierId) {
|
||||||
|
return `${DRAFT_KEY_PREFIX}${supplierId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function emptyRecord() {
|
||||||
|
return {
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
phone1: '',
|
||||||
|
phone1Desc: '',
|
||||||
|
phone1WechatId: '',
|
||||||
|
phone2: '',
|
||||||
|
phone2Desc: '',
|
||||||
|
phone2WechatId: '',
|
||||||
|
phone3: '',
|
||||||
|
phone3Desc: '',
|
||||||
|
phone3WechatId: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'supplierTimePhoneEdit',
|
||||||
|
mixins: [myMixins],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
supplierId: null,
|
||||||
|
index: -1,
|
||||||
|
form: emptyRecord(),
|
||||||
|
periodErr: '',
|
||||||
|
phoneErr: ['', '', ''],
|
||||||
|
showTimePicker: false,
|
||||||
|
pickerField: 'startTime',
|
||||||
|
pickerValue: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isEdit() {
|
||||||
|
return this.index >= 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.supplierId = this.$route.query.supplierId || getSupplierIdFromToken()
|
||||||
|
this.index = Number(this.$route.query.index)
|
||||||
|
if (Number.isNaN(this.index)) {
|
||||||
|
this.index = -1
|
||||||
|
}
|
||||||
|
if (this.isEdit) {
|
||||||
|
this.loadRecord()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
validate() {
|
||||||
|
this.periodErr = ''
|
||||||
|
this.phoneErr = ['', '', '']
|
||||||
|
let valid = true
|
||||||
|
|
||||||
|
const timeRes = validateTimeRange(this.form.startTime, this.form.endTime)
|
||||||
|
if (!timeRes.valid) {
|
||||||
|
this.periodErr = timeRes.message
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.form.phone1) {
|
||||||
|
this.phoneErr[0] = '电话 1 为必填项'
|
||||||
|
valid = false
|
||||||
|
} else if (!isValidPhone(this.form.phone1)) {
|
||||||
|
this.phoneErr[0] = '电话 1 格式不正确'
|
||||||
|
valid = false
|
||||||
|
} else if (!this.form.phone1Desc) {
|
||||||
|
this.phoneErr[0] = '电话 1 备注为必填项'
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.form.phone2) {
|
||||||
|
if (!isValidPhone(this.form.phone2)) {
|
||||||
|
this.phoneErr[1] = '电话 2 格式不正确'
|
||||||
|
valid = false
|
||||||
|
} else if (!this.form.phone2Desc) {
|
||||||
|
this.phoneErr[1] = '电话 2 备注为必填项'
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.form.phone3) {
|
||||||
|
if (!isValidPhone(this.form.phone3)) {
|
||||||
|
this.phoneErr[2] = '电话 3 格式不正确'
|
||||||
|
valid = false
|
||||||
|
} else if (!this.form.phone3Desc) {
|
||||||
|
this.phoneErr[2] = '电话 3 备注为必填项'
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid
|
||||||
|
},
|
||||||
|
loadRecord() {
|
||||||
|
const key = getDraftKey(this.supplierId)
|
||||||
|
const raw = localStorage.getItem(key)
|
||||||
|
if (!raw) {
|
||||||
|
Toast('未找到时段数据')
|
||||||
|
this.h5GoBack()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const draft = JSON.parse(raw)
|
||||||
|
const list = draft.phoneList || []
|
||||||
|
if (this.index >= 0 && this.index < list.length) {
|
||||||
|
this.form = { ...list[this.index] }
|
||||||
|
} else {
|
||||||
|
Toast('时段数据不存在')
|
||||||
|
this.h5GoBack()
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
Toast('数据读取失败')
|
||||||
|
this.h5GoBack()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
if (!this.validate()) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const key = getDraftKey(this.supplierId)
|
||||||
|
const draft = JSON.parse(localStorage.getItem(key) || '{}')
|
||||||
|
const list = draft.phoneList || []
|
||||||
|
const record = {
|
||||||
|
startTime: this.form.startTime,
|
||||||
|
endTime: this.form.endTime,
|
||||||
|
phone1: this.form.phone1,
|
||||||
|
phone1Desc: this.form.phone1Desc,
|
||||||
|
phone1WechatId: this.form.phone1WechatId,
|
||||||
|
phone2: this.form.phone2,
|
||||||
|
phone2Desc: this.form.phone2Desc,
|
||||||
|
phone2WechatId: this.form.phone2WechatId,
|
||||||
|
phone3: this.form.phone3,
|
||||||
|
phone3Desc: this.form.phone3Desc,
|
||||||
|
phone3WechatId: this.form.phone3WechatId
|
||||||
|
}
|
||||||
|
if (this.isEdit) {
|
||||||
|
list[this.index] = record
|
||||||
|
} else {
|
||||||
|
list.push(record)
|
||||||
|
}
|
||||||
|
draft.phoneList = list
|
||||||
|
localStorage.setItem(key, JSON.stringify(draft))
|
||||||
|
Toast('已保存')
|
||||||
|
this.h5GoBack()
|
||||||
|
} catch (e) {
|
||||||
|
Toast('保存失败')
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openTimePicker(field) {
|
||||||
|
this.pickerField = field
|
||||||
|
this.pickerValue = this.form[field] || '00:00'
|
||||||
|
this.showTimePicker = true
|
||||||
|
},
|
||||||
|
onTimeConfirm(value) {
|
||||||
|
this.form[this.pickerField] = value
|
||||||
|
this.showTimePicker = false
|
||||||
|
},
|
||||||
|
timeFormatter(type, value) {
|
||||||
|
return value.toString().padStart(2, '0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "@/styles/mixin.scss";
|
||||||
|
@import "@/styles/common.scss";
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
@include wh(100%, 100%);
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #f4f5f7;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .nav-bar {
|
||||||
|
.van-nav-bar {
|
||||||
|
background: #2b6cff;
|
||||||
|
}
|
||||||
|
.van-nav-bar__title,
|
||||||
|
.van-nav-bar__arrow {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.field-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
}
|
||||||
|
.period-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.sep {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input[type='text'] {
|
||||||
|
width: 100%;
|
||||||
|
height: 42px;
|
||||||
|
border: 1px solid #dfe1e6;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #1a1a1a;
|
||||||
|
background: #fafbfc;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
&:focus {
|
||||||
|
border-color: #2b6cff;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
&[readonly] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone-block {
|
||||||
|
border: 1px solid #eef0f3;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
background: #fcfcfd;
|
||||||
|
&__head {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #2b6cff;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.sub-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.err-msg {
|
||||||
|
color: #ff4d4f;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: #fff;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 10px;
|
||||||
|
.btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 46px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.btn-save {
|
||||||
|
background: #2b6cff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.btn-cancel {
|
||||||
|
background: #f2f3f5;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.btn-del {
|
||||||
|
background: #fff;
|
||||||
|
color: #ff4d4f;
|
||||||
|
border: 1.5px solid #ffccc7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -287,10 +287,10 @@
|
|||||||
if(!res.data.ocrInvoiceRes.data.sellerName.startsWith("国家税务总局") &&
|
if(!res.data.ocrInvoiceRes.data.sellerName.startsWith("国家税务总局") &&
|
||||||
conList.every(a => {
|
conList.every(a => {
|
||||||
let items = a.itemName.split("*")
|
let items = a.itemName.split("*")
|
||||||
return (!items[1].includes("现代服务") && !items[1].includes("运输服务")) || (!items[2].includes("拖车") && !items[2].includes("救援") && !items[2].includes("施救") && !items[2].includes("清障") && !items[2].includes("道路救援"))
|
return (!items[1].includes("生产生活服务") && !items[1].includes("交通运输服务")) || (!items[2].includes("拖车") && !items[2].includes("救援") && !items[2].includes("施救") && !items[2].includes("清障") && !items[2].includes("道路救援"))
|
||||||
})
|
})
|
||||||
){
|
){
|
||||||
errorStr = errorStr + "分类必须包含:现代服务/运输服务,服务名称必须包含:拖车/救援/施救/清障/道路救援 ;例如《*现代服务*拖车费》";
|
errorStr = errorStr + "分类必须包含:生产生活服务/交通运输服务,服务名称必须包含:拖车/救援/施救/清障/道路救援 ;例如《*现代服务*拖车费》";
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !flag ) {
|
if( !flag ) {
|
||||||
|
|||||||
+496
-29
@@ -8,7 +8,7 @@
|
|||||||
:border="false"
|
:border="false"
|
||||||
:fixed="true"
|
:fixed="true"
|
||||||
:safe-area-inset-top="true"
|
:safe-area-inset-top="true"
|
||||||
@click-left="h5GoBack"
|
@click-left="back"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="addContentWrap">
|
<div class="addContentWrap">
|
||||||
@@ -17,45 +17,54 @@
|
|||||||
<img class="startImg" src="@/assets/start.png" />
|
<img class="startImg" src="@/assets/start.png" />
|
||||||
<span>行驶证首页</span>
|
<span>行驶证首页</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="uploadSlotWrap">
|
||||||
<van-uploader
|
<van-uploader
|
||||||
|
ref="licenseFrontUploader"
|
||||||
v-model="vehicleLicenseFrontList"
|
v-model="vehicleLicenseFrontList"
|
||||||
:after-read="vehicleLicenseFrontHandler"
|
:after-read="vehicleLicenseFrontHandler"
|
||||||
@oversize="onOversize"
|
@delete="vehicleLicenseFrontDelete"
|
||||||
:max-size="5 * 1024 * 1024"
|
|
||||||
max-count="1"
|
max-count="1"
|
||||||
:preview-size="54"
|
:preview-size="54"
|
||||||
accept="image "
|
accept="image/*"
|
||||||
/>
|
/>
|
||||||
|
<div class="slotClickMask" @click="onLicenseSlotClick('front')"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="itemContent">
|
<div class="itemContent">
|
||||||
<div class="titleType">
|
<div class="titleType">
|
||||||
<img class="startImg" src="@/assets/start.png" />
|
<img class="startImg" src="@/assets/start.png" />
|
||||||
<span>行驶证副页</span>
|
<span>行驶证副页</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="uploadSlotWrap">
|
||||||
<van-uploader
|
<van-uploader
|
||||||
|
ref="licenseBackUploader"
|
||||||
v-model="vehicleLicenseBackList"
|
v-model="vehicleLicenseBackList"
|
||||||
:after-read="vehicleLicenseBackHandler"
|
:after-read="vehicleLicenseBackHandler"
|
||||||
@oversize="onOversize"
|
@delete="vehicleLicenseBackDelete"
|
||||||
:max-size="5 * 1024 * 1024"
|
|
||||||
max-count="1"
|
max-count="1"
|
||||||
:preview-size="54"
|
:preview-size="54"
|
||||||
accept="image "
|
accept="image/*"
|
||||||
/>
|
/>
|
||||||
|
<div class="slotClickMask" @click="onLicenseSlotClick('back')"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="itemContent">
|
<div class="itemContent">
|
||||||
<div class="titleType">
|
<div class="titleType">
|
||||||
<img class="startImg" src="@/assets/start.png" />
|
<img class="startImg" src="@/assets/start.png" />
|
||||||
<span>行驶证车辆照片页</span>
|
<span>行驶证车辆照片页</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="uploadSlotWrap">
|
||||||
<van-uploader
|
<van-uploader
|
||||||
|
ref="licenseCarUploader"
|
||||||
v-model="vehicleLicenseCarPhotoList"
|
v-model="vehicleLicenseCarPhotoList"
|
||||||
:after-read="vehicleLicenseCarHandler"
|
:after-read="vehicleLicenseCarHandler"
|
||||||
@oversize="onOversize"
|
@delete="vehicleLicenseCarPhotoDelete"
|
||||||
:max-size="5 * 1024 * 1024"
|
|
||||||
max-count="1"
|
max-count="1"
|
||||||
:preview-size="54"
|
:preview-size="54"
|
||||||
accept="image "
|
accept="image/*"
|
||||||
/>
|
/>
|
||||||
|
<div class="slotClickMask" @click="onLicenseSlotClick('car')"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="itemContent">
|
<div class="itemContent">
|
||||||
<div class="titleType">
|
<div class="titleType">
|
||||||
@@ -65,11 +74,10 @@
|
|||||||
<van-uploader
|
<van-uploader
|
||||||
v-model="vehicleFrontPhotoList"
|
v-model="vehicleFrontPhotoList"
|
||||||
:after-read="vehicleFrontPhotoHandler"
|
:after-read="vehicleFrontPhotoHandler"
|
||||||
@oversize="onOversize"
|
@delete="vehicleFrontPhotoDelete"
|
||||||
:max-size="5 * 1024 * 1024"
|
|
||||||
max-count="1"
|
max-count="1"
|
||||||
:preview-size="54"
|
:preview-size="54"
|
||||||
accept="image "
|
accept="image/*"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -104,6 +112,7 @@
|
|||||||
value-key="name"
|
value-key="name"
|
||||||
class="elSelect"
|
class="elSelect"
|
||||||
collapse-tags="collapse-tags"
|
collapse-tags="collapse-tags"
|
||||||
|
:popper-append-to-body="false"
|
||||||
placeholder="请选择" style="width: 55%"
|
placeholder="请选择" style="width: 55%"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
@@ -197,7 +206,7 @@
|
|||||||
<span>车辆状态</span>
|
<span>车辆状态</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="isJoin">
|
<div class="isJoin">
|
||||||
<van-radio-group v-model="vehicleStatus" :class="{ 'disabled-tree': vehicleInfoDisabled }" :disabled="vehicleInfoDisabled" @change="isVehicleChange" class="joinWrap">
|
<van-radio-group v-model="vehicleStatus" :class="{ 'disabled-tree': vehicleInfoDisabled || vehicleStatusDisabled }" :disabled="vehicleInfoDisabled || vehicleStatusDisabled" @change="isVehicleChange" class="joinWrap">
|
||||||
<van-radio :name="1" style="margin-right: 14px">
|
<van-radio :name="1" style="margin-right: 14px">
|
||||||
启用
|
启用
|
||||||
<img
|
<img
|
||||||
@@ -311,9 +320,83 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="lineBot" v-if="permissonList?.includes('hasInsuranceAudit')"></div>
|
||||||
|
<div class="itemContent" v-if="permissonList?.includes('hasInsuranceAudit')">
|
||||||
|
<div class="titleType">
|
||||||
|
<img class="startImg" src="@/assets/start.png" />
|
||||||
|
<span>保费</span>
|
||||||
|
</div>
|
||||||
|
<van-field
|
||||||
|
type="number"
|
||||||
|
class="vanIpt"
|
||||||
|
v-model="liabilityInsuranceAmount"
|
||||||
|
input-align="right"
|
||||||
|
>
|
||||||
|
<template slot="right-icon" >
|
||||||
|
<span style="white-space: nowrap;">元</span>
|
||||||
</template>
|
</template>
|
||||||
|
</van-field>
|
||||||
|
</div>
|
||||||
|
<div class="lineBot" v-if="permissonList?.includes('hasInsuranceAudit')"></div>
|
||||||
|
<div class="itemContent" v-if="permissonList?.includes('hasInsuranceAudit')">
|
||||||
|
<div class="titleType">
|
||||||
|
<img class="startImg" src="@/assets/start.png" />
|
||||||
|
<span>保额</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items: center;justify-content: flex-end">
|
||||||
|
<el-select
|
||||||
|
v-model="liabilityInsuranceQuota"
|
||||||
|
value-key="name"
|
||||||
|
class="elSelect"
|
||||||
|
collapse-tags="collapse-tags"
|
||||||
|
placeholder="请选择" style="width: 55%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in liabilityQuotaOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
>
|
||||||
|
</el-option>
|
||||||
|
</el-select><span style="margin-right: 16px;opacity: .5;">万元</span>
|
||||||
|
</div>
|
||||||
|
<!-- <van-field
|
||||||
|
type="number"
|
||||||
|
class="vanIpt"
|
||||||
|
v-model="liabilityInsuranceQuota"
|
||||||
|
input-align="right"
|
||||||
|
>
|
||||||
|
<template slot="right-icon" >
|
||||||
|
<span style="white-space: nowrap;">万元 </span>
|
||||||
|
</template>
|
||||||
|
</van-field>-->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="lineBot"></div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div class="titleType">
|
||||||
|
<img class="startImg" src="@/assets/start.png" />
|
||||||
|
<span>保单号(救援)</span>
|
||||||
|
</div>
|
||||||
|
<van-field
|
||||||
|
class="vanIpt"
|
||||||
|
style="width: 72%"
|
||||||
|
v-model="insuranceCode"
|
||||||
|
input-align="right"
|
||||||
|
> </van-field>
|
||||||
|
</div>
|
||||||
|
<div class="lineBot"></div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div class="titleType"> <span>保单号(中道物流)</span> </div>
|
||||||
|
<van-field
|
||||||
|
class="vanIpt"
|
||||||
|
style="width: 65%"
|
||||||
|
v-model="insuranceCodeZd"
|
||||||
|
input-align="right"
|
||||||
|
> </van-field>
|
||||||
|
</div>
|
||||||
|
<div class="lineBot"></div>-->
|
||||||
|
</template>
|
||||||
<div class="itemContent">
|
<div class="itemContent">
|
||||||
<div class="titleType">
|
<div class="titleType">
|
||||||
<img class="startImg" src="@/assets/start.png" />
|
<img class="startImg" src="@/assets/start.png" />
|
||||||
@@ -344,7 +427,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>-->
|
</div>-->
|
||||||
</div>
|
</div>
|
||||||
<two-common-btn class="btn" @cancelClick="h5GoBack" @submitClick="submitBtn" />
|
<two-common-btn class="btn" @cancelClick="back" @submitClick="submitBtn" />
|
||||||
<van-calendar v-model="showDatePicker" :min-date="minDate"
|
<van-calendar v-model="showDatePicker" :min-date="minDate"
|
||||||
:max-date="maxDate" type="range" @confirm="onConfirm" />
|
:max-date="maxDate" type="range" @confirm="onConfirm" />
|
||||||
|
|
||||||
@@ -371,6 +454,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
|
|
||||||
|
<camera-guide
|
||||||
|
:visible="cameraGuideVisible"
|
||||||
|
:type="cameraGuideType"
|
||||||
|
@confirm="onCameraConfirm"
|
||||||
|
@cancel="onCameraCancel"
|
||||||
|
@fallback="onCameraFallback"
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -383,6 +474,7 @@ import {vehicleTypeList,saveVehicle,getInfoById,supplierServiceTree, uploadImage
|
|||||||
userOperationPermissions,saveSupplierApproval} from "@/api/mine"
|
userOperationPermissions,saveSupplierApproval} from "@/api/mine"
|
||||||
import TwoCommonBtn from "@/components/twoBtnCommon.vue"
|
import TwoCommonBtn from "@/components/twoBtnCommon.vue"
|
||||||
import CellGroup from "@/components/cellGroup.vue";
|
import CellGroup from "@/components/cellGroup.vue";
|
||||||
|
import CameraGuide from "@/components/CameraGuide.vue";
|
||||||
export default {
|
export default {
|
||||||
name: "vehicleAdd",
|
name: "vehicleAdd",
|
||||||
mixins:[myMixins],
|
mixins:[myMixins],
|
||||||
@@ -396,7 +488,8 @@ export default {
|
|||||||
maxDate: new Date(2099, 11, 31), // 设置最大可选日期(2099年12月31日)
|
maxDate: new Date(2099, 11, 31), // 设置最大可选日期(2099年12月31日)
|
||||||
checkList: [],
|
checkList: [],
|
||||||
virtualVehicle: '',
|
virtualVehicle: '',
|
||||||
vehicleStatus: '',
|
potentialFlag: new URLSearchParams(window.location.search).get('potentialFlag') || '',
|
||||||
|
vehicleStatus: new URLSearchParams(window.location.search).get('potentialFlag') === '1' ? 2 : '',
|
||||||
showDatePicker: false,
|
showDatePicker: false,
|
||||||
dateVal: '',
|
dateVal: '',
|
||||||
trailerService: '',//拖车服务
|
trailerService: '',//拖车服务
|
||||||
@@ -417,6 +510,8 @@ export default {
|
|||||||
oldSupplierServiceList:[],
|
oldSupplierServiceList:[],
|
||||||
show:false,
|
show:false,
|
||||||
imageUrl: require('@/assets/arr_right.png'),
|
imageUrl: require('@/assets/arr_right.png'),
|
||||||
|
cameraGuideVisible: false, // 拍摄引导弹层显示
|
||||||
|
cameraGuideType: 'front', // 当前拍摄槽位 front/back/car
|
||||||
vehicleLicenseFrontList: [],
|
vehicleLicenseFrontList: [],
|
||||||
vehicleLicenseBackList: [],
|
vehicleLicenseBackList: [],
|
||||||
vehicleLicenseCarPhotoList: [],
|
vehicleLicenseCarPhotoList: [],
|
||||||
@@ -540,6 +635,7 @@ export default {
|
|||||||
name: '其他',
|
name: '其他',
|
||||||
value: 14
|
value: 14
|
||||||
}],
|
}],
|
||||||
|
liabilityQuotaOptions:[{value:10},{value:20},{value:30},{value:50},{value:70},{value:80},{value:100},{value:200}],
|
||||||
approvalForm:{
|
approvalForm:{
|
||||||
type:2,
|
type:2,
|
||||||
supplierId:'',
|
supplierId:'',
|
||||||
@@ -557,7 +653,10 @@ export default {
|
|||||||
vehicleInfoChange:false,
|
vehicleInfoChange:false,
|
||||||
insuranceChange:false,
|
insuranceChange:false,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
liabilityInsuranceAmount:'',
|
||||||
|
liabilityInsuranceQuota:'',
|
||||||
|
insuranceCode:'',
|
||||||
|
insuranceCodeZd:'',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -570,6 +669,9 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
vehicleStatusDisabled() {
|
||||||
|
return !this.id && this.potentialFlag === '1';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch:{
|
watch:{
|
||||||
@@ -608,6 +710,9 @@ export default {
|
|||||||
async mounted() {
|
async mounted() {
|
||||||
this.id=this.$route.params?.id
|
this.id=this.$route.params?.id
|
||||||
this.approvalForm.supplierId=this.$route.params?.supplierId
|
this.approvalForm.supplierId=this.$route.params?.supplierId
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const rawId = this.$route.query.supplierId || urlParams.get('supplierId');
|
||||||
|
this.supplierId = rawId ? Number(rawId) : '';
|
||||||
await this.getSupplierServiceTree();
|
await this.getSupplierServiceTree();
|
||||||
await this.getTypeList();
|
await this.getTypeList();
|
||||||
if( this.id){
|
if( this.id){
|
||||||
@@ -713,33 +818,288 @@ export default {
|
|||||||
onOversize() {
|
onOversize() {
|
||||||
this.$toast(`文件大小不能超过5M`)
|
this.$toast(`文件大小不能超过5M`)
|
||||||
},
|
},
|
||||||
|
// 将 base64 dataURL 转为 Blob,用于 APP WebView 只返回 content 的场景
|
||||||
|
dataURLToBlob(dataURL) {
|
||||||
|
const arr = dataURL.split(',');
|
||||||
|
const mime = arr[0].match(/:(.*?);/)[1];
|
||||||
|
const bstr = atob(arr[1]);
|
||||||
|
let n = bstr.length;
|
||||||
|
const u8arr = new Uint8Array(n);
|
||||||
|
while (n--) {
|
||||||
|
u8arr[n] = bstr.charCodeAt(n);
|
||||||
|
}
|
||||||
|
return new Blob([u8arr], { type: mime });
|
||||||
|
},
|
||||||
|
blobToDataURL(blob) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => resolve(e.target.result);
|
||||||
|
reader.onerror = () => reject(new Error('读取压缩图片失败'));
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
compressImage(file, maxSize = 5 * 1024 * 1024) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const originWidth = img.width;
|
||||||
|
const originHeight = img.height;
|
||||||
|
|
||||||
|
const tryQuality = (quality = 0.9) => {
|
||||||
|
canvas.width = originWidth;
|
||||||
|
canvas.height = originHeight;
|
||||||
|
ctx.clearRect(0, 0, originWidth, originHeight);
|
||||||
|
ctx.drawImage(img, 0, 0, originWidth, originHeight);
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
if (!blob) return reject(new Error('压缩失败'));
|
||||||
|
if (blob.size <= maxSize || quality <= 0.1) {
|
||||||
|
if (blob.size <= maxSize) {
|
||||||
|
resolve(blob);
|
||||||
|
} else {
|
||||||
|
tryResolution();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tryQuality(quality - 0.1);
|
||||||
|
}, 'image/jpeg', quality);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tryResolution = (scale = 0.9, quality = 0.8) => {
|
||||||
|
const width = Math.floor(originWidth * scale);
|
||||||
|
const height = Math.floor(originHeight * scale);
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
ctx.drawImage(img, 0, 0, width, height);
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
if (!blob) return reject(new Error('压缩失败'));
|
||||||
|
if (blob.size <= maxSize || scale <= 0.1) {
|
||||||
|
if (blob.size <= maxSize) {
|
||||||
|
resolve(blob);
|
||||||
|
} else {
|
||||||
|
reject(new Error('超过5M'));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tryResolution(scale - 0.1, quality);
|
||||||
|
}, 'image/jpeg', quality);
|
||||||
|
};
|
||||||
|
|
||||||
|
tryQuality();
|
||||||
|
};
|
||||||
|
img.onerror = () => reject(new Error('图片加载失败'));
|
||||||
|
img.src = e.target.result;
|
||||||
|
};
|
||||||
|
reader.onerror = () => reject(new Error('文件读取失败'));
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async compressFileItem(fileItem, maxSize = 5 * 1024 * 1024) {
|
||||||
|
let rawFile = fileItem?.file;
|
||||||
|
const rawContent = fileItem?.content;
|
||||||
|
|
||||||
|
// 打印可序列化的基础信息,VConsole 中对象会显示为空,但 name/size/type 能看到
|
||||||
|
console.log('rawFile info:', {
|
||||||
|
name: rawFile?.name,
|
||||||
|
size: rawFile?.size,
|
||||||
|
type: rawFile?.type,
|
||||||
|
constructor: rawFile?.constructor?.name
|
||||||
|
});
|
||||||
|
console.log('rawContent prefix:', typeof rawContent === 'string' ? rawContent.slice(0, 60) : rawContent);
|
||||||
|
|
||||||
|
// 部分 APP WebView 返回的文件对象异常或为空,但 content 是 base64,此时用 content 兜底
|
||||||
|
if ((!rawFile || !(rawFile instanceof File) || rawFile.size === 0) &&
|
||||||
|
typeof rawContent === 'string' && rawContent.startsWith('data:')) {
|
||||||
|
try {
|
||||||
|
const blob = this.dataURLToBlob(rawContent);
|
||||||
|
const ext = blob.type === 'image/png' ? 'png' : 'jpg';
|
||||||
|
rawFile = new File([blob], `upload_${Date.now()}.${ext}`, { type: blob.type || 'image/jpeg' });
|
||||||
|
console.log('fallback file from content, size:', rawFile.size);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('content to file error:', e);
|
||||||
|
throw new Error('文件解析失败,请重新上传');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rawFile) {
|
||||||
|
throw new Error('未获取到文件,请重新上传');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawFile.size <= maxSize) return rawFile;
|
||||||
|
const blob = await this.compressImage(rawFile, maxSize);
|
||||||
|
if (blob.size > maxSize) {
|
||||||
|
throw new Error('超过5M');
|
||||||
|
}
|
||||||
|
const fileName = (rawFile.name || 'image').replace(/\.[^.]+$/, '') + '.jpg';
|
||||||
|
const compressedFile = new File([blob], fileName, { type: 'image/jpeg' });
|
||||||
|
fileItem.file = compressedFile;
|
||||||
|
fileItem.size = compressedFile.size;
|
||||||
|
fileItem.content = await this.blobToDataURL(blob);
|
||||||
|
const mb1 = (compressedFile.size / 1024 / 1024).toFixed(2);
|
||||||
|
console.log(`压缩后为:${mb1}M`)
|
||||||
|
return compressedFile;
|
||||||
|
},
|
||||||
|
isCameraSupported() { // 是否支持 getUserMedia(不支持则降级系统选择器)
|
||||||
|
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)
|
||||||
|
},
|
||||||
|
onLicenseSlotClick(type) { // 行驶证三槽位点击拦截:支持相机进引导页,不支持降级原系统选择器
|
||||||
|
if (this.isCameraSupported()) {
|
||||||
|
this.cameraGuideType = type
|
||||||
|
this.cameraGuideVisible = true
|
||||||
|
} else {
|
||||||
|
this.triggerNativeUpload(type)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
triggerNativeUpload(type) { // 主动触发对应 uploader 的原生文件选择
|
||||||
|
const refMap = { front: 'licenseFrontUploader', back: 'licenseBackUploader', car: 'licenseCarUploader' }
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const uploader = this.$refs[refMap[type]]
|
||||||
|
const input = uploader && uploader.$el && uploader.$el.querySelector('.van-uploader__input')
|
||||||
|
if (input) input.click()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCameraConfirm({ blob, dataURL }) { // 拍摄确认:构造 {file, content} 走现有压缩→上传→OCR 链路
|
||||||
|
this.cameraGuideVisible = false
|
||||||
|
const type = this.cameraGuideType
|
||||||
|
const file = new File([blob], `vehicle_license_${type}_${Date.now()}.jpg`, { type: 'image/jpeg' })
|
||||||
|
const fileItem = { file, content: dataURL }
|
||||||
|
const handlerMap = {
|
||||||
|
front: 'vehicleLicenseFrontHandler',
|
||||||
|
back: 'vehicleLicenseBackHandler',
|
||||||
|
car: 'vehicleLicenseCarHandler'
|
||||||
|
}
|
||||||
|
const listMap = {
|
||||||
|
front: 'vehicleLicenseFrontList',
|
||||||
|
back: 'vehicleLicenseBackList',
|
||||||
|
car: 'vehicleLicenseCarPhotoList'
|
||||||
|
}
|
||||||
|
// 写回 uploader 的 v-model 列表,使槽位缩略图回显
|
||||||
|
this[listMap[type]] = [fileItem]
|
||||||
|
this[handlerMap[type]](fileItem)
|
||||||
|
},
|
||||||
|
onCameraCancel() { // 返回放弃
|
||||||
|
this.cameraGuideVisible = false
|
||||||
|
},
|
||||||
|
onCameraFallback() { // 权限拒绝/相机不可用:弹框询问,重试或降级系统选择器
|
||||||
|
this.cameraGuideVisible = false
|
||||||
|
Dialog.confirm({
|
||||||
|
title: '提示',
|
||||||
|
message: '相机权限未开启,无法使用拍摄引导。是否重新尝试开启相机?',
|
||||||
|
confirmButtonText: '重新尝试',
|
||||||
|
cancelButtonText: '从相册选择',
|
||||||
|
confirmButtonColor: '#354D93',
|
||||||
|
}).then(() => {
|
||||||
|
// 重新打开拍摄引导(等弹层关闭状态先刷新,确保 visible 变化被组件感知)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.cameraGuideVisible = true
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
// 降级为系统选择器
|
||||||
|
this.triggerNativeUpload(this.cameraGuideType)
|
||||||
|
})
|
||||||
|
},
|
||||||
async vehicleLicenseFrontHandler(file) { // 上传 行驶证首页
|
async vehicleLicenseFrontHandler(file) { // 上传 行驶证首页
|
||||||
|
console.log('file',file)
|
||||||
|
try {
|
||||||
|
const uploadFile = await this.compressFileItem(file);
|
||||||
|
console.log('uploadFile',uploadFile)
|
||||||
|
if (uploadFile.size > 5 * 1024 * 1024) {
|
||||||
|
this.onOversize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file" , file.file);
|
formData.append("file" , uploadFile);
|
||||||
let res = await uploadImage(formData)
|
let res = await uploadImage(formData)
|
||||||
this.vehicleLicenseFront = res.data;
|
this.vehicleLicenseFront = res.data;
|
||||||
await this.vehicleOcrHandler();
|
await this.vehicleOcrHandler();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('upload compress error:', e);
|
||||||
|
if (e && e.message === '超过5M') {
|
||||||
|
this.onOversize();
|
||||||
|
} else {
|
||||||
|
this.$toast(e && e.message ? e.message : '上传失败,请重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async vehicleLicenseBackHandler(file) { // 上传 行驶证副页
|
async vehicleLicenseBackHandler(file) { // 上传 行驶证副页
|
||||||
|
try {
|
||||||
this.vehicleLicenseBackOcrFlag = true;
|
this.vehicleLicenseBackOcrFlag = true;
|
||||||
|
const uploadFile = await this.compressFileItem(file);
|
||||||
|
if (uploadFile.size > 5 * 1024 * 1024) {
|
||||||
|
this.onOversize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file" , file.file);
|
formData.append("file" , uploadFile);
|
||||||
let res = await uploadImage(formData);
|
let res = await uploadImage(formData);
|
||||||
this.vehicleLicenseBack = res.data;
|
this.vehicleLicenseBack = res.data;
|
||||||
await this.vehicleBackOcrHandler();
|
await this.vehicleBackOcrHandler();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('upload compress error:', e);
|
||||||
|
if (e && e.message === '超过5M') {
|
||||||
|
this.onOversize();
|
||||||
|
} else {
|
||||||
|
this.$toast(e && e.message ? e.message : '上传失败,请重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async vehicleLicenseCarHandler(file) { // 上传 行驶证车辆照片
|
async vehicleLicenseCarHandler(file) { // 上传 行驶证车辆照片
|
||||||
|
try {
|
||||||
|
const uploadFile = await this.compressFileItem(file);
|
||||||
|
if (uploadFile.size > 5 * 1024 * 1024) {
|
||||||
|
this.onOversize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file" , file.file);
|
formData.append("file" , uploadFile);
|
||||||
let res = await uploadImage(formData);
|
let res = await uploadImage(formData);
|
||||||
this.vehicleLicenseCarPhoto = res.data;
|
this.vehicleLicenseCarPhoto = res.data;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('upload compress error:', e);
|
||||||
|
if (e && e.message === '超过5M') {
|
||||||
|
this.onOversize();
|
||||||
|
} else {
|
||||||
|
this.$toast(e && e.message ? e.message : '上传失败,请重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async vehicleFrontPhotoHandler(file) { // 上传 车头照
|
async vehicleFrontPhotoHandler(file) { // 上传 车头照
|
||||||
|
try {
|
||||||
|
const uploadFile = await this.compressFileItem(file);
|
||||||
|
if (uploadFile.size > 5 * 1024 * 1024) {
|
||||||
|
this.onOversize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file" , file.file);
|
formData.append("file" , uploadFile);
|
||||||
let res = await uploadImage(formData);
|
let res = await uploadImage(formData);
|
||||||
this.vehicleFrontPhoto = res.data;
|
this.vehicleFrontPhoto = res.data;
|
||||||
await this.ocrCarFrontHandler()
|
await this.ocrCarFrontHandler()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('upload compress error:', e);
|
||||||
|
if (e && e.message === '超过5M') {
|
||||||
|
this.onOversize();
|
||||||
|
} else {
|
||||||
|
this.$toast(e && e.message ? e.message : '上传失败,请重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
vehicleLicenseFrontDelete() { // 删除 行驶证首页
|
||||||
|
this.vehicleLicenseFront = '';
|
||||||
|
},
|
||||||
|
vehicleLicenseBackDelete() { // 删除 行驶证副页
|
||||||
|
this.vehicleLicenseBack = '';
|
||||||
|
this.vehicleLicenseBackOcrFlag = false;
|
||||||
|
},
|
||||||
|
vehicleLicenseCarPhotoDelete() { // 删除 行驶证车辆照片
|
||||||
|
this.vehicleLicenseCarPhoto = '';
|
||||||
|
},
|
||||||
|
vehicleFrontPhotoDelete() { // 删除 车头照
|
||||||
|
this.vehicleFrontPhoto = '';
|
||||||
},
|
},
|
||||||
clickPreview(){
|
clickPreview(){
|
||||||
console.log(' this.insurancePicturePhoto', this.insurancePicturePhoto)
|
console.log(' this.insurancePicturePhoto', this.insurancePicturePhoto)
|
||||||
@@ -790,7 +1150,7 @@ export default {
|
|||||||
this.vehicleLicenseInfo.address = frontInfo?.address;
|
this.vehicleLicenseInfo.address = frontInfo?.address;
|
||||||
this.vehicleLicenseInfo.useNature = frontInfo?.useCharacter;
|
this.vehicleLicenseInfo.useNature = frontInfo?.useCharacter;
|
||||||
this.vehicleLicenseInfo.model = frontInfo?.model;
|
this.vehicleLicenseInfo.model = frontInfo?.model;
|
||||||
this.vehicleLicenseInfo.vinCode = frontInfo?.vin;
|
this.vehicleLicenseInfo.vinCode = String(frontInfo?.vin || '').toUpperCase().replace(/O/g, '0');
|
||||||
this.vehicleLicenseInfo.engineNumber = frontInfo?.engineNo;
|
this.vehicleLicenseInfo.engineNumber = frontInfo?.engineNo;
|
||||||
this.vehicleLicenseInfo.registrationDate = frontInfo?.registerDate;
|
this.vehicleLicenseInfo.registrationDate = frontInfo?.registerDate;
|
||||||
this.vehicleLicenseInfo.issueDate = frontInfo?.issueDate;
|
this.vehicleLicenseInfo.issueDate = frontInfo?.issueDate;
|
||||||
@@ -870,7 +1230,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getSupplierServiceTree(){
|
async getSupplierServiceTree(){
|
||||||
let res = await supplierServiceTree();
|
let res = await supplierServiceTree({ supplierId: this.supplierId });
|
||||||
this.supplierServiceList=res.data
|
this.supplierServiceList=res.data
|
||||||
this.oldSupplierServiceList=this.supplierServiceList
|
this.oldSupplierServiceList=this.supplierServiceList
|
||||||
},
|
},
|
||||||
@@ -952,7 +1312,7 @@ export default {
|
|||||||
isVehicleChange(e) {
|
isVehicleChange(e) {
|
||||||
this.vehicleStatus=e
|
this.vehicleStatus=e
|
||||||
},
|
},
|
||||||
async submitAuditHandle(){//提交审核
|
/* async submitAuditHandle(){//提交审核
|
||||||
let urls=[]
|
let urls=[]
|
||||||
this.insurancePictureFiles?.forEach(item => urls.push(item.url))
|
this.insurancePictureFiles?.forEach(item => urls.push(item.url))
|
||||||
let time =this.dateVal ? this.formatDateTimeRange(this.dateVal) : ''
|
let time =this.dateVal ? this.formatDateTimeRange(this.dateVal) : ''
|
||||||
@@ -1030,7 +1390,7 @@ export default {
|
|||||||
this.approvalDialogShow=false
|
this.approvalDialogShow=false
|
||||||
await this.submitBtn();
|
await this.submitBtn();
|
||||||
}
|
}
|
||||||
},
|
},*/
|
||||||
async submitApprovalHandle(){//提交审批-走接口
|
async submitApprovalHandle(){//提交审批-走接口
|
||||||
if(this.vehicleInfoChange){
|
if(this.vehicleInfoChange){
|
||||||
if(!(this.selectedOption.length > 0)){
|
if(!(this.selectedOption.length > 0)){
|
||||||
@@ -1133,7 +1493,66 @@ export default {
|
|||||||
this.vehicleInfoChange=false
|
this.vehicleInfoChange=false
|
||||||
this.insuranceChange=false
|
this.insuranceChange=false
|
||||||
},
|
},
|
||||||
|
back() {
|
||||||
|
this.$router.back();
|
||||||
|
/*if( this.supplierId ) {
|
||||||
|
this.$router.back();
|
||||||
|
} else {
|
||||||
|
this.$router.push({
|
||||||
|
name:'vehicleManage',
|
||||||
|
params:{
|
||||||
|
id: this.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}*/
|
||||||
|
|
||||||
|
},
|
||||||
|
validatePlateNumber(value) {
|
||||||
|
if (!value) {
|
||||||
|
return { valid: false, message: '请输入车牌号' }
|
||||||
|
}
|
||||||
|
const provinceList = ['京','津','冀','晋','蒙','辽','吉','黑','沪','苏','浙','皖','闽','赣','鲁','豫','鄂','湘','粤','桂','琼','渝','川','贵','云','藏','陕','甘','青','宁','新']
|
||||||
|
const firstChar = value.charAt(0)
|
||||||
|
if (!provinceList.includes(firstChar)) {
|
||||||
|
return { valid: false, message: '车牌号首位必须是省级简称(京/沪/津/渝/冀/晋/辽/吉/黑/苏/浙/皖/闽/赣/鲁/豫/鄂/湘/粤/琼/川/贵/云/陕/甘/青/蒙/桂/藏/宁/新)' }
|
||||||
|
}
|
||||||
|
if (!/^[\u4e00-\u9fa5][A-Z0-9]+$/.test(value)) {
|
||||||
|
return { valid: false, message: '车牌号只能包含汉字、字母和数字' }
|
||||||
|
}
|
||||||
|
if (![7, 8].includes(value.length)) {
|
||||||
|
return { valid: false, message: `车牌号必须为 7 位或 8 位(当前 ${value.length} 位)` }
|
||||||
|
}
|
||||||
|
return { valid: true, message: '' }
|
||||||
|
},
|
||||||
|
validateVinNo(value) {
|
||||||
|
const normalizedValue = String(value || '').toUpperCase()
|
||||||
|
if (!normalizedValue) {
|
||||||
|
return { valid: false, message: '车架号识别错误,请重新拍照上传。' }
|
||||||
|
}
|
||||||
|
if (!/^[A-NP-Z0-9]{17}$/.test(normalizedValue)) {
|
||||||
|
if (normalizedValue.includes('O')) {
|
||||||
|
return { valid: false, message: '车架号识别错误,请重新拍照上传。' }
|
||||||
|
}
|
||||||
|
if (!/^[A-NP-Z0-9]+$/.test(normalizedValue)) {
|
||||||
|
return { valid: false, message: '车架号识别错误,请重新拍照上传。' }
|
||||||
|
}
|
||||||
|
return { valid: false, message: `车架号识别错误,请重新拍照上传。` }
|
||||||
|
}
|
||||||
|
return { valid: true, message: '' }
|
||||||
|
},
|
||||||
async submitBtn(){
|
async submitBtn(){
|
||||||
|
const plateResult = this.validatePlateNumber(this.carNum)
|
||||||
|
if (!plateResult.valid) {
|
||||||
|
Dialog.alert({ message: plateResult.message })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const vinCode = String(this.vehicleLicenseInfo.vinCode || '')
|
||||||
|
const vinResult = this.validateVinNo(vinCode)
|
||||||
|
if (!vinResult.valid) {
|
||||||
|
Dialog.alert({ message: vinResult.message })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.vehicleLicenseInfo.vinCode = vinCode
|
||||||
if( !this.vehicleLicenseFront ) {
|
if( !this.vehicleLicenseFront ) {
|
||||||
this.$toast('行驶证主页照片不能为空')
|
this.$toast('行驶证主页照片不能为空')
|
||||||
return
|
return
|
||||||
@@ -1194,15 +1613,30 @@ export default {
|
|||||||
this.$toast('保单有效期不能为空')
|
this.$toast('保单有效期不能为空')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if( this.hasLiabilityInsurance == 1){
|
||||||
|
if(this.permissonList.includes('hasInsuranceAudit') && !this.liabilityInsuranceAmount && this.liabilityInsuranceAmount!=0){
|
||||||
|
this.$toast('保费不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(this.permissonList.includes('hasInsuranceAudit') && !this.liabilityInsuranceQuota){
|
||||||
|
this.$toast('保额不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
/* if(!this.insuranceCode){
|
||||||
|
this.$toast('保单号(救援)不能为空')
|
||||||
|
return
|
||||||
|
}*/
|
||||||
|
}
|
||||||
let timeObj;
|
let timeObj;
|
||||||
if(this.dateVal) {
|
if(this.dateVal) {
|
||||||
timeObj = this.formatDateTimeRange(this.dateVal)
|
timeObj = this.formatDateTimeRange(this.dateVal)
|
||||||
}
|
}
|
||||||
this.serviceIds = this.$refs.tree.getCheckedKeys(true)
|
this.serviceIds = this.$refs.tree.getCheckedKeys(true)
|
||||||
console.log('1122',this.vehicleStatus)
|
// console.log('1122',this.vehicleStatus)
|
||||||
// return
|
// return
|
||||||
await saveVehicle({
|
await saveVehicle({
|
||||||
vehicleId:this.id ? this.id : '',
|
vehicleId:this.id ? this.id : '',
|
||||||
|
supplierId: this.supplierId || undefined,
|
||||||
plateNumber:this.carNum ? this.carNum :'',
|
plateNumber:this.carNum ? this.carNum :'',
|
||||||
vehicleType:this.selectedOption?.length>0 ? this.selectedOption.join(',') : '',
|
vehicleType:this.selectedOption?.length>0 ? this.selectedOption.join(',') : '',
|
||||||
// hasPolymerization:this.isJoin,
|
// hasPolymerization:this.isJoin,
|
||||||
@@ -1221,7 +1655,11 @@ export default {
|
|||||||
liabilityInsuranceEndTime: this.hasLiabilityInsurance == 1 ? (timeObj?.endTime || '') : '',
|
liabilityInsuranceEndTime: this.hasLiabilityInsurance == 1 ? (timeObj?.endTime || '') : '',
|
||||||
virtualVehicle: this.virtualVehicle,
|
virtualVehicle: this.virtualVehicle,
|
||||||
canSubmitApproval:true,
|
canSubmitApproval:true,
|
||||||
vehicleStatus:this.vehicleStatus
|
vehicleStatus:this.vehicleStatus,
|
||||||
|
liabilityInsuranceAmount:this.liabilityInsuranceAmount,
|
||||||
|
liabilityInsuranceQuota:this.liabilityInsuranceQuota,
|
||||||
|
insuranceCode:this.insuranceCode,
|
||||||
|
insuranceCodeZd:this.insuranceCodeZd,
|
||||||
})
|
})
|
||||||
if(this.id){
|
if(this.id){
|
||||||
this.$toast('修改成功')
|
this.$toast('修改成功')
|
||||||
@@ -1235,7 +1673,8 @@ export default {
|
|||||||
},
|
},
|
||||||
components:{
|
components:{
|
||||||
CellGroup,
|
CellGroup,
|
||||||
TwoCommonBtn
|
TwoCommonBtn,
|
||||||
|
CameraGuide
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -1249,6 +1688,23 @@ export default {
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "@/styles/mixin.scss";
|
@import "@/styles/mixin.scss";
|
||||||
@import "@/styles/common.scss";
|
@import "@/styles/common.scss";
|
||||||
|
/* 行驶证三槽位透明点击拦截层 */
|
||||||
|
.uploadSlotWrap{
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
.slotClickMask{
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
/* 已上传图片的删除按钮置于拦截层之上,保证可删除重传 */
|
||||||
|
::v-deep .van-uploader__preview-delete{
|
||||||
|
z-index: 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
.mySelect{
|
.mySelect{
|
||||||
@include fontWeightSize(bold,13px);
|
@include fontWeightSize(bold,13px);
|
||||||
color: #323643;
|
color: #323643;
|
||||||
@@ -1261,6 +1717,9 @@ export default {
|
|||||||
}
|
}
|
||||||
.navBar{
|
.navBar{
|
||||||
margin-bottom: 46px;
|
margin-bottom: 46px;
|
||||||
|
::v-deep .van-nav-bar--fixed {
|
||||||
|
z-index: 9999 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.wrap{
|
.wrap{
|
||||||
@include wh(100%,100%);
|
@include wh(100%,100%);
|
||||||
@@ -1427,4 +1886,12 @@ export default {
|
|||||||
transform: translate(-50%, -50%) rotate(360deg);
|
transform: translate(-50%, -50%) rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.vanIpt{
|
||||||
|
width: 80%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.vanIpt1{
|
||||||
|
width: 72%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,15 +3,15 @@
|
|||||||
<div class="navBar">
|
<div class="navBar">
|
||||||
<van-nav-bar
|
<van-nav-bar
|
||||||
title="车辆管理"
|
title="车辆管理"
|
||||||
left-arrow
|
:left-arrow="!supplierId"
|
||||||
left-arrow-color="#FFFFFF"
|
left-arrow-color="#FFFFFF"
|
||||||
:border="false"
|
:border="false"
|
||||||
:fixed="true"
|
:fixed="true"
|
||||||
:safe-area-inset-top="true"
|
:safe-area-inset-top="true"
|
||||||
@click-left="goBack"
|
@click-left="goBack"
|
||||||
>
|
>
|
||||||
<template slot="right" v-if="permissonList.includes('vehicleAddBtn')">
|
<template slot="right" v-if="permissonList.includes('vehicleAddBtn') || supplierId">
|
||||||
<div class="rightWrap" @click="goPage('vehicleAdd')">
|
<div class="rightWrap" @click="goPage('vehicleAdd', supplierId ? { supplierId, potentialFlag } : {})">
|
||||||
<img src="@/assets/addImg.png" />
|
<img src="@/assets/addImg.png" />
|
||||||
<span class="addTxt">添加</span>
|
<span class="addTxt">添加</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,6 +71,7 @@
|
|||||||
<div class="wrap_cls">
|
<div class="wrap_cls">
|
||||||
<van-pull-refresh v-model="isLoading" @refresh="onRefresh">
|
<van-pull-refresh v-model="isLoading" @refresh="onRefresh">
|
||||||
<van-list
|
<van-list
|
||||||
|
v-if="initialized"
|
||||||
v-model="loading"
|
v-model="loading"
|
||||||
:finished="finished"
|
:finished="finished"
|
||||||
finished-text="没有更多了"
|
finished-text="没有更多了"
|
||||||
@@ -114,6 +115,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<van-dialog v-model="show" title="确定删除吗" show-cancel-button @confirm="handleConfirm"></van-dialog>
|
<van-dialog v-model="show" title="确定删除吗" show-cancel-button @confirm="handleConfirm"></van-dialog>
|
||||||
|
<div class="btn_wrap" v-if="supplierId">
|
||||||
|
<div class="btn" @click="nextToDriver">下一步</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
@@ -121,7 +125,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import {Dialog} from "vant";
|
import {Dialog} from "vant";
|
||||||
import { myMixins} from "@/utils/myMixins";
|
import { myMixins} from "@/utils/myMixins";
|
||||||
import {supplierVehicleList, deleteVehicle, userOperationPermissions, enableVehicle} from "@/api/mine"
|
import {supplierVehicleList, deleteVehicle, userOperationPermissions, enableVehicle, supplierSelectById} from "@/api/mine"
|
||||||
export default {
|
export default {
|
||||||
name: "vehicleManage",
|
name: "vehicleManage",
|
||||||
mixins:[myMixins],
|
mixins:[myMixins],
|
||||||
@@ -130,6 +134,9 @@ export default {
|
|||||||
vehicleList:[],
|
vehicleList:[],
|
||||||
show:false,
|
show:false,
|
||||||
id:'',
|
id:'',
|
||||||
|
supplierId: '',
|
||||||
|
routeId: '',
|
||||||
|
initialized: false,
|
||||||
pageNum:1,
|
pageNum:1,
|
||||||
pageSize:10,
|
pageSize:10,
|
||||||
total:'',
|
total:'',
|
||||||
@@ -142,6 +149,7 @@ export default {
|
|||||||
searchVal: '',
|
searchVal: '',
|
||||||
authStates: [],
|
authStates: [],
|
||||||
liabilityInsuranceAuditList: [],
|
liabilityInsuranceAuditList: [],
|
||||||
|
potentialFlag: '',
|
||||||
inputStatusListOptions: [{
|
inputStatusListOptions: [{
|
||||||
name: '行驶证未录入',
|
name: '行驶证未录入',
|
||||||
value: 0
|
value: 0
|
||||||
@@ -189,10 +197,31 @@ export default {
|
|||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
async mounted() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const rawId = this.$route.query.id || urlParams.get('id');
|
||||||
|
this.potentialFlag = this.$route.query.potentialFlag || urlParams.get('potentialFlag') || '';
|
||||||
|
if (rawId) {
|
||||||
|
this.routeId = rawId;
|
||||||
|
await this.getSupplierById(Number(rawId));
|
||||||
|
} else {
|
||||||
|
this.initialized = true;
|
||||||
|
}
|
||||||
|
let _token = localStorage.getItem('token')
|
||||||
|
if( _token ) {
|
||||||
this.getPermissions();
|
this.getPermissions();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
|
nextToDriver() {
|
||||||
|
localStorage.setItem('lastRoute', JSON.stringify({ name: 'driverManage', query: { id: this.supplierId, potentialId: this.routeId, potentialFlag: this.potentialFlag } }));
|
||||||
|
this.goPage('driverManage', { id: this.supplierId, potentialId: this.routeId, potentialFlag: this.potentialFlag });
|
||||||
|
},
|
||||||
|
async getSupplierById(id) {
|
||||||
|
let res = await supplierSelectById({ id });
|
||||||
|
this.supplierId = res?.data?.supplierId || '';
|
||||||
|
this.initialized = true;
|
||||||
|
},
|
||||||
showTip(msg) {
|
showTip(msg) {
|
||||||
Dialog.alert({
|
Dialog.alert({
|
||||||
title: '未通过原因',
|
title: '未通过原因',
|
||||||
@@ -277,6 +306,7 @@ export default {
|
|||||||
inputStatusList: this.inputStatusList,
|
inputStatusList: this.inputStatusList,
|
||||||
authStates: this.authStates,
|
authStates: this.authStates,
|
||||||
liabilityInsuranceAuditList: this.liabilityInsuranceAuditList,
|
liabilityInsuranceAuditList: this.liabilityInsuranceAuditList,
|
||||||
|
supplierId: this.supplierId || undefined,
|
||||||
})
|
})
|
||||||
this.total=result.total
|
this.total=result.total
|
||||||
if(this.pageNum == 1){// 第一页直接赋值
|
if(this.pageNum == 1){// 第一页直接赋值
|
||||||
@@ -585,5 +615,23 @@ export default {
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
.btn_wrap {
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px 0;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 10px;
|
||||||
|
z-index: 10000;
|
||||||
|
.btn {
|
||||||
|
width: calc(100% - 80px);
|
||||||
|
margin-left: 40px;
|
||||||
|
height: 46px;
|
||||||
|
background: #0E76F4;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 46px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,41 +1,57 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<div class="baseInfo common" :style="{'height':orderDetailInfo.contractParentId == 110 ? '300px' : '240px'}">
|
<div class="baseInfo common">
|
||||||
<div class="title">基本信息:</div>
|
<div class="title">基本信息:</div>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
<div class="infoWrap fontColor">
|
<div class="infoWrap fontColor">
|
||||||
<div class="leftTitle">
|
<div class="infoRow">
|
||||||
<div class="leftItem">工单编号:</div>
|
<span class="leftItem">工单编号:</span>
|
||||||
<div class="leftItem">合同:</div>
|
<span class="rightItem" style="display: flex;align-items: center;">
|
||||||
<template v-if="orderDetailInfo.contractParentId == 110">
|
|
||||||
<div class="leftItem">机构名称:</div>
|
|
||||||
<div class="leftItem">销售人员:</div>
|
|
||||||
<div class="leftItem">司机号码:</div>
|
|
||||||
</template>
|
|
||||||
<div class="leftItem">结算方式:</div>
|
|
||||||
<div class="leftItem">客户姓名:</div>
|
|
||||||
<div class="leftItem">客户电话:</div>
|
|
||||||
<div class="leftItem">车牌号:</div>
|
|
||||||
<div class="leftItem">车型品牌:</div>
|
|
||||||
</div>
|
|
||||||
<div class="rightContent">
|
|
||||||
<div class="rightItem" style="display: flex;align-items: center;">
|
|
||||||
<span id="copyText">{{orderDetailInfo.orderCode}}</span>
|
<span id="copyText">{{orderDetailInfo.orderCode}}</span>
|
||||||
<img @click="copyText" src="@/assets/copy.png" style="width: 35px;height: 15px;margin-left: 10px"/>
|
<img @click="copyText" src="@/assets/copy.png" style="width: 35px;height: 15px;margin-left: 10px"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="infoRow" v-if="!([7,9,11,12].includes(Number(queryType)))">
|
||||||
|
<span class="leftItem">合同:</span>
|
||||||
|
<span class="rightItem">{{ orderDetailInfo.contractName }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="rightItem">{{ orderDetailInfo.contractName }}</div>
|
|
||||||
<template v-if="orderDetailInfo.contractParentId == 110">
|
<template v-if="orderDetailInfo.contractParentId == 110">
|
||||||
<div class="rightItem">{{ orderDetailInfo.organizeName }}</div>
|
<div class="infoRow">
|
||||||
<div class="rightItem">{{ orderDetailInfo.saleName }}</div>
|
<span class="leftItem">机构名称:</span>
|
||||||
<div class="rightItem">{{ orderDetailInfo.driverPhone ? orderDetailInfo.driverPhone : ' '}}</div>
|
<span class="rightItem">{{ orderDetailInfo.organizeName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="infoRow">
|
||||||
|
<span class="leftItem">销售人员:</span>
|
||||||
|
<span class="rightItem">{{ orderDetailInfo.saleName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="infoRow">
|
||||||
|
<span class="leftItem">司机号码:</span>
|
||||||
|
<span class="rightItem">{{ orderDetailInfo.driverPhone ? orderDetailInfo.driverPhone : ' '}}</span>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="rightItem">{{ orderDetailInfo.contractSettleType?.label }}</div>
|
<div class="infoRow">
|
||||||
<div class="rightItem">{{ orderDetailInfo.userName }}</div>
|
<span class="leftItem">结算方式:</span>
|
||||||
<div class="rightItem">{{ orderDetailInfo.userPhone }}</div>
|
<span class="rightItem">{{ orderDetailInfo.contractSettleType?.label }}</span>
|
||||||
<div class="rightItem">{{ orderDetailInfo.plateNumber }}</div>
|
</div>
|
||||||
<!-- <div class="rightItem" >理想智动LXA6500SHEVM理想智动LXA6500SHEVM</div>-->
|
<div class="infoRow">
|
||||||
<div class="rightItem" >{{orderDetailInfo.model}}{{ orderDetailInfo.brand }}</div>
|
<span class="leftItem">客户姓名:</span>
|
||||||
|
<span class="rightItem">{{ [7,9,11,12].includes(Number(queryType)) ? maskName(orderDetailInfo.userName) : orderDetailInfo.userName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="infoRow">
|
||||||
|
<span class="leftItem">客户电话:</span>
|
||||||
|
<span class="rightItem">{{ maskPhone(orderDetailInfo.userPhone) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="infoRow">
|
||||||
|
<span class="leftItem">车牌号:</span>
|
||||||
|
<span class="rightItem">{{ [7,9,11,12].includes(Number(queryType)) ? maskPlate(orderDetailInfo.plateNumber) : orderDetailInfo.plateNumber }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="infoRow">
|
||||||
|
<span class="leftItem">车型品牌:</span>
|
||||||
|
<span class="rightItem">{{orderDetailInfo.model}}{{ orderDetailInfo.brand }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="infoRow" v-if="!([7,9,11,12].includes(Number(queryType)))">
|
||||||
|
<span class="leftItem">车架号:</span>
|
||||||
|
<span class="rightItem">{{orderDetailInfo.vinNo}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,13 +90,38 @@
|
|||||||
<div class="item">
|
<div class="item">
|
||||||
<span class="leftTitle fontColor">服务师傅:</span><span class="rightContent">{{ orderDetailInfo.driverName }} {{orderDetailInfo.drivePhone ? '/' : ''}} {{orderDetailInfo.drivePhone }}
|
<span class="leftTitle fontColor">服务师傅:</span><span class="rightContent">{{ orderDetailInfo.driverName }} {{orderDetailInfo.drivePhone ? '/' : ''}} {{orderDetailInfo.drivePhone }}
|
||||||
<span v-if="queryType == 5" class="driverPoiBtn" @click="noMultipleClicks(showMap)">查看司机位置</span>
|
<span v-if="queryType == 5" class="driverPoiBtn" @click="noMultipleClicks(showMap)">查看司机位置</span>
|
||||||
|
<span v-if="orderDetailInfo.driverName && orderDetailInfo.proprietary?.code==1" class="driverPoiBtn" @click="noMultipleClicks(createDriverInfo)">生成司机信息</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="item" v-if="queryType == 9 || queryType ==11 || queryType ==12">
|
<div class="item" v-if="queryType == 9 || queryType ==11 || queryType ==12 || queryType ==5">
|
||||||
<span class="leftTitle fontColor">工单照片:</span><span class="rightContent">
|
<span class="leftTitle fontColor">工单照片:</span><span class="rightContent">
|
||||||
<span class="driverPoiBtn" @click="checkPhoto">查看照片</span>
|
<span class="driverPoiBtn" @click="checkPhoto">查看照片</span>
|
||||||
|
<span class="driverPoiBtn" v-if="orderDetailInfo.beforeDispatchImages && orderDetailInfo.beforeDispatchImages.length > 0" @click="environmentPhoto">环境照片</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="orderDetailInfo.firstAggregateFailInfo" class="aggregate-fail-card">
|
||||||
|
<div class="aggregate-fail-card__title">⚠ 首次聚合未接单</div>
|
||||||
|
<div class="aggregate-fail-card__row">
|
||||||
|
<span class="aggregate-fail-card__label">师傅姓名:</span>
|
||||||
|
<span class="aggregate-fail-card__value">{{ orderDetailInfo.firstAggregateFailInfo.driverName || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="aggregate-fail-card__row">
|
||||||
|
<span class="aggregate-fail-card__label">师傅手机号:</span>
|
||||||
|
<span class="aggregate-fail-card__value">{{ orderDetailInfo.firstAggregateFailInfo.driverPhone || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="aggregate-fail-card__row">
|
||||||
|
<span class="aggregate-fail-card__label">车辆:</span>
|
||||||
|
<span class="aggregate-fail-card__value">{{ aggregateFailVehicle }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="aggregate-fail-card__row">
|
||||||
|
<span class="aggregate-fail-card__label">失败原因:</span>
|
||||||
|
<span class="aggregate-fail-card__value aggregate-fail-card__value--warning">{{ orderDetailInfo.firstAggregateFailInfo.failReason || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="aggregate-fail-card__row">
|
||||||
|
<span class="aggregate-fail-card__label">聚合时间:</span>
|
||||||
|
<span class="aggregate-fail-card__value">{{ orderDetailInfo.firstAggregateFailInfo.dispatchTime || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="serviceInfo common" v-if="orderDetailInfo.batteryNo">
|
<div class="serviceInfo common" v-if="orderDetailInfo.batteryNo">
|
||||||
@@ -114,7 +155,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import {myMixins} from '@/utils/myMixins'
|
import {myMixins} from '@/utils/myMixins'
|
||||||
import {getOrderDetail,showVehiclePositionInfo, getConfigByCode} from "@/api/order"
|
import {getOrderDetail,showVehiclePositionInfo, getConfigByCode,getDriverInfo} from "@/api/order"
|
||||||
import minePosition from '@/assets/minePosition.png';
|
import minePosition from '@/assets/minePosition.png';
|
||||||
import vehiclePosition from '@/assets/vehiclePosition.png';
|
import vehiclePosition from '@/assets/vehiclePosition.png';
|
||||||
import desitationPosition from '@/assets/desitationPosition.png'
|
import desitationPosition from '@/assets/desitationPosition.png'
|
||||||
@@ -136,6 +177,16 @@ export default {
|
|||||||
carTypeList: []
|
carTypeList: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed:{
|
||||||
|
aggregateFailVehicle(){
|
||||||
|
const info = this.orderDetailInfo.firstAggregateFailInfo
|
||||||
|
if(!info) return '—'
|
||||||
|
const plate = info.licensePlate || ''
|
||||||
|
const type = info.vehicleTypeName || ''
|
||||||
|
if(plate && type) return `${plate}(${type})`
|
||||||
|
return plate || type || '—'
|
||||||
|
}
|
||||||
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
// 获取当前 URL
|
// 获取当前 URL
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
@@ -152,7 +203,7 @@ export default {
|
|||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.initMap();
|
this.initMap();
|
||||||
});
|
});
|
||||||
|
console.log('queryType', this.queryType)
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
async getConfigByCodeHandler() {
|
async getConfigByCodeHandler() {
|
||||||
@@ -161,6 +212,26 @@ export default {
|
|||||||
});
|
});
|
||||||
this.carTypeList = res?.data?.userVehicleType
|
this.carTypeList = res?.data?.userVehicleType
|
||||||
},
|
},
|
||||||
|
async getDriverInfo(){
|
||||||
|
let result=await getDriverInfo({
|
||||||
|
driverId:this.orderDetailInfo.driverId
|
||||||
|
})
|
||||||
|
if(result.data){
|
||||||
|
// console.log('--result--',result.data)
|
||||||
|
let params=`姓名:${this.orderDetailInfo.driverName}\n身份证:${result.data.identityCardNumber}\n车牌:${this.orderDetailInfo.vehiclePlateNumber}\n手机:${this.orderDetailInfo.driverPhone}`
|
||||||
|
// console.log('params',params)
|
||||||
|
let data = {"action":"copyToClipboard","params":params}
|
||||||
|
var u = navigator.userAgent;
|
||||||
|
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
|
||||||
|
if(isiOS){
|
||||||
|
window.webkit.messageHandlers.nativeObject.postMessage(data);
|
||||||
|
}else {
|
||||||
|
window.android.copyToClipboard(params);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
this.$toast('未获取到司机信息')
|
||||||
|
}
|
||||||
|
},
|
||||||
async getDetail(){
|
async getDetail(){
|
||||||
let result=await getOrderDetail({
|
let result=await getOrderDetail({
|
||||||
queryType:this.queryType,
|
queryType:this.queryType,
|
||||||
@@ -176,6 +247,9 @@ export default {
|
|||||||
this.showPopup = true;
|
this.showPopup = true;
|
||||||
this.mapMarkers();
|
this.mapMarkers();
|
||||||
},
|
},
|
||||||
|
async createDriverInfo(){//生成司机信息
|
||||||
|
await this.getDriverInfo()
|
||||||
|
},
|
||||||
checkPhoto(){
|
checkPhoto(){
|
||||||
let isAllowImage = this.queryType == 9 ? 0 : 1
|
let isAllowImage = this.queryType == 9 ? 0 : 1
|
||||||
let data = {
|
let data = {
|
||||||
@@ -195,6 +269,12 @@ export default {
|
|||||||
window.android.sendMessage("orderPhoto,"+this.userOrderId+","+this.orderCode+","+this.taskOrderId+","+isAllowImage);
|
window.android.sendMessage("orderPhoto,"+this.userOrderId+","+this.orderCode+","+this.taskOrderId+","+isAllowImage);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
environmentPhoto() {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/environmentPhotos',
|
||||||
|
query: { userOrderId: this.userOrderId }
|
||||||
|
})
|
||||||
|
},
|
||||||
initMap() {
|
initMap() {
|
||||||
this.map = new AMap.Map('container',{
|
this.map = new AMap.Map('container',{
|
||||||
zoom:14,//级别(缩放比例 3-20 )
|
zoom:14,//级别(缩放比例 3-20 )
|
||||||
@@ -238,6 +318,21 @@ export default {
|
|||||||
this.map.add(marker2);
|
this.map.add(marker2);
|
||||||
this.map.setFitView([marker,marker1,marker2])
|
this.map.setFitView([marker,marker1,marker2])
|
||||||
},
|
},
|
||||||
|
maskName(name) {
|
||||||
|
if (!name) return '';
|
||||||
|
return name.slice(0, 1) + '*'.repeat(name.length - 1);
|
||||||
|
},
|
||||||
|
maskPhone(phone) {
|
||||||
|
if (!phone) return '';
|
||||||
|
const str = String(phone);
|
||||||
|
if (str.length <= 7) return str;
|
||||||
|
return str.slice(0, 3) + '*'.repeat(str.length - 7) + str.slice(-4);
|
||||||
|
},
|
||||||
|
maskPlate(plate) {
|
||||||
|
if (!plate) return '';
|
||||||
|
if (plate.length <= 2) return plate;
|
||||||
|
return plate.slice(0, 1) + '*'.repeat(plate.length - 2) + plate.slice(-1);
|
||||||
|
},
|
||||||
async getDriverPoi(){
|
async getDriverPoi(){
|
||||||
let res=await showVehiclePositionInfo({
|
let res=await showVehiclePositionInfo({
|
||||||
userOrderId:this.userOrderId,
|
userOrderId:this.userOrderId,
|
||||||
@@ -294,21 +389,25 @@ export default {
|
|||||||
}
|
}
|
||||||
.baseInfo{
|
.baseInfo{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
//@include wh(100%,260px);
|
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
.infoWrap{
|
.infoWrap{
|
||||||
|
.infoRow{
|
||||||
display: flex;
|
display: flex;
|
||||||
.leftTitle{
|
align-items: center;
|
||||||
|
min-height: 24px;
|
||||||
|
.leftItem{
|
||||||
|
width: 60px;
|
||||||
|
flex-shrink: 0;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
@include wh(60px,189px);
|
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
}
|
}
|
||||||
.rightContent{
|
|
||||||
height: 189px;
|
|
||||||
width: calc(100% - 60px);
|
|
||||||
.rightItem{
|
.rightItem{
|
||||||
height: 24px;
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -352,6 +451,38 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.aggregate-fail-card{
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
background: #FFF7F0;
|
||||||
|
border: 1px solid #FFBB96;
|
||||||
|
border-radius: 6px;
|
||||||
|
&__title{
|
||||||
|
@include fontWeightSize(bold,13px);
|
||||||
|
color: #FA541C;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
&__row{
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
&__label{
|
||||||
|
width: 78px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
@include fontWeightSize(400,12px);
|
||||||
|
color: #000000;
|
||||||
|
opacity: .65;
|
||||||
|
}
|
||||||
|
&__value{
|
||||||
|
flex: 1;
|
||||||
|
@include fontWeightSize(bold,12px);
|
||||||
|
color: #000000;
|
||||||
|
&--warning{
|
||||||
|
color: #FA541C;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.title{
|
.title{
|
||||||
@include fontWeightSize(bold,14px);
|
@include fontWeightSize(bold,14px);
|
||||||
@include colorOpa(#323643,0.6);
|
@include colorOpa(#323643,0.6);
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ import {
|
|||||||
getDriverName,getDriverStatisticsKpi,
|
getDriverName,getDriverStatisticsKpi,
|
||||||
getRecentSupplierKpi,
|
getRecentSupplierKpi,
|
||||||
} from "@/api/kpi.js"
|
} from "@/api/kpi.js"
|
||||||
|
import { getUserBO } from "@/api/order"
|
||||||
// querySupplierDriverStatisticsScore
|
// querySupplierDriverStatisticsScore
|
||||||
import {myMixins} from "@/utils/myMixins"
|
import {myMixins} from "@/utils/myMixins"
|
||||||
import CircleChar from "@/views/kpi/components/circleChar.vue";
|
import CircleChar from "@/views/kpi/components/circleChar.vue";
|
||||||
@@ -330,6 +331,10 @@ export default {
|
|||||||
showScoreChart:true,
|
showScoreChart:true,
|
||||||
continueMonthKpi:[],
|
continueMonthKpi:[],
|
||||||
isBtn:false,//是否有信息变更申请按钮权限
|
isBtn:false,//是否有信息变更申请按钮权限
|
||||||
|
source: 'App',
|
||||||
|
currentSupplierId: '',
|
||||||
|
userId: '',
|
||||||
|
enterTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -338,13 +343,41 @@ export default {
|
|||||||
this.isZd = urlParams?.get('isZd') || ''
|
this.isZd = urlParams?.get('isZd') || ''
|
||||||
this.supplierId = urlParams?.get('supplierId') || ''
|
this.supplierId = urlParams?.get('supplierId') || ''
|
||||||
this.isBtn= Number(urlParams?.get('isBtn'))
|
this.isBtn= Number(urlParams?.get('isBtn'))
|
||||||
|
this.source = urlParams?.get('source') || 'App'
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
await this.checkMobile();
|
await this.checkMobile();
|
||||||
await this.initDate();
|
await this.initDate();
|
||||||
await this.selectSupplierNameHandle();
|
await this.selectSupplierNameHandle();
|
||||||
|
await this.getUserInfo();
|
||||||
|
document.addEventListener('visibilitychange', async ( ) => {
|
||||||
|
let state = document.visibilityState
|
||||||
|
if (state == 'hidden') { // 用户离开了
|
||||||
|
if( this.source == 'App' ) {
|
||||||
|
this.setLogHandler({
|
||||||
|
supplierId: this.currentSupplierId, // 服务商 id
|
||||||
|
userId: this.userId, // userId
|
||||||
|
source: '服务商KPI',
|
||||||
|
event: this.source, // 区分 App 还是 system
|
||||||
|
openTime: this.enterTime, // log 时间,其中 阅读时长是由 enter 和 leave 的时间计算而来
|
||||||
|
leaveTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state == 'visible') {
|
||||||
|
this.enterTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async getUserInfo() {
|
||||||
|
let res = await getUserBO();
|
||||||
|
this.userId = res.data.id;
|
||||||
|
this.currentSupplierId = res.data.supplierId
|
||||||
|
console.log('getUserBO', res)
|
||||||
|
},
|
||||||
applicateHandle() {
|
applicateHandle() {
|
||||||
if (window.parent) {
|
if (window.parent) {
|
||||||
const hasListener = window.parent.dispatchEvent(new Event('checkCloseDialog'));
|
const hasListener = window.parent.dispatchEvent(new Event('checkCloseDialog'));
|
||||||
@@ -1147,8 +1180,8 @@ export default {
|
|||||||
{label: '服务车辆', prop: 'vehicleName'},
|
{label: '服务车辆', prop: 'vehicleName'},
|
||||||
{label: '服务完成状况', prop: 'orderStatusString'},
|
{label: '服务完成状况', prop: 'orderStatusString'},
|
||||||
{label: '案件完成时间', prop: 'finishTime'},
|
{label: '案件完成时间', prop: 'finishTime'},
|
||||||
{label: '省(B)', prop: 'bProvince'},
|
{label: '省(B)', prop: 'bprovince'},
|
||||||
{label: '市(B)', prop: 'bCity'},
|
{label: '市(B)', prop: 'bcity'},
|
||||||
{label: '区(B)', prop: 'area'},
|
{label: '区(B)', prop: 'area'},
|
||||||
]
|
]
|
||||||
} else if (this.active === 6) {//投诉明细
|
} else if (this.active === 6) {//投诉明细
|
||||||
|
|||||||
@@ -13,23 +13,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="contentWrap">
|
<div class="contentWrap">
|
||||||
<div>
|
<div>
|
||||||
为降低日常救援工作中的不可预期的车损赔付带来的运营风险和资金压力,减少由此衍生而来的可能投诉,顺应行业服务标准不断增长的局势,中道救援自有车辆已全部投保了救援职业责任险。
|
为降低日常救援工作中的不可预期的车损赔付带来的运营风险和资金压力,减少由此衍生而来的投诉,顺应行业服务标准不断增长的局势,中道救援自有车辆已全部投保了救援职业责任险。
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
通过日常的交流了解,此险种对于服务商而言,投保相对较难,很多服务商在无投保渠道的情况下选择了对于我们救援场景保障能力稍弱、不太匹配的货物险,从而从形成了现在很多救援车无保障、或者特殊场景货物险理赔难的局面。
|
通过交流了解,我们的合作服务商日常运营中会遇到责任险或者货物险的“投保难、理赔难”的问题,为此我们与保司沟通协调,特为与中道救援合作紧密的服务商,提供救援职业责任险参保渠道,解决大家的实际困难,实现共同进步。
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
为此中道救援经过与保司沟通协调,特为与中道救援合作紧密的服务商,提供救援职业责任险参保渠道,提升服务商服务能力,降低运营风险,实现共同进步。
|
|
||||||
</div>
|
</div>
|
||||||
<div>一、救援职业责任险简介:</div>
|
<div>一、救援职业责任险简介:</div>
|
||||||
<div> 1、综合保额100万元(其中救援责任50万元,物流责任50万元),可全方位覆盖我们日常的经营活动。</div>
|
<div> 1、保额有50万和100万两套方案可选。</div>
|
||||||
<div> 2、保费支付可采用救援服务费抵扣的方式,降低资金压力。</div>
|
<div> 2、保费支付可采用救援服务费抵扣的方式,降低资金压力。</div>
|
||||||
<div> 3、详细保险条款、保费和其他问题,如需了解,请与辖区区域经理联系。</div>
|
<div> 3、其他详情如需了解,请与辖区区域经理联系。</div>
|
||||||
<div> 二、其他意见建议如需交流,请与18621558505曲仁杰联系。</div>
|
<div> 二、如有好的建议需要交流,请与18621558505曲仁杰联系。</div>
|
||||||
<div> 三、如确认参保,请点击下方“参保”按钮,会有专人与贵司联系。</div>
|
<div> 三、如有参保需要,请点击下方“参保”按钮,我们会与您联系。</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="upload_btn_wrap">
|
<div class="upload_btn_wrap">
|
||||||
<div class="btn_save" :style="{'backgroundColor':used ? '#cccccc' : '#354D93'}" @click="showPoup = true">参保</div>
|
<div class="btn_save" :style="{'backgroundColor':used ? '#cccccc' : '#354D93'}" @click="showPoup = true">我想咨询</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="poupCommon" v-if="showPoup">
|
<div class="poupCommon" v-if="showPoup">
|
||||||
<div class="showPoupContainer">
|
<div class="showPoupContainer">
|
||||||
@@ -37,12 +34,10 @@
|
|||||||
<img class="insure" src="@/assets/insured.png">
|
<img class="insure" src="@/assets/insured.png">
|
||||||
<div class="con">
|
<div class="con">
|
||||||
<template v-if="used">
|
<template v-if="used">
|
||||||
<div class="title">您已成功申请参保,请等待</div>
|
<div class="title">咨询已收到,我们会与您联系。</div>
|
||||||
<div class="title">专人与贵司联系。</div>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="title">请确认</div>
|
<div class="title">请确认咨询</div>
|
||||||
<div class="title">是否参保救援职业责任险</div>
|
|
||||||
</template>
|
</template>
|
||||||
<div class="btnWrap">
|
<div class="btnWrap">
|
||||||
<div v-if="used" class="btn confirmBtn" @click="showPoup = false">确认</div>
|
<div v-if="used" class="btn confirmBtn" @click="showPoup = false">确认</div>
|
||||||
|
|||||||
@@ -35,9 +35,17 @@
|
|||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="left opaCol"><span class="star" style="opacity: 0">*</span><span>角色:</span></div>
|
<div class="left opaCol"><span class="star" style="opacity: 0">*</span><span>角色:</span></div>
|
||||||
<div class="right" :class="{'opaCol':type==2}">
|
<div class="right" :class="{'opaCol':type==2}">
|
||||||
<van-field :input-align="type==2 ? 'right' : 'left'" readonly v-model="item.roleName" placeholder="请输入" />
|
<van-field :input-align="type==2 ? 'right' : 'left'" readonly v-model="item.roleDisplay" placeholder="请输入" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<template v-if="item.hasRelationRole">
|
||||||
|
<div class="item" v-for="(row, rIndex) in item.relationRows" :key="'relation-' + rIndex">
|
||||||
|
<div class="left relationLeft" :class="{'opaCol':type!=2}"><span class="star" :style="{opacity: type==2 ? 1 : 0}">*</span><span>{{ item.relationRows.length > 1 ? row.base : '关系' }}:</span></div>
|
||||||
|
<div class="right">
|
||||||
|
<van-field :input-align="type==2 ? 'right' : 'left'" :readonly="type!=2" v-model="row.relation" placeholder="请输入关系" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="left" :class="{'opaCol':type!=2}"><span class="star">*</span><span>电话1:</span></div>
|
<div class="left" :class="{'opaCol':type!=2}"><span class="star">*</span><span>电话1:</span></div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
@@ -83,6 +91,8 @@
|
|||||||
import {myMixins} from "@/utils/myMixins"
|
import {myMixins} from "@/utils/myMixins"
|
||||||
import {getConfirmPerson,getConfirmStatus,getQrCode,confirmAddWechat,confirm,submitConfirm} from "@/api/authentication";
|
import {getConfirmPerson,getConfirmStatus,getQrCode,confirmAddWechat,confirm,submitConfirm} from "@/api/authentication";
|
||||||
import { Dialog } from 'vant';
|
import { Dialog } from 'vant';
|
||||||
|
// 带关系角色(存储格式:角色名-关系)
|
||||||
|
const RELATION_ROLES = ['其他', '法人关系人', '实控人关系人', '合伙人']
|
||||||
export default {
|
export default {
|
||||||
name: "personList",
|
name: "personList",
|
||||||
mixins:[myMixins],
|
mixins:[myMixins],
|
||||||
@@ -121,7 +131,7 @@ export default {
|
|||||||
let flag=this.validatePhone(val)
|
let flag=this.validatePhone(val)
|
||||||
// console.log('flag',flag)
|
// console.log('flag',flag)
|
||||||
if(!flag){
|
if(!flag){
|
||||||
this.$toast('手机号格式不正确')
|
this.$toast('电话号码格式不正确')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
validatePhone(val) {
|
validatePhone(val) {
|
||||||
@@ -129,13 +139,22 @@ export default {
|
|||||||
if(!val){
|
if(!val){
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
const purePhone = val
|
const purePhone = String(val).trim()
|
||||||
// 情况1:11位且1开头 → 合法
|
const mobileReg = /^1[3-9]\d{9}$/ // 大陆手机号正则表达式
|
||||||
if (/^1[0-9]{10}$/.test(purePhone)) {
|
const hkMobileReg = /^[569]\d{3}[\s-]?\d{4}$/ // 香港手机号正则表达式,允许使用空格或 "-" 分隔
|
||||||
|
const macauMobileReg = /^6\d{3}[\s-]?\d{4}$/ // 澳门手机号正则表达式,允许使用空格或 "-" 分隔
|
||||||
|
const telReg = /^(0\d{2,3})([\s-])?\d{7,8}$/ // 大陆座机号正则表达式,允许区号和座机号用空格或 "-" 分开
|
||||||
|
const hkTelReg = /^\(?\d{2,4}\)?[\s-]?\d{4}[\s-]?\d{4}$/ // 香港座机号正则表达式,允许使用空格或 "-" 分隔
|
||||||
|
const macauTelReg = /^\(?\d{2,4}\)?[\s-]?\d{4}[\s-]?\d{4}$/ // 澳门座机号正则表达式,允许使用空格或 "-" 分隔
|
||||||
|
const phone400Reg = /^400[\s-]?\d{3}[\s-]?\d{4}$/ // 以 "400" 开头的号码正则表达式,允许使用空格或 "-" 分隔
|
||||||
|
// 情况1:12位且首位是0、第二位是1 → 去0后按大陆手机号校验
|
||||||
|
if (/^01[0-9]{10}$/.test(purePhone)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// 情况2:12位且首位是0、第二位是1 → 去0后合法
|
// 匹配任意一种格式即为合法
|
||||||
if (/^01[0-9]{10}$/.test(purePhone)) {
|
if (mobileReg.test(purePhone) || hkMobileReg.test(purePhone) || macauMobileReg.test(purePhone) ||
|
||||||
|
telReg.test(purePhone) || hkTelReg.test(purePhone) || macauTelReg.test(purePhone) ||
|
||||||
|
phone400Reg.test(purePhone)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// 其他情况 → 不合法
|
// 其他情况 → 不合法
|
||||||
@@ -154,11 +173,26 @@ export default {
|
|||||||
// this.type=2
|
// this.type=2
|
||||||
let res=await getConfirmPerson({supplierId:this.supplierId})
|
let res=await getConfirmPerson({supplierId:this.supplierId})
|
||||||
if(res.data && res.data.length>0){
|
if(res.data && res.data.length>0){
|
||||||
this.personList=res.data
|
this.personList=res.data.map(item => this.buildRoleDisplay(item))
|
||||||
}else{
|
}else{
|
||||||
this.personList=[]
|
this.personList=[]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 拆分带关系角色:roleName 按逗号拆项,识别 `角色名-关系`,按第一个 '-' 拆出基础角色与关系
|
||||||
|
buildRoleDisplay(item){
|
||||||
|
const parts = (item.roleName || '').split(',').filter(Boolean).map(raw => {
|
||||||
|
const dashIndex = raw.indexOf('-')
|
||||||
|
if (dashIndex > -1 && RELATION_ROLES.includes(raw.substring(0, dashIndex))) {
|
||||||
|
return { raw, base: raw.substring(0, dashIndex), relation: raw.substring(dashIndex + 1) }
|
||||||
|
}
|
||||||
|
return { raw, base: raw, relation: null }
|
||||||
|
})
|
||||||
|
item.roleParts = parts
|
||||||
|
item.roleDisplay = parts.map(p => p.relation != null ? p.base : p.raw).join(',')
|
||||||
|
item.relationRows = parts.filter(p => p.relation != null)
|
||||||
|
item.hasRelationRole = item.relationRows.length > 0
|
||||||
|
return item
|
||||||
|
},
|
||||||
async wechatConfirm(){//确认已添加微信按钮
|
async wechatConfirm(){//确认已添加微信按钮
|
||||||
Dialog.confirm({
|
Dialog.confirm({
|
||||||
message:'请确认相关人员是否已添加该二维码。',
|
message:'请确认相关人员是否已添加该二维码。',
|
||||||
@@ -226,8 +260,21 @@ export default {
|
|||||||
console.log('phone1Flag',phone1Flag)
|
console.log('phone1Flag',phone1Flag)
|
||||||
console.log('phone2Flag',phone2Flag)
|
console.log('phone2Flag',phone2Flag)
|
||||||
if(!phone1Flag || !phone2Flag){
|
if(!phone1Flag || !phone2Flag){
|
||||||
|
this.$toast('电话号码格式不正确')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 带关系角色:关系必填校验,并将 roleDisplay + relation 重新拼接为 `角色名-关系` 写回 roleName
|
||||||
|
for (const item of this.personList) {
|
||||||
|
if (item.hasRelationRole) {
|
||||||
|
for (const row of item.relationRows) {
|
||||||
|
if (!row.relation || !String(row.relation).trim()) {
|
||||||
|
this.$toast(`请填写${row.base}关系`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.roleName = item.roleParts.map(p => p.relation != null ? `${p.base}-${String(p.relation).trim()}` : p.raw).join(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
await submitConfirm({supplierId:this.supplierId,infos:this.personList})
|
await submitConfirm({supplierId:this.supplierId,infos:this.personList})
|
||||||
this.$toast('操作成功')
|
this.$toast('操作成功')
|
||||||
if(this.isWebFunc()){
|
if(this.isWebFunc()){
|
||||||
@@ -397,6 +444,12 @@ export default {
|
|||||||
color: #323643;
|
color: #323643;
|
||||||
opacity: .65;
|
opacity: .65;
|
||||||
}
|
}
|
||||||
|
.item .left.relationLeft{
|
||||||
|
width: auto;
|
||||||
|
min-width: 66px;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
.star{
|
.star{
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|||||||
@@ -478,6 +478,21 @@ export default {
|
|||||||
otherPhoto:this.otherImgSrc?.join(',')
|
otherPhoto:this.otherImgSrc?.join(',')
|
||||||
})
|
})
|
||||||
this.clearStorageFormInfo()
|
this.clearStorageFormInfo()
|
||||||
|
if(res.code == 200 && !res.msg){
|
||||||
|
this.$toast('发布成功')
|
||||||
|
if(this.isWebFunc()){
|
||||||
|
setTimeout(()=>{
|
||||||
|
this.closeParentDialog()
|
||||||
|
},1000)
|
||||||
|
}else{
|
||||||
|
setTimeout(()=>{
|
||||||
|
this.$router.push({ name: "mineRelease"})
|
||||||
|
sessionStorage.setItem('mineActiveTab',String(1) )
|
||||||
|
},1000)
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
this.$toast(res.msg)
|
||||||
|
}
|
||||||
console.log("车源发布publishCarInfo",res)
|
console.log("车源发布publishCarInfo",res)
|
||||||
} catch (e){
|
} catch (e){
|
||||||
console.log('e',e)
|
console.log('e',e)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<img class="mine" src="@/assets/secondHandCar/mine.png" @click="goMine" />
|
<img class="mine" src="@/assets/secondHandCar/mine.png" @click="goMine" />
|
||||||
<el-input
|
<el-input
|
||||||
@blur="getList"
|
@blur="getList"
|
||||||
placeholder="请输入"
|
placeholder="牌照|车辆类型|落板|排放|品牌|描述|底座|刹车|自营"
|
||||||
v-model.trim="topSearch">
|
v-model.trim="topSearch">
|
||||||
<img slot="suffix" class="search" src="@/assets/secondHandCar/search.png"/>
|
<img slot="suffix" class="search" src="@/assets/secondHandCar/search.png"/>
|
||||||
</el-input>
|
</el-input>
|
||||||
@@ -99,6 +99,11 @@
|
|||||||
></i>
|
></i>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
<div class="customSel wantSourceSel" @click.stop="toggleWantSource">
|
||||||
|
<img v-if="wantSource === 1" class="checkIcon" src="@/assets/secondHandCar/checked.png" />
|
||||||
|
<img v-else class="checkIcon" src="@/assets/secondHandCar/uncheck.png" />
|
||||||
|
<span :class="{'has-price': wantSource === 1}">想要的车源</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="contentWrap" v-show="!show">
|
<div class="contentWrap" v-show="!show">
|
||||||
<van-pull-refresh v-model="isLoading" @refresh="onRefresh" style="min-height:85vh">
|
<van-pull-refresh v-model="isLoading" @refresh="onRefresh" style="min-height:85vh">
|
||||||
@@ -223,6 +228,7 @@ export default {
|
|||||||
showDatePicker: false,
|
showDatePicker: false,
|
||||||
areaShow:false,
|
areaShow:false,
|
||||||
isClearing:false,
|
isClearing:false,
|
||||||
|
wantSource: 0,
|
||||||
minDate: new Date(1970, 0, 1), // 设置最小可选日期(1970年1月1日)
|
minDate: new Date(1970, 0, 1), // 设置最小可选日期(1970年1月1日)
|
||||||
maxDate: new Date(2099, 11, 31) // 设置最大可选日期(2099年12月31日)
|
maxDate: new Date(2099, 11, 31) // 设置最大可选日期(2099年12月31日)
|
||||||
}
|
}
|
||||||
@@ -267,6 +273,10 @@ export default {
|
|||||||
this.setSearchVal();
|
this.setSearchVal();
|
||||||
this.getList()
|
this.getList()
|
||||||
},
|
},
|
||||||
|
wantSource(){
|
||||||
|
this.setSearchVal();
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getSearchVal();
|
this.getSearchVal();
|
||||||
@@ -281,6 +291,9 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
|
toggleWantSource(){
|
||||||
|
this.wantSource = this.wantSource === 1 ? 0 : 1;
|
||||||
|
},
|
||||||
confirmHandle(val){
|
confirmHandle(val){
|
||||||
if(val[0].name.includes('市')){
|
if(val[0].name.includes('市')){
|
||||||
this.areaName=val[0].name
|
this.areaName=val[0].name
|
||||||
@@ -356,6 +369,7 @@ export default {
|
|||||||
sessionStorage.setItem('dateVal',this.dateVal);
|
sessionStorage.setItem('dateVal',this.dateVal);
|
||||||
sessionStorage.setItem('brandModel',this.brandModel);
|
sessionStorage.setItem('brandModel',this.brandModel);
|
||||||
sessionStorage.setItem('underpanBrand',this.underpanBrand);
|
sessionStorage.setItem('underpanBrand',this.underpanBrand);
|
||||||
|
sessionStorage.setItem('wantSource',String(this.wantSource));
|
||||||
},
|
},
|
||||||
getSearchVal(){
|
getSearchVal(){
|
||||||
this.activeTab = Number(sessionStorage.getItem('indexActiveTab')) || 0;
|
this.activeTab = Number(sessionStorage.getItem('indexActiveTab')) || 0;
|
||||||
@@ -371,6 +385,7 @@ export default {
|
|||||||
this.dateVal=sessionStorage.getItem('dateVal') || '';
|
this.dateVal=sessionStorage.getItem('dateVal') || '';
|
||||||
this.brandModel=sessionStorage.getItem('brandModel') || '';
|
this.brandModel=sessionStorage.getItem('brandModel') || '';
|
||||||
this.underpanBrand=sessionStorage.getItem('underpanBrand') || '';
|
this.underpanBrand=sessionStorage.getItem('underpanBrand') || '';
|
||||||
|
this.wantSource=Number(sessionStorage.getItem('wantSource')) || 0;
|
||||||
},
|
},
|
||||||
goMine(){//我的发布
|
goMine(){//我的发布
|
||||||
this.$router.push({ name: "mineRelease"})
|
this.$router.push({ name: "mineRelease"})
|
||||||
@@ -434,6 +449,7 @@ export default {
|
|||||||
endTime:this.endTime,
|
endTime:this.endTime,
|
||||||
brandModel:this.brandModel,
|
brandModel:this.brandModel,
|
||||||
underpanBrand:this.underpanBrand,
|
underpanBrand:this.underpanBrand,
|
||||||
|
wantSource:this.wantSource,
|
||||||
})
|
})
|
||||||
this.total=res.total
|
this.total=res.total
|
||||||
if(this.pageNum == 1){// 第一页直接赋值
|
if(this.pageNum == 1){// 第一页直接赋值
|
||||||
@@ -526,6 +542,9 @@ export default {
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
border: 2px solid #264B94;
|
border: 2px solid #264B94;
|
||||||
}
|
}
|
||||||
|
::v-deep .el-input__inner::placeholder{
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
::v-deep .el-input__suffix{
|
::v-deep .el-input__suffix{
|
||||||
top: 12px;
|
top: 12px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
@@ -622,6 +641,22 @@ export default {
|
|||||||
.has-price{
|
.has-price{
|
||||||
color: #007BE9 !important;
|
color: #007BE9 !important;
|
||||||
}
|
}
|
||||||
|
.wantSourceSel{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 3px;
|
||||||
|
width: 68px;
|
||||||
|
cursor: pointer;
|
||||||
|
.checkIcon{
|
||||||
|
width: 13px;
|
||||||
|
height: 13px;
|
||||||
|
}
|
||||||
|
span{
|
||||||
|
font-size: 10px;
|
||||||
|
color: #323233;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.contentWrap{
|
.contentWrap{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -54,6 +54,11 @@
|
|||||||
<span
|
<span
|
||||||
class="content">{{ orderInfo.carClothingToolAmount }}</span>
|
class="content">{{ orderInfo.carClothingToolAmount }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="titleLeft">免拖:</span>
|
||||||
|
<span
|
||||||
|
class="content">{{ orderInfo.customerDistanceLimit }} {{orderInfo.hedgingMileage && orderInfo.hedgingMileage > 0 ? '(超限对冲,只结免拖)' : ''}}</span>
|
||||||
|
</div>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<span class="titleLeft">换电瓶费用:</span>
|
<span class="titleLeft">换电瓶费用:</span>
|
||||||
<span
|
<span
|
||||||
@@ -72,35 +77,35 @@
|
|||||||
<div class="inpInfo">
|
<div class="inpInfo">
|
||||||
<div class="titleContent">
|
<div class="titleContent">
|
||||||
<div class="flex-between" v-show="abState">
|
<div class="flex-between" v-show="abState">
|
||||||
<div> 到事发地距离:</div>
|
<div><span style="color: red;">*</span> 到事发地距离:</div>
|
||||||
<div class="halfOpcity"><input @input="validateIntegerInput" class="ipt" type="number" v-model="form.supplierSettleMileageAb"/>公里</div>
|
<div class="halfOpcity"><input @input="validateIntegerInput" class="ipt" type="number" v-model="form.supplierSettleMileageAb"/>公里</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between" v-show="bcState">
|
<div class="flex-between" v-show="bcState">
|
||||||
<div> 拖车里程:</div>
|
<div><span style="color: red;">*</span> 拖车里程:</div>
|
||||||
<div class="halfOpcity"><input class="ipt" type="number" v-model="form.supplierSettleMileageBc"/>公里</div>
|
<div class="halfOpcity"><input class="ipt" type="number" v-model="form.supplierSettleMileageBc"/>公里</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between" v-show="caState">
|
<div class="flex-between" v-show="caState">
|
||||||
<div>返程里程:</div>
|
<div><span style="color: red;">*</span>返程里程:</div>
|
||||||
<div class="halfOpcity"><input class="ipt" type="number" v-model="form.supplierSettleMileageCa"/>公里</div>
|
<div class="halfOpcity"><input class="ipt" type="number" v-model="form.supplierSettleMileageCa"/>公里</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between" v-show="bridgeAbState">
|
<div class="flex-between" v-show="bridgeAbState">
|
||||||
<div>ab路桥费:</div>
|
<div><span v-show="!isUnsuccessfulSettle" style="color: red;">*</span>ab路桥费:</div>
|
||||||
<div class="halfOpcity"><input class="ipt" @input="validateIntegerInput" :disabled="isUnsuccessfulSettle" type="number" v-model="form.supplierBridgeAmountAb"/>元</div>
|
<div class="halfOpcity"><input class="ipt" @input="validateIntegerInput" :disabled="isUnsuccessfulSettle" type="number" v-model="form.supplierBridgeAmountAb"/>元</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between" v-show="bridgeBcState">
|
<div class="flex-between" v-show="bridgeBcState">
|
||||||
<div>bc路桥费:</div>
|
<div><span v-show="!isUnsuccessfulSettle" style="color: red;">*</span>bc路桥费:</div>
|
||||||
<div class="halfOpcity"><input class="ipt" @input="validateIntegerInput" :disabled="isUnsuccessfulSettle" type="number" v-model="form.supplierBridgeAmountBc"/>元</div>
|
<div class="halfOpcity"><input class="ipt" @input="validateIntegerInput" :disabled="isUnsuccessfulSettle" type="number" v-model="form.supplierBridgeAmountBc"/>元</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between" v-show="bridgeCaState">
|
<div class="flex-between" v-show="bridgeCaState">
|
||||||
<div>ca路桥费:</div>
|
<div><span v-show="!isUnsuccessfulSettle" style="color: red;">*</span>ca路桥费:</div>
|
||||||
<div class="halfOpcity"><input class="ipt" @input="validateIntegerInput" :disabled="isUnsuccessfulSettle" type="number" v-model="form.supplierBridgeAmountCa"/>元</div>
|
<div class="halfOpcity"><input class="ipt" @input="validateIntegerInput" :disabled="isUnsuccessfulSettle" type="number" v-model="form.supplierBridgeAmountCa"/>元</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between" v-show="waitAmountState">
|
<div class="flex-between" v-show="waitAmountState">
|
||||||
<div>等候费:</div>
|
<div><span style="color: red;">*</span>等候费:</div>
|
||||||
<div class="halfOpcity inputContent"><input class="ipt" @input="validateIntegerInput" type="number" v-model="form.supplierWaitAmount"/>元</div>
|
<div class="halfOpcity inputContent"><input class="ipt" @input="validateIntegerInput" type="number" v-model="form.supplierWaitAmount"/>元</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between" v-show="tyreAmountState">
|
<div class="flex-between" v-show="tyreAmountState">
|
||||||
<div>小轮个数:</div>
|
<div><span v-show="!isUnsuccessfulSettle" style="color: red;">*</span>小轮个数:</div>
|
||||||
<div class="halfOpcity inputContent"><input class="ipt" @input="validateIntegerInput" :disabled="isUnsuccessfulSettle" type="number" v-model="form.tyreNumber"/>个</div>
|
<div class="halfOpcity inputContent"><input class="ipt" @input="validateIntegerInput" :disabled="isUnsuccessfulSettle" type="number" v-model="form.tyreNumber"/>个</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between" v-show="tyreAmountState">
|
<div class="flex-between" v-show="tyreAmountState">
|
||||||
@@ -108,11 +113,11 @@
|
|||||||
<div class="halfOpcity inputContent"><input @input="validateIntegerInput" disabled type="number" v-model="form.supplierTyreAmount"/>元</div>
|
<div class="halfOpcity inputContent"><input @input="validateIntegerInput" disabled type="number" v-model="form.supplierTyreAmount"/>元</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between" v-show="dilemmaAmountState && basePriceDisabled">
|
<div class="flex-between" v-show="dilemmaAmountState && basePriceDisabled">
|
||||||
<div>困境费:</div>
|
<div><span style="color: red;">*</span>困境费:</div>
|
||||||
<div class="halfOpcity inputContent"><input class="ipt" @input="validateIntegerInput" type="number" v-model="form.supplierDilemmaAmount"/>元</div>
|
<div class="halfOpcity inputContent"><input class="ipt" @input="validateIntegerInput" type="number" v-model="form.supplierDilemmaAmount"/>元</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between" v-show="basementFeeState">
|
<div class="flex-between" v-show="basementFeeState">
|
||||||
<div>地库费:</div>
|
<div><span v-show="isBasementSettle && !isUnsuccessfulSettle" style="color: red;">*</span>地库费:</div>
|
||||||
<div class="halfOpcity inputContent"><input @input="validateIntegerInput" class="ipt" type="number" v-model="form.supplierBasementFee" :disabled="!isBasementSettle || isUnsuccessfulSettle" />元</div>
|
<div class="halfOpcity inputContent"><input @input="validateIntegerInput" class="ipt" type="number" v-model="form.supplierBasementFee" :disabled="!isBasementSettle || isUnsuccessfulSettle" />元</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between">
|
<div class="flex-between">
|
||||||
@@ -120,7 +125,7 @@
|
|||||||
<div class="halfOpcity inputContent"><input type="number" class="ipt" v-model="form.supplierCustomerAmount"/>元</div>
|
<div class="halfOpcity inputContent"><input type="number" class="ipt" v-model="form.supplierCustomerAmount"/>元</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-between bgDisable" :class=" { 'flex-between': true, 'bgDisable' : basePriceDisabled } ">
|
<div class="flex-between bgDisable" :class=" { 'flex-between': true, 'bgDisable' : basePriceDisabled } ">
|
||||||
<div>基本费用:</div>
|
<div><span v-show="!basePriceDisabled && !isBasementSettle" style="color: red;">*</span>基本费用:</div>
|
||||||
<div class="halfOpcity inputContent "><input class="disable" type="number" :readonly="basePriceDisabled || isBasementSettle"
|
<div class="halfOpcity inputContent "><input class="disable" type="number" :readonly="basePriceDisabled || isBasementSettle"
|
||||||
v-model="form.supplierBasePrice"/><span>元</span></div>
|
v-model="form.supplierBasePrice"/><span>元</span></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -493,13 +498,7 @@ export default {
|
|||||||
console.log("ii", i)
|
console.log("ii", i)
|
||||||
},
|
},
|
||||||
goback() {
|
goback() {
|
||||||
this.$router.push({
|
this.$router.back()
|
||||||
name: "workOrderReconciliation",
|
|
||||||
params: {
|
|
||||||
activeIndex: this.index,
|
|
||||||
queryTime: this.queryTime,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
async compute() {
|
async compute() {
|
||||||
if(this.orderInfo.contractSettleTypeString == '车主现收'){
|
if(this.orderInfo.contractSettleTypeString == '车主现收'){
|
||||||
@@ -657,6 +656,49 @@ export default {
|
|||||||
this.goback()
|
this.goback()
|
||||||
},
|
},
|
||||||
async save() {
|
async save() {
|
||||||
|
const isBlank = (v) => v === '' || v === null || v === undefined || parseFloat(v) < 0
|
||||||
|
if (this.abState && isBlank(this.form.supplierSettleMileageAb)) {
|
||||||
|
this.$toast('请填写到事发地距离'); return;
|
||||||
|
}
|
||||||
|
if (this.bcState && isBlank(this.form.supplierSettleMileageBc)) {
|
||||||
|
this.$toast('请填写拖车里程'); return;
|
||||||
|
}
|
||||||
|
if (this.caState && isBlank(this.form.supplierSettleMileageCa)) {
|
||||||
|
this.$toast('请填写返程里程'); return;
|
||||||
|
}
|
||||||
|
if (this.bridgeAbState && !this.isUnsuccessfulSettle && isBlank(this.form.supplierBridgeAmountAb)) {
|
||||||
|
this.$toast('请填写ab路桥费'); return;
|
||||||
|
}
|
||||||
|
if (this.bridgeBcState && !this.isUnsuccessfulSettle && isBlank(this.form.supplierBridgeAmountBc)) {
|
||||||
|
this.$toast('请填写bc路桥费'); return;
|
||||||
|
}
|
||||||
|
if (this.bridgeCaState && !this.isUnsuccessfulSettle && isBlank(this.form.supplierBridgeAmountCa)) {
|
||||||
|
this.$toast('请填写ca路桥费'); return;
|
||||||
|
}
|
||||||
|
if (this.waitAmountState && isBlank(this.form.supplierWaitAmount)) {
|
||||||
|
this.$toast('请填写等候费'); return;
|
||||||
|
}
|
||||||
|
if (!this.auditFormShow && this.tyreAmountState && !this.isUnsuccessfulSettle && isBlank(this.form.tyreNumber)) {
|
||||||
|
this.$toast('请填写小轮个数'); return;
|
||||||
|
}
|
||||||
|
if (this.dilemmaAmountState && this.basePriceDisabled && isBlank(this.form.supplierDilemmaAmount)) {
|
||||||
|
this.$toast('请填写困境费'); return;
|
||||||
|
}
|
||||||
|
if (this.basementFeeState && this.isBasementSettle && !this.isUnsuccessfulSettle && isBlank(this.form.supplierBasementFee)) {
|
||||||
|
this.$toast('请填写地库费'); return;
|
||||||
|
}
|
||||||
|
if (!this.basePriceDisabled && !this.isBasementSettle && isBlank(this.form.supplierBasePrice)) {
|
||||||
|
this.$toast('请填写基本费用'); return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.orderInfo.customerDistanceLimit &&
|
||||||
|
parseFloat(this.form.supplierSettleMileageBc) > parseFloat(this.orderInfo.customerDistanceLimit)
|
||||||
|
) {
|
||||||
|
if (!this.form.supplierCustomerAmount || parseFloat(this.form.supplierCustomerAmount) == 0) {
|
||||||
|
this.$toast('拖车里程超出免拖范围,请填写收取客户金额');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (this.form.supplierOtherAmount > 0) {
|
if (this.form.supplierOtherAmount > 0) {
|
||||||
if (this.form.supplierOtherAmountRemark == '') {
|
if (this.form.supplierOtherAmountRemark == '') {
|
||||||
this.$toast('请填写其他费用说明');
|
this.$toast('请填写其他费用说明');
|
||||||
@@ -694,6 +736,7 @@ export default {
|
|||||||
await this.compute();
|
await this.compute();
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
let obj = this.form;
|
let obj = this.form;
|
||||||
|
console.log('obj',obj)
|
||||||
Object.keys(obj).forEach((key) => {
|
Object.keys(obj).forEach((key) => {
|
||||||
formData.append(key, obj[key]);
|
formData.append(key, obj[key]);
|
||||||
});
|
});
|
||||||
@@ -725,6 +768,45 @@ export default {
|
|||||||
this.$toast('已进入后续流程,不允许操作');
|
this.$toast('已进入后续流程,不允许操作');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const isBlank = (v) => v === '' || v === null || v === undefined || parseFloat(v) < 0
|
||||||
|
if (this.abState && isBlank(this.form.supplierSettleMileageAb)) {
|
||||||
|
this.$toast('请填写到事发地距离'); return;
|
||||||
|
}
|
||||||
|
if (this.bcState && isBlank(this.form.supplierSettleMileageBc)) {
|
||||||
|
this.$toast('请填写拖车里程'); return;
|
||||||
|
}
|
||||||
|
if (this.caState && isBlank(this.form.supplierSettleMileageCa)) {
|
||||||
|
this.$toast('请填写返程里程'); return;
|
||||||
|
}
|
||||||
|
if (this.bridgeAbState && !this.isUnsuccessfulSettle && isBlank(this.form.supplierBridgeAmountAb)) {
|
||||||
|
this.$toast('请填写ab路桥费'); return;
|
||||||
|
}
|
||||||
|
if (this.bridgeBcState && !this.isUnsuccessfulSettle && isBlank(this.form.supplierBridgeAmountBc)) {
|
||||||
|
this.$toast('请填写bc路桥费'); return;
|
||||||
|
}
|
||||||
|
if (this.bridgeCaState && !this.isUnsuccessfulSettle && isBlank(this.form.supplierBridgeAmountCa)) {
|
||||||
|
this.$toast('请填写ca路桥费'); return;
|
||||||
|
}
|
||||||
|
if (this.waitAmountState && isBlank(this.form.supplierWaitAmount)) {
|
||||||
|
this.$toast('请填写等候费'); return;
|
||||||
|
}
|
||||||
|
if (this.dilemmaAmountState && this.basePriceDisabled && isBlank(this.form.supplierDilemmaAmount)) {
|
||||||
|
this.$toast('请填写困境费'); return;
|
||||||
|
}
|
||||||
|
if (this.basementFeeState && this.isBasementSettle && !this.isUnsuccessfulSettle && isBlank(this.form.supplierBasementFee)) {
|
||||||
|
this.$toast('请填写地库费'); return;
|
||||||
|
}
|
||||||
|
if (!this.basePriceDisabled && !this.isBasementSettle && isBlank(this.form.supplierBasePrice)) {
|
||||||
|
this.$toast('请填写基本费用'); return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.orderInfo.customerDistanceLimit &&
|
||||||
|
parseFloat(this.form.supplierSettleMileageBc) > parseFloat(this.orderInfo.customerDistanceLimit)
|
||||||
|
) {
|
||||||
|
if (!this.form.supplierCustomerAmount || parseFloat(this.form.supplierCustomerAmount) == 0) {
|
||||||
|
this.$toast('拖车里程超出免拖范围,请填写收取客户金额'); return;
|
||||||
|
}
|
||||||
|
}
|
||||||
this.loading1 = true;
|
this.loading1 = true;
|
||||||
this.form.supplierExtraPrice = this.form.extraPrice || 0
|
this.form.supplierExtraPrice = this.form.extraPrice || 0
|
||||||
this.form.supplierBasePrice = this.form.basePrice || 0
|
this.form.supplierBasePrice = this.form.basePrice || 0
|
||||||
@@ -942,7 +1024,7 @@ img {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.baseInfo {
|
.baseInfo {
|
||||||
@include wh(100%, 344px);
|
width: 100%;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user