Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 17c482e21a | |||
| 2684624979 | |||
| 2131e99ec4 | |||
| 6e5b15f203 | |||
| fbfd5cde89 |
@@ -0,0 +1,6 @@
|
||||
# 2026-09-10
|
||||
|
||||
- 移除 project-apply 的 fund-risk-stats 请求(后端未实现端点,每次列表加载都弹 No endpoint 报错):删除 onLoad 内 refreshFundRiskStats 调用与该方法、data 中 fundRiskStats;头部风险标签去掉「(数量)」只留「高风险/中风险」;api/business/project-apply.js 保留 getFundRiskStats 定义并注释「后端就绪后恢复」。
|
||||
EOF2
|
||||
}
|
||||
cat >> /Users/gxwebsoft/VUE/tms-erp-web-ws/.workbuddy/memory/2026-09-10.md <<'EOF'
|
||||
@@ -0,0 +1,28 @@
|
||||
# 2026-09-11
|
||||
|
||||
## 上传证件区域固定宽度导致跨机器错位(transportCapacity)
|
||||
- 现象:driver.vue 新增弹窗的身份证/大头照上传框在本机对齐,他人电脑不对齐。
|
||||
- 根因:`--large` 上传框写死 `width:240px`(ship 写 248px),而弹窗 `width:90%` + `el-col :span=6` 列宽是弹性的、上方输入框 `width:100%`;列宽随分辨率 / Windows 系统缩放 / 浏览器缩放变化,240px 只在特定宽度下恰好等于列宽。叠加 `labelWidth:auto` 靠 JS 实测字体(苹方 vs 微软雅黑)宽度,label 列宽也不同 → 起始位置漂移。
|
||||
- 修复:三个文件改为 `width:100%; max-width:240px; flex:none`(ship 保留 248px 上限),窄屏跟随列宽、宽屏保持比例上限,左边缘恒与输入框对齐。
|
||||
- `src/views/transportCapacity/driver.vue`(.driver-uploader--large)
|
||||
- `src/views/transportCapacity/vehicle.vue`(.vehicle-uploader--large)
|
||||
- `src/views/transportCapacity/ship.vue`(.ship-uploader--large)
|
||||
- 用 @vue/compiler-sfc(脚本放项目根目录)验证三个 SFC 编译通过。
|
||||
- 教训:本项目所有「固定 px 宽度的上传/控件」都要配 `max-width` + `width:100%`,否则高分屏/低分屏下必然错位。
|
||||
|
||||
## 合同详情由弹窗改为独立页面(contract-manage)
|
||||
- 路由:新增 `/business/contract-manage/detail`(`src/router/views/index.js`,name『合同详情』,meta `keepAlive:false` + `activeMenu:'/business/contract-manage'`),组件仍复用 `contract-manage.vue`(与 form 页同一套路)。
|
||||
- 页面内三分支:`v-if="!isFormPage && !isDetailPage"`(列表)/ `v-else-if="isFormPage"`(表单)/ `v-else`(详情)。**v-else 必须紧邻 form 分支的 `</template>`**,中间不能插其它组件,否则 Vue 编译报「v-else 无相邻 v-if」——弹窗类组件(billing-plan-editor / 附件预览 / 变更记录详情 / 流程)放在三个分支之后。
|
||||
- 关键改动:`openDetail` 改 `router.push({ path:'/business/contract-manage/detail', query:{ id, name } })`;新增 `initDetailPage()`(按 query.id 拉详情,`resetDetailState()` 先清空)、`closeDetailPage()`(closeTag + 回列表)、`isDetailPage` / `detailPageTitle` computed;`created` 与 `$route.fullPath` watch 都要加 `else if (this.isDetailPage)`。
|
||||
- 样式:`.contract-manage-detail--page { max-height:none; overflow:visible }`,去掉弹窗的 74vh 限制;底栏复用 `.contract-manage-page__footer`(fixed 浮动),只读详情页按钮保留「关闭」。
|
||||
- 校验方式:dev server 已运行时 `curl "http://localhost:2889/src/views/business/contract-manage.vue"` 返回 200 即 Vite 编译通过(比整包 build 快)。
|
||||
|
||||
## 只给「查看弹窗」加宽 label(temporary-credit-limit)
|
||||
- 场景:详情页长 label(「剩余项目资金使用额度(万元)」)换行超 2 行撑坏详情表格,但又不能影响新增/编辑页。
|
||||
- **关键发现:Avue 的 `avue-form` 在 detail 模式下根节点会挂 `.avue--detail` 类**(源码 `node_modules/@smallwei/avue/lib/packages/element-plus/form/index3.js` 中 `class: normalizeClass([b(), { 'avue--detail': isDetail }])`)。所以「只针对详情态」的选择器就是:
|
||||
```scss
|
||||
.temporary-credit-limit-dialog .avue--detail .el-form-item__label { width:210px !important; flex:0 0 210px !important; }
|
||||
```
|
||||
新增/编辑弹窗无 `.avue--detail`,天然不受影响,不需要在 option 里做 mode 判断。
|
||||
- 校验样式块编译:dev server 请求 `?vue&type=style&index=1&lang.scss`(**lang 必须写 scss,写 css 会 500**),grep 规则确认产物。
|
||||
- 结论备忘:长 label 优先「加宽 + 换行 ≤2 行」,不要用单行省略 + tooltip —— 详情是纯阅读场景,hover 才能看全字段名的可发现性差。
|
||||
@@ -0,0 +1,12 @@
|
||||
# 2026-09-17 工作日志
|
||||
|
||||
## 运单管理详情:打卡时间轴改为按打卡顺序展示
|
||||
|
||||
- 文件:`src/views/business/components/waybill-manage-page.vue`(详情页「执行详情 → 打卡详情」tab)
|
||||
- 需求:时间轴不再固定按流程节点顺序(发货/在途/到货/卸货/签收/回单),改为按打卡时间先后展示。
|
||||
- 实现(不改后端、不改原始数据):
|
||||
1. 新增 computed `orderedWaybillPunchRecords`:映射出 `punchedAt`(毫秒时间戳)后排序 —— **已打卡记录按打卡时间升序排在前**,未打卡记录保持后端返回的节点顺序排在其后;时间相同用原索引稳定排序。
|
||||
2. 新增 method `waybillPunchRecordTimestamp(record)`:`punched === false` 或 `statusName === '未打卡'` 直接返回 null(防止后端返回计划时间造成误序);优先 `this.$dayjs(text).valueOf()`,失败兜底 `new Date(text.replace(/-/g,'/'))`(兼容 Safari)。
|
||||
3. 模板 `el-timeline` 的 `v-for` 改用 `orderedWaybillPunchRecords`;`key` 由 `record.nodeCode || record.id || index` 改为 `${nodeCode||id||'punch'}-${index}`,避免同节点多次打卡(在途)时 key 重复。
|
||||
- 校验:项目根目录临时脚本 `_verify-sfc.mjs`(@vue/compiler-sfc parse + compileScript + compileTemplate)通过,已删除脚本。
|
||||
- 注意:`getPunchRecords`(`src/api/business/waybill-manage.js` → `/punch-records`)后端未提供节点排序字段,排序只能在前端做。
|
||||
@@ -30,8 +30,13 @@
|
||||
- Avue 内置弹窗 footer 真实 class 是 `.avue-dialog__footer`,须 `position:sticky; bottom:0; flex:none; background:#fff` 贴底。
|
||||
|
||||
### 上传证件区域尺寸(transportCapacity)
|
||||
- 身份证/行驶证/道路运输证等固定 **240×151px**(1.586:1)。
|
||||
- scoped 覆盖 `.xxx-uploader--large { width:240px }` 与 `--large .el-upload { width:100%; height:151px }`。
|
||||
- 身份证/行驶证/道路运输证等目标比例 **240×151px**(1.586:1),ship 为 248px 上限。
|
||||
- ⚠️ **不能写死 `width:240px`**:弹窗 `width:90%` + 列 `el-col :span=6` 是弹性的,固定 px 只在特定分辨率/系统缩放下恰好等于列宽(本机对齐、他人错位)。统一写法:
|
||||
```scss
|
||||
.xxx-uploader--large { display:block; width:100%; max-width:240px; flex:none; margin:0; }
|
||||
.xxx-uploader--large .el-upload { width:100%; height:151px; }
|
||||
```
|
||||
2026-09-11 已修正 driver / vehicle / ship 三处。
|
||||
|
||||
### 上传图片点击放大预览
|
||||
- `src/components/image-upload-field/main.vue` 用 `<el-image preview-src-list fit="contain" preview-teleported :z-index="3000" @click.stop>`,阻止冒泡到 el-upload。
|
||||
|
||||
@@ -16,6 +16,7 @@ export const submit = api.submit;
|
||||
export const remove = api.remove;
|
||||
|
||||
// 项目资金使用风险统计(高风险 / 中风险数量),供列表头部快速筛选标签使用
|
||||
// 暂未启用:后端 /fund-risk-stats 端点尚未实现,列表页已移除调用,等后端就绪后恢复
|
||||
export const getFundRiskStats = params =>
|
||||
request({
|
||||
url: `${baseUrl}/fund-risk-stats`,
|
||||
|
||||
@@ -299,6 +299,18 @@ export default [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/business/contract-manage/detail',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: '合同详情',
|
||||
meta: { keepAlive: false, activeMenu: '/business/contract-manage' },
|
||||
component: () => import('@/views/business/contract-manage.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/business/contract-manage/change',
|
||||
component: Layout,
|
||||
|
||||
@@ -1923,6 +1923,12 @@
|
||||
}}</strong
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
v-if="!waybillIsCarrier(detailRow)"
|
||||
class="waybill-manage-page__waybill-detail-field"
|
||||
>
|
||||
<span>里程(km)</span><strong>{{ detailRow.mileage || '-' }}</strong>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="waybillIsNonRoadTransport(detailRow)">
|
||||
<div class="waybill-manage-page__waybill-detail-field">
|
||||
@@ -2029,8 +2035,8 @@
|
||||
<div v-loading="waybillPunchRecordsLoading">
|
||||
<el-timeline v-if="waybillPunchRecords.length">
|
||||
<el-timeline-item
|
||||
v-for="(record, index) in waybillPunchRecords"
|
||||
:key="record.nodeCode || record.id || index"
|
||||
v-for="(record, index) in orderedWaybillPunchRecords"
|
||||
:key="`${record.nodeCode || record.id || 'punch'}-${index}`"
|
||||
:timestamp="record.punchTime || record.statusName || '未打卡'"
|
||||
:type="record.punched ? 'primary' : 'info'"
|
||||
:color="record.punched ? '#2b6ce8' : '#c0c4cc'"
|
||||
@@ -3545,6 +3551,25 @@ export default {
|
||||
addBtn: this.canCreate,
|
||||
};
|
||||
},
|
||||
// 打卡时间轴按打卡时间先后展示:已打卡记录按打卡时间升序在前,
|
||||
// 未打卡记录保持流程节点原顺序排在其后。
|
||||
orderedWaybillPunchRecords() {
|
||||
const records = Array.isArray(this.waybillPunchRecords) ? this.waybillPunchRecords : [];
|
||||
return records
|
||||
.map((record, index) => ({
|
||||
record,
|
||||
index,
|
||||
punchedAt: this.waybillPunchRecordTimestamp(record),
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
const aPunched = a.punchedAt !== null;
|
||||
const bPunched = b.punchedAt !== null;
|
||||
if (aPunched && bPunched) return a.punchedAt - b.punchedAt || a.index - b.index;
|
||||
if (aPunched !== bPunched) return aPunched ? -1 : 1;
|
||||
return a.index - b.index;
|
||||
})
|
||||
.map(item => item.record);
|
||||
},
|
||||
isStandaloneWaybillPage() {
|
||||
return this.standaloneFormPage && standaloneWaybillFormPaths.includes(this.$route.path);
|
||||
},
|
||||
@@ -4492,6 +4517,19 @@ export default {
|
||||
})
|
||||
.catch(() => []);
|
||||
},
|
||||
// 解析打卡时间(支持常见日期字符串格式),未打卡或无有效时间返回 null
|
||||
waybillPunchRecordTimestamp(record = {}) {
|
||||
if (record.punched === false || record.statusName === '未打卡') return null;
|
||||
const raw = record.punchTime || record.punchAt || record.punchDateTime || '';
|
||||
const text = String(raw).trim();
|
||||
if (!text) return null;
|
||||
const parsed = this.$dayjs ? this.$dayjs(text) : null;
|
||||
if (parsed && typeof parsed.isValid === 'function' && parsed.isValid()) {
|
||||
return parsed.valueOf();
|
||||
}
|
||||
const fallback = new Date(text.replace(/-/g, '/')).getTime();
|
||||
return Number.isNaN(fallback) ? null : fallback;
|
||||
},
|
||||
loadWaybillPunchRecords() {
|
||||
const waybillId = this.detailRow?.id;
|
||||
if (!waybillId || this.waybillPunchRecordsLoading) return;
|
||||
|
||||
@@ -318,7 +318,7 @@
|
||||
<el-form-item prop="fundLimit" class="project-apply-form__tip-label">
|
||||
<template #label>
|
||||
<span>项目资金使用额度</span>
|
||||
<el-tooltip content="实际业务回款周期内付款" placement="top">
|
||||
<el-tooltip content="实际业务回款周期内付款额度" placement="top">
|
||||
<el-icon class="project-apply-form__label-tip"><QuestionFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
@@ -1892,10 +1892,13 @@ export default {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 大尺寸模式:缩成 1.586:1 比例,左对齐(不再 100% 占栏)
|
||||
// 大尺寸模式:跟随列宽自适应(最大 240px,约 1.586:1 证件比例),
|
||||
// 保证不同分辨率 / 系统缩放下都与上方输入框左右对齐
|
||||
:deep(.driver-uploader--large) {
|
||||
display: block;
|
||||
width: 240px;
|
||||
width: 100%;
|
||||
max-width: 240px;
|
||||
flex: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1349,9 +1349,13 @@ export default {
|
||||
height: 151px;
|
||||
}
|
||||
|
||||
// 大尺寸模式:跟随列宽自适应(最大 248px),
|
||||
// 保证不同分辨率 / 系统缩放下都与上方输入框左右对齐
|
||||
:deep(.ship-uploader--large) {
|
||||
display: block;
|
||||
width: 248px;
|
||||
width: 100%;
|
||||
max-width: 248px;
|
||||
flex: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
+13
-13
@@ -47,26 +47,26 @@ export default ({ mode, command }) => {
|
||||
__VUE_I18N_LEGACY_API__: true,
|
||||
__INTLIFY_PROD_DEVTOOLS__: false,
|
||||
},
|
||||
server: {
|
||||
port: 2888,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost',
|
||||
//target: 'https://saber3.bladex.cn/api',
|
||||
changeOrigin: true,
|
||||
rewrite: path => path.replace(/^\/api/, ''),
|
||||
},
|
||||
},
|
||||
},
|
||||
// server: {
|
||||
// port: 2889,
|
||||
// port: 2888,
|
||||
// proxy: {
|
||||
// '/api': {
|
||||
// target: 'http://172.16.203.228:8000',
|
||||
// target: 'http://localhost',
|
||||
// //target: 'https://saber3.bladex.cn/api',
|
||||
// changeOrigin: true,
|
||||
// rewrite: path => path.replace(/^\/api/, ''),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
server: {
|
||||
port: 2889,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://172.16.203.228:8000',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'~': resolve(__dirname, './'),
|
||||
|
||||
Reference in New Issue
Block a user