|
|
|
@ -1,20 +1,29 @@ |
|
|
|
<script setup lang="ts"> |
|
|
|
/** |
|
|
|
* 用户管理页面 |
|
|
|
* device管理页面 |
|
|
|
*/ |
|
|
|
import {ref, reactive, onMounted, watch} from 'vue'; |
|
|
|
import {ref, reactive, onMounted, watch, h} from 'vue'; |
|
|
|
import {useVbenVxeGrid} from '#/adapter/vxe-table'; |
|
|
|
import {useVbenModal} from '@vben/common-ui'; |
|
|
|
import {useVbenForm} from '#/adapter/form'; |
|
|
|
import {Upload, message, Image} from 'ant-design-vue'; |
|
|
|
import dayjs from 'dayjs'; |
|
|
|
import {columns} from './data'; |
|
|
|
import {formSchema} from './form'; |
|
|
|
import { deviceApi } from '#/api/device'; |
|
|
|
// ========== 获取 API 基础 URL ========== |
|
|
|
const API_BASE_URL = import.meta.env.VITE_GLOB_API_URL || ''; |
|
|
|
|
|
|
|
// ========== 状态变量 ========== |
|
|
|
const currentRow = ref(null); |
|
|
|
const isEdit = ref(false); |
|
|
|
const modalTitle = ref('新增'); |
|
|
|
const uploadVisible = ref(false); |
|
|
|
const uploadFieldName = ref(''); |
|
|
|
const uploadImageUrl = ref(''); |
|
|
|
const uploadedUrl = ref(''); // 上传成功后返回的 |
|
|
|
const formUploadUrls = ref<Record<string, string>>({}); // 存储新增弹窗中各字段的上传 URL |
|
|
|
|
|
|
|
// ========== 动态生成的查询表单 Schema ========== |
|
|
|
const querySchema = ref([]); |
|
|
|
// ========== 枚举数据配置 ========== |
|
|
|
@ -23,7 +32,14 @@ const enumData = reactive({ |
|
|
|
list: [], // 存储接口返回的原始枚举数据 |
|
|
|
}); |
|
|
|
//todo 枚举数据在查询里面不展示的配置 |
|
|
|
const hiddenColumns = ref(['fun_code','graduallyIntervalTime','funMsgTitle','funImg','createdAt', 'updatedBy', 'updatedAt', 'userPassword', 'userId', 'userSys', 'deletedFlag', "userFace"]) |
|
|
|
const hiddenColumns = ref(['fun_code', 'graduallyIntervalTime', 'funMsgTitle', 'funImg', 'createdAt', 'updatedBy', 'updatedAt', 'userPassword', 'userId', 'userSys', 'deletedFlag', "userFace"]) |
|
|
|
const editFields = ['userId', 'createdAt', 'createdBy', 'updatedAt', 'updatedBy', 'deletedFlag']; |
|
|
|
|
|
|
|
// ========== 判断是否是图片字段 ========== |
|
|
|
function isImageField(fieldName: string): boolean { |
|
|
|
const lowerName = fieldName.toLowerCase(); |
|
|
|
return lowerName.includes('img') || lowerName.includes('face') || lowerName.includes('picture'); |
|
|
|
} |
|
|
|
|
|
|
|
// ========== 初始化枚举数据和查询表单 ========== |
|
|
|
async function initEnumData() { |
|
|
|
@ -118,10 +134,146 @@ watch( |
|
|
|
{immediate: false} |
|
|
|
); |
|
|
|
|
|
|
|
// ========== 表格配置 ========== |
|
|
|
// ========== 表格配置 ========== |
|
|
|
const gridOptions = { |
|
|
|
columns: [ |
|
|
|
...columns, |
|
|
|
...columns.map(col => { |
|
|
|
// ✅ 如果是图片字段 (包含 img, face, picture),使用图片渲染 + 上传按钮 |
|
|
|
if (isImageField(col.field)) { |
|
|
|
return { |
|
|
|
...col, |
|
|
|
width: 200, |
|
|
|
align: 'center', |
|
|
|
slots: { |
|
|
|
default: ({row}: any) => { |
|
|
|
const imgUrl = row[col.field]; |
|
|
|
|
|
|
|
// 如果没有图片,显示提示文字 |
|
|
|
if (!imgUrl) { |
|
|
|
return h('div', { |
|
|
|
style: { |
|
|
|
display: 'flex', |
|
|
|
justifyContent: 'center', |
|
|
|
alignItems: 'center', |
|
|
|
gap: '8px', |
|
|
|
height: '50px', |
|
|
|
} |
|
|
|
}, [ |
|
|
|
h('span', { |
|
|
|
style: { |
|
|
|
color: '#999', |
|
|
|
fontSize: '12px' |
|
|
|
} |
|
|
|
}, '无图片'), |
|
|
|
h('button', { |
|
|
|
onClick: (e: Event) => { |
|
|
|
e.stopPropagation(); |
|
|
|
// 设置当前行数据 |
|
|
|
currentRow.value = row; |
|
|
|
isEdit.value = true; // 设置为编辑模式 |
|
|
|
openUploadDialog(col.field, imgUrl); |
|
|
|
}, |
|
|
|
style: { |
|
|
|
padding: '4px 8px', |
|
|
|
fontSize: '12px', |
|
|
|
color: '#1890ff', |
|
|
|
backgroundColor: '#e6f7ff', |
|
|
|
border: '1px solid #1890ff', |
|
|
|
borderRadius: '4px', |
|
|
|
cursor: 'pointer', |
|
|
|
} |
|
|
|
}, '上传') |
|
|
|
]); |
|
|
|
} |
|
|
|
|
|
|
|
// 判断是否是完整的 URL |
|
|
|
let fullUrl = imgUrl; |
|
|
|
if (!imgUrl.startsWith('http://') && !imgUrl.startsWith('https://')) { |
|
|
|
// 如果不是完整 URL,拼接基础 API 地址 |
|
|
|
const apiURL = import.meta.env.VITE_GLOB_API_URL || ''; |
|
|
|
fullUrl = `${apiURL}${imgUrl}`; |
|
|
|
} |
|
|
|
|
|
|
|
console.log('🖼️ 图片 URL:', fullUrl); |
|
|
|
|
|
|
|
// 返回图片组件和上传按钮 |
|
|
|
try { |
|
|
|
return h('div', { |
|
|
|
style: { |
|
|
|
display: 'flex', |
|
|
|
justifyContent: 'center', |
|
|
|
alignItems: 'center', |
|
|
|
gap: '8px', |
|
|
|
} |
|
|
|
}, [ |
|
|
|
h(Image, { |
|
|
|
src: fullUrl, |
|
|
|
height: 60, |
|
|
|
width: 80, |
|
|
|
preview: true, |
|
|
|
style: { |
|
|
|
borderRadius: '4px', |
|
|
|
objectFit: 'cover', |
|
|
|
cursor: 'pointer', |
|
|
|
boxShadow: '0 2px 4px rgba(0,0,0,0.1)', |
|
|
|
}, |
|
|
|
onError: () => { |
|
|
|
console.error('❌ 图片加载失败:', fullUrl); |
|
|
|
return h('div', { |
|
|
|
style: { |
|
|
|
display: 'flex', |
|
|
|
justifyContent: 'center', |
|
|
|
alignItems: 'center', |
|
|
|
height: '50px', |
|
|
|
backgroundColor: '#f5f5f5', |
|
|
|
borderRadius: '4px', |
|
|
|
color: '#999', |
|
|
|
fontSize: '12px' |
|
|
|
} |
|
|
|
}, '加载失败'); |
|
|
|
} |
|
|
|
}), |
|
|
|
h('button', { |
|
|
|
onClick: (e: Event) => { |
|
|
|
e.stopPropagation(); |
|
|
|
// 设置当前行数据 |
|
|
|
currentRow.value = row; |
|
|
|
isEdit.value = true; // 设置为编辑模式 |
|
|
|
openUploadDialog(col.field, imgUrl); |
|
|
|
}, |
|
|
|
style: { |
|
|
|
padding: '4px 8px', |
|
|
|
fontSize: '12px', |
|
|
|
color: '#1890ff', |
|
|
|
backgroundColor: '#e6f7ff', |
|
|
|
border: '1px solid #1890ff', |
|
|
|
borderRadius: '4px', |
|
|
|
cursor: 'pointer', |
|
|
|
} |
|
|
|
}, '更换') |
|
|
|
]); |
|
|
|
} catch (error) { |
|
|
|
console.error('❌ 图片渲染错误:', error); |
|
|
|
return h('div', { |
|
|
|
style: { |
|
|
|
display: 'flex', |
|
|
|
justifyContent: 'center', |
|
|
|
alignItems: 'center', |
|
|
|
height: '50px', |
|
|
|
backgroundColor: '#fffbe6', |
|
|
|
borderRadius: '4px', |
|
|
|
color: '#faad14', |
|
|
|
fontSize: '12px' |
|
|
|
} |
|
|
|
}, '渲染失败'); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
return col; |
|
|
|
}), |
|
|
|
{ |
|
|
|
field: 'action', |
|
|
|
title: '操作', |
|
|
|
@ -157,7 +309,7 @@ const gridOptions = { |
|
|
|
} |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
console.error('查询用户列表失败:', error) |
|
|
|
console.error('查询列表失败:', error) |
|
|
|
throw error |
|
|
|
} |
|
|
|
} |
|
|
|
@ -171,10 +323,15 @@ const gridOptions = { |
|
|
|
enabled: true, |
|
|
|
pageSize: 10, |
|
|
|
}, |
|
|
|
showOverflow: true, |
|
|
|
minHeight: '100%', |
|
|
|
maxHeight: 'auto', |
|
|
|
showHeaderOverflow: true, |
|
|
|
} |
|
|
|
|
|
|
|
const [Grid, gridApi] = useVbenVxeGrid({gridOptions}) |
|
|
|
|
|
|
|
// ========== 过滤后的编辑表单 Schema ========== |
|
|
|
const editFormSchema = formSchema.filter(item => !editFields.includes(item.fieldName)); |
|
|
|
// ========== 弹窗配置 ========== |
|
|
|
const [Modal, modalApi] = useVbenModal({ |
|
|
|
centered: true, |
|
|
|
@ -190,20 +347,30 @@ const [Modal, modalApi] = useVbenModal({ |
|
|
|
const values = await formApi.validateAndSubmitForm(); |
|
|
|
if (!values) return; |
|
|
|
|
|
|
|
// 合并上传的 URL 到提交数据中(只在新增模式下) |
|
|
|
const submitValues = !isEdit.value |
|
|
|
? {...values, ...formUploadUrls.value} |
|
|
|
: values; |
|
|
|
|
|
|
|
// 处理日期格式,将 Day.js 对象转换为字符串 |
|
|
|
const submitValues = { |
|
|
|
...values, |
|
|
|
userBirthday: values.userBirthday ? dayjs(values.userBirthday).format('YYYY-MM-DD') : null, |
|
|
|
const finalSubmitValues = { |
|
|
|
...submitValues, |
|
|
|
userBirthday: submitValues.userBirthday ? dayjs(submitValues.userBirthday).format('YYYY-MM-DD') : null, |
|
|
|
}; |
|
|
|
|
|
|
|
if (isEdit.value && currentRow.value?.id) { |
|
|
|
await deviceApi.save({...submitValues, id: currentRow.value.id}); |
|
|
|
await deviceApi.save({...finalSubmitValues, id: currentRow.value.id}); |
|
|
|
} else { |
|
|
|
await deviceApi.save(submitValues); |
|
|
|
await deviceApi.add(finalSubmitValues); |
|
|
|
} |
|
|
|
|
|
|
|
modalApi.close(); |
|
|
|
gridApi.reload(); |
|
|
|
|
|
|
|
// 重置上传 URL 记录 |
|
|
|
if (!isEdit.value) { |
|
|
|
formUploadUrls.value = {}; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('保存失败:', error); |
|
|
|
} |
|
|
|
@ -211,13 +378,169 @@ const [Modal, modalApi] = useVbenModal({ |
|
|
|
onOpenChange(isOpen: boolean) { |
|
|
|
if (!isOpen && !isEdit.value) { |
|
|
|
formApi.resetForm(); |
|
|
|
formUploadUrls.value = {}; |
|
|
|
} |
|
|
|
}, |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
// ========== 上传弹窗配置 ========== |
|
|
|
const uploadFileList = ref<any[]>([]); |
|
|
|
const [UploadModal, uploadModalApi] = useVbenModal({ |
|
|
|
centered: true, |
|
|
|
closable: true, |
|
|
|
maskClosable: false, |
|
|
|
draggable: true, |
|
|
|
width: 600, |
|
|
|
title: '上传图片', |
|
|
|
onCancel() { |
|
|
|
uploadModalApi.close(); |
|
|
|
uploadFileList.value = []; |
|
|
|
uploadedUrl.value = ''; |
|
|
|
}, |
|
|
|
onConfirm: async () => { |
|
|
|
console.log('=== 点击确认保存 ===', { |
|
|
|
uploadedUrl: uploadedUrl.value, |
|
|
|
isEdit: isEdit.value, |
|
|
|
currentRowId: currentRow.value?.id, |
|
|
|
uploadFieldName: uploadFieldName.value, |
|
|
|
currentRowData: currentRow.value, |
|
|
|
}); |
|
|
|
|
|
|
|
// 如果有上传成功的 URL,更新到当前行并保存 |
|
|
|
if (uploadedUrl.value) { |
|
|
|
try { |
|
|
|
// 更新当前行的图片 URL |
|
|
|
if (currentRow.value) { |
|
|
|
currentRow.value[uploadFieldName.value] = uploadedUrl.value; |
|
|
|
console.log('✅ 已更新当前行图片字段:', { |
|
|
|
fieldName: uploadFieldName.value, |
|
|
|
newUrl: uploadedUrl.value, |
|
|
|
updatedRow: currentRow.value, |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
// 调用 save 接口保存修改(只在编辑模式下) |
|
|
|
if (isEdit.value && currentRow.value?.id) { |
|
|
|
const submitData = { |
|
|
|
...currentRow.value, |
|
|
|
id: currentRow.value.id |
|
|
|
}; |
|
|
|
console.log('📤 提交保存数据:', submitData); |
|
|
|
|
|
|
|
await deviceApi.save(submitData); |
|
|
|
message.success('保存成功!'); |
|
|
|
|
|
|
|
// 关闭弹窗并刷新表格 |
|
|
|
uploadModalApi.close(); |
|
|
|
gridApi.reload(); |
|
|
|
|
|
|
|
// 重置状态 |
|
|
|
uploadFileList.value = []; |
|
|
|
uploadedUrl.value = ''; |
|
|
|
} else { |
|
|
|
console.warn('⚠️ 不满足保存条件:', { |
|
|
|
isEdit: isEdit.value, |
|
|
|
hasId: !!currentRow.value?.id, |
|
|
|
}); |
|
|
|
message.warning('非编辑模式或无 ID,仅更新预览'); |
|
|
|
|
|
|
|
// 只更新预览,不保存 |
|
|
|
uploadModalApi.close(); |
|
|
|
gridApi.reload(); |
|
|
|
} |
|
|
|
|
|
|
|
} catch (error: any) { |
|
|
|
console.error('❌ 保存失败:', error); |
|
|
|
message.error(error?.message || '保存失败'); |
|
|
|
} |
|
|
|
} else { |
|
|
|
message.warning('请先选择并上传图片'); |
|
|
|
} |
|
|
|
}, |
|
|
|
}) |
|
|
|
|
|
|
|
// ========== 动态计算编辑表单 Schema ========== |
|
|
|
// 新增时:过滤掉 id 字段,并为图片字段添加上传按钮 |
|
|
|
// 编辑时:包含 id 字段 |
|
|
|
const getEditFormSchema = () => { |
|
|
|
return formSchema.filter(item => { |
|
|
|
// 不在 editFields 中的才显示 |
|
|
|
if (!editFields.includes(item.fieldName)) { |
|
|
|
// 新增时排除 id 字段 |
|
|
|
if (!isEdit.value && item.fieldName === 'id') { |
|
|
|
return false; |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
return false; |
|
|
|
}).map(item => { |
|
|
|
// 只在新增模式下,且是图片字段,添加上传组件 |
|
|
|
if (!isEdit.value && isImageField(item.fieldName)) { |
|
|
|
// 优先使用 formUploadUrls 中存储的 URL,其次使用表单值 |
|
|
|
const currentImageUrl = formUploadUrls.value[item.fieldName] || formApi.getValues()?.[item.fieldName] || ''; |
|
|
|
return { |
|
|
|
...item, |
|
|
|
component: 'Upload', |
|
|
|
componentProps: { |
|
|
|
class: 'w-full', |
|
|
|
accept: 'image/*', |
|
|
|
maxCount: 1, |
|
|
|
listType: 'picture-card', |
|
|
|
fileList: currentImageUrl && typeof currentImageUrl === 'string' ? [{ |
|
|
|
uid: '-1', |
|
|
|
name: 'image.png', |
|
|
|
status: 'done', |
|
|
|
url: currentImageUrl, |
|
|
|
}] : (Array.isArray(currentImageUrl) ? currentImageUrl : []), |
|
|
|
beforeUpload: async (file: any) => { |
|
|
|
console.log('📦 新增表单内上传文件:', file); |
|
|
|
|
|
|
|
try { |
|
|
|
const formData = new FormData(); |
|
|
|
formData.append('file', file); |
|
|
|
|
|
|
|
// 调用上传 API |
|
|
|
const res = await deviceApi.upload(formData); |
|
|
|
const resultUrl = res.result || res.message || res.url || res; |
|
|
|
|
|
|
|
console.log(item.fieldName + ' 新增表单内上传成功,URL:', resultUrl); |
|
|
|
message.success('上传成功!'); |
|
|
|
|
|
|
|
// 保存上传返回的 URL 到专用存储 |
|
|
|
formUploadUrls.value[item.fieldName] = resultUrl; |
|
|
|
|
|
|
|
// 同时更新表单值(用于显示) |
|
|
|
const currentValues = formApi.getValues(); |
|
|
|
formApi.setValues({ |
|
|
|
...currentValues, |
|
|
|
[item.fieldName]: resultUrl |
|
|
|
}); |
|
|
|
|
|
|
|
// 上传成功后,重新生成 schema 以更新 fileList 显示 |
|
|
|
setTimeout(() => { |
|
|
|
formApi.setState({ |
|
|
|
schema: getEditFormSchema(), |
|
|
|
}); |
|
|
|
}, 0); |
|
|
|
formApi.resetForm(); |
|
|
|
// 返回 false 阻止默认上传行为 |
|
|
|
return false; |
|
|
|
} catch (error: any) { |
|
|
|
console.error('❌ 新增表单内上传失败:', error); |
|
|
|
message.error(error?.message || '上传失败'); |
|
|
|
return false; |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
}; |
|
|
|
} |
|
|
|
return item; |
|
|
|
}); |
|
|
|
}; |
|
|
|
// ========== 表单配置 ========== |
|
|
|
const [Form, formApi] = useVbenForm({ |
|
|
|
schema: formSchema, |
|
|
|
schema: editFormSchema, |
|
|
|
showDefaultActions: false, |
|
|
|
wrapperClass: 'grid-cols-1 md:grid-cols-2', |
|
|
|
commonConfig: { |
|
|
|
@ -228,10 +551,93 @@ const [Form, formApi] = useVbenForm({ |
|
|
|
}, |
|
|
|
}) |
|
|
|
|
|
|
|
// ========== 打开上传对话框 ========== |
|
|
|
function openUploadDialog(fieldName: string, currentUrl: string) { |
|
|
|
console.log('=== 打开上传对话框 ===', { |
|
|
|
fieldName, |
|
|
|
currentUrl, |
|
|
|
isEdit: isEdit.value, |
|
|
|
currentRowId: currentRow.value?.id, |
|
|
|
currentRowFull: currentRow.value, |
|
|
|
}); |
|
|
|
|
|
|
|
uploadFieldName.value = fieldName; |
|
|
|
uploadImageUrl.value = currentUrl || ''; |
|
|
|
uploadedUrl.value = ''; // 重置上传后的 URL |
|
|
|
uploadFileList.value = []; // 清空文件列表 |
|
|
|
uploadVisible.value = true; |
|
|
|
uploadModalApi.open(); |
|
|
|
} |
|
|
|
|
|
|
|
// ========== 处理图片上传 - 使用 custom-request ========== |
|
|
|
async function handleCustomRequest(options: any) { |
|
|
|
const {file, onSuccess, onError} = options; |
|
|
|
|
|
|
|
try { |
|
|
|
console.log('📦 开始上传文件:', file); |
|
|
|
console.log('📦 文件详情:', { |
|
|
|
name: file.name, |
|
|
|
type: file.type, |
|
|
|
size: file.size, |
|
|
|
lastModified: file.lastModified, |
|
|
|
}); |
|
|
|
|
|
|
|
const formData = new FormData(); |
|
|
|
formData.append('file', file); |
|
|
|
|
|
|
|
// 调用上传 API |
|
|
|
const res = await deviceApi.upload(formData); |
|
|
|
const resultUrl = res.result || res.message || res.url || res; |
|
|
|
|
|
|
|
console.log('✅ 上传成功,URL:', resultUrl); |
|
|
|
message.success('上传成功!'); |
|
|
|
|
|
|
|
// 保存上传返回的 URL |
|
|
|
uploadedUrl.value = resultUrl; |
|
|
|
console.log('=== uploadedUrl 赋值后 ===', uploadedUrl.value); |
|
|
|
|
|
|
|
// 添加到文件列表用于显示 |
|
|
|
uploadFileList.value = [{ |
|
|
|
uid: file.uid, |
|
|
|
name: file.name, |
|
|
|
status: 'done', |
|
|
|
url: resultUrl, |
|
|
|
}]; |
|
|
|
|
|
|
|
// 通知组件上传成功 |
|
|
|
onSuccess({url: resultUrl}); |
|
|
|
|
|
|
|
} catch (error: any) { |
|
|
|
console.error('❌ 上传失败:', error); |
|
|
|
message.error(error?.message || '上传失败'); |
|
|
|
onError(error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ========== 上传成功或失败后的处理 |
|
|
|
function handleChange(info: any) { |
|
|
|
const {status} = info.file; |
|
|
|
console.log('📋 文件状态变化:', status, info.file); |
|
|
|
|
|
|
|
if (status === 'done') { |
|
|
|
console.log('✅ 上传完成响应:', info); |
|
|
|
} else if (status === 'error') { |
|
|
|
console.error('❌ 上传失败:', info); |
|
|
|
message.error(`${info.file.name} 上传失败`); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ========== 打开新增弹窗 ========== |
|
|
|
function handleAdd() { |
|
|
|
isEdit.value = false; |
|
|
|
modalTitle.value = '新增用户'; |
|
|
|
modalTitle.value = '功能配置表新增'; |
|
|
|
// 重置上传 URL 记录 |
|
|
|
formUploadUrls.value = {}; |
|
|
|
// 更新表单 schema (不包含 id) |
|
|
|
formApi.setState({ |
|
|
|
schema: getEditFormSchema(), |
|
|
|
}); |
|
|
|
formApi.resetForm(); |
|
|
|
modalApi.open(); |
|
|
|
} |
|
|
|
@ -240,104 +646,19 @@ function handleAdd() { |
|
|
|
function handleEdit(row: any) { |
|
|
|
isEdit.value = true; |
|
|
|
currentRow.value = row; |
|
|
|
modalTitle.value = '编辑用户'; |
|
|
|
// 设置表单值,过滤掉不必要的字段,并处理日期格式 |
|
|
|
const formValues = { |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
id: row.id, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceName: row.deviceName, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceBrand: row.deviceBrand, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceModel: row.deviceModel, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceType: row.deviceType, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceOrder: row.deviceOrder, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceOnline: row.deviceOnline, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
roomId: row.roomId, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
roomName: row.roomName, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceSwitch: row.deviceSwitch, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
createdAt: row.createdAt, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
createdBy: row.createdBy, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
updatedAt: row.updatedAt, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
updatedBy: row.updatedBy, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deletedFlag: row.deletedFlag, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
userId: row.userId, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
classId: row.classId, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceUrl: row.deviceUrl, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceCode: row.deviceCode, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceLanUser: row.deviceLanUser, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceLanPassword: row.deviceLanPassword, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceLanIp: row.deviceLanIp, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceNote: row.deviceNote, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceAddressIp: row.deviceAddressIp, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceBind: row.deviceBind, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
deviceImg: row.deviceImg, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
warnImg: row.warnImg, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
familyId: row.familyId, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
serverVersion: row.serverVersion, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
aiVersion: row.aiVersion, |
|
|
|
|
|
|
|
// 字段名 |
|
|
|
upSwitch: row.upSwitch, |
|
|
|
|
|
|
|
}; |
|
|
|
modalTitle.value = '功能配置表编辑'; |
|
|
|
// 更新表单 schema (包含 id) |
|
|
|
formApi.setState({ |
|
|
|
schema: getEditFormSchema(), |
|
|
|
}); |
|
|
|
// 设置表单值,只设置 editFields 中包含的字段 |
|
|
|
const formValues: any = {}; |
|
|
|
editFormSchema.forEach(item => { |
|
|
|
const field = item.fieldName; |
|
|
|
if (row[field] !== undefined && row[field] !== null) { |
|
|
|
formValues[field] = row[field]; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
formApi.setValues(formValues); |
|
|
|
modalApi.open(); |
|
|
|
@ -367,12 +688,17 @@ function handleReset() { |
|
|
|
|
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
|
|
<template> |
|
|
|
<div style="height: 100vh; padding: 16px; box-sizing: border-box;"> |
|
|
|
<div |
|
|
|
style="height: 100vh; padding: 16px; box-sizing: border-box; display: flex; flex-direction: column;"> |
|
|
|
<!-- 查询表单 --> |
|
|
|
<div class="bg-card mb-4 p-4 rounded shadow"> |
|
|
|
<div class="bg-card mb-4 p-4 rounded shadow flex-shrink-0"> |
|
|
|
<h3 class="text-lg font-semibold mb-3">查询条件</h3> |
|
|
|
<QueryForm/> |
|
|
|
<div v-if="enumData.loading" class="text-center py-4 text-gray-500"> |
|
|
|
正在加载查询条件... |
|
|
|
</div> |
|
|
|
<QueryForm v-else/> |
|
|
|
<div class="mt-3 flex gap-2"> |
|
|
|
<button @click="handleSearch" |
|
|
|
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"> |
|
|
|
@ -385,25 +711,81 @@ function handleReset() { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 数据表格 --> |
|
|
|
<Grid> |
|
|
|
<template #toolbar-tools> |
|
|
|
<button @click="handleAdd" class="mr-2">➕ 新增</button> |
|
|
|
<button @click="() => gridApi.reload()">🔄 刷新</button> |
|
|
|
</template> |
|
|
|
|
|
|
|
<template #action="{ row }"> |
|
|
|
<div class="flex gap-2"> |
|
|
|
<button @click="() => handleEdit(row)" class="text-blue-500 hover:text-blue-700">✏️ 编辑 |
|
|
|
</button> |
|
|
|
<button @click="() => handleDelete(row)" class="text-red-500 hover:text-red-700">🗑️ 删除 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</Grid> |
|
|
|
<!-- 数据表格 (可滚动区域) --> |
|
|
|
<div class="flex-1 overflow-hidden bg-card rounded shadow"> |
|
|
|
<Grid> |
|
|
|
<template #toolbar-tools> |
|
|
|
<button @click="handleAdd" class="mr-2">➕ 新增</button> |
|
|
|
<button @click="() => gridApi.reload()">🔄 刷新</button> |
|
|
|
</template> |
|
|
|
|
|
|
|
<template #action="{ row }"> |
|
|
|
<div class="flex gap-2"> |
|
|
|
<button @click="() => handleEdit(row)" class="text-blue-500 hover:text-blue-700">✏️ 编辑 |
|
|
|
</button> |
|
|
|
<button @click="() => handleDelete(row)" class="text-red-500 hover:text-red-700">🗑️ 删除 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</Grid> |
|
|
|
</div> |
|
|
|
|
|
|
|
<Modal :title="modalTitle"> |
|
|
|
<Form/> |
|
|
|
</Modal> |
|
|
|
<!-- 上传对话框 --> |
|
|
|
<!-- 上传对话框 --> |
|
|
|
<UploadModal> |
|
|
|
<div style="padding: 20px;margin-top: 16px; text-align: center;"> |
|
|
|
<Upload |
|
|
|
name="file" |
|
|
|
:file-list="uploadFileList" |
|
|
|
:custom-request="handleCustomRequest" |
|
|
|
:show-upload-list="true" |
|
|
|
accept="image/*" style="width: 100%;" |
|
|
|
> |
|
|
|
<div |
|
|
|
style="width: 100%; height: 150px; border: 2px dashed #d9d9d9; border-radius: 8px; background: #fafafa; display: flex; flex-direction: column; justify-content: center; align-items: center; cursor: pointer;"> |
|
|
|
<!-- 使用 SVG 图标代替 --> |
|
|
|
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#1890ff" |
|
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" |
|
|
|
style="margin-bottom: 8px;"> |
|
|
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> |
|
|
|
<polyline points="17 8 12 3 7 8"></polyline> |
|
|
|
<line x1="12" y1="3" x2="12" y2="15"></line> |
|
|
|
</svg> |
|
|
|
<span style="color: #666;">点击或拖拽图片到此上传</span> |
|
|
|
<span style="color: #999; font-size: 12px; margin-top: 4px;">支持 JPG、PNG 格式</span> |
|
|
|
</div> |
|
|
|
</Upload> |
|
|
|
|
|
|
|
<!-- 展示上传成功后的图片 --> |
|
|
|
<div v-if="uploadedUrl" style="margin-top: 16px; text-align: center;"> |
|
|
|
<p style="color: #67c23a; font-size: 14px; margin-bottom: 8px;">✅ 上传成功!</p> |
|
|
|
<p style="color: #999; font-size: 12px; margin-bottom: 8px;">新图片预览:</p> |
|
|
|
<Image |
|
|
|
:src="uploadedUrl" |
|
|
|
style="max-width: 200px; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);" |
|
|
|
:preview="true" |
|
|
|
/> |
|
|
|
<p style="color: #999; font-size: 12px; margin-top: 8px; word-break: break-all;"></p> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 展示原图(如果有) --> |
|
|
|
<div v-if="uploadImageUrl && !uploadedUrl" style="margin-top: 16px; text-align: center;"> |
|
|
|
<p style="color: #999; font-size: 12px; margin-bottom: 8px;">当前图片:</p> |
|
|
|
<Image |
|
|
|
:src="uploadImageUrl.startsWith('http') || uploadImageUrl.startsWith('/') ? uploadImageUrl : `${API_BASE_URL}${uploadImageUrl}`" |
|
|
|
style="max-width: 200px; border-radius: 4px;" |
|
|
|
:preview="true" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</UploadModal> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</template> |
|
|
|
<style scoped> |
|
|
|
:deep(.vxe-pager--wrapper) { |
|
|
|
margin-bottom: 0.5rem; |
|
|
|
} |
|
|
|
</style> |