feat: refine aggregate dashboard display

This commit is contained in:
2026-07-03 15:40:27 +08:00
parent ea35196c8c
commit 765ed0a104
6 changed files with 145 additions and 43 deletions

69
gptlog-process/gpdlog.md Normal file
View File

@@ -0,0 +1,69 @@
# GPT 执行过程日志
## 2026-07-02 装配中心任务看板修改
1. 接收任务:根据 `doc/装配中心任务看板修改方案.docx` 修改 `src/views/index.vue`,并创建 work 文档与过程日志。
2. 初始检查读取项目目录、doc 目录、现有 `src/views/index.vue`、参考项目 `C:\WorkYuanLy\MES_Manage_View_V20\src\views`
3. 文档解析:使用 .NET Zip 方式解包 docx读取 `word/document.xml`,确认方案要求包括移除缺件/已完成/可执行/及时送检/加急模块,右侧放发货和在制列表,左侧新增 16 行分类汇总表。
4. 截图确认:查看 docx 内 `image1.png` 当前页面截图和 `image2.png` 新增表格样式截图。
5. 参考页面读取:使用 UTF-8 读取发货状态、阀类任务、系统类任务、装配及时送检页面,确认接口和字段。
6. 用户中断并要求先上传远程:暂停页面修改,检查本地目录不是 Git 仓库。
7. 远程确认:访问 `http://154.8.160.151:3000/api/v1/version`,确认远程服务可用。
8. 仓库处理:账号下已有仓库与当前项目不完全匹配,因此创建新仓库 `zhangxinyang/zhuangpei-task-kanban`
9. 基线提交:执行 `git init -b main`,提交修改前代码,提交号 `e003b23 chore: baseline before dashboard update`
10. 基线推送:将 `main` 推送到 `http://154.8.160.151:3000/zhangxinyang/zhuangpei-task-kanban`,远程地址未写入密码。
11. 页面修改:重写 `src/views/index.vue`,保留缩放布局和全局注入的 `CreateData``ExecDatabase`
12. 布局调整:顶部改为派工汇总、齐套汇总、加急汇总、滞留汇总、装配合格率、异常未关闭 6 个卡片;左侧新增分类汇总表;右侧展示发货状态列表和在制任务列表。
13. 数据调整:移除不再需要的缺件明细、已完成任务、可执行任务数、及时送检数、加急任务总数查询。
14. 聚合逻辑:新增阀类、系统类、滞留查询,统一按 `计划数量 > 完成数量` 过滤,再按派工、齐套、加急、滞留统计。
15. 分类逻辑:阀类按产品名称是否包含“阀”分为阀类/其他类;系统类按指派对象包含“电气”或“机械/装配”分为电气类/装配类。
16. 构建验证:执行 `npm run build` 两次,均成功;第二次在调整 16 行表行高后再次通过。
17. 文档归档:创建 `work/装配中心任务看板修改方案`,复制原始方案 docx新增 README 和 01-06 文档。
18. 本地预览5173 端口已被其他进程占用,启动 Vite 到 `http://localhost:5174/`,访问入口返回 `StatusCode=200`
## 2026-07-03 日志记录规则确认
### 提问
用户要求建立后续日志记录规则:每次 GPT/Codex 执行完毕后,必须将完整执行过程日志追加至 `gptlog-process/gpdlog.md`,日志需要包含提问、结论和完整执行过程,并且必须使用中文记录。
### 执行过程
1. 接收用户规则要求,确认该要求适用于后续每次 GPT/Codex 任务执行结束后的记录动作。
2. 检查 `gptlog-process` 目录,确认目录已存在。
3. 检查 `gptlog-process/gpdlog.md`,确认日志文件已存在且已有历史中文执行记录。
4. 将本次规则确认作为新的日志段落追加到 `gptlog-process/gpdlog.md`
### 结论
后续每次 GPT/Codex 执行完毕后,将按用户要求把中文执行日志追加到 `gptlog-process/gpdlog.md`,内容包含提问、结论和完整执行过程。
## 2026-07-03 装配任务分类汇总显示调整
### 提问
用户要求修改 `C:\WorkYuanLy\装配任务看板\src\views\index.vue`
1. “装配任务分类汇总”中加急数量和滞留数量改成与指标列相同颜色。
2. “装配任务分类汇总”中分类图片不要作为背景图片,改成真实图片显示,布局为文字和数量在左、图片在右。
3. “发货状态列表”数据有几条就显示几条,不要重复填充。
4. 随后用户补充:四个分类图片是不一样的,图片路径在 `C:\WorkYuanLy\装配任务看板\src\assets\dashboard\`
### 执行过程
1. 读取 `src/views/index.vue`,定位分类汇总表模板、数量列样式、分类图片显示样式、发货列表显示函数和滚动函数。
2. 检查当前工作区状态,确认存在与本次任务无关的未提交文件,例如 `dist.zip``AGENTS.md`、旧 `work` 目录和临时 Word 锁文件;本次不处理这些无关项。
3. 将分类汇总数量列从固定白色改为绑定 `metric-${row.metricKey}` 类,使数量列颜色与指标列保持一致,包括加急红色、滞留橙色。
4. 将分类图片从伪元素背景改为 `<img>` 真实图片元素,布局调整为左侧分类名称和汇总数量、右侧图片。
5. 修改发货列表显示逻辑:`deliveryDisplayRows` 调用非循环模式,少于可见行数时按实际条数显示,不再用取模方式重复补满。
6. 调整发货列表滚动逻辑:当发货数据条数小于等于可见行数时,偏移量固定为 0超过可见行数时顺序滚动到末尾后回到开头。
7. 根据用户补充要求检查 `src/assets/dashboard` 目录,确认存在 `阀.png``其他.png``电气.png``机械.png`
8. 修改图片 import分别导入 `阀.png``其他.png``电气.png``机械.png`
9. 修改分类配置:`阀` 使用 `阀.png``其他` 使用 `其他.png``电气` 使用 `电气.png``装配` 使用 `机械.png`
10. 修改聚合行数据,给每个分类行附加对应 `image` 字段,模板使用 `row.image` 显示。
11. 调整 `.subclass-image` 样式为 `object-fit: contain`,避免产品图被裁切,并增加深色图片底板、边框和阴影以匹配看板风格。
12. 执行 `npm run build` 验证,构建成功,四个中文图片资源均被正常打包。
### 结论
已完成本次 `index.vue` 修改:分类汇总数量颜色与指标列一致,分类图片改为左文右图的真实图片显示,四个分类分别使用不同图片;发货状态列表不再重复填充不足的行数。构建验证通过,只有 Vite chunk size 和 Sass legacy JS API 的常规警告。

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

View File

@@ -31,8 +31,8 @@
<table class="summary-table">
<thead>
<tr>
<th class="group-th">大类</th>
<th class="subclass-th">分类</th>
<th class="group-th">项目</th>
<th class="subclass-th">产品</th>
<th class="metric-th">指标</th>
<!-- <th>条件</th> -->
<th class="count-th">数量</th>
@@ -50,16 +50,18 @@
v-if="row.childRowspan"
:rowspan="row.childRowspan"
class="subclass-cell"
:style="{ '--part-bg': `url(${partSchematicBg})` }"
>
<div class="category-total subclass-total">
<span>{{ row.child }}</span>
<strong>{{ row.childTotal }}</strong>
<div class="subclass-content">
<div class="category-total subclass-total">
<span>{{ row.child }}</span>
<strong>{{ row.childTotal }}</strong>
</div>
<img class="subclass-image" :src="row.image" alt="" />
</div>
</td>
<td class="metric-cell" :class="`metric-${row.metricKey}`">{{ row.metric }}</td>
<!-- <td class="condition-cell">{{ row.condition }}</td> -->
<td class="count-cell">{{ row.value }}</td>
<td class="count-cell" :class="`metric-${row.metricKey}`">{{ row.value }}</td>
</tr>
</tbody>
</table>
@@ -68,7 +70,7 @@
<aside class="right-column">
<article class="panel table-panel">
<div class="panel-caption">发货状态列表</div>
<div class="panel-caption">发货任务列表</div>
<div class="scroll-wrap">
<table>
<thead>
@@ -131,7 +133,10 @@
<script setup>
import { computed, inject, onMounted, onUnmounted, reactive, ref } from 'vue'
import partSchematicBg from '@/assets/dashboard/part-schematic-bg.png'
import assemblyImage from '@/assets/dashboard/机械.png'
import electricImage from '@/assets/dashboard/电气.png'
import otherImage from '@/assets/dashboard/其他.png'
import valveImage from '@/assets/dashboard/阀.png'
defineOptions({ name: 'AssemblyCenterTaskDashboard' })
@@ -178,15 +183,15 @@ const categoryGroups = [
{
parent: '阀类',
children: [
{ key: 'valveMain', label: '阀' },
{ key: 'valveOther', label: '其他' }
{ key: 'valveMain', label: '阀', image: valveImage },
{ key: 'valveOther', label: '其他', image: otherImage }
]
},
{
parent: '系统类',
children: [
{ key: 'systemElectric', label: '电气' },
{ key: 'systemAssembly', label: '装配' }
{ key: 'systemElectric', label: '电气', image: electricImage },
{ key: 'systemAssembly', label: '装配', image: assemblyImage }
]
}
]
@@ -225,6 +230,7 @@ const aggregateRows = computed(() => {
: 0,
child: child.label,
childTotal,
image: child.image,
childRowspan: metric.key === metricDefinitions[0].key ? metricDefinitions.length : 0,
categoryKey: child.key,
metricKey: metric.key,
@@ -239,7 +245,7 @@ const aggregateRows = computed(() => {
return rows
})
const deliveryDisplayRows = computed(() => makeDisplayRows(deliveryRows.value, deliveryOffset.value, VISIBLE_ROWS.delivery))
const deliveryDisplayRows = computed(() => makeDisplayRows(deliveryRows.value, deliveryOffset.value, VISIBLE_ROWS.delivery, false))
const taskDisplayRows = computed(() => makeDisplayRows(taskRows.value, taskOffset.value, VISIBLE_ROWS.task))
onMounted(() => {
@@ -282,15 +288,24 @@ const startClock = () => {
}
const startAllScroll = () => {
startScroll(deliveryRows, deliveryOffset)
startScroll(taskRows, taskOffset)
startScroll(deliveryRows, deliveryOffset, VISIBLE_ROWS.delivery, false)
startScroll(taskRows, taskOffset, VISIBLE_ROWS.task)
}
const startScroll = (rowsRef, offsetRef) => {
const startScroll = (rowsRef, offsetRef, visibleCount, loop = true) => {
const timerId = window.setInterval(() => {
if (rowsRef.value.length > 0) {
offsetRef.value = (offsetRef.value + 1) % rowsRef.value.length
if (rowsRef.value.length <= visibleCount) {
offsetRef.value = 0
return
}
if (loop) {
offsetRef.value = (offsetRef.value + 1) % rowsRef.value.length
return
}
const maxOffset = rowsRef.value.length - visibleCount
offsetRef.value = offsetRef.value >= maxOffset ? 0 : offsetRef.value + 1
}, 2500)
scrollTimers.push(timerId)
}
@@ -300,11 +315,20 @@ const stopAllScroll = () => {
scrollTimers = []
}
const makeDisplayRows = (rows, offset, visibleCount) => {
const makeDisplayRows = (rows, offset, visibleCount, loop = true) => {
if (!Array.isArray(rows) || rows.length === 0) {
return []
}
if (!loop) {
const maxOffset = Math.max(rows.length - visibleCount, 0)
const safeOffset = Math.min(Math.max(offset, 0), maxOffset)
return rows.slice(safeOffset, safeOffset + visibleCount).map((row, index) => ({
...row,
_idx: safeOffset + index + 1
}))
}
const displayRows = []
for (let index = 0; index < visibleCount; index += 1) {
const rowIndex = (offset + index) % rows.length
@@ -414,7 +438,7 @@ const applyDeliveryData = rows => {
completeSet: formatComplete(item.齐套),
issueStatus: formatSendStatus(item.发料状态),
operator: normalizeText(item.操作人),
startStatus: formatStartStatus(item.任务状态)
startStatus: item.合同装配工时
}))
}
@@ -621,10 +645,10 @@ const deliveryColumns = [
{ label: '物料编码', key: 'materialCode', width: '164px' },
{ label: '数量', key: 'quantity', width: '58px' },
{ label: '要求发货日期', key: 'requiredDate', width: '108px' },
{ label: '齐套', key: 'completeSet', width: '54px' },
{ label: '发料状态', key: 'issueStatus', width: '86px' },
{ label: '操作人', key: 'operator', width: '76px' },
{ label: '装配开工', key: 'startStatus', width: '82px' }
// { label: '齐套', key: 'completeSet', width: '54px' },
// { label: '发料状态', key: 'issueStatus', width: '86px' },
// { label: '操作人', key: 'operator', width: '76px' },
{ label: '累计工时', key: 'startStatus', width: '82px' }
]
const taskColumns = [
@@ -895,7 +919,7 @@ td {
.summary-table td {
height: 45px;
font-size: 19px;
font-size: 25px;
}
.group-th {
@@ -922,7 +946,6 @@ td {
}
.subclass-cell {
position: relative;
overflow: hidden;
color: #e8f7ff;
font-size: 18px;
@@ -931,16 +954,6 @@ td {
background: rgba(7, 45, 82, 0.58);
}
.subclass-cell::before {
position: absolute;
inset: 8px 12px;
content: '';
pointer-events: none;
background: var(--part-bg) center / contain no-repeat;
filter: drop-shadow(0 0 10px rgba(65, 199, 255, 0.35));
opacity: 0.28;
}
.category-total {
position: relative;
z-index: 1;
@@ -952,29 +965,50 @@ td {
.category-total span {
color: #ffffff;
font-size: 22px;
font-size: 32px;
font-weight: 800;
}
.category-total strong {
color: #ff3040;
font-size: 32px;
font-size: 40px;
font-weight: 900;
line-height: 1;
text-shadow: 0 0 10px rgba(255, 48, 64, 0.28);
}
.subclass-total {
min-height: 106px;
align-content: center;
justify-items: center;
min-width: 0;
}
.subclass-total span {
font-size: 20px;
font-size: 28px;
}
.subclass-total strong {
font-size: 31px;
font-size: 38px;
}
.subclass-content {
display: grid;
grid-template-columns: minmax(0, 1fr) 140px;
align-items: center;
gap: 8px;
min-height: 126px;
padding: 8px 8px 8px 14px;
}
.subclass-image {
width: 230px;
height: 145px;
// padding: 6px;
object-fit: contain;
// background: rgba(1, 22, 52, 0.9);
// border: 1px solid rgba(54, 200, 255, 0.38);
// border-radius: 4px;
// box-shadow: 0 0 12px rgba(65, 199, 255, 0.24);
}
.metric-cell {
@@ -1004,7 +1038,6 @@ td {
}
.count-cell {
color: #ffffff;
font-size: 26px;
font-weight: 900;
}