feat: refine aggregate dashboard display
This commit is contained in:
69
gptlog-process/gpdlog.md
Normal file
69
gptlog-process/gpdlog.md
Normal 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 的常规警告。
|
||||||
BIN
src/assets/dashboard/其他.png
Normal file
BIN
src/assets/dashboard/其他.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 MiB |
BIN
src/assets/dashboard/机械.png
Normal file
BIN
src/assets/dashboard/机械.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
BIN
src/assets/dashboard/电气.png
Normal file
BIN
src/assets/dashboard/电气.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 126 KiB |
BIN
src/assets/dashboard/阀.png
Normal file
BIN
src/assets/dashboard/阀.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
@@ -31,8 +31,8 @@
|
|||||||
<table class="summary-table">
|
<table class="summary-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="group-th">大类</th>
|
<th class="group-th">项目</th>
|
||||||
<th class="subclass-th">分类</th>
|
<th class="subclass-th">产品</th>
|
||||||
<th class="metric-th">指标</th>
|
<th class="metric-th">指标</th>
|
||||||
<!-- <th>条件</th> -->
|
<!-- <th>条件</th> -->
|
||||||
<th class="count-th">数量</th>
|
<th class="count-th">数量</th>
|
||||||
@@ -50,16 +50,18 @@
|
|||||||
v-if="row.childRowspan"
|
v-if="row.childRowspan"
|
||||||
:rowspan="row.childRowspan"
|
:rowspan="row.childRowspan"
|
||||||
class="subclass-cell"
|
class="subclass-cell"
|
||||||
:style="{ '--part-bg': `url(${partSchematicBg})` }"
|
|
||||||
>
|
>
|
||||||
<div class="category-total subclass-total">
|
<div class="subclass-content">
|
||||||
<span>{{ row.child }}</span>
|
<div class="category-total subclass-total">
|
||||||
<strong>{{ row.childTotal }}</strong>
|
<span>{{ row.child }}</span>
|
||||||
|
<strong>{{ row.childTotal }}</strong>
|
||||||
|
</div>
|
||||||
|
<img class="subclass-image" :src="row.image" alt="" />
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="metric-cell" :class="`metric-${row.metricKey}`">{{ row.metric }}</td>
|
<td class="metric-cell" :class="`metric-${row.metricKey}`">{{ row.metric }}</td>
|
||||||
<!-- <td class="condition-cell">{{ row.condition }}</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>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -68,7 +70,7 @@
|
|||||||
|
|
||||||
<aside class="right-column">
|
<aside class="right-column">
|
||||||
<article class="panel table-panel">
|
<article class="panel table-panel">
|
||||||
<div class="panel-caption">发货状态列表</div>
|
<div class="panel-caption">发货任务列表</div>
|
||||||
<div class="scroll-wrap">
|
<div class="scroll-wrap">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -131,7 +133,10 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, inject, onMounted, onUnmounted, reactive, ref } from 'vue'
|
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' })
|
defineOptions({ name: 'AssemblyCenterTaskDashboard' })
|
||||||
|
|
||||||
@@ -178,15 +183,15 @@ const categoryGroups = [
|
|||||||
{
|
{
|
||||||
parent: '阀类',
|
parent: '阀类',
|
||||||
children: [
|
children: [
|
||||||
{ key: 'valveMain', label: '阀类' },
|
{ key: 'valveMain', label: '阀', image: valveImage },
|
||||||
{ key: 'valveOther', label: '其他类' }
|
{ key: 'valveOther', label: '其他', image: otherImage }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
parent: '系统类',
|
parent: '系统类',
|
||||||
children: [
|
children: [
|
||||||
{ key: 'systemElectric', label: '电气类' },
|
{ key: 'systemElectric', label: '电气', image: electricImage },
|
||||||
{ key: 'systemAssembly', label: '装配类' }
|
{ key: 'systemAssembly', label: '装配', image: assemblyImage }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -225,6 +230,7 @@ const aggregateRows = computed(() => {
|
|||||||
: 0,
|
: 0,
|
||||||
child: child.label,
|
child: child.label,
|
||||||
childTotal,
|
childTotal,
|
||||||
|
image: child.image,
|
||||||
childRowspan: metric.key === metricDefinitions[0].key ? metricDefinitions.length : 0,
|
childRowspan: metric.key === metricDefinitions[0].key ? metricDefinitions.length : 0,
|
||||||
categoryKey: child.key,
|
categoryKey: child.key,
|
||||||
metricKey: metric.key,
|
metricKey: metric.key,
|
||||||
@@ -239,7 +245,7 @@ const aggregateRows = computed(() => {
|
|||||||
return rows
|
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))
|
const taskDisplayRows = computed(() => makeDisplayRows(taskRows.value, taskOffset.value, VISIBLE_ROWS.task))
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -282,15 +288,24 @@ const startClock = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const startAllScroll = () => {
|
const startAllScroll = () => {
|
||||||
startScroll(deliveryRows, deliveryOffset)
|
startScroll(deliveryRows, deliveryOffset, VISIBLE_ROWS.delivery, false)
|
||||||
startScroll(taskRows, taskOffset)
|
startScroll(taskRows, taskOffset, VISIBLE_ROWS.task)
|
||||||
}
|
}
|
||||||
|
|
||||||
const startScroll = (rowsRef, offsetRef) => {
|
const startScroll = (rowsRef, offsetRef, visibleCount, loop = true) => {
|
||||||
const timerId = window.setInterval(() => {
|
const timerId = window.setInterval(() => {
|
||||||
if (rowsRef.value.length > 0) {
|
if (rowsRef.value.length <= visibleCount) {
|
||||||
offsetRef.value = (offsetRef.value + 1) % rowsRef.value.length
|
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)
|
}, 2500)
|
||||||
scrollTimers.push(timerId)
|
scrollTimers.push(timerId)
|
||||||
}
|
}
|
||||||
@@ -300,11 +315,20 @@ const stopAllScroll = () => {
|
|||||||
scrollTimers = []
|
scrollTimers = []
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeDisplayRows = (rows, offset, visibleCount) => {
|
const makeDisplayRows = (rows, offset, visibleCount, loop = true) => {
|
||||||
if (!Array.isArray(rows) || rows.length === 0) {
|
if (!Array.isArray(rows) || rows.length === 0) {
|
||||||
return []
|
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 = []
|
const displayRows = []
|
||||||
for (let index = 0; index < visibleCount; index += 1) {
|
for (let index = 0; index < visibleCount; index += 1) {
|
||||||
const rowIndex = (offset + index) % rows.length
|
const rowIndex = (offset + index) % rows.length
|
||||||
@@ -414,7 +438,7 @@ const applyDeliveryData = rows => {
|
|||||||
completeSet: formatComplete(item.齐套),
|
completeSet: formatComplete(item.齐套),
|
||||||
issueStatus: formatSendStatus(item.发料状态),
|
issueStatus: formatSendStatus(item.发料状态),
|
||||||
operator: normalizeText(item.操作人),
|
operator: normalizeText(item.操作人),
|
||||||
startStatus: formatStartStatus(item.任务状态)
|
startStatus: item.合同装配工时
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,10 +645,10 @@ const deliveryColumns = [
|
|||||||
{ label: '物料编码', key: 'materialCode', width: '164px' },
|
{ label: '物料编码', key: 'materialCode', width: '164px' },
|
||||||
{ label: '数量', key: 'quantity', width: '58px' },
|
{ label: '数量', key: 'quantity', width: '58px' },
|
||||||
{ label: '要求发货日期', key: 'requiredDate', width: '108px' },
|
{ label: '要求发货日期', key: 'requiredDate', width: '108px' },
|
||||||
{ label: '齐套', key: 'completeSet', width: '54px' },
|
// { label: '齐套', key: 'completeSet', width: '54px' },
|
||||||
{ label: '发料状态', key: 'issueStatus', width: '86px' },
|
// { label: '发料状态', key: 'issueStatus', width: '86px' },
|
||||||
{ label: '操作人', key: 'operator', width: '76px' },
|
// { label: '操作人', key: 'operator', width: '76px' },
|
||||||
{ label: '装配开工', key: 'startStatus', width: '82px' }
|
{ label: '累计工时', key: 'startStatus', width: '82px' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const taskColumns = [
|
const taskColumns = [
|
||||||
@@ -895,7 +919,7 @@ td {
|
|||||||
|
|
||||||
.summary-table td {
|
.summary-table td {
|
||||||
height: 45px;
|
height: 45px;
|
||||||
font-size: 19px;
|
font-size: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-th {
|
.group-th {
|
||||||
@@ -922,7 +946,6 @@ td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.subclass-cell {
|
.subclass-cell {
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: #e8f7ff;
|
color: #e8f7ff;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@@ -931,16 +954,6 @@ td {
|
|||||||
background: rgba(7, 45, 82, 0.58);
|
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 {
|
.category-total {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@@ -952,29 +965,50 @@ td {
|
|||||||
|
|
||||||
.category-total span {
|
.category-total span {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-size: 22px;
|
font-size: 32px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-total strong {
|
.category-total strong {
|
||||||
color: #ff3040;
|
color: #ff3040;
|
||||||
font-size: 32px;
|
font-size: 40px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
text-shadow: 0 0 10px rgba(255, 48, 64, 0.28);
|
text-shadow: 0 0 10px rgba(255, 48, 64, 0.28);
|
||||||
}
|
}
|
||||||
|
|
||||||
.subclass-total {
|
.subclass-total {
|
||||||
min-height: 106px;
|
|
||||||
align-content: center;
|
align-content: center;
|
||||||
|
justify-items: center;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subclass-total span {
|
.subclass-total span {
|
||||||
font-size: 20px;
|
font-size: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subclass-total strong {
|
.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 {
|
.metric-cell {
|
||||||
@@ -1004,7 +1038,6 @@ td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.count-cell {
|
.count-cell {
|
||||||
color: #ffffff;
|
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user