Просмотр исходного кода

新增图片上传和二维码更新功能,优化二维码管理视图中的表单和验证逻辑

wuyi 1 день назад
Родитель
Сommit
ce398d97fc
2 измененных файлов с 482 добавлено и 71 удалено
  1. 19 0
      src/services/api.js
  2. 463 71
      src/views/QrCodeManageView.vue

+ 19 - 0
src/services/api.js

@@ -122,6 +122,19 @@ export const uploadFile = async (file) => {
   return response.data
 }
 
+// 图片上传API
+export const uploadImage = async (file) => {
+  const formData = new FormData()
+  formData.append('file', file)
+
+  const response = await api.post('/files/upload/image', formData, {
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  })
+  return response.data
+}
+
 // 文件下载API
 export const downloadFile = async (key) => {
   const response = await api.post(
@@ -194,6 +207,12 @@ export const resetMaintenanceCode = async (qrCode, maintenanceCode) => {
   return response.data
 }
 
+// 更新二维码(包含维护码和关联信息)
+export const updateQrCode = async (data) => {
+  const response = await api.post('/qr/update', data)
+  return response.data
+}
+
 // ==================== 人员信息相关API ====================
 
 // 创建人员信息

+ 463 - 71
src/views/QrCodeManageView.vue

@@ -4,7 +4,8 @@ import {
   queryQrCodes,
   getQrCodeScanRecords,
   getQrCodeInfo,
-  resetMaintenanceCode
+  updateQrCode,
+  uploadImage
 } from '@/services/api'
 import { getQrCodeTypeConfig } from '@/enums'
 import { useDateFormat } from '@vueuse/core'
@@ -18,8 +19,10 @@ import IconField from 'primevue/iconfield'
 import InputIcon from 'primevue/inputicon'
 import InputText from 'primevue/inputtext'
 import InputNumber from 'primevue/inputnumber'
+import Textarea from 'primevue/textarea'
 import DatePicker from 'primevue/datepicker'
 import Tag from 'primevue/tag'
+import Checkbox from 'primevue/checkbox'
 import { useToast } from 'primevue/usetoast'
 import { onMounted, ref } from 'vue'
 import QRCodeUtil from '@/utils/qrcode'
@@ -42,7 +45,9 @@ const filters = ref({
   isActivated: null,
   startDate: null,
   endDate: null,
-  search: ''
+  search: '',
+  ownerId: '',
+  ownerName: ''
 })
 
 // 二维码类型选项
@@ -85,14 +90,19 @@ const detailDialog = ref(false)
 const detailLoading = ref(false)
 const qrCodeDetail = ref(null)
 
-// 修改维护码对话框
-const resetDialog = ref(false)
-const resetForm = ref({
+// 更新二维码对话框
+const updateDialog = ref(false)
+const updateForm = ref({
+  id: null,
+  qrType: null,
   qrCode: '',
   maintenanceCode: '',
-  codeLength: 10
+  remark: '',
+  info: {}
 })
-const resetLoading = ref(false)
+const updateLoading = ref(false)
+const currentQrCodeData = ref(null)
+const photoUploading = ref(false)
 
 // 展示二维码对话框
 const showQrCodeDialog = ref(false)
@@ -139,6 +149,8 @@ const fetchData = async () => {
     if (filters.value.endDate) params.endDate = useDateFormat(filters.value.endDate, 'YYYY-MM-DD').value
 
     if (filters.value.search?.trim()) params.qrCode = filters.value.search.trim()
+    if (filters.value.ownerId?.trim()) params.ownerId = filters.value.ownerId.trim()
+    if (filters.value.ownerName?.trim()) params.ownerName = filters.value.ownerName.trim()
 
     const response = await queryQrCodes(params)
 
@@ -172,7 +184,9 @@ const resetAndRefresh = () => {
     isActivated: null,
     startDate: null,
     endDate: null,
-    search: ''
+    search: '',
+    ownerId: '',
+    ownerName: ''
   }
   tableData.value.metadata.page = 0
   fetchData()
@@ -381,80 +395,277 @@ const viewDetail = async (qrCode) => {
   }
 }
 
-// 打开修改维护码对话框
-const openResetDialog = (qrCode) => {
-  resetForm.value = {
-    qrCode: qrCode.qrCode,
-    maintenanceCode: '',
-    codeLength: 10
+// 打开更新二维码对话框
+const openUpdateDialog = async (qrCode) => {
+  try {
+    updateLoading.value = true
+    currentQrCodeData.value = qrCode
+
+    // 获取二维码详情
+    const response = await getQrCodeInfo(qrCode.id)
+
+    // 初始化表单数据
+    updateForm.value = {
+      id: response.id,
+      qrType: response.qrType,
+      qrCode: response.qrCode,
+      maintenanceCode: response.maintenanceCode || '',
+      remark: response.remark || '',
+      info: response.info ? { ...response.info } : {}
+    }
+
+    updateDialog.value = true
+  } catch (error) {
+    toast.add({
+      severity: 'error',
+      summary: '错误',
+      detail: error.message || '获取二维码信息失败',
+      life: 3000
+    })
+  } finally {
+    updateLoading.value = false
   }
-  resetDialog.value = true
 }
 
-// 生成随机维护码(根据选择的位数)
+// 生成随机维护码
 const generateRandomCode = () => {
-  const length = resetForm.value.codeLength || 10
+  const length = 10
   const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
   let code = ''
   for (let i = 0; i < length; i++) {
     code += chars.charAt(Math.floor(Math.random() * chars.length))
   }
-  resetForm.value.maintenanceCode = code
+  updateForm.value.maintenanceCode = code
 }
 
-// 确认修改维护码
-const handleResetMaintenanceCode = async () => {
-  if (!resetForm.value.maintenanceCode) {
-    toast.add({
-      severity: 'warn',
-      summary: '警告',
-      detail: '请输入维护码',
-      life: 3000
-    })
-    return
+// 上传图片
+const uploadPhoto = async () => {
+  const input = document.createElement('input')
+  input.type = 'file'
+  input.accept = 'image/*'
+  input.onchange = async (e) => {
+    const file = e.target.files[0]
+    if (!file) return
+
+    // 验证文件类型
+    if (!file.type.startsWith('image/')) {
+      toast.add({
+        severity: 'warn',
+        summary: '警告',
+        detail: '请选择图片文件',
+        life: 3000
+      })
+      return
+    }
+
+    // 验证文件大小(限制为 15MB)
+    if (file.size > 15 * 1024 * 1024) {
+      toast.add({
+        severity: 'warn',
+        summary: '警告',
+        detail: '图片大小不能超过 15MB',
+        life: 3000
+      })
+      return
+    }
+
+    photoUploading.value = true
+
+    try {
+      const result = await uploadImage(file)
+      // 使用API返回的data.url值作为图片URL
+      if (result.data && result.data.url) {
+        updateForm.value.info.photoUrl = result.data.url
+        toast.add({
+          severity: 'success',
+          summary: '成功',
+          detail: '图片上传成功',
+          life: 3000
+        })
+      } else {
+        throw new Error('上传响应格式错误')
+      }
+    } catch (error) {
+      console.error('图片上传失败', error)
+      toast.add({
+        severity: 'error',
+        summary: '错误',
+        detail: '图片上传失败: ' + (error.message || error),
+        life: 3000
+      })
+    } finally {
+      photoUploading.value = false
+    }
   }
 
-  const code = resetForm.value.maintenanceCode.trim()
-  if (code.length < 8 || code.length > 20) {
-    toast.add({
-      severity: 'warn',
-      summary: '警告',
-      detail: '维护码长度必须在8-20位之间',
-      life: 3000
-    })
-    return
+  input.click()
+}
+
+// 验证维护码格式
+const validateMaintenanceCode = (code) => {
+  if (!code) return null
+  const trimmed = code.trim()
+  if (trimmed.length < 8 || trimmed.length > 20) {
+    return '维护码长度必须在8-20位之间'
   }
+  if (!/^[A-Za-z0-9]+$/.test(trimmed)) {
+    return '维护码只能包含字母和数字'
+  }
+  return null
+}
 
-  if (!/^[A-Za-z0-9]+$/.test(code)) {
-    toast.add({
-      severity: 'warn',
-      summary: '警告',
-      detail: '维护码只能包含字母和数字',
-      life: 3000
-    })
-    return
+// 验证邮箱格式
+const validateEmail = (email) => {
+  if (!email) return null
+  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
+  if (!emailRegex.test(email)) {
+    return '邮箱格式不正确'
+  }
+  return null
+}
+
+// 验证URL格式
+const validateUrl = (url) => {
+  if (!url) return null
+  try {
+    new URL(url)
+    return null
+  } catch {
+    return 'URL格式不正确'
+  }
+}
+
+// 确认更新二维码
+const handleUpdateQrCode = async () => {
+  // 验证维护码
+  if (updateForm.value.maintenanceCode) {
+    const maintenanceCodeError = validateMaintenanceCode(updateForm.value.maintenanceCode)
+    if (maintenanceCodeError) {
+      toast.add({
+        severity: 'warn',
+        summary: '警告',
+        detail: maintenanceCodeError,
+        life: 3000
+      })
+      return
+    }
+  }
+
+  // 根据 qrType 验证 info 字段
+  const qrType = updateForm.value.qrType
+  const info = updateForm.value.info
+
+  if (qrType === 'person') {
+    // 验证人员信息
+    if (info.emergencyContactEmail) {
+      const emailError = validateEmail(info.emergencyContactEmail)
+      if (emailError) {
+        toast.add({
+          severity: 'warn',
+          summary: '警告',
+          detail: emailError,
+          life: 3000
+        })
+        return
+      }
+    }
+  } else if (qrType === 'pet' || qrType === 'goods') {
+    // 验证宠物/物品信息
+    if (info.contactEmail) {
+      const emailError = validateEmail(info.contactEmail)
+      if (emailError) {
+        toast.add({
+          severity: 'warn',
+          summary: '警告',
+          detail: emailError,
+          life: 3000
+        })
+        return
+      }
+    }
+  } else if (qrType === 'link') {
+    // 验证链接信息
+    if (info.jumpUrl) {
+      const urlError = validateUrl(info.jumpUrl)
+      if (urlError) {
+        toast.add({
+          severity: 'warn',
+          summary: '警告',
+          detail: urlError,
+          life: 3000
+        })
+        return
+      }
+    }
   }
 
-  resetLoading.value = true
+  updateLoading.value = true
   try {
-    await resetMaintenanceCode(resetForm.value.qrCode, code)
+    // 构建请求数据
+    const requestData = {
+      id: updateForm.value.id
+    }
+
+    if (updateForm.value.maintenanceCode) {
+      requestData.maintenanceCode = updateForm.value.maintenanceCode.trim()
+    }
+
+    if (updateForm.value.remark) {
+      requestData.remark = updateForm.value.remark.trim()
+    }
+
+    // 构建 info 对象
+    const infoData = {}
+    if (qrType === 'person') {
+      if (info.maintenanceCode) infoData.maintenanceCode = info.maintenanceCode.trim()
+      if (info.photoUrl) infoData.photoUrl = info.photoUrl.trim()
+      if (info.name) infoData.name = info.name.trim()
+      if (info.gender) infoData.gender = info.gender
+      if (info.phone) infoData.phone = info.phone.trim()
+      if (info.specialNote) infoData.specialNote = info.specialNote.trim()
+      if (info.emergencyContactName) infoData.emergencyContactName = info.emergencyContactName.trim()
+      if (info.emergencyContactPhone) infoData.emergencyContactPhone = info.emergencyContactPhone.trim()
+      if (info.emergencyContactEmail) infoData.emergencyContactEmail = info.emergencyContactEmail.trim()
+      if (info.location) infoData.location = info.location.trim()
+      if (info.isVisible !== undefined) infoData.isVisible = info.isVisible
+    } else if (qrType === 'pet' || qrType === 'goods') {
+      if (info.maintenanceCode) infoData.maintenanceCode = info.maintenanceCode.trim()
+      if (info.photoUrl) infoData.photoUrl = info.photoUrl.trim()
+      if (info.name) infoData.name = info.name.trim()
+      if (info.contactName) infoData.contactName = info.contactName.trim()
+      if (info.contactPhone) infoData.contactPhone = info.contactPhone.trim()
+      if (info.contactEmail) infoData.contactEmail = info.contactEmail.trim()
+      if (info.location) infoData.location = info.location.trim()
+      if (info.isVisible !== undefined) infoData.isVisible = info.isVisible
+    } else if (qrType === 'link') {
+      if (info.maintenanceCode) infoData.maintenanceCode = info.maintenanceCode.trim()
+      if (info.jumpUrl) infoData.jumpUrl = info.jumpUrl.trim()
+      if (info.isVisible !== undefined) infoData.isVisible = info.isVisible
+      if (info.remark) infoData.remark = info.remark.trim()
+    }
+
+    if (Object.keys(infoData).length > 0) {
+      requestData.info = infoData
+    }
+
+    await updateQrCode(requestData)
     toast.add({
       severity: 'success',
       summary: '成功',
-      detail: '维护码修改成功',
+      detail: '更新成功',
       life: 3000
     })
-    resetDialog.value = false
+    updateDialog.value = false
     fetchData()
   } catch (error) {
     toast.add({
       severity: 'error',
       summary: '错误',
-      detail: error.message || '修改维护码失败',
+      detail: error.message || '更新失败',
       life: 3000
     })
   } finally {
-    resetLoading.value = false
+    updateLoading.value = false
   }
 }
 
@@ -524,6 +735,8 @@ onMounted(() => {
             placeholder="类型" class="w-30" size="small" />
           <Select v-model="filters.isActivated" :options="activatedOptions" optionLabel="label" optionValue="value"
             placeholder="激活状态" class="w-30" size="small" />
+          <InputText v-model="filters.ownerId" placeholder="所属客户ID" size="small" class="w-25" />
+          <InputText v-model="filters.ownerName" placeholder="所属客户名称" size="small" class="w-28" />
           <DatePicker v-model="filters.startDate" placeholder="开始日期" dateFormat="yy-mm-dd" class="w-40" size="small" />
           <DatePicker v-model="filters.endDate" placeholder="结束日期" dateFormat="yy-mm-dd" class="w-40" size="small" />
           <Button icon="pi pi-search" @click="fetchData" label="查询" size="small" />
@@ -620,8 +833,8 @@ onMounted(() => {
               @click="showQrCode(slotProps.data)" />
             <Button icon="pi pi-chart-line" label="扫描记录" size="small" text style="white-space: nowrap"
               @click="viewScanRecords(slotProps.data)" />
-            <Button icon="pi pi-key" label="修改维护码" severity="warn" size="small" text style="white-space: nowrap"
-              @click="openResetDialog(slotProps.data)" />
+            <Button icon="pi pi-pencil" label="修改信息" severity="warn" size="small" text style="white-space: nowrap"
+              @click="openUpdateDialog(slotProps.data)" />
           </div>
         </template>
       </Column>
@@ -760,31 +973,210 @@ onMounted(() => {
       </template>
     </Dialog>
 
-    <!-- 修改维护码对话框 -->
-    <Dialog v-model:visible="resetDialog" :modal="true" header="修改维护码" :style="{ width: '500px' }" position="center">
-      <div class="space-y-4">
-        <div class="field" style="margin-top: 10px;">
-          <label class="block mb-2 text-sm font-medium">二维码编号</label>
-          <InputText :value="resetForm.qrCode" disabled fluid />
+    <!-- 更新二维码对话框 -->
+    <Dialog v-model:visible="updateDialog" :modal="true" header="更新二维码" :style="{ width: '700px' }" position="center">
+      <div v-if="updateLoading" class="py-10 text-center text-gray-500">加载中...</div>
+      <div v-else class="space-y-4 max-h-[70vh] overflow-y-auto">
+        <!-- 基本信息 -->
+        <div class="border rounded p-4">
+          <h4 class="font-semibold mb-3">基本信息</h4>
+          <div class="space-y-4">
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">二维码编号</label>
+              <InputText :value="updateForm.qrCode" disabled fluid />
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">类型</label>
+              <InputText :value="getTypeConfig(updateForm.qrType).label" disabled fluid />
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">维护码</label>
+              <div class="flex gap-2 items-stretch">
+                <InputText v-model="updateForm.maintenanceCode" placeholder="8-20位字母和数字" class="flex-1" />
+                <Button icon="pi pi-refresh" severity="secondary" @click="generateRandomCode" v-tooltip.top="'随机生成'"
+                  style="width: 2.8rem; padding: 0" />
+              </div>
+              <small class="text-gray-500 mt-1 block">维护码长度为8-20位,只能包含字母和数字(可选)</small>
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">备注</label>
+              <Textarea v-model="updateForm.remark" placeholder="备注信息" rows="3" fluid />
+            </div>
+          </div>
         </div>
 
-        <div class="field" style="margin-top: 20px;">
-          <label class="block mb-2 text-sm font-medium">新维护码 *</label>
-          <div class="flex gap-2 items-stretch">
-            <InputText id="maintenanceCode" v-model="resetForm.maintenanceCode" placeholder="8-20位字母和数字"
-              class="flex-1" />
-            <InputNumber v-model="resetForm.codeLength" :min="8" :max="20" showButtons :step="1"
-              :inputStyle="{ minWidth: '45px', textAlign: 'center' }" style="width: 100px" v-tooltip.top="'位数'" />
-            <Button icon="pi pi-refresh" severity="secondary" @click="generateRandomCode" v-tooltip.top="'随机生成'"
-              style="width: 2.8rem; padding: 0" />
+        <!-- 关联信息 - PERSON 类型 -->
+        <div v-if="updateForm.qrType === 'person'" class="border rounded p-4">
+          <h4 class="font-semibold mb-3">人员信息</h4>
+          <div class="space-y-4">
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">照片</label>
+              <Button icon="pi pi-upload" label="上传图片" @click="uploadPhoto" :loading="photoUploading" size="small" />
+              <div v-if="updateForm.info.photoUrl" class="mt-2">
+                <img :src="updateForm.info.photoUrl" alt="照片预览" class="max-w-xs max-h-48 rounded border" />
+              </div>
+              <small class="text-gray-500 mt-1 block">图片大小不能超过 15MB</small>
+            </div>
+            <div class="grid grid-cols-2 gap-4">
+              <div class="field">
+                <label class="block mb-2 text-sm font-medium">姓名</label>
+                <InputText v-model="updateForm.info.name" placeholder="姓名" fluid />
+              </div>
+              <div class="field">
+                <label class="block mb-2 text-sm font-medium">性别</label>
+                <Select v-model="updateForm.info.gender" :options="[
+                  { label: '男', value: 'male' },
+                  { label: '女', value: 'female' },
+                  { label: '其他', value: 'other' }
+                ]" optionLabel="label" optionValue="value" placeholder="选择性别" fluid />
+              </div>
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">电话</label>
+              <InputText v-model="updateForm.info.phone" placeholder="电话" fluid />
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">特殊说明</label>
+              <Textarea v-model="updateForm.info.specialNote" placeholder="特殊说明" rows="3" fluid />
+            </div>
+            <div class="grid grid-cols-2 gap-4">
+              <div class="field">
+                <label class="block mb-2 text-sm font-medium">紧急联系人姓名</label>
+                <InputText v-model="updateForm.info.emergencyContactName" placeholder="紧急联系人姓名" fluid />
+              </div>
+              <div class="field">
+                <label class="block mb-2 text-sm font-medium">紧急联系人电话</label>
+                <InputText v-model="updateForm.info.emergencyContactPhone" placeholder="紧急联系人电话" fluid />
+              </div>
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">紧急联系人邮箱</label>
+              <InputText v-model="updateForm.info.emergencyContactEmail" placeholder="紧急联系人邮箱" fluid />
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">位置信息</label>
+              <InputText v-model="updateForm.info.location" placeholder="位置信息" fluid />
+            </div>
+            <div class="field">
+              <div class="flex items-center gap-2">
+                <Checkbox v-model="updateForm.info.isVisible" inputId="personVisible" :binary="true" />
+                <label for="personVisible" class="text-sm">是否可见</label>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- 关联信息 - PET 类型 -->
+        <div v-else-if="updateForm.qrType === 'pet'" class="border rounded p-4">
+          <h4 class="font-semibold mb-3">宠物信息</h4>
+          <div class="space-y-4">
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">照片</label>
+              <Button icon="pi pi-upload" label="上传图片" @click="uploadPhoto" :loading="photoUploading" size="small" />
+              <div v-if="updateForm.info.photoUrl" class="mt-2">
+                <img :src="updateForm.info.photoUrl" alt="照片预览" class="max-w-xs max-h-48 rounded border" />
+              </div>
+              <small class="text-gray-500 mt-1 block">图片大小不能超过 15MB</small>
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">宠物名称</label>
+              <InputText v-model="updateForm.info.name" placeholder="宠物名称" fluid />
+            </div>
+            <div class="grid grid-cols-2 gap-4">
+              <div class="field">
+                <label class="block mb-2 text-sm font-medium">联系人姓名</label>
+                <InputText v-model="updateForm.info.contactName" placeholder="联系人姓名" fluid />
+              </div>
+              <div class="field">
+                <label class="block mb-2 text-sm font-medium">联系人电话</label>
+                <InputText v-model="updateForm.info.contactPhone" placeholder="联系人电话" fluid />
+              </div>
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">联系人邮箱</label>
+              <InputText v-model="updateForm.info.contactEmail" placeholder="联系人邮箱" fluid />
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">位置信息</label>
+              <InputText v-model="updateForm.info.location" placeholder="位置信息" fluid />
+            </div>
+            <div class="field">
+              <div class="flex items-center gap-2">
+                <Checkbox v-model="updateForm.info.isVisible" inputId="petVisible" :binary="true" />
+                <label for="petVisible" class="text-sm">是否可见</label>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- 关联信息 - GOODS 类型 -->
+        <div v-else-if="updateForm.qrType === 'goods'" class="border rounded p-4">
+          <h4 class="font-semibold mb-3">物品信息</h4>
+          <div class="space-y-4">
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">照片</label>
+              <Button icon="pi pi-upload" label="上传图片" @click="uploadPhoto" :loading="photoUploading" size="small" />
+              <div v-if="updateForm.info.photoUrl" class="mt-2">
+                <img :src="updateForm.info.photoUrl" alt="照片预览" class="max-w-xs max-h-48 rounded border" />
+              </div>
+              <small class="text-gray-500 mt-1 block">图片大小不能超过 15MB</small>
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">物品名称</label>
+              <InputText v-model="updateForm.info.name" placeholder="物品名称" fluid />
+            </div>
+            <div class="grid grid-cols-2 gap-4">
+              <div class="field">
+                <label class="block mb-2 text-sm font-medium">联系人姓名</label>
+                <InputText v-model="updateForm.info.contactName" placeholder="联系人姓名" fluid />
+              </div>
+              <div class="field">
+                <label class="block mb-2 text-sm font-medium">联系人电话</label>
+                <InputText v-model="updateForm.info.contactPhone" placeholder="联系人电话" fluid />
+              </div>
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">联系人邮箱</label>
+              <InputText v-model="updateForm.info.contactEmail" placeholder="联系人邮箱" fluid />
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">位置信息</label>
+              <InputText v-model="updateForm.info.location" placeholder="位置信息" fluid />
+            </div>
+            <div class="field">
+              <div class="flex items-center gap-2">
+                <Checkbox v-model="updateForm.info.isVisible" inputId="goodsVisible" :binary="true" />
+                <label for="goodsVisible" class="text-sm">是否可见</label>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- 关联信息 - LINK 类型 -->
+        <div v-else-if="updateForm.qrType === 'link'" class="border rounded p-4">
+          <h4 class="font-semibold mb-3">链接信息</h4>
+          <div class="space-y-4">
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">跳转URL</label>
+              <InputText v-model="updateForm.info.jumpUrl" placeholder="跳转URL" fluid />
+            </div>
+            <div class="field">
+              <div class="flex items-center gap-2">
+                <Checkbox v-model="updateForm.info.isVisible" inputId="linkVisible" :binary="true" />
+                <label for="linkVisible" class="text-sm">是否可见</label>
+              </div>
+            </div>
+            <div class="field">
+              <label class="block mb-2 text-sm font-medium">备注</label>
+              <Textarea v-model="updateForm.info.remark" placeholder="备注信息" rows="3" fluid />
+            </div>
           </div>
-          <small class="text-gray-500 mt-1 block">维护码长度为8-20位,只能包含字母和数字</small>
         </div>
       </div>
 
       <template #footer>
-        <Button label="取消" severity="secondary" @click="resetDialog = false" :disabled="resetLoading" />
-        <Button label="确认修改" @click="handleResetMaintenanceCode" :loading="resetLoading" />
+        <Button label="取消" severity="secondary" @click="updateDialog = false" :disabled="updateLoading" />
+        <Button label="确认更新" @click="handleUpdateQrCode" :loading="updateLoading" />
       </template>
     </Dialog>