product_control_client/docs/superpowers/plans/2026-03-26-coal-feeding-lay...

11 KiB

Coal Feeding Layout Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Replace the right-side <table> in src/views/coalFeeding.vue with a block-based unit-column layout while preserving the current row/column visual structure and existing interactions.

Architecture: Keep all business state and handlers in src/views/coalFeeding.vue, but reorganize the right-side rendering around a fixed parameter label column plus horizontally scrollable unit columns. Use small in-file render configurations for upper rows, lower rows, and fixed-height visual blocks so the new block layout stays aligned with the former table structure.

Tech Stack: Vue 3 <script setup lang="ts">, existing CSS utility classes, @opentiny/vue, Vite build verification


Task 1: Prepare In-File Layout Metadata

Files:

  • Modify: D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue

  • Step 1: Add typed row metadata for the upper and lower time-setting blocks

Insert these constants near the existing type declarations in src/views/coalFeeding.vue so the new template can loop over block rows instead of repeating hard-coded table rows:

type TimeFieldKey =
  | "tmyxsds"
  | "tmtzsds"
  | "tmljsds"
  | "blyxsds"
  | "tmyxsdx"
  | "tmtzsdx"
  | "tmljsdx"
  | "blyxsdx";

type DisplayFieldKey =
  | "tmljsjs"
  | "tmljsjx"
  | "tempera1"
  | "tempera2"
  | "carNumber";

type BlockRowConfig = {
  key: TimeFieldKey | DisplayFieldKey;
  label: string;
  kind: "time" | "display";
  idPrefix?: string;
};

const upperBlockRows: BlockRowConfig[] = [
  { key: "tmyxsds", label: "投煤运行时间设定", kind: "time", idPrefix: "tmyxsds" },
  { key: "tmtzsds", label: "投煤停止时间设定", kind: "time", idPrefix: "tmtzsds" },
  { key: "tmljsds", label: "投煤累积时间设定", kind: "time", idPrefix: "tmljsds" },
  { key: "blyxsds", label: "布料运行时间设定", kind: "time", idPrefix: "blyxsds" },
  { key: "tmljsjs", label: "投煤累积时间", kind: "display", idPrefix: "tmljsjs" },
];

const lowerBlockRows: BlockRowConfig[] = [
  { key: "tmyxsdx", label: "投煤运行时间设定", kind: "time", idPrefix: "tmyxsdx" },
  { key: "tmtzsdx", label: "投煤停止时间设定", kind: "time", idPrefix: "tmtzsdx" },
  { key: "tmljsdx", label: "投煤累积时间设定", kind: "time", idPrefix: "tmljsdx" },
  { key: "blyxsdx", label: "布料运行时间设定", kind: "time", idPrefix: "blyxsdx" },
  { key: "tmljsjx", label: "投煤累积时间", kind: "display", idPrefix: "tmljsjx" },
];
  • Step 2: Add typed helpers for rendering ids and editable values

Add these helpers below the row metadata so the template can render the new block layout without string duplication:

const getCellId = (prefix: string | undefined, index: number) => {
  return prefix ? `${prefix}${index}` : undefined;
};

const getTempDropId = (side: "tempera1" | "tempera2", item: any, index: number) => {
  return `${side}_unit${item.unit}_${index + 1}`;
};

const isTimeRow = (row: BlockRowConfig) => row.kind === "time";
  • Step 3: Run a quick type sanity scan before touching the template

Run: rg -n "upperBlockRows|lowerBlockRows|getCellId|getTempDropId|isTimeRow" src\views\coalFeeding.vue

Expected: five matches for the newly added metadata/helpers, all inside src/views\coalFeeding.vue

Task 2: Replace the Table Template With a Column Layout

Files:

  • Modify: D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue

  • Step 1: Replace the <table> wrapper with a block-based layout shell

Replace the current <table> section with this outer structure inside the existing right-side panel container:

<div class="control-panel">
  <div class="control-panel__labels">
    <div class="control-panel__corner"></div>
    <div class="control-panel__section">
      <div
        v-for="row in upperBlockRows"
        :key="`upper-label-${row.key}`"
        class="control-panel__label control-panel__label--row"
      >
        {{ row.label }}
      </div>
      <div class="control-panel__label control-panel__label--motor">中间侧</div>
    </div>
    <div class="control-panel__label control-panel__label--temp">2侧温度</div>
    <div class="control-panel__label control-panel__label--car">车位号</div>
    <div class="control-panel__label control-panel__label--temp">1侧温度</div>
    <div class="control-panel__section">
      <div
        v-for="row in lowerBlockRows"
        :key="`lower-label-${row.key}`"
        class="control-panel__label control-panel__label--row"
      >
        {{ row.label }}
      </div>
      <div class="control-panel__label control-panel__label--motor">回车侧</div>
    </div>
  </div>

  <div class="control-panel__columns">
    <!-- unit columns render here -->
  </div>
</div>
  • Step 2: Render each unit as one vertical column

Inside control-panel__columns, render one column per dataList item using this structure:

<div
  v-for="(item, index) in dataList"
  :key="item.team"
  class="control-panel__column"
>
  <div class="control-panel__column-header">
    <span class="inline_flex items_center justify_center h1_25 min_w_6 px0_375 rounded bg_primary text_primary_color fontw700 text_11">
      {{ item.team }}
    </span>
  </div>

  <div class="control-panel__section">
    <!-- upper rows -->
  </div>

  <div
    :id="getTempDropId('tempera2', item, index)"
    class="control-panel__cell control-panel__cell--temp"
    @dragover="onDjDragOver"
    @drop="onDjDrop($event, item, getTempDropId('tempera2', item, index))"
  >
    <span class="text_xs fontw700 min_w_2_25 text_right text_red">{{ item.tempera2 }}</span>
    <span> °C</span>
  </div>

  <div class="control-panel__cell control-panel__cell--car">
    <span class="inline_flex items_center justify_center h1_25 min_w_1_75 px0_375 rounded_md bg_primary text_primary_color my0_3 fontw700 text_sm shadow_sm">
      {{ item.carNumber }}
    </span>
  </div>

  <div
    :id="getTempDropId('tempera1', item, index)"
    class="control-panel__cell control-panel__cell--temp"
    @dragover="onDjDragOver"
    @drop="onDjDrop($event, item, getTempDropId('tempera1', item, index))"
  >
    <span class="text_xs fontw700 min_w_2_25 text_right text_red">{{ item.tempera1 }}</span>
    <span> °C</span>
  </div>

  <div class="control-panel__section">
    <!-- lower rows -->
  </div>
</div>
  • Step 3: Render the upper and lower time/display rows from metadata

Use this pattern in both the upper and lower sections so editable rows and display rows remain consistent:

<div
  v-for="row in upperBlockRows"
  :key="`upper-${item.team}-${row.key}`"
  :id="getCellId(row.idPrefix, index)"
  class="control-panel__cell control-panel__cell--row"
>
  <span v-if="!isTimeRow(row) || item.statusAuto" class="px0_25 py0_125 text_center text_xs color_base">
    {{ item[row.key] }}
  </span>
  <input
    v-else
    v-model="item[row.key]"
    class="px0_25 py0_125 text_center text_xs color_base control-panel__input"
  />
</div>

For the lower section, switch upperBlockRows to lowerBlockRows and keep the same markup.

  • Step 4: Move the middle-side and return-side motor rendering into the new sections

Render the motor groups as fixed-height cells so the old 中间侧 and 回车侧 rows become block elements:

<div class="control-panel__cell control-panel__cell--motor">
  <div
    v-for="dj in item.dianji"
    :id="dj.id"
    :key="dj.id"
    class="flexs flex_col items_center gap0_25 text_center dianji_status"
    @dragover="onDjDragOver"
    @drop="onDjDrop($event, dj, 'id')"
  >
    <!-- preserve existing motor inner markup and click handlers -->
  </div>
</div>

For the lower section, keep the same structure but iterate item.dianji2 and continue calling toggleDjStop(dj, item.team, item.statusAuto, 'dianji2').

Task 3: Rebuild Styling for Alignment and Visual Parity

Files:

  • Modify: D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue

  • Step 1: Add block-layout container styles

Append styles like these near the existing <style> block:

.control-panel {
  display: flex;
  align-items: flex-start;
  min-width: max-content;
}

.control-panel__labels {
  flex: 0 0 132px;
}

.control-panel__columns {
  display: flex;
  align-items: flex-start;
}

.control-panel__column {
  min-width: 96px;
  border-right: 1px solid rgba(148, 163, 184, 0.6);
}
  • Step 2: Add fixed row heights so labels and unit cells stay aligned

Add shared row classes to replace the natural table sizing:

.control-panel__corner,
.control-panel__column-header {
  height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-bottom: 1px solid rgba(148, 163, 184, 0.6);
}

.control-panel__label--row,
.control-panel__cell--row {
  height: 42px;
}

.control-panel__label--temp,
.control-panel__cell--temp,
.control-panel__label--car,
.control-panel__cell--car {
  height: 42px;
}

.control-panel__label--motor,
.control-panel__cell--motor {
  min-height: 120px;
}
  • Step 3: Restore row backgrounds and dense control-panel styling

Add styling hooks so the new block elements preserve the current visual categories:

.control-panel__label,
.control-panel__cell {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.25rem;
  border-bottom: 1px solid rgba(148, 163, 184, 0.6);
  background: #ffffff;
}

.control-panel__label--temp,
.control-panel__cell--temp {
  background: #fff1e8;
}

.control-panel__label--car,
.control-panel__cell--car {
  background: #dbeafe;
}

.control-panel__label--motor,
.control-panel__cell--motor {
  background: #eff6ff;
}

.control-panel__input {
  width: 6rem;
}
  • Step 4: Run a targeted selector scan to make sure the old <table> layout is gone

Run: rg -n "<table|<thead|<tbody|<tr|<td|<th" src\views\coalFeeding.vue

Expected: no matches

Task 4: Verify Build and Behavior

Files:

  • Modify: D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue

  • Step 1: Run the production build

Run: npm run build

Expected: Vite build completes successfully and outputs the dist bundle without Vue template or TypeScript errors.

  • Step 2: Manually verify the right panel structure in the app

Open the page and confirm:

1. The right panel no longer uses a table layout.
2. The left parameter column remains visually aligned with each unit column.
3. Each unit column shows:
   - upper block
   - 2侧温度
   - 车位号
   - 1侧温度
   - lower block
4. 中间侧 and 回车侧 motor areas still render with the expected controls.
  • Step 3: Manually verify preserved interactions

Confirm these behaviors still work:

1. Auto mode shows text; manual mode shows inputs.
2. Clicking a motor still toggles stop/run state as before.
3. Temperature targets still accept drag-and-drop point binding.
4. Bottom manual/auto switch panel still renders and responds.
  • Step 4: Record completion status instead of committing

Because this workspace currently has no .git directory, do not run commit commands here. Instead, record completion by listing the verified items in the final handoff:

- build result
- layout conversion status
- interaction checks completed