|
|
@@ -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>
|
|
|
|