# Coal Feeding Motor Grid Alignment 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:** Update the motor sections in `src/views/coalFeeding.vue` so both `中间侧` and `回车侧` render motors two per row, with odd counts left-aligned, while keeping the left labels height-aligned with the right motor sections.

**Architecture:** Keep the existing block-based control-panel layout as the baseline. Add small computed helpers in `coalFeeding.vue` to calculate a shared minimum height for the middle and return motor sections, then apply that shared height to both the left labels and right motor containers. Replace the motor-item vertical stack with a two-column CSS grid wrapper while preserving all existing motor card interactions.

**Tech Stack:** Vue 3 `<script setup lang="ts">`, existing `coalFeeding.vue` local state, scoped CSS, Vite build verification

---

### Task 1: Add Shared Motor Height Helpers

**Files:**
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`

- [ ] **Step 1: Add height constants near the existing control-panel helper functions**

Insert these constants near `getCellId`, `getTempDropId`, and `isTimeRow` so the motor section height calculation is explicit and easy to tune:

```ts
const MOTOR_GRID_COLUMNS = 2;
const MOTOR_SECTION_MIN_HEIGHT = 120;
const MOTOR_CARD_HEIGHT = 92;
const MOTOR_SECTION_VERTICAL_PADDING = 8;
const MOTOR_GRID_ROW_GAP = 8;
```

- [ ] **Step 2: Add helpers to calculate motor row counts and shared heights**

Add these helpers below the constants so both left labels and right sections can consume the same height values:

```ts
const getMotorRowCount = (count: number) => {
  return Math.max(1, Math.ceil(count / MOTOR_GRID_COLUMNS));
};

const getMotorSectionHeight = (count: number) => {
  const rows = getMotorRowCount(count);
  const computedHeight =
    MOTOR_SECTION_VERTICAL_PADDING * 2 +
    rows * MOTOR_CARD_HEIGHT +
    (rows - 1) * MOTOR_GRID_ROW_GAP;

  return `${Math.max(MOTOR_SECTION_MIN_HEIGHT, computedHeight)}px`;
};

const middleMotorHeight = computed(() => {
  const maxCount = dataList.reduce((max, item) => Math.max(max, item.dianji.length), 0);
  return getMotorSectionHeight(maxCount);
});

const returnMotorHeight = computed(() => {
  const maxCount = dataList.reduce((max, item) => Math.max(max, item.dianji2.length), 0);
  return getMotorSectionHeight(maxCount);
});
```

- [ ] **Step 3: Import the missing Vue utility for computed values**

Update the top Vue import so the new computed helpers are available:

```ts
import { computed, onMounted, onUnmounted, reactive, ref } from "vue";
```

- [ ] **Step 4: Run a quick search to confirm the new helper symbols exist once**

Run: `rg -n "MOTOR_GRID_COLUMNS|getMotorRowCount|middleMotorHeight|returnMotorHeight|computed" src\views\coalFeeding.vue`

Expected: matches for the new constants/helpers and the updated Vue import in `src\views\coalFeeding.vue`

### Task 2: Apply Shared Heights to Labels and Motor Sections

**Files:**
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`

- [ ] **Step 1: Bind the left motor labels to the shared section heights**

Update the two left-side labels so they use the same computed heights as the corresponding right-side sections:

```vue
<div class="control-panel__label control-panel__label--motor" :style="{ minHeight: middleMotorHeight }">
  中间侧
</div>
```

```vue
<div class="control-panel__label control-panel__label--motor" :style="{ minHeight: returnMotorHeight }">
  回车侧
</div>
```

- [ ] **Step 2: Bind the right-side upper motor section to the middle motor height**

Update the `中间侧` motor wrapper from a plain section cell into a shared-height container:

```vue
<div class="control-panel__cell control-panel__cell--motor" :style="{ minHeight: middleMotorHeight }">
  <div class="control-panel__motor-grid">
    <!-- existing dianji cards stay here -->
  </div>
</div>
```

- [ ] **Step 3: Bind the right-side lower motor section to the return motor height**

Update the `回车侧` motor wrapper the same way:

```vue
<div class="control-panel__cell control-panel__cell--motor" :style="{ minHeight: returnMotorHeight }">
  <div class="control-panel__motor-grid">
    <!-- existing dianji2 cards stay here -->
  </div>
</div>
```

### Task 3: Convert Motor Items to Two-Column Grid Layout

**Files:**
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`

- [ ] **Step 1: Wrap the upper motor cards in a dedicated grid container**

Move the existing `item.dianji` loop inside a grid wrapper without changing the motor card internals:

```vue
<div class="control-panel__motor-grid">
  <div
    v-for="dj in item.dianji"
    :key="dj.id"
    :id="dj.id"
    class="control-panel__motor-card flexs flex_col items_center gap0_25 text_center dianji_status"
    @dragover="onDjDragOver"
    @drop="onDjDrop($event, dj, 'id')"
  >
    <!-- keep existing motor content and click handlers -->
  </div>
</div>
```

- [ ] **Step 2: Wrap the lower motor cards in the same grid container pattern**

Apply the same grid wrapper to `item.dianji2`, keeping the existing click handler with the `dianji2` motor key:

```vue
<div class="control-panel__motor-grid">
  <div
    v-for="dj in item.dianji2"
    :key="dj.id"
    class="control-panel__motor-card flexs flex_col items_center gap0_25 text_center dianji_status"
    @dragover="onDjDragOver"
    @drop="onDjDrop($event, dj, 'id')"
  >
    <!-- keep existing motor content and click handlers -->
  </div>
</div>
```

- [ ] **Step 3: Add the motor grid styles so cards render two per row**

Append these scoped styles near the current control-panel CSS:

```css
.control-panel__motor-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  align-items: start;
  width: 100%;
}

.control-panel__motor-card {
  width: 100%;
  min-height: 92px;
}
```

This keeps odd counts left-aligned automatically because the final odd card occupies the first cell of the last grid row.

- [ ] **Step 4: Update the motor container styles to support top-aligned grid content**

Refine the existing motor container style so the new grid aligns correctly:

```css
.control-panel__cell--motor {
  min-height: 120px;
  background: #eff6ff;
  align-items: flex-start;
  justify-content: flex-start;
  padding: 8px;
}
```

### Task 4: Verify Layout and Build

**Files:**
- Modify: `D:\testProjects\yaodaoxiangmu\vite_vue3_tsproject_change\src\views\coalFeeding.vue`

- [ ] **Step 1: Run a selector scan to confirm the new motor-grid classes are present**

Run: `rg -n "control-panel__motor-grid|control-panel__motor-card|middleMotorHeight|returnMotorHeight" src\views\coalFeeding.vue`

Expected: matches for the new template bindings, helper logic, and styles inside `src\views\coalFeeding.vue`

- [ ] **Step 2: Run the production build**

Run: `npm run build`

Expected: Vite finishes successfully with no Vue template or TypeScript errors.

- [ ] **Step 3: Manually verify the refined motor layout**

Open the page and confirm:

```text
1. 中间侧 motors render two per row.
2. 回车侧 motors render two per row.
3. If a section has an odd motor count, the last motor stays on the left side of the final row.
4. The left labels for 中间侧 and 回车侧 stay height-aligned with the right motor blocks.
5. Existing motor click and drag behavior still works.
```

- [ ] **Step 4: Record completion status instead of committing**

Because this workspace has no `.git` directory, do not run commit commands here.
In the final handoff, report:

```text
- build result
- motor grid conversion status
- left/right height alignment status
- interaction checks completed
```
