JhHardwareWRS/docs/superpowers/plans/2026-06-23-continuous-die-changeover-count.md
2026-06-24 15:19:14 +08:00

34 KiB

Continuous Die Changeover Count 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: Add the special stamping method 连续模, require/report 换料次数 for continuous die report items, and carry it through worker reporting, admin review, records, manager dashboard, and Excel export.

Architecture: Treat 连续模 as a special stamping method recognized by exact trimmed text, parallel to 清洗 but without changing normal work-time, device, beat, workload, lock, or wage rules. Store 换料次数 on production_report_items because it belongs to one product/process detail, not the whole report. Keep product 冲压方式 free-text with two suggested values, so field usage remains flexible for current shop-floor terms.

Tech Stack: FastAPI, SQLAlchemy, Pydantic, MySQL, openpyxl, WeChat Mini Program WXML/WXSS/JS.


File Map

Backend:

  • Create: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/services/continuous_die.py
  • Create: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/scripts/migrate_continuous_die_changeover_count.py
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/models.py
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/schemas.py
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/services/serializers.py
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/routers/reports.py
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/routers/reviews.py
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/routers/dashboard.py
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/services/excel_export.py
  • Test: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/tests/test_continuous_die.py

Frontend:

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/utils/api.js
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/manageProducts/manageProducts.js
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/manageProducts/manageProducts.wxml
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/manageProducts/manageProducts.wxss
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/deviceQr/deviceQr.wxml
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/deviceQr/deviceQr.wxss
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportForm/reportForm.js
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportForm/reportForm.wxml
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportForm/reportForm.wxss
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/review/review.js
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/review/review.wxml
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/review/review.wxss
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/records/records.wxml
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/records/records.wxss
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/dashboard/dashboard.wxml
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/dashboard/dashboard.wxss
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportResult/reportResult.js
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportResult/reportResult.wxml
  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportResult/reportResult.wxss

Important non-goal:

  • Do not include 换料次数 in 参考工资 yet. Current wage formula is 成品数量 * 工序单价; there is no maintained 换料单价, so calculating extra pay now would require a hidden hard-coded price. This task only records and exports the count for later wage integration.

Task 1: Backend Continuous Die Recognition And Schema Field

Files:

  • Create: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/services/continuous_die.py

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/models.py

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/schemas.py

  • Create: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/scripts/migrate_continuous_die_changeover_count.py

  • Test: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/tests/test_continuous_die.py

  • Step 1: Write the helper test

Add this to /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/tests/test_continuous_die.py:

from app.services.continuous_die import (
    CONTINUOUS_DIE_STAMPING_METHOD,
    is_continuous_die_stamping_method,
)


def test_continuous_die_stamping_method_is_trimmed_exact_match():
    assert CONTINUOUS_DIE_STAMPING_METHOD == "连续模"
    assert is_continuous_die_stamping_method("连续模") is True
    assert is_continuous_die_stamping_method(" 连续模 ") is True
    assert is_continuous_die_stamping_method("连续模具") is False
    assert is_continuous_die_stamping_method("清洗") is False
    assert is_continuous_die_stamping_method(None) is False
  • Step 2: Run the new test and verify it fails

Run:

cd /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint
.venv/bin/python -m pytest tests/test_continuous_die.py -q

Expected: FAIL because app.services.continuous_die does not exist.

  • Step 3: Create the backend helper

Create /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/services/continuous_die.py:

CONTINUOUS_DIE_STAMPING_METHOD = "连续模"


def clean_text(value: str | None) -> str:
    return str(value or "").strip()


def is_continuous_die_stamping_method(value: str | None) -> bool:
    return clean_text(value) == CONTINUOUS_DIE_STAMPING_METHOD


def is_continuous_die_product(product) -> bool:
    return bool(product) and is_continuous_die_stamping_method(getattr(product, "stamping_method", None))


def is_continuous_die_item(item) -> bool:
    return is_continuous_die_stamping_method(getattr(item, "stamping_method", None))
  • Step 4: Add the DB model field

In /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/models.py, add this field to ProductionReportItem immediately after process_unit_price_yuan:

changeover_count: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False, default=0)
  • Step 5: Add the migration script

Create /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/scripts/migrate_continuous_die_changeover_count.py:

from sqlalchemy import text

from app.database import engine


def main() -> None:
    with engine.begin() as conn:
        exists = conn.execute(
            text(
                """
                SELECT COUNT(*)
                FROM information_schema.columns
                WHERE table_schema = DATABASE()
                  AND table_name = 'production_report_items'
                  AND column_name = 'changeover_count'
                """
            )
        ).scalar()
        if not exists:
            conn.execute(
                text(
                    """
                    ALTER TABLE production_report_items
                    ADD COLUMN changeover_count DECIMAL(12, 2) NOT NULL DEFAULT 0
                    AFTER process_unit_price_yuan
                    """
                )
            )
    print("migrate_continuous_die_changeover_count done")


if __name__ == "__main__":
    main()
  • Step 6: Add schema fields

In /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/schemas.py, add these exact fields to the existing classes:

# class MoldNameOut
is_continuous_die: bool = False

# class ProductOption
is_continuous_die: bool = False

# class ReportDraftItem
changeover_count: float = 0
is_continuous_die: bool = False

# class ReportDraftBlock
is_continuous_die: bool = False

# class ReportSubmitItem
changeover_count: float = Field(default=0, ge=0)
is_continuous_die: bool = False

# class ReportItemOut
changeover_count: float = 0
is_continuous_die: bool = False

# class ReportItemCorrection
changeover_count: float | None = Field(default=None, ge=0)

# class DeviceQrOut
is_continuous_die: bool = False

# class DashboardRow
changeover_count: float = 0
is_continuous_die: bool = False
  • Step 7: Run the helper test

Run:

cd /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint
.venv/bin/python -m pytest tests/test_continuous_die.py -q

Expected: PASS.


Task 2: Backend Report Flow, Review Flow, Dashboard, And Export

Files:

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/services/serializers.py

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/routers/reports.py

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/routers/reviews.py

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/routers/dashboard.py

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/services/excel_export.py

  • Test: /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/tests/test_continuous_die.py

  • Step 1: Extend backend serialization tests

Append this test to /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/tests/test_continuous_die.py:

from types import SimpleNamespace

from app.services.serializers import product_option


def test_product_option_marks_continuous_die():
    product = SimpleNamespace(
        attendance_point_name="嘉恒",
        project_no="P001",
        product_name="测试产品",
        profile_no=None,
        material_code=None,
        material_name=None,
        supplier=None,
        product_net_weight_kg=None,
        product_gross_weight_kg=None,
        scrap_loss_rate=None,
        waste_price_yuan_per_kg=None,
        device_no="",
        process_name="1",
        stamping_method=" 连续模 ",
        operator_count=1,
        process_unit_price_yuan=0,
        standard_beat=10,
        standard_workload=0,
    )

    option = product_option(product)

    assert option.stamping_method == " 连续模 "
    assert option.is_continuous_die is True
    assert option.is_cleaning is False
  • Step 2: Run the test and verify it fails

Run:

cd /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint
.venv/bin/python -m pytest tests/test_continuous_die.py -q

Expected: FAIL because ProductOption is not yet populated with is_continuous_die.

  • Step 3: Update serializers

In /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/services/serializers.py:

Add import:

from app.services.continuous_die import is_continuous_die_item, is_continuous_die_product

Update product_option:

def product_option(product: Product) -> ProductOption:
    data = product_out(product).model_dump()
    data["is_misc"] = is_misc_product(product)
    data["is_multi_person"] = is_multi_person_product(product)
    data["is_continuous_die"] = is_continuous_die_product(product)
    return ProductOption(**data)

Update report_out item serialization so every ReportItemOut includes:

changeover_count=as_float(item.changeover_count),
is_continuous_die=is_continuous_die_item(item),

Update correction maps so changeover_count is treated as numeric:

numeric_fields = {
    "operator_count",
    "process_unit_price_yuan",
    "good_qty",
    "defect_qty",
    "scrap_qty",
    "changeover_count",
}
  • Step 4: Update report draft and submit

In /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/routers/reports.py:

Add import:

from app.services.continuous_die import is_continuous_die_item, is_continuous_die_product

When building ReportDraftBlock, compute:

block_is_continuous_die = any(option.is_continuous_die for option in options)

Set:

is_continuous_die=block_is_continuous_die,

When building ReportDraftItem, set:

changeover_count=0,
is_continuous_die=is_continuous_die_product(first) if first else False,

When submitting normal report items, compute:

item_is_continuous_die = is_continuous_die_product(product) or is_continuous_die_item(item)
changeover_count = as_float(item.changeover_count)
if not item_is_continuous_die:
    changeover_count = 0
if changeover_count < 0:
    raise HTTPException(status_code=400, detail="换料次数不能为负数")

Inside each ProductionReportItem( constructor in normal report submission, add this keyword argument:

changeover_count=changeover_count,

In cleaning submit path, set:

changeover_count=0,
  • Step 5: Update admin review

In /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/routers/reviews.py:

Add import:

from app.services.continuous_die import is_continuous_die_item, is_continuous_die_product

In _snapshot, include:

"changeover_count": float(item.changeover_count or 0),

When product correction changes an item, set:

item.changeover_count = item.changeover_count if is_continuous_die_product(product) else 0

When applying item corrections, add:

if correction.changeover_count is not None:
    next_changeover_count = as_float(correction.changeover_count)
    if next_changeover_count < 0:
        raise HTTPException(status_code=400, detail="换料次数不能为负数")
    if not is_continuous_die_item(item):
        next_changeover_count = 0
    if next_changeover_count != as_float(item.changeover_count):
        item.changeover_count = next_changeover_count
        has_correction = True

In system auto-submit validation, continuous die follows normal production validation; do not skip device or quantity validation.

  • Step 6: Update dashboard aggregation

In /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/routers/dashboard.py:

Add import:

from app.services.continuous_die import is_continuous_die_item, is_continuous_die_product

Add correction label:

"changeover_count": "换料次数",

Inside each item loop:

is_continuous_die = is_continuous_die_item(item) or is_continuous_die_product(product)

Initialize group fields:

"changeover_count": 0.0,
"is_continuous_die": is_continuous_die,

Accumulate:

group["changeover_count"] += as_float(item.changeover_count)
group["is_continuous_die"] = group["is_continuous_die"] or is_continuous_die

Set DashboardRow fields:

changeover_count=round2(group["changeover_count"]),
is_continuous_die=group["is_continuous_die"],
  • Step 7: Update Excel export

In /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint/app/services/excel_export.py, add 换料次数 after 冲压方式:

"冲压方式",
"换料次数",
"操作人数",

Add the row value:

row.stamping_method,
row.changeover_count,
row.operator_count,

Add report flag:

if getattr(row, "is_continuous_die", False):
    report_flags.append("连续模")

Leave reference_wage as-is:

# reference_wage remains good_qty * process_unit_price_yuan until a changeover unit price is maintained.
  • Step 8: Run backend tests

Run:

cd /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint
.venv/bin/python -m pytest tests/test_continuous_die.py tests/test_metrics.py tests/test_review_corrections.py -q

Expected: all selected tests PASS.


Task 3: Frontend API Normalization

Files:

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/utils/api.js

  • Step 1: Add frontend continuous die helpers

Near the existing cleaning helper in /Users/souplearn/Gitlab/app/JhHardwareWRS/utils/api.js, add:

const CONTINUOUS_DIE_STAMPING_METHOD = '连续模'

const isContinuousDieStampingMethod = value => String(value || '').trim() === CONTINUOUS_DIE_STAMPING_METHOD

const isContinuousDieRow = row => (
  !!row && (
    !!row.is_continuous_die
    || !!row.isContinuousDie
    || isContinuousDieStampingMethod(row.stamping_method || row.stampingMethod)
  )
)
  • Step 2: Add continuous die to product and mold mapping

In toCamelProduct, add:

isContinuousDie: isContinuousDieRow(row),

In toCamelMoldOption, add:

isContinuousDie: !!row.is_continuous_die || isContinuousDieRow(row),
  • Step 3: Add continuous die to report item mapping

In toCamelReportItem, add:

const isContinuousDie = isContinuousDieRow(row)

Return these fields:

isContinuousDie,
changeoverCount: row.changeover_count === null || row.changeover_count === undefined ? 0 : Number(row.changeover_count || 0),

Add correction display:

correctionPair(corrections, 'changeover_count', '换料次数', row.changeover_count || 0),
  • Step 4: Add continuous die to draft mapping and submit payload

In emptyDraftItem, optionToDraftItem, and apiDraftItemToCamel, add:

isContinuousDie: false,
changeoverCount: '',
changeoverCountError: false,

For product-derived items:

isContinuousDie: option ? !!option.isContinuousDie : false,
changeoverCount: '',

In toSubmitPayload, add:

changeover_count: item.isContinuousDie ? Number(item.changeoverCount || 0) : 0,
is_continuous_die: !!item.isContinuousDie,
  • Step 5: Add dashboard mapping

In listDashboardReports row mapping, add:

isContinuousDie: isContinuousDieRow(row),
changeoverCount: row.changeover_count || 0,
  • Step 6: Export helpers

At the bottom module export object, export:

isContinuousDieStampingMethod,
isContinuousDieRow,

Task 4: Product List Input Suggestions And Continuous Die Watermark

Files:

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/manageProducts/manageProducts.js

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/manageProducts/manageProducts.wxml

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/manageProducts/manageProducts.wxss

  • Step 1: Add product page state and handlers

In data, add:

stampingMethodSuggestions: ['清洗', '连续模'],
stampingSuggestionVisible: false,

In onInput, trim stamping method only on save, not while typing. Add:

if (field === 'stampingMethod') {
  this.setData({
    stampingSuggestionVisible: false,
    [`form.${field}`]: value,
  })
  return
}

Add handlers:

onStampingFocus() {
  this.setData({ stampingSuggestionVisible: true })
},

chooseStampingSuggestion(e) {
  const value = e.currentTarget.dataset.value
  this.setData({
    'form.stampingMethod': value,
    stampingSuggestionVisible: false,
  })
},

hideStampingSuggestion() {
  setTimeout(() => {
    this.setData({ stampingSuggestionVisible: false })
  }, 150)
},

In save, normalize before validation:

form.stampingMethod = String(form.stampingMethod || '').trim()

Keep this rule unchanged except for trimming:

if (['清洗', '处理杂活'].includes(form.stampingMethod)) {
  form.standardBeat = 0
}

Continuous die must still show and require standard beat.

  • Step 2: Update product page WXML

Replace the 冲压方式 input block with:

<view class="form-item stamping-field">
  <text class="label">冲压方式</text>
  <input
    class="input"
    value="{{form.stampingMethod}}"
    data-field="stampingMethod"
    bindfocus="onStampingFocus"
    bindblur="hideStampingSuggestion"
    bindinput="onInput"
    placeholder="可输入,或选择清洗/连续模"
  />
  <view wx:if="{{stampingSuggestionVisible}}" class="stamping-suggestion-panel">
    <view
      wx:for="{{stampingMethodSuggestions}}"
      wx:key="*this"
      class="stamping-suggestion-option"
      data-value="{{item}}"
      catchtap="chooseStampingSuggestion"
    >{{item}}</view>
  </view>
</view>

On product cards, add:

<text wx:if="{{item.isContinuousDie}}" class="continuous-die-stamp {{item.isCleaning || item.isMisc || item.isMultiPerson ? 'lower-product-stamp' : ''}}">连续模</text>
  • Step 3: Add product page CSS

Add styles to /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/manageProducts/manageProducts.wxss:

.stamping-field {
  position: relative;
}

.stamping-suggestion-panel {
  position: absolute;
  left: 0;
  right: 0;
  top: 112rpx;
  z-index: 20;
  background: #fff;
  border: 1rpx solid #d9e2f2;
  border-radius: 8rpx;
  box-shadow: 0 12rpx 32rpx rgba(16, 35, 72, 0.12);
  overflow: hidden;
}

.stamping-suggestion-option {
  padding: 22rpx 24rpx;
  font-size: 28rpx;
  color: #1f2a44;
  border-bottom: 1rpx solid #eef2f7;
}

.stamping-suggestion-option:last-child {
  border-bottom: 0;
}

.continuous-die-stamp {
  position: absolute;
  right: 24rpx;
  top: 24rpx;
  transform: rotate(12deg);
  color: #c26b00;
  border: 2rpx solid rgba(194, 107, 0, 0.55);
  border-radius: 6rpx;
  padding: 6rpx 12rpx;
  font-size: 24rpx;
  font-weight: 700;
  background: rgba(255, 244, 224, 0.88);
}

Task 5: Worker Reporting Form

Files:

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportForm/reportForm.js

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportForm/reportForm.wxml

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportForm/reportForm.wxss

  • Step 1: Add validation for changeover count

In validateDraft, after raw material batch validation and before quantity validation, add:

if (item.isContinuousDie) {
  const text = String(item.changeoverCount || '').trim()
  const value = Number(text)
  if (!text || !Number.isInteger(value) || value < 0) {
    this.setData({
      [`draft.deviceBlocks[${blockIndex}].items[${itemIndex}].changeoverCountError`]: true,
    })
    wx.showToast({
      title: `${blockDisplay(block)}请填写非负整数换料次数`,
      icon: 'none',
    })
    return false
  }
}

Add a generic input handler or reuse onQtyInput with data-field="changeoverCount":

if (field === 'changeoverCount') {
  this.setData({
    [`draft.deviceBlocks[${blockIndex}].items[${itemIndex}].changeoverCountError`]: false,
  })
}
  • Step 2: Add WXML field

Inside each non-misc report item, place this between 材料库存批次号 and the readonly product grid:

<view wx:if="{{reportItem.isContinuousDie}}" class="form-item">
  <text class="label">换料次数</text>
  <input
    class="input number-input {{reportItem.changeoverCountError ? 'field-error' : ''}}"
    type="number"
    value="{{reportItem.changeoverCount}}"
    placeholder="请输入换料次数"
    data-block-index="{{blockIndex}}"
    data-item-index="{{itemIndex}}"
    data-field="changeoverCount"
    bindinput="onQtyInput"
  />
</view>

Add watermarks:

<text wx:if="{{reportItem.isContinuousDie}}" class="continuous-item-stamp {{reportItem.isMultiPerson ? 'lower-item-stamp' : ''}}">连续模</text>
  • Step 3: Add report form CSS

In /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportForm/reportForm.wxss, add:

.continuous-item-stamp {
  position: absolute;
  right: 20rpx;
  top: 20rpx;
  transform: rotate(12deg);
  padding: 6rpx 12rpx;
  border-radius: 6rpx;
  border: 2rpx solid rgba(194, 107, 0, 0.55);
  color: #b45f00;
  background: rgba(255, 244, 224, 0.9);
  font-size: 22rpx;
  font-weight: 700;
}

.lower-item-stamp {
  top: 72rpx;
}

Task 6: Admin Review, Records, Dashboard, QR, And Result UI

Files:

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/review/review.js

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/review/review.wxml

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/review/review.wxss

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/records/records.wxml

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/records/records.wxss

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/dashboard/dashboard.wxml

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/dashboard/dashboard.wxss

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/deviceQr/deviceQr.wxml

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/deviceQr/deviceQr.wxss

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportResult/reportResult.js

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportResult/reportResult.wxml

  • Modify: /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportResult/reportResult.wxss

  • Step 1: Review page JS

In makeEditItem, include:

isContinuousDie: !!detail.isContinuousDie,
changeoverCount: detail.changeoverCount || 0,
changeoverCountError: false,

In productToEditItem, after product replacement:

isContinuousDie: !!product.isContinuousDie,
changeoverCount: product.isContinuousDie ? (item.changeoverCount || 0) : 0,

In restoreProduct, carry:

isContinuousDie: product.isContinuousDie === undefined ? original.isContinuousDie : product.isContinuousDie,
changeoverCount: product.isContinuousDie ? (item.changeoverCount || 0) : 0,

In onItemInput, recalculate only quantity fields as today, but allow changeoverCount to update and clear error:

if (field === 'changeoverCount') {
  updates[`rows[${reportIndex}].editItems[${itemIndex}].changeoverCountError`] = false
}

In approve, validate:

const invalidChangeoverItemIndex = (row.editItems || []).findIndex(item => {
  if (!item.isContinuousDie) {
    return false
  }
  const value = Number(item.changeoverCount)
  return !Number.isInteger(value) || value < 0
})
if (invalidChangeoverItemIndex >= 0) {
  const reportIndex = this.data.rows.findIndex(item => String(item.id) === String(id))
  this.setData({
    [`rows[${reportIndex}].editItems[${invalidChangeoverItemIndex}].changeoverCountError`]: true,
  })
  wx.showToast({ title: '请填写非负整数换料次数', icon: 'none' })
  return
}

In approve patch item corrections, include:

changeover_count: item.isContinuousDie ? Number(item.changeoverCount || 0) : 0,
  • Step 2: Review page WXML

Add report-level watermark:

<text wx:if="{{item.hasContinuousDie}}" class="continuous-die-watermark {{item.isSystemAutoSubmitted || item.isVoided || item.isModified || item.hasMisc || item.isCleaning || item.hasMultiPerson ? 'lower-watermark' : ''}}">连续模</text>

Add detail-level watermark:

<text wx:if="{{detail.isContinuousDie}}" class="detail-continuous-stamp">连续模</text>

Add display under non-cleaning/non-misc detail metrics:

<view wx:if="{{detail.isContinuousDie}}">
  <text class="label">换料次数</text>
  <text class="value">{{detail.changeoverCount || 0}}</text>
</view>

Add editable field in the review form grid:

<view wx:if="{{detail.isContinuousDie}}" class="form-item">
  <text class="label">换料次数</text>
  <input
    class="input number-input {{detail.changeoverCountError ? 'field-error' : ''}}"
    type="number"
    value="{{detail.changeoverCount}}"
    data-report-index="{{reportIndex}}"
    data-item-index="{{itemIndex}}"
    data-field="changeoverCount"
    bindinput="onItemInput"
  />
</view>

In product replacement modal cards, add:

<text wx:if="{{item.isContinuousDie}}" class="continuous-die-watermark product-select-stamp">连续模</text>
  • Step 3: Records page

In /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/records/records.wxml, add report and detail watermarks plus a detail metric:

<text wx:if="{{item.hasContinuousDie}}" class="continuous-die-watermark {{item.isSystemAutoSubmitted || item.isVoided || item.isModified || item.hasMisc || item.isCleaning || item.hasMultiPerson ? 'lower-watermark' : ''}}">连续模</text>
<text wx:if="{{detail.isContinuousDie}}" class="detail-continuous-stamp">连续模</text>
<view wx:if="{{detail.isContinuousDie}}">
  <text class="label">换料次数</text>
  <text class="value">{{detail.changeoverCount || 0}}</text>
</view>
  • Step 4: Dashboard page

In /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/dashboard/dashboard.wxml, add:

<text wx:if="{{item.isContinuousDie}}" class="continuous-die-watermark {{item.isSystemAutoSubmitted || item.isVoided || item.correctionPairs.length || item.isMisc || item.isCleaning || item.isMultiPerson ? 'lower-watermark' : ''}}">连续模</text>
<view wx:if="{{item.isContinuousDie}}">
  <text class="label">换料次数</text>
  <text class="value">{{item.changeoverCount || 0}}</text>
</view>
  • Step 5: QR page

In /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/deviceQr/deviceQr.wxml, add tags in single-select and batch-select rows:

<text wx:if="{{item.isContinuousDie}}" class="mold-option-tag continuous-tag">连续模</text>
  • Step 6: Report result page

In /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportResult/reportResult.js, include item result fields:

isContinuousDie: !!item.isContinuousDie,
changeoverCount: item.changeoverCount || 0,

In /Users/souplearn/Gitlab/app/JhHardwareWRS/pages/reportResult/reportResult.wxml, add:

<text wx:if="{{result.isContinuousDie}}" class="result-continuous-stamp">连续模</text>
<view wx:if="{{result.isContinuousDie}}">
  <text class="label">换料次数</text>
  <text class="value">{{result.changeoverCount || 0}}</text>
</view>
  • Step 7: Shared CSS pattern

Add orange continuous die styles to each page CSS touched in this task:

.continuous-die-watermark,
.detail-continuous-stamp,
.result-continuous-stamp {
  position: absolute;
  right: 24rpx;
  top: 24rpx;
  transform: rotate(12deg);
  padding: 6rpx 12rpx;
  border-radius: 6rpx;
  border: 2rpx solid rgba(194, 107, 0, 0.55);
  color: #b45f00;
  background: rgba(255, 244, 224, 0.9);
  font-size: 22rpx;
  font-weight: 700;
}

.continuous-tag {
  color: #b45f00;
  background: #fff4e0;
}

Use the class names shown in this task's WXML snippets. If a page already has .lower-watermark, use that existing class for vertical offset; otherwise add only the continuous die classes shown above.


Task 7: Manual Verification And Release Steps

Files:

  • Verify changed frontend files above.

  • Verify backend files above.

  • Step 1: Run backend migration locally

Run:

cd /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint
.venv/bin/python scripts/migrate_continuous_die_changeover_count.py

Expected output:

migrate_continuous_die_changeover_count done
  • Step 2: Run backend tests

Run:

cd /Users/souplearn/Gitlab/app/JhHardwareWRS_BackPoint
.venv/bin/python -m pytest tests/test_continuous_die.py tests/test_metrics.py tests/test_review_corrections.py -q

Expected: all tests PASS.

  • Step 3: Product list verification

Manual test in WeChat devtools:

  • Open 产品清单.

  • Click 新增产品.

  • Click 冲压方式 input.

  • Verify suggestions show only 清洗 and 连续模.

  • Select 连续模.

  • Verify 标准节拍 is still visible and required.

  • Save a product with 冲压方式= 连续模 including spaces.

  • Reopen the product and verify it is recognized as 连续模.

  • Verify product card shows orange 连续模 watermark.

  • Step 4: Worker reporting verification

Manual test:

  • Scan a normal single-person 连续模 product QR.

  • Start work.

  • Finish work.

  • In 下班报工, verify the product card shows 连续模 watermark.

  • Verify 换料次数 input appears between 材料库存批次号 and product readonly information.

  • Try submitting empty value. Expected: toast 请填写非负整数换料次数.

  • Enter 2, fill production quantity and device, submit.

  • Verify result page shows 换料次数 2.

  • Step 5: Admin review verification

Manual test:

  • Open 报工审核.

  • Expand the continuous die report.

  • Verify detail card shows 连续模 watermark and 换料次数.

  • Change 换料次数 from 2 to 3.

  • Approve.

  • Reopen record and verify:

    • Card has 更改 watermark.
    • Correction display shows 换料次数 2 -> 3.
    • Work time, standard workload, actual beat still recalculate from time and quantity only.
  • Step 6: Manager dashboard and export verification

Manual test:

  • Open 经理报工看板.

  • Find the approved continuous die row.

  • Verify orange 连续模 watermark.

  • Verify 换料次数 3 displays.

  • Export Excel.

  • Verify first sheet has 换料次数 column.

  • Verify the row has 报工标记=连续模.

  • Verify 参考工资 remains 成品数量 * 工序单价, without adding changeover pay.

  • Step 7: QR verification

Manual test:

  • Open 模具二维码.

  • Search the continuous die product.

  • Verify single-select and batch modal rows show 连续模 tag.

  • Generate QR.

  • Verify bottom annotation includes product name / process / stamping method; for continuous die it includes 连续模.

  • Step 8: Server release

Backend release order:

cd /software/JhHardwareWRS_BackPoint
git pull
.venv/bin/python scripts/migrate_continuous_die_changeover_count.py

If backend is running with reload, verify logs show reload. If not, restart the backend service.

Frontend release:

  • Upload WeChat mini-program version after devtools verification.
  • This feature needs frontend release because worker/admin/manager pages all need new visible fields.

Self-Review

Spec coverage:

  • 连续模 special judgment by trimmed exact stamping method: Task 1 and Task 3.
  • Product 冲压方式 remains free input with suggested 清洗 and 连续模: Task 4.
  • Continuous die watermark in product list, QR, report, review, records, dashboard, result: Tasks 4 and 6.
  • Worker report requires 换料次数: Task 5.
  • Admin review displays and can edit 换料次数: Task 6 plus backend Task 2.
  • Manager dashboard and Excel export carry 换料次数: Task 2 and Task 6.
  • Dashboard aggregation sums 换料次数: Task 2.
  • No forced dictionary-maintenance page: preserved by Task 4.
  • Wage calculation is not changed because no changeover unit price exists: File Map non-goal and Task 7.

Placeholder scan:

  • No unresolved placeholder markers remain.
  • Every modified behavior has a concrete file target and exact code shape or verification step.

Type consistency:

  • Backend snake_case: changeover_count, is_continuous_die.
  • Frontend camelCase: changeoverCount, isContinuousDie.
  • Display label: 换料次数.