2025-12-18 17:14:47 +08:00

420 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import type { VbenFormProps } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
import { h, nextTick, ref } from 'vue';
import { useRouter } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui';
import { message as Message, Tag } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
postFilesDownload,
postFilesUpload,
postOneNetProductDeleteAsync,
postOneNetProductInsertAsync,
postOneNetProductListAsync,
postOneNetProductModifyAsync,
postOneNetProductProductStatusChangeAsync,
} from '#/api-client';
import { TableAction } from '#/components/table-action';
import { $t } from '#/locales';
import {
addProductFormSchema,
editProductFormSchemaEdit,
querySchema,
setFileSelectedCallback,
tableSchema,
} from './schema';
defineOptions({
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) => {
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: () => {
// 只在确认提交后重置,而不是每次关闭都重置
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',
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',
schema: editProductFormSchemaEdit.value,
showCollapseButton: false,
showDefaultActions: false,
wrapperClass: 'grid-cols-2',
});
// 新增和编辑提交的逻辑
async function submit() {
const isEdit = !!editRow.value.id;
const formApi = isEdit ? editFormApi : addFormApi;
const api = isEdit
? postOneNetProductModifyAsync
: postOneNetProductInsertAsync;
const { valid } = await formApi.validate();
if (!valid) return;
const formValues = await formApi.getValues();
// 提交前校验
if (!formValues.deviceThingModelFileName) {
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;
// 设置文件名
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;
}
} catch {
Message.error('文件上传失败');
userModalApi.setState({ loading: false, confirmLoading: false });
return;
}
}
// 清空全局变量,防止下次误用
selectedFile = null;
// 继续后续表单提交逻辑
const fetchParams: any = isEdit
? {
id: editRow.value.id,
...formValues,
}
: {
...formValues,
};
try {
const resp = await api({ body: fetchParams });
if (resp.data) {
Message.success(
editRow.value.id ? $t('common.editSuccess') : $t('common.addSuccess'),
);
userModalApi.close();
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;
};
// 删除函数
async function onDel(record: any) {
try {
const resp = await postOneNetProductDeleteAsync({
body: { id: record.id },
});
if (resp.data) {
Message.success($t('common.deleteSuccess'));
// 重置文件选择
selectedFile = null;
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;
}
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;
link.setAttribute(
'download',
record.deviceThingModelFileName || 'device-model-file',
);
document.body.append(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
Message.success('文件下载成功');
} catch {
Message.error('文件下载失败');
}
}
// 状态更改函数
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 ? '禁用失败' : '启用失败');
}
}
// 设备管理函数
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
productName: record.productName,
ioTPlatform: '2', // OneNET平台类型为2
},
});
}
</script>
<template>
<Page auto-content-height>
<Grid>
<template #toolbar-actions>
<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 }">
<component :is="h(Tag, { color: row.isEnabled ? 'green' : 'red' }, () =>
row.isEnabled ? $t('common.yes') : $t('common.no'),
)
" />
</template>
<template #deviceThingModelFileName="{ row }">
<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 }">
<TableAction :actions="[
{
label: $t('common.edit'),
type: 'link',
size: 'small',
auth: ['AbpIdentity.Users.Update'],
onClick: onEdit.bind(null, row),
},
{
label: $t('abp.deviceInfos.deviceInfoManage'),
type: 'link',
size: 'small',
onClick: onDeviceManagement.bind(null, row),
},
{
label: $t('abp.deviceInfos.thingModelInfoManage'),
type: 'link',
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',
danger: row.isEnabled,
size: 'small',
auth: ['AbpIdentity.Users.Update'],
popConfirm: {
title: `确定要${row.isEnabled ? $t('common.disabled') : $t('common.enabled')}该产品吗`,
confirm: onStatusChange.bind(null, row),
},
},
{
label: $t('common.delete'),
icon: 'ant-design:delete-outlined',
type: 'primary',
popConfirm: {
title: $t('common.askConfirmDelete'),
confirm: onDel.bind(null, row),
},
},
]" />
</template>
</Grid>
<UserModal :title="editRow.id ? $t('common.edit') : $t('common.add')" class="w-[800px]">
<component :is="editRow.id ? EditForm : AddForm" />
</UserModal>
</Page>
</template>