From 8bc9027e4614944c6d5ee4fa2d50d01ebb87f3e1 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期四, 27 八月 2026 22:26:51 +0800
Subject: [PATCH] fix: persist battery group batteries through api

---
 tests/battery-group-persistence.contract.test.mjs |   31 ++++++
 src/api/batteryGroups.ts                          |   85 ++++++++++++++++
 src/api/batteryGroupPayload.ts                    |   51 ++++++++++
 src/views/BatteryGroupForm.vue                    |   96 ++++++++++--------
 src/views/BatteryGroup.vue                        |    3 
 5 files changed, 219 insertions(+), 47 deletions(-)

diff --git a/src/api/batteryGroupPayload.ts b/src/api/batteryGroupPayload.ts
new file mode 100644
index 0000000..28014a2
--- /dev/null
+++ b/src/api/batteryGroupPayload.ts
@@ -0,0 +1,51 @@
+export type GroupStatus = '启用' | '禁用'
+
+export interface BatteryGroupPayload {
+  groupName: string
+  model: string
+  purpose: string
+  remark: string
+  status: '0' | '1'
+  z: string
+  h: string
+  f: string
+  a: string
+  b: string
+  batteryNos: string[]
+  templateId: number | null
+  templateFieldRelations?: Array<{ batteryGroupFieldId: number }>
+}
+
+interface BatteryGroupFormPayloadInput {
+  name: string
+  model: string
+  purpose: string
+  remark: string
+  status: GroupStatus
+  specs: Record<string, string>
+  batteries: Array<{ value: string }>
+}
+
+export function buildBatteryGroupPayload(
+  form: BatteryGroupFormPayloadInput,
+  templateId: number | null,
+  templateFieldIds: number[],
+): BatteryGroupPayload {
+  return {
+    groupName: form.name.trim(),
+    model: form.model.trim(),
+    purpose: form.purpose.trim(),
+    remark: form.remark.trim(),
+    status: form.status === '禁用' ? '1' : '0',
+    z: form.specs.z || '',
+    h: form.specs.h || '',
+    f: form.specs.f || '',
+    a: form.specs.a || '',
+    b: form.specs.b || '',
+    batteryNos: form.batteries.map(item => item.value.trim()).filter(Boolean),
+    templateId,
+    templateFieldRelations: templateId
+      ? templateFieldIds.map(batteryGroupFieldId => ({ batteryGroupFieldId }))
+      : undefined,
+  }
+}
diff --git a/src/api/batteryGroups.ts b/src/api/batteryGroups.ts
index 25ea979..3a5de68 100644
--- a/src/api/batteryGroups.ts
+++ b/src/api/batteryGroups.ts
@@ -1,5 +1,7 @@
-import type { GroupStatus } from '@/stores/batteryGroups'
 import { request, type ApiResponse } from './request'
+import type { BatteryGroupPayload, GroupStatus } from './batteryGroupPayload'
+
+export type { GroupStatus } from './batteryGroupPayload'
 
 export interface BatteryGroupListParams {
   pageNum: number
@@ -13,6 +15,17 @@
   endCreateTime?: string
 }
 
+interface BatteryGroupBatteryDto {
+  batteryNo: string | null
+}
+
+interface BatteryGroupTemplateFieldDto {
+  templateId: number | null
+  batteryGroupFieldId: number | null
+  fieldName: string | null
+  fieldType: string | null
+}
+
 interface BatteryGroupDto {
   groupId: number
   groupName: string | null
@@ -22,6 +35,14 @@
   createBy: string | null
   createTime: string | null
   batteryCount: number | null
+  remark?: string | null
+  z?: string | null
+  h?: string | null
+  f?: string | null
+  a?: string | null
+  b?: string | null
+  batteries?: BatteryGroupBatteryDto[] | null
+  templateFields?: BatteryGroupTemplateFieldDto[] | null
 }
 
 interface BatteryGroupListResponse extends ApiResponse {
@@ -49,6 +70,10 @@
   data: BatteryGroupCreatorDto[]
 }
 
+interface BatteryGroupDetailResponse extends ApiResponse {
+  data: BatteryGroupDto
+}
+
 interface BatteryGroupCreatorDto {
   userName: string | null
   nickName: string | null
@@ -57,6 +82,26 @@
 export interface BatteryGroupCreatorOption {
   label: string
   value: string
+}
+
+export interface BatteryGroupTemplateField {
+  templateId: number
+  batteryGroupFieldId: number
+  fieldName: string
+  fieldType: string
+}
+
+export interface BatteryGroupDetail {
+  id: number
+  name: string
+  model: string
+  purpose: string
+  remark: string
+  status: GroupStatus
+  creator: string
+  specs: Record<'z' | 'h' | 'f' | 'a' | 'b', string>
+  batteries: string[]
+  templateFields: BatteryGroupTemplateField[]
 }
 
 function mapBatteryGroup(item: BatteryGroupDto): BatteryGroupListItem {
@@ -96,3 +141,41 @@
     })
     .filter(item => item.value)
 }
+
+export async function getBatteryGroupDetail(groupId: number): Promise<BatteryGroupDetail> {
+  const result = await request<BatteryGroupDetailResponse>(`/system/batteryGroup/${groupId}`)
+  const item = result.data
+  return {
+    id: item.groupId,
+    name: item.groupName || '',
+    model: item.model || '',
+    purpose: item.purpose || '',
+    remark: item.remark || '',
+    status: item.status === '1' ? '禁用' : '启用',
+    creator: item.createBy || '',
+    specs: { z: item.z || '', h: item.h || '', f: item.f || '', a: item.a || '', b: item.b || '' },
+    batteries: (item.batteries || []).map(battery => battery.batteryNo?.trim() || '').filter(Boolean),
+    templateFields: (item.templateFields || [])
+      .filter(field => field.templateId != null && field.batteryGroupFieldId != null)
+      .map(field => ({
+        templateId: Number(field.templateId),
+        batteryGroupFieldId: Number(field.batteryGroupFieldId),
+        fieldName: field.fieldName || '',
+        fieldType: field.fieldType || '文本',
+      })),
+  }
+}
+
+export function createBatteryGroup(payload: BatteryGroupPayload): Promise<ApiResponse> {
+  return request<ApiResponse>('/system/batteryGroup', {
+    method: 'POST',
+    body: JSON.stringify(payload),
+  })
+}
+
+export function updateBatteryGroup(groupId: number, payload: BatteryGroupPayload): Promise<ApiResponse> {
+  return request<ApiResponse>('/system/batteryGroup', {
+    method: 'PUT',
+    body: JSON.stringify({ groupId, ...payload }),
+  })
+}
diff --git a/src/views/BatteryGroup.vue b/src/views/BatteryGroup.vue
index 09eda1f..7ea5942 100644
--- a/src/views/BatteryGroup.vue
+++ b/src/views/BatteryGroup.vue
@@ -2,8 +2,7 @@
 import { onMounted, reactive, ref, watch } from 'vue'
 import { ElMessage } from 'element-plus'
 import { useRouter } from 'vue-router'
-import { getBatteryGroupCreatorOptions, getBatteryGroupList, type BatteryGroupCreatorOption, type BatteryGroupListItem } from '@/api/batteryGroups'
-import type { GroupStatus } from '@/stores/batteryGroups'
+import { getBatteryGroupCreatorOptions, getBatteryGroupList, type BatteryGroupCreatorOption, type BatteryGroupListItem, type GroupStatus } from '@/api/batteryGroups'
 
 interface Filters {
   id: string
diff --git a/src/views/BatteryGroupForm.vue b/src/views/BatteryGroupForm.vue
index d45b38b..5b76108 100644
--- a/src/views/BatteryGroupForm.vue
+++ b/src/views/BatteryGroupForm.vue
@@ -1,8 +1,10 @@
 <script setup lang="ts">
-import { computed, reactive, ref } from 'vue'
+import { computed, onMounted, reactive, ref } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { useRoute, useRouter } from 'vue-router'
-import { useBatteryGroups, type GroupStatus, type TemplateField } from '@/stores/batteryGroups'
+import { createBatteryGroup, getBatteryGroupDetail, updateBatteryGroup } from '@/api/batteryGroups'
+import { buildBatteryGroupPayload } from '@/api/batteryGroupPayload'
+import type { GroupStatus, TemplateField } from '@/stores/batteryGroups'
 import { useBatteryGroupFields, type BatteryGroupField } from '@/stores/batteryGroupFields'
 
 interface FormState {
@@ -20,11 +22,13 @@
 
 const route = useRoute()
 const router = useRouter()
-const { groups, createGroup, updateGroup } = useBatteryGroups()
 const { fields: managedFields } = useBatteryGroupFields()
 const creators = ['张工', '李工', '周工', '王工', '赵工', '陈工']
 const templatePanelOpen = ref(false)
+const loading = ref(false)
 const saving = ref(false)
+const persistedTemplateId = ref<number | null>(null)
+const persistedTemplateFieldIds = ref<number[]>([])
 let batteryKey = 1
 
 const editingId = computed(() => Number(route.params.id) || null)
@@ -74,35 +78,40 @@
     .map(field => ({ key: `managed-field-${field.id}`, label: field.name, type: field.type, value: previousValues.get(field.name) || '' }))
 }
 
-function loadEditingGroup() {
+async function loadEditingGroup() {
   if (!editingId.value) return
-  const target = groups.value.find(item => item.id === editingId.value)
-  if (!target) {
-    ElMessage.warning('未找到需要编辑的电池组')
-    router.replace('/battery-group')
-    return
+  loading.value = true
+  try {
+    const target = await getBatteryGroupDetail(editingId.value)
+    const templateFields = target.templateFields.map(field => ({
+      key: `persisted-field-${field.batteryGroupFieldId}`,
+      label: field.fieldName,
+      type: field.fieldType as TemplateField['type'],
+      value: '',
+    }))
+    persistedTemplateId.value = target.templateFields[0]?.templateId || null
+    persistedTemplateFieldIds.value = target.templateFields.map(field => field.batteryGroupFieldId)
+    Object.assign(form, {
+      name: target.name,
+      model: target.model,
+      purpose: target.purpose,
+      remark: target.remark,
+      creator: target.creator,
+      status: target.status,
+      templateName: '自定义',
+      specs: { ...target.specs },
+      customFields: templateFields,
+      batteries: target.batteries.map(value => ({ key: batteryKey++, value })),
+    })
+    if (!form.batteries.length) form.batteries.push({ key: batteryKey++, value: '' })
+    selectedFieldIds.value = [...persistedTemplateFieldIds.value]
+  } catch (error) {
+    ElMessage.error(error instanceof Error ? error.message : '加载电池组失败')
+    await router.replace('/battery-group')
+  } finally {
+    loading.value = false
   }
-  Object.assign(form, {
-    name: target.name,
-    model: target.model,
-    purpose: target.purpose,
-    remark: target.remark,
-    creator: target.creator,
-    status: target.status,
-    templateName: target.templateName,
-    specs: { ...target.specs },
-    customFields: target.customFields.map(field => {
-      const managedField = managedFields.value.find(item => item.name === field.label)
-      return { ...field, type: managedField?.type || field.type }
-    }),
-    batteries: target.batteries.map(value => ({ key: batteryKey++, value })),
-  })
-  selectedFieldIds.value = target.customFields
-    .map(customField => managedFields.value.find(field => field.name === customField.label)?.id)
-    .filter((id): id is number => id !== undefined)
 }
-
-loadEditingGroup()
 
 function addBattery() {
   form.batteries.push({ key: batteryKey++, value: '' })
@@ -155,8 +164,6 @@
   if (form.customFields.some(field => !field.label.trim())) return '请填写全部模板字段名称'
   const fieldLabels = form.customFields.map(field => field.label.trim().toLowerCase())
   if (new Set(fieldLabels).size !== fieldLabels.length) return '模板字段名称不能重复'
-  const duplicateGroup = groups.value.some(item => item.id !== editingId.value && item.name.trim().toLowerCase() === form.name.trim().toLowerCase())
-  if (duplicateGroup) return '电池组名称已存在'
   return ''
 }
 
@@ -167,19 +174,20 @@
     return
   }
   saving.value = true
-  await new Promise(resolve => window.setTimeout(resolve, 250))
-  const payload = {
-    name: form.name.trim(), model: form.model.trim(), purpose: form.purpose.trim(), remark: form.remark.trim(),
-    creator: form.creator, status: form.status, templateName: form.templateName, specs: { ...form.specs },
-    customFields: form.customFields.map(field => ({ ...field, label: field.label.trim() })),
-    batteries: form.batteries.map(item => item.value.trim()).filter(Boolean),
+  try {
+    const payload = buildBatteryGroupPayload(form, persistedTemplateId.value, persistedTemplateFieldIds.value)
+    if (editingId.value) await updateBatteryGroup(editingId.value, payload)
+    else await createBatteryGroup(payload)
+    ElMessage.success(isEditing.value ? '电池组已更新' : '电池组已创建')
+    await router.push('/battery-group')
+  } catch (error) {
+    ElMessage.error(error instanceof Error ? error.message : '保存电池组失败')
+  } finally {
+    saving.value = false
   }
-  if (editingId.value) updateGroup(editingId.value, payload)
-  else createGroup(payload)
-  saving.value = false
-  ElMessage.success(isEditing.value ? '电池组已更新' : '电池组已创建')
-  await router.push('/battery-group')
 }
+
+onMounted(loadEditingGroup)
 </script>
 
 <template>
@@ -190,7 +198,7 @@
     </div>
 
     <div class="form-layout">
-      <div class="form-content">
+      <div v-loading="loading" class="form-content">
         <section class="detail-panel">
           <header class="panel-heading"><div><i></i><strong>样品详情</strong><span>SAMPLE DETAILS</span></div><small><b>*</b> 为必填项</small></header>
           <div class="sample-grid">
@@ -221,7 +229,7 @@
           <label><span>状态</span><el-radio-group v-model="form.status"><el-radio-button value="启用">启用</el-radio-button><el-radio-button value="禁用">禁用</el-radio-button></el-radio-group></label>
         </section>
 
-        <footer class="submit-bar"><button class="secondary-button" type="button" @click="router.push('/battery-group')">取消</button><button class="primary-button submit-button" type="button" :disabled="saving" @click="submit"><el-icon><Check /></el-icon>{{ saving ? '保存中' : `保存${pageTitle}` }}</button></footer>
+        <footer class="submit-bar"><button class="secondary-button" type="button" @click="router.push('/battery-group')">取消</button><button class="primary-button submit-button" type="button" :disabled="loading || saving" @click="submit"><el-icon><Check /></el-icon>{{ saving ? '保存中' : `保存${pageTitle}` }}</button></footer>
       </div>
 
       <aside class="template-panel" :aria-hidden="!templatePanelOpen">
diff --git a/tests/battery-group-persistence.contract.test.mjs b/tests/battery-group-persistence.contract.test.mjs
new file mode 100644
index 0000000..d100854
--- /dev/null
+++ b/tests/battery-group-persistence.contract.test.mjs
@@ -0,0 +1,31 @@
+import assert from 'node:assert/strict'
+import test from 'node:test'
+import { buildBatteryGroupPayload } from '../src/api/batteryGroupPayload.ts'
+
+test('battery group payload contains the entered battery numbers', () => {
+  const payload = buildBatteryGroupPayload({
+    name: ' 样品组 ',
+    model: ' M-01 ',
+    purpose: ' 容量验证 ',
+    remark: ' 首批 ',
+    status: '启用',
+    specs: { z: 'Z1', h: '', f: '', a: '', b: '' },
+    batteries: [{ value: ' BAT-01 ' }, { value: '' }, { value: 'BAT-02' }],
+  }, null, [])
+
+  assert.deepEqual(payload, {
+    groupName: '样品组',
+    model: 'M-01',
+    purpose: '容量验证',
+    remark: '首批',
+    status: '0',
+    z: 'Z1',
+    h: '',
+    f: '',
+    a: '',
+    b: '',
+    batteryNos: ['BAT-01', 'BAT-02'],
+    templateId: null,
+    templateFieldRelations: undefined,
+  })
+})

--
Gitblit v1.7.1