420 lines
11 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import type { VbenFormProps } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
2025-07-29 14:43:27 +08:00
import { h, nextTick, ref } from 'vue';
import { useRouter } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui';
2025-07-28 15:24:23 +08:00
import { message as Message, Tag } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
2025-07-29 14:43:27 +08:00
postFilesDownload,
postFilesUpload,
2025-07-28 14:38:00 +08:00
postOneNetProductDeleteAsync,
2025-07-29 14:43:27 +08:00
postOneNetProductInsertAsync,
2025-07-28 14:38:00 +08:00
postOneNetProductListAsync,
postOneNetProductModifyAsync,
2025-07-29 14:43:27 +08:00
postOneNetProductProductStatusChangeAsync,
} from '#/api-client';
import { TableAction } from '#/components/table-action';
import { $t } from '#/locales';
import {
2025-07-28 15:24:23 +08:00
addProductFormSchema,
editProductFormSchemaEdit,
querySchema,
setFileSelectedCallback,
2025-07-29 14:43:27 +08:00
tableSchema,
} from './schema';
defineOptions({
2025-07-28 15:24:23 +08:00
name: 'OneNETProduct',
});
const router = useRouter();
const formOptions: VbenFormProps = {
schema: querySchema.value,
};
const gridOptions: VxeGridProps<any> = {
checkboxConfig: {
highlight: true,
labelField: 'name',
},
columns: tableSchema.value,
height: 'auto',
keepSource: true,
pagerConfig: {},
toolbarConfig: {
custom: true,
},
customConfig: {
storage: true,
},
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
2025-07-28 14:38:00 +08:00
const { data } = await postOneNetProductListAsync({
body: {
pageIndex: page.currentPage,
pageSize: page.pageSize,
...formValues,
},
});
return data;
},
},
},
};
const [Grid, gridApi] = useVbenVxeGrid({ formOptions, gridOptions });
const editRow: Record<string, any> = ref({});
// 声明文件变量,用于存储选择的文件
let selectedFile: File | null = null;
// 设置文件选择回调
setFileSelectedCallback((file) => {
selectedFile = file;
});
const [UserModal, userModalApi] = useVbenModal({
draggable: true,
footer: true,
showCancelButton: true,
showConfirmButton: true,
onConfirm: submit,
onBeforeClose: () => {
2025-07-28 15:24:23 +08:00
// 只在确认提交后重置,而不是每次关闭都重置
return true;
},
onOpen: () => {
// 重置文件选择
selectedFile = null;
},
onOpenChange: (isOpen: boolean) => {
if (isOpen && editRow.value.id) {
// 编辑模式下,模态框打开后设置表单值
nextTick(() => {
editFormApi.setValues({ ...editRow.value });
});
}
},
onCancel: () => {
// 取消时也重置文件选择
selectedFile = null;
// 关闭模态框
userModalApi.close();
},
});
const [AddForm, addFormApi] = useVbenForm({
// 默认展开
collapsed: false,
// 所有表单项共用,可单独在表单内覆盖
commonConfig: {
labelWidth: 110,
componentProps: {
class: 'w-4/5',
},
},
layout: 'horizontal',
2025-07-28 15:24:23 +08:00
schema: addProductFormSchema.value,
showCollapseButton: false,
showDefaultActions: false,
wrapperClass: 'grid-cols-2',
});
const [EditForm, editFormApi] = useVbenForm({
// 默认展开
collapsed: false,
// 所有表单项共用,可单独在表单内覆盖
commonConfig: {
labelWidth: 110,
componentProps: {
class: 'w-4/5',
},
},
// 提交函数
layout: 'horizontal',
2025-07-28 15:24:23 +08:00
schema: editProductFormSchemaEdit.value,
showCollapseButton: false,
showDefaultActions: false,
wrapperClass: 'grid-cols-2',
});
// 新增和编辑提交的逻辑
async function submit() {
const isEdit = !!editRow.value.id;
const formApi = isEdit ? editFormApi : addFormApi;
2025-07-29 14:43:27 +08:00
const api = isEdit
? postOneNetProductModifyAsync
: postOneNetProductInsertAsync;
const { valid } = await formApi.validate();
if (!valid) return;
const formValues = await formApi.getValues();
2025-07-29 14:43:27 +08:00
// 提交前校验
if (!formValues.deviceThingModelFileName) {
2025-07-29 11:17:27 +08:00
Message.error('请选择设备模型文件');
return;
}
// 如果有文件需要上传,先上传
if (selectedFile) {
try {
userModalApi.setState({ loading: true, confirmLoading: true });
const result = await postFilesUpload({ body: { files: [selectedFile] } });
if (result.status === 204 || result.status === 200) {
const fileInfo = result.data?.[0];
if (fileInfo && fileInfo.id) {
formValues.deviceThingModelFileId = fileInfo.id;
// 设置文件名
2025-10-21 17:21:34 +08:00
formValues.deviceThingModelFileName =
fileInfo.fileName || selectedFile.name;
} else {
Message.error('文件上传成功但未获取到文件ID');
userModalApi.setState({ loading: false, confirmLoading: false });
return;
}
} else {
Message.error('文件上传失败');
userModalApi.setState({ loading: false, confirmLoading: false });
return;
}
2025-07-29 14:43:27 +08:00
} catch {
Message.error('文件上传失败');
userModalApi.setState({ loading: false, confirmLoading: false });
return;
}
}
// 清空全局变量,防止下次误用
selectedFile = null;
// 继续后续表单提交逻辑
2025-07-29 14:43:27 +08:00
const fetchParams: any = isEdit
? {
2025-07-29 14:43:27 +08:00
id: editRow.value.id,
...formValues,
}
: {
2025-07-29 14:43:27 +08:00
...formValues,
};
try {
const resp = await api({ body: fetchParams });
if (resp.data) {
Message.success(
editRow.value.id ? $t('common.editSuccess') : $t('common.addSuccess'),
);
userModalApi.close();
2025-07-28 15:24:23 +08:00
editRow.value = {};
gridApi.reload();
} else {
Message.error(
editRow.value.id ? $t('common.editFail') : $t('common.addFail'),
);
}
} finally {
userModalApi.setState({ loading: false, confirmLoading: false });
}
}
async function onEdit(record: any) {
editRow.value = record;
userModalApi.open();
// 重置文件选择状态
selectedFile = null;
}
const openAddModal = async () => {
editRow.value = {};
userModalApi.open();
// 重置文件选择状态
selectedFile = null;
};
2025-07-28 15:24:23 +08:00
// 删除函数
async function onDel(record: any) {
try {
2025-07-29 14:43:27 +08:00
const resp = await postOneNetProductDeleteAsync({
body: { id: record.id },
});
2025-07-28 15:24:23 +08:00
if (resp.data) {
Message.success($t('common.deleteSuccess'));
// 重置文件选择
selectedFile = null;
2025-07-28 15:24:23 +08:00
gridApi.reload();
} else {
Message.error($t('common.deleteFail'));
}
} catch {
Message.error($t('common.deleteFail'));
}
}
// 下载文件函数
async function onDownloadFile(record: any) {
if (!record.deviceThingModelFileId) {
Message.error('文件ID不存在无法下载');
return;
}
2025-07-29 14:43:27 +08:00
try {
const { data } = await postFilesDownload({
body: { id: record.deviceThingModelFileId },
responseType: 'blob',
});
const url = window.URL.createObjectURL(new Blob([data as Blob]));
const link = document.createElement('a');
link.href = url;
2025-07-29 14:43:27 +08:00
link.setAttribute(
'download',
record.deviceThingModelFileName || 'device-model-file',
);
document.body.append(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
Message.success('文件下载成功');
2025-07-29 14:43:27 +08:00
} catch {
Message.error('文件下载失败');
}
}
2025-07-29 14:43:27 +08:00
// 状态更改函数
async function onStatusChange(record: any) {
try {
const resp = await postOneNetProductProductStatusChangeAsync({
body: {
id: record.id,
enabled: !record.isEnabled,
},
});
if (resp.data) {
Message.success(record.isEnabled ? '禁用成功' : '启用成功');
gridApi.reload();
} else {
Message.error(record.isEnabled ? '禁用失败' : '启用失败');
}
} catch {
Message.error(record.isEnabled ? '禁用失败' : '启用失败');
}
}
2025-10-21 17:21:34 +08:00
// 设备管理函数
function onDeviceManagement(record: any) {
console.log('设备管理按钮被点击', record);
// 跳转到设备管理页面传递平台类型和平台产品ID作为参数
router.push({
path: '/devicemanagement/deviceinfo',
query: {
ioTPlatform: '2', // OneNET平台类型为2
ioTPlatformDeviceOpenInfo: record.ioTPlatformProductId, // 平台产品ID
productName: record.productName,
},
});
}
// 物模型管理函数
function onThingModelManagement(record: any) {
console.log('物模型管理按钮被点击', record);
// 跳转到物模型管理页面传递平台类型和平台产品ID作为参数
router.push({
path: '/thingmodelinfo/ioTPlatformThingModelInfo',
query: {
productId: record.ioTPlatformProductId, // 平台产品ID
2025-12-18 17:14:47 +08:00
productName: record.productName,
ioTPlatform: '2', // OneNET平台类型为2
},
});
2025-10-21 17:21:34 +08:00
}
</script>
<template>
<Page auto-content-height>
<Grid>
<template #toolbar-actions>
2025-07-29 14:43:27 +08:00
<TableAction :actions="[
{
label: $t('common.add'),
type: 'primary',
icon: 'ant-design:plus-outlined',
onClick: openAddModal.bind(null),
auth: ['AbpIdentity.Users.Create'],
},
]" />
</template>
<template #isEnable="{ row }">
2025-07-29 14:43:27 +08:00
<component :is="h(Tag, { color: row.isEnabled ? 'green' : 'red' }, () =>
row.isEnabled ? $t('common.yes') : $t('common.no'),
)
" />
</template>
<template #deviceThingModelFileName="{ row }">
2025-07-29 14:43:27 +08:00
<a v-if="row.deviceThingModelFileName && row.deviceThingModelFileId" @click="onDownloadFile(row)"
style="color: #1890ff; text-decoration: underline; cursor: pointer">
{{ row.deviceThingModelFileName }}
</a>
<span v-else>{{ row.deviceThingModelFileName || '-' }}</span>
</template>
<template #action="{ row }">
2025-07-29 14:43:27 +08:00
<TableAction :actions="[
{
label: $t('common.edit'),
type: 'link',
size: 'small',
auth: ['AbpIdentity.Users.Update'],
onClick: onEdit.bind(null, row),
},
{
2025-10-21 17:21:34 +08:00
label: $t('abp.deviceInfos.deviceInfoManage'),
type: 'link',
size: 'small',
onClick: onDeviceManagement.bind(null, row),
},
{
label: $t('abp.deviceInfos.thingModelInfoManage'),
2025-07-29 14:43:27 +08:00
type: 'link',
2025-10-21 17:21:34 +08:00
size: 'small',
onClick: onThingModelManagement.bind(null, row),
}
]" :drop-down-actions="[
{
label: row.isEnabled ? $t('common.disabled') : $t('common.enabled'),
icon: 'ant-design:edit-filled',
type: 'primary',
2025-07-29 14:43:27 +08:00
danger: row.isEnabled,
size: 'small',
auth: ['AbpIdentity.Users.Update'],
popConfirm: {
2025-10-21 17:21:34 +08:00
title: `确定要${row.isEnabled ? $t('common.disabled') : $t('common.enabled')}该产品吗?`,
2025-07-29 14:43:27 +08:00
confirm: onStatusChange.bind(null, row),
},
2025-07-29 14:43:27 +08:00
},
2025-10-21 17:21:34 +08:00
{
label: $t('common.delete'),
icon: 'ant-design:delete-outlined',
type: 'primary',
popConfirm: {
title: $t('common.askConfirmDelete'),
confirm: onDel.bind(null, row),
},
2025-10-21 17:21:34 +08:00
},
]" />
</template>
</Grid>
2025-07-29 14:43:27 +08:00
<UserModal :title="editRow.id ? $t('common.edit') : $t('common.add')" class="w-[800px]">
<component :is="editRow.id ? EditForm : AddForm" />
</UserModal>
</Page>
</template>