From 2b4fb46f9153b8a25d9e493279bfa722b653cd9e Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期四, 27 八月 2026 22:51:33 +0800
Subject: [PATCH] fix: connect battery group management APIs

---
 src/views/BatteryGroupForm.vue |  215 +++++++++++++++++++++++++++++++----------------------
 1 files changed, 126 insertions(+), 89 deletions(-)

diff --git a/src/views/BatteryGroupForm.vue b/src/views/BatteryGroupForm.vue
index 5b76108..da7358d 100644
--- a/src/views/BatteryGroupForm.vue
+++ b/src/views/BatteryGroupForm.vue
@@ -2,10 +2,19 @@
 import { computed, onMounted, reactive, ref } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { useRoute, useRouter } from 'vue-router'
+import { getUserInfo } from '@/api/auth'
+import { getBatteryGroupFields, type BatteryGroupField, type BatteryGroupFieldType } from '@/api/batteryGroupFields'
+import { buildBatteryGroupPayload, type GroupStatus } from '@/api/batteryGroupPayload'
 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'
+import { createBatteryGroupTemplate, getBatteryGroupTemplate, getBatteryGroupTemplateOptions, updateBatteryGroupTemplate, type BatteryGroupTemplateOption } from '@/api/batteryGroupTemplates'
+
+interface TemplateField {
+  key: string
+  fieldId: number
+  label: string
+  type: BatteryGroupFieldType
+  value: string
+}
 
 interface FormState {
   name: string
@@ -14,7 +23,7 @@
   remark: string
   creator: string
   status: GroupStatus
-  templateName: string
+  templateId: number
   specs: Record<string, string>
   customFields: TemplateField[]
   batteries: Array<{ key: number, value: string }>
@@ -22,91 +31,91 @@
 
 const route = useRoute()
 const router = useRouter()
-const { fields: managedFields } = useBatteryGroupFields()
-const creators = ['张工', '李工', '周工', '王工', '赵工', '陈工']
+const creators = ref<string[]>([])
+const managedFields = ref<BatteryGroupField[]>([])
+const templateOptions = ref<BatteryGroupTemplateOption[]>([])
 const templatePanelOpen = ref(false)
 const loading = ref(false)
 const saving = ref(false)
-const persistedTemplateId = ref<number | null>(null)
-const persistedTemplateFieldIds = ref<number[]>([])
+const templateSaving = ref(false)
+const selectedFieldIds = ref<number[]>([])
+const loadedTemplateFieldIds = ref<number[]>([])
 let batteryKey = 1
 
 const editingId = computed(() => Number(route.params.id) || null)
 const isEditing = computed(() => editingId.value !== null)
 const pageTitle = computed(() => isEditing.value ? '编辑电池组' : '新建电池组')
-const templateStorageKey = 'battery_detection_group_field_templates_v1'
-const presetTemplateLabels: Record<string, string[]> = {
-  '标准容量样品': ['额定容量', '电流', '内阻'],
-  '标准循环样品': ['额定容量', '循环温度', '内阻'],
-  '低温性能样品': ['目标温度', '静置时间', '环境备注'],
-}
-const savedTemplateSelections = (() => {
-  try {
-    const stored = localStorage.getItem(templateStorageKey)
-    return stored ? JSON.parse(stored) as Record<string, number[]> : {}
-  } catch {
-    localStorage.removeItem(templateStorageKey)
-    return {}
-  }
-})()
-const templateSelections = reactive<Record<string, number[]>>(savedTemplateSelections)
-const templateNames = ref(['自定义', '标准容量样品', '标准循环样品', '低温性能样品', ...Object.keys(savedTemplateSelections).filter(name => !['自定义', '标准容量样品', '标准循环样品', '低温性能样品'].includes(name))])
 const templateFieldOptions = computed(() => [...managedFields.value].sort((a, b) => b.createdAt.localeCompare(a.createdAt)))
-const selectableFields = computed(() => templateFieldOptions.value.filter(field => field.status === '启用'))
-const selectedFieldIds = ref<number[]>([])
 const form = reactive<FormState>({
-  name: '', model: '', purpose: '', remark: '', creator: '', status: '启用', templateName: '自定义',
+  name: '', model: '', purpose: '', remark: '', creator: '', status: '启用', templateId: 0,
   specs: { z: '', h: '', f: '', a: '', b: '' }, customFields: [], batteries: [{ key: batteryKey++, value: '' }],
 })
 
-function persistTemplateSelections() {
-  localStorage.setItem(templateStorageKey, JSON.stringify(templateSelections))
-}
-
-function resolveTemplateFieldIds(name: string) {
-  const savedIds = templateSelections[name]
-  if (savedIds) return savedIds.filter(id => selectableFields.value.some(field => field.id === id))
-  const labels = presetTemplateLabels[name] || []
-  return selectableFields.value.filter(field => labels.includes(field.name)).map(field => field.id)
-}
-
 function syncSelectedFields() {
-  const previousValues = new Map(form.customFields.map(field => [field.label, field.value]))
+  const previousValues = new Map(form.customFields.map(field => [field.fieldId, field.value]))
   form.customFields = selectedFieldIds.value
-    .map(id => selectableFields.value.find(field => field.id === id))
+    .map(id => templateFieldOptions.value.find(field => field.id === id))
     .filter((field): field is BatteryGroupField => Boolean(field))
-    .map(field => ({ key: `managed-field-${field.id}`, label: field.name, type: field.type, value: previousValues.get(field.name) || '' }))
+    .map(field => ({
+      key: `managed-field-${field.id}`, fieldId: field.id, label: field.name,
+      type: field.type, value: previousValues.get(field.id) || '',
+    }))
+}
+
+async function applyTemplate(templateId: number) {
+  if (!templateId) {
+    selectedFieldIds.value = []
+    loadedTemplateFieldIds.value = []
+    form.customFields = []
+    return
+  }
+  try {
+    const template = await getBatteryGroupTemplate(templateId)
+    selectedFieldIds.value = [...template.fieldIds]
+    loadedTemplateFieldIds.value = [...template.fieldIds]
+    syncSelectedFields()
+  } catch (error) {
+    ElMessage.error(error instanceof Error ? error.message : '加载模板失败')
+  }
 }
 
 async function loadEditingGroup() {
   if (!editingId.value) return
+  const target = await getBatteryGroupDetail(editingId.value)
+  const templateId = target.templateFields[0]?.templateId || 0
+  const templateFields = target.templateFields.map(field => ({
+    key: `persisted-field-${field.batteryGroupFieldId}`,
+    fieldId: field.batteryGroupFieldId,
+    label: field.fieldName,
+    type: (field.fieldType === 'text' ? '文本' : field.fieldType) as BatteryGroupFieldType,
+    value: field.fieldValue,
+  }))
+  Object.assign(form, {
+    name: target.name, model: target.model, purpose: target.purpose, remark: target.remark,
+    creator: target.creator, status: target.status, templateId, specs: { ...target.specs },
+    customFields: templateFields,
+    batteries: target.batteries.map(value => ({ key: batteryKey++, value })),
+  })
+  if (target.creator && !creators.value.includes(target.creator)) creators.value.push(target.creator)
+  if (!form.batteries.length) form.batteries.push({ key: batteryKey++, value: '' })
+  selectedFieldIds.value = templateFields.map(field => field.fieldId)
+  loadedTemplateFieldIds.value = [...selectedFieldIds.value]
+}
+
+async function loadPage() {
   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]
+    const [fields, templates, userInfo] = await Promise.all([
+      getBatteryGroupFields(), getBatteryGroupTemplateOptions(), getUserInfo(),
+    ])
+    managedFields.value = fields
+    templateOptions.value = templates
+    const currentUser = userInfo.user.userName?.trim() || ''
+    creators.value = currentUser ? [currentUser] : []
+    form.creator = currentUser
+    await loadEditingGroup()
   } catch (error) {
-    ElMessage.error(error instanceof Error ? error.message : '加载电池组失败')
+    ElMessage.error(error instanceof Error ? error.message : '加载电池组表单失败')
     await router.replace('/battery-group')
   } finally {
     loading.value = false
@@ -125,45 +134,70 @@
   form.batteries.splice(index, 1)
 }
 
-function applyTemplate(name: string) {
-  selectedFieldIds.value = resolveTemplateFieldIds(name)
-  syncSelectedFields()
+function templateName(templateId: number) {
+  return templateOptions.value.find(item => item.id === templateId)?.name || ''
 }
 
-function saveTemplate() {
-  templateSelections[form.templateName] = [...selectedFieldIds.value]
-  persistTemplateSelections()
-  ElMessage.success(`模板“${form.templateName}”已保存`)
+async function saveTemplate() {
+  if (!form.templateId) {
+    ElMessage.warning('自定义字段请使用“另存模板”')
+    return
+  }
+  if (!selectedFieldIds.value.length) {
+    ElMessage.warning('请至少选择一个模板字段')
+    return
+  }
+  templateSaving.value = true
+  try {
+    await updateBatteryGroupTemplate(form.templateId, templateName(form.templateId), selectedFieldIds.value)
+    loadedTemplateFieldIds.value = [...selectedFieldIds.value]
+    ElMessage.success(`模板“${templateName(form.templateId)}”已保存`)
+  } catch (error) {
+    ElMessage.error(error instanceof Error ? error.message : '保存模板失败')
+  } finally {
+    templateSaving.value = false
+  }
 }
 
 async function saveTemplateAs() {
+  if (!selectedFieldIds.value.length) {
+    ElMessage.warning('请至少选择一个模板字段')
+    return
+  }
   try {
     const result = await ElMessageBox.prompt('输入新模板名称,当前动态字段会保存为新模板。', '另存模板', {
       confirmButtonText: '保存', cancelButtonText: '取消', inputPlaceholder: '输入模板名称',
       inputValidator: value => value.trim() ? true : '请输入模板名称',
     })
     const name = result.value.trim()
-    if (!templateNames.value.includes(name)) templateNames.value.push(name)
-    form.templateName = name
-    templateSelections[name] = [...selectedFieldIds.value]
-    persistTemplateSelections()
+    templateSaving.value = true
+    await createBatteryGroupTemplate(name, selectedFieldIds.value)
+    templateOptions.value = await getBatteryGroupTemplateOptions()
+    const created = templateOptions.value.find(item => item.name === name)
+    if (!created) throw new Error('模板已保存,但未能重新加载')
+    form.templateId = created.id
+    loadedTemplateFieldIds.value = [...selectedFieldIds.value]
     ElMessage.success(`已另存为“${name}”`)
-  } catch {
-    // 用户取消另存。
+  } catch (error) {
+    if (error instanceof Error && error.message !== 'cancel') {
+      ElMessage.error(error.message || '另存模板失败')
+    }
+  } finally {
+    templateSaving.value = false
   }
 }
 
 function validate() {
   if (!form.name.trim()) return '请输入电池组名称'
   if (!form.model.trim()) return '请输入型号'
-  if (!form.creator) return '请选择创建人'
+  if (!form.creator) return '当前登录用户信息不可用'
   if (!form.purpose.trim()) return '请输入目的'
   const batteries = form.batteries.map(item => item.value.trim()).filter(Boolean)
   if (!batteries.length) return '请至少填写一个电池号'
   if (new Set(batteries).size !== batteries.length) return '电池号不能重复'
-  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 '模板字段名称不能重复'
+  if (form.customFields.some(field => field.value.length > 500)) return '模板字段值不能超过500个字符'
+  if (!form.templateId && selectedFieldIds.value.length) return '请先将自定义字段另存为模板'
+  if (form.templateId && loadedTemplateFieldIds.value.join(',') !== selectedFieldIds.value.join(',')) return '模板字段已变更,请先保存当前模板'
   return ''
 }
 
@@ -175,7 +209,10 @@
   }
   saving.value = true
   try {
-    const payload = buildBatteryGroupPayload(form, persistedTemplateId.value, persistedTemplateFieldIds.value)
+    const relations = form.customFields.map(field => ({
+      batteryGroupFieldId: field.fieldId, fieldValue: field.value,
+    }))
+    const payload = buildBatteryGroupPayload(form, form.templateId || null, relations)
     if (editingId.value) await updateBatteryGroup(editingId.value, payload)
     else await createBatteryGroup(payload)
     ElMessage.success(isEditing.value ? '电池组已更新' : '电池组已创建')
@@ -187,7 +224,7 @@
   }
 }
 
-onMounted(loadEditingGroup)
+onMounted(loadPage)
 </script>
 
 <template>
@@ -213,7 +250,7 @@
             <label class="textarea-field"><span>备注</span><el-input v-model="form.remark" type="textarea" :rows="4" maxlength="200" show-word-limit placeholder="输入备注" /></label>
             <label class="textarea-field"><span><b>*</b>目的</span><el-input v-model="form.purpose" type="textarea" :rows="4" maxlength="200" show-word-limit placeholder="输入目的" /></label>
           </div>
-          <div class="template-actions"><label><span>当前模板</span><el-select v-model="form.templateName" @change="applyTemplate"><el-option v-for="name in templateNames" :key="name" :label="name" :value="name" /></el-select></label><button class="secondary-button" type="button" @click="templatePanelOpen = !templatePanelOpen"><el-icon><EditPen /></el-icon>{{ templatePanelOpen ? '收起模板编辑' : '编辑当前模板' }}</button></div>
+          <div class="template-actions"><label><span>当前模板</span><el-select v-model="form.templateId" @change="applyTemplate"><el-option label="自定义" :value="0" /><el-option v-for="item in templateOptions" :key="item.id" :label="item.name" :value="item.id" /></el-select></label><button class="secondary-button" type="button" @click="templatePanelOpen = !templatePanelOpen"><el-icon><EditPen /></el-icon>{{ templatePanelOpen ? '收起模板编辑' : '编辑当前模板' }}</button></div>
         </section>
 
         <section class="battery-panel">
@@ -225,7 +262,7 @@
         </section>
 
         <section class="meta-panel">
-          <label><span><b>*</b>创建人</span><el-select v-model="form.creator" placeholder="选择创建人"><el-option v-for="item in creators" :key="item" :label="item" :value="item" /></el-select></label>
+          <label><span><b>*</b>创建人</span><el-select v-model="form.creator" disabled placeholder="当前登录用户"><el-option v-for="item in creators" :key="item" :label="item" :value="item" /></el-select></label>
           <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>
 
@@ -234,14 +271,14 @@
 
       <aside class="template-panel" :aria-hidden="!templatePanelOpen">
         <header><div><strong>模板字段</strong><span>TEMPLATE FIELDS</span></div><button type="button" aria-label="关闭模板编辑" title="关闭" @click="templatePanelOpen = false"><el-icon><Close /></el-icon></button></header>
-        <label class="template-select"><span>当前模板</span><el-select v-model="form.templateName" @change="applyTemplate"><el-option v-for="name in templateNames" :key="name" :label="name" :value="name" /></el-select></label>
+        <label class="template-select"><span>当前模板</span><el-select v-model="form.templateId" @change="applyTemplate"><el-option label="自定义" :value="0" /><el-option v-for="item in templateOptions" :key="item.id" :label="item.name" :value="item.id" /></el-select></label>
         <div class="field-checkbox-list">
           <el-checkbox-group v-if="templateFieldOptions.length" v-model="selectedFieldIds" class="field-checkbox-grid" @change="syncSelectedFields">
             <el-checkbox v-for="field in templateFieldOptions" :key="field.id" :value="field.id" :disabled="field.status === '禁用'" :title="`${field.name}(${field.type},${field.status})`">{{ field.name }}</el-checkbox>
           </el-checkbox-group>
           <div v-else class="field-empty">暂无字段,请先到电池组字段管理中添加字段</div>
         </div>
-        <footer><button class="primary-button" type="button" @click="saveTemplate"><el-icon><Check /></el-icon>保存</button><button class="secondary-button" type="button" @click="saveTemplateAs"><el-icon><DocumentCopy /></el-icon>另存模板</button></footer>
+        <footer><button class="primary-button" type="button" :disabled="templateSaving" @click="saveTemplate"><el-icon><Check /></el-icon>保存</button><button class="secondary-button" type="button" :disabled="templateSaving" @click="saveTemplateAs"><el-icon><DocumentCopy /></el-icon>另存模板</button></footer>
       </aside>
     </div>
   </div>

--
Gitblit v1.7.1