mirror of
https://gitee.com/yudaocode/yudao-ui-admin-vben.git
synced 2025-12-30 02:22:25 +00:00
Merge branch 'dev' of https://gitee.com/yudaocode/yudao-ui-admin-vben into reform-mp
This commit is contained in:
@@ -3,7 +3,8 @@ import type { PageParam, PageResult } from '@vben/request';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace IotDeviceApi {
|
||||
/** IoT 设备 VO */
|
||||
// TODO @haohao:需要跟后端对齐,必要的 ReqVO、RespVO
|
||||
/** 设备 */
|
||||
export interface Device {
|
||||
id?: number; // 设备 ID,主键,自增
|
||||
deviceName: string; // 设备名称
|
||||
@@ -48,7 +49,7 @@ export namespace IotDeviceApi {
|
||||
dataSpecsList: any[]; // 数据定义列表
|
||||
}
|
||||
|
||||
/** IoT 设备属性 VO */
|
||||
/** 设备属性 VO */
|
||||
export interface DeviceProperty {
|
||||
identifier: string; // 属性标识符
|
||||
value: string; // 最新值
|
||||
@@ -62,7 +63,7 @@ export namespace IotDeviceApi {
|
||||
password: string; // 密码
|
||||
}
|
||||
|
||||
/** IoT 设备发送消息 Request VO */
|
||||
/** 设备发送消息 Request VO */
|
||||
export interface DeviceMessageSendReq {
|
||||
deviceId: number; // 设备编号
|
||||
method: string; // 请求方法
|
||||
@@ -77,6 +78,7 @@ export namespace IotDeviceApi {
|
||||
}
|
||||
|
||||
/** IoT 设备状态枚举 */
|
||||
// TODO @haohao:packages/constants/src/biz-iot-enum.ts 枚举;
|
||||
export enum DeviceStateEnum {
|
||||
INACTIVE = 0, // 未激活
|
||||
OFFLINE = 2, // 离线
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { PageParam, PageResult } from '@vben/request';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace IotDeviceGroupApi {
|
||||
/** IoT 设备分组 VO */
|
||||
/** 设备分组 */
|
||||
export interface DeviceGroup {
|
||||
id?: number; // 分组 ID
|
||||
name: string; // 分组名字
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { PageParam, PageResult } from '@vben/request';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace IotProductApi {
|
||||
/** IoT 产品 VO */
|
||||
/** 产品 */
|
||||
export interface Product {
|
||||
id?: number; // 产品编号
|
||||
name: string; // 产品名称
|
||||
@@ -27,6 +27,8 @@ export namespace IotProductApi {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @haohao:packages/constants/src/biz-iot-enum.ts 枚举;
|
||||
|
||||
/** IOT 产品设备类型枚举类 */
|
||||
export enum DeviceTypeEnum {
|
||||
DEVICE = 0, // 直连设备
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { PageParam, PageResult } from '@vben/request';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace RuleSceneApi {
|
||||
/** IoT 场景联动规则 VO */
|
||||
/** 场景联动规则 */
|
||||
export interface SceneRule {
|
||||
id?: number;
|
||||
name: string;
|
||||
@@ -14,7 +14,7 @@ export namespace RuleSceneApi {
|
||||
createTime?: Date;
|
||||
}
|
||||
|
||||
/** IoT 场景联动规则触发器 */
|
||||
/** 场景联动规则的触发器 */
|
||||
export interface Trigger {
|
||||
type?: string;
|
||||
productId?: number;
|
||||
@@ -26,13 +26,13 @@ export namespace RuleSceneApi {
|
||||
conditionGroups?: TriggerConditionGroup[];
|
||||
}
|
||||
|
||||
/** IoT 场景联动规则触发条件组 */
|
||||
/** 场景联动规则的触发条件组 */
|
||||
export interface TriggerConditionGroup {
|
||||
conditions?: TriggerCondition[];
|
||||
operator?: string;
|
||||
}
|
||||
|
||||
/** IoT 场景联动规则触发条件 */
|
||||
/** 场景联动规则的触发条件 */
|
||||
export interface TriggerCondition {
|
||||
productId?: number;
|
||||
deviceId?: number;
|
||||
@@ -42,7 +42,7 @@ export namespace RuleSceneApi {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
/** IoT 场景联动规则动作 */
|
||||
/** 场景联动规则的动作 */
|
||||
export interface Action {
|
||||
type?: string;
|
||||
productId?: number;
|
||||
@@ -53,6 +53,7 @@ export namespace RuleSceneApi {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @haohao:貌似下面的,和 RuleSceneApi 重复了。
|
||||
/** IoT 场景联动规则 */
|
||||
export interface IotSceneRule {
|
||||
id?: number;
|
||||
@@ -135,6 +136,7 @@ export function deleteSceneRule(id: number) {
|
||||
}
|
||||
|
||||
/** 批量删除场景联动规则 */
|
||||
// TODO @haohao:貌似用上。
|
||||
export function deleteSceneRuleList(ids: number[]) {
|
||||
return requestClient.delete('/iot/scene-rule/delete-list', {
|
||||
params: { ids: ids.join(',') },
|
||||
|
||||
@@ -48,6 +48,7 @@ const viewMode = ref<'card' | 'list'>('card');
|
||||
const cardViewRef = ref();
|
||||
|
||||
// Modal instances
|
||||
// TODO @haohao:这个界面,等 product 改完,在一起看看怎么弄更好。
|
||||
const [DeviceFormModal, deviceFormModalApi] = useVbenModal({
|
||||
connectedComponent: DeviceForm,
|
||||
destroyOnClose: true,
|
||||
|
||||
@@ -43,9 +43,7 @@ async function handleDelete(row: IotDeviceGroupApi.DeviceGroup) {
|
||||
});
|
||||
try {
|
||||
await deleteDeviceGroup(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
});
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
@@ -55,7 +53,6 @@ async function handleDelete(row: IotDeviceGroupApi.DeviceGroup) {
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
showCollapseButton: true,
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
|
||||
@@ -19,6 +19,8 @@ import { useFormSchema } from '../data';
|
||||
|
||||
defineOptions({ name: 'IoTDeviceGroupForm' });
|
||||
|
||||
// TODO @haohao:web-antd/src/views/iot/product/category/modules/product-category-form.vue 类似问题
|
||||
|
||||
const emit = defineEmits<{
|
||||
success: [];
|
||||
}>();
|
||||
|
||||
@@ -23,7 +23,6 @@ export function useFormSchema(formApi?: any): VbenFormSchema[] {
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
// 创建时的 ProductKey 字段(带生成按钮)
|
||||
{
|
||||
fieldName: 'productKey',
|
||||
label: 'ProductKey',
|
||||
@@ -34,7 +33,6 @@ export function useFormSchema(formApi?: any): VbenFormSchema[] {
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
if(values) {
|
||||
// 仅在创建时显示(没有 id)
|
||||
return !values.id;
|
||||
},
|
||||
},
|
||||
@@ -43,6 +41,7 @@ export function useFormSchema(formApi?: any): VbenFormSchema[] {
|
||||
.min(1, 'ProductKey 不能为空')
|
||||
.max(32, 'ProductKey 长度不能超过 32 个字符'),
|
||||
suffix: () => {
|
||||
// 创建时的 ProductKey 字段(带生成按钮)
|
||||
return h(
|
||||
Button,
|
||||
{
|
||||
@@ -55,19 +54,17 @@ export function useFormSchema(formApi?: any): VbenFormSchema[] {
|
||||
);
|
||||
},
|
||||
},
|
||||
// 编辑时的 ProductKey 字段(禁用,无按钮)
|
||||
{
|
||||
fieldName: 'productKey',
|
||||
label: 'ProductKey',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入 ProductKey',
|
||||
disabled: true,
|
||||
disabled: true, // 编辑时的 ProductKey 字段(禁用,无按钮)
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
if(values) {
|
||||
// 仅在编辑时显示(有 id)
|
||||
return !!values.id;
|
||||
},
|
||||
},
|
||||
@@ -176,7 +173,6 @@ export function useBasicFormSchema(formApi?: any): VbenFormSchema[] {
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
// 创建时的 ProductKey 字段(带生成按钮)
|
||||
{
|
||||
fieldName: 'productKey',
|
||||
label: 'ProductKey',
|
||||
@@ -187,7 +183,6 @@ export function useBasicFormSchema(formApi?: any): VbenFormSchema[] {
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
if(values) {
|
||||
// 仅在创建时显示(没有 id)
|
||||
return !values.id;
|
||||
},
|
||||
},
|
||||
@@ -208,7 +203,6 @@ export function useBasicFormSchema(formApi?: any): VbenFormSchema[] {
|
||||
);
|
||||
},
|
||||
},
|
||||
// 编辑时的 ProductKey 字段(禁用,无按钮)
|
||||
{
|
||||
fieldName: 'productKey',
|
||||
label: 'ProductKey',
|
||||
@@ -220,7 +214,6 @@ export function useBasicFormSchema(formApi?: any): VbenFormSchema[] {
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
if(values) {
|
||||
// 仅在编辑时显示(有 id)
|
||||
return !!values.id;
|
||||
},
|
||||
},
|
||||
@@ -341,6 +334,7 @@ export function useAdvancedFormSchema(): VbenFormSchema[] {
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
// TODO @haohao:貌似用不上?
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
@@ -367,7 +361,6 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{ type: 'checkbox', width: 40 },
|
||||
{
|
||||
field: 'id',
|
||||
title: 'ID',
|
||||
@@ -413,7 +406,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 180,
|
||||
width: 220,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
@@ -421,6 +414,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
}
|
||||
|
||||
/** 查询产品列表 */
|
||||
// TODO @haohao:貌似可以删除?
|
||||
export async function queryProductList({ page }: any, searchParams: any) {
|
||||
return await getProductPage({
|
||||
pageNo: page.currentPage,
|
||||
@@ -430,6 +424,7 @@ export async function queryProductList({ page }: any, searchParams: any) {
|
||||
}
|
||||
|
||||
/** 创建图片预览状态 */
|
||||
// TODO @haohao:可能不一定用的上;
|
||||
export function useImagePreview() {
|
||||
const previewVisible = ref(false);
|
||||
const previewImage = ref('');
|
||||
@@ -446,6 +441,7 @@ export function useImagePreview() {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO @haohao:放到对应的 form 里
|
||||
/** 生成 ProductKey(包含大小写字母和数字) */
|
||||
export function generateProductKey(): string {
|
||||
const chars =
|
||||
|
||||
@@ -26,17 +26,14 @@ import ProductForm from './modules/product-form.vue';
|
||||
defineOptions({ name: 'IoTProduct' });
|
||||
|
||||
const router = useRouter();
|
||||
const categoryList = ref<any[]>([]);
|
||||
const categoryList = ref<any[]>([]); // TODO @haohao:category 类型
|
||||
const viewMode = ref<'card' | 'list'>('card');
|
||||
const cardViewRef = ref();
|
||||
|
||||
// 搜索参数
|
||||
const searchParams = ref({
|
||||
name: '',
|
||||
productKey: '',
|
||||
});
|
||||
}); // 搜索参数
|
||||
|
||||
// 图片预览
|
||||
const { previewVisible, previewImage, handlePreviewImage } = useImagePreview();
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
@@ -44,18 +41,19 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
// 加载产品分类列表
|
||||
/** 加载产品分类列表 */
|
||||
async function loadCategories() {
|
||||
categoryList.value = await getSimpleProductCategoryList();
|
||||
}
|
||||
|
||||
// 获取分类名称
|
||||
/** 获取分类名称 */
|
||||
function getCategoryNameByValue(categoryId: number) {
|
||||
const category = categoryList.value.find((c: any) => c.id === categoryId);
|
||||
return category?.name || '未分类';
|
||||
}
|
||||
|
||||
/** 搜索 */
|
||||
// TODO @haohao:要不要改成 handleRefresh,注释改成“刷新表格”,更加统一。
|
||||
/** 搜索产品 */
|
||||
function handleSearch() {
|
||||
if (viewMode.value === 'list') {
|
||||
gridApi.formApi.setValues(searchParams.value);
|
||||
@@ -65,14 +63,14 @@ function handleSearch() {
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置 */
|
||||
/** 重置搜索 */
|
||||
function handleReset() {
|
||||
searchParams.value.name = '';
|
||||
searchParams.value.productKey = '';
|
||||
handleSearch();
|
||||
}
|
||||
|
||||
/** 刷新 */
|
||||
/** 刷新表格 */
|
||||
function handleRefresh() {
|
||||
if (viewMode.value === 'list') {
|
||||
gridApi.query();
|
||||
@@ -84,7 +82,7 @@ function handleRefresh() {
|
||||
/** 导出表格 */
|
||||
async function handleExport() {
|
||||
const data = await exportProduct(searchParams.value);
|
||||
await downloadFileFromBlobPart({ fileName: '产品列表.xls', source: data });
|
||||
downloadFileFromBlobPart({ fileName: '产品列表.xls', source: data });
|
||||
}
|
||||
|
||||
/** 打开产品详情 */
|
||||
@@ -117,12 +115,12 @@ function handleEdit(row: any) {
|
||||
/** 删除产品 */
|
||||
async function handleDelete(row: any) {
|
||||
const hideLoading = message.loading({
|
||||
content: `正在删除 ${row.name}...`,
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteProduct(row.id);
|
||||
message.success(`删除 ${row.name} 成功`);
|
||||
await deleteProduct(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
@@ -130,6 +128,7 @@ async function handleDelete(row: any) {
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
// TODO @haohao:这个不用,可以删除掉的
|
||||
formOptions: {
|
||||
schema: [],
|
||||
},
|
||||
@@ -156,9 +155,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions,
|
||||
} as VxeTableGridOptions, // TODO @haohao:这里有个 <> 泛型
|
||||
});
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
loadCategories();
|
||||
});
|
||||
@@ -172,22 +172,24 @@ onMounted(() => {
|
||||
<Card :body-style="{ padding: '16px' }" class="mb-4">
|
||||
<!-- 搜索表单 -->
|
||||
<div class="mb-3 flex items-center gap-3">
|
||||
<!-- TODO @haohao:tindwind -->
|
||||
<Input
|
||||
v-model:value="searchParams.name"
|
||||
placeholder="请输入产品名称"
|
||||
allow-clear
|
||||
style="width: 200px"
|
||||
style="width: 220px"
|
||||
@press-enter="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<span class="text-gray-400">产品名称</span>
|
||||
</template>
|
||||
</Input>
|
||||
<!-- TODO @haohao:tindwind -->
|
||||
<Input
|
||||
v-model:value="searchParams.productKey"
|
||||
placeholder="请输入产品标识"
|
||||
allow-clear
|
||||
style="width: 200px"
|
||||
style="width: 220px"
|
||||
@press-enter="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
@@ -203,20 +205,20 @@ onMounted(() => {
|
||||
重置
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="flex items-center justify-between">
|
||||
<Space :size="12">
|
||||
<Button type="primary" @click="handleCreate">
|
||||
<!-- TODO @haohao:按钮使用中立的,ACTION_ICON.ADD -->
|
||||
<IconifyIcon icon="ant-design:plus-outlined" class="mr-1" />
|
||||
新增产品
|
||||
</Button>
|
||||
<Button type="primary" @click="handleExport">
|
||||
<!-- TODO @haohao:按钮使用中立的,ACTION_ICON.EXPORT -->
|
||||
<IconifyIcon icon="ant-design:download-outlined" class="mr-1" />
|
||||
导出
|
||||
</Button>
|
||||
</Space>
|
||||
|
||||
<!-- 视图切换 -->
|
||||
<Space :size="4">
|
||||
<Button
|
||||
@@ -236,16 +238,17 @@ onMounted(() => {
|
||||
</Card>
|
||||
|
||||
<Grid v-show="viewMode === 'list'">
|
||||
<!-- TODO @haohao:这里貌似可以删除掉 -->
|
||||
<template #toolbar-tools>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<!-- 产品分类列 -->
|
||||
<!-- TODO @haohao:这里应该可以拿到 data.ts,参考别的模块;类似 apps/web-antd/src/views/ai/image/manager/data.ts 里,里面查询 category ,和自己渲染-->
|
||||
<template #category="{ row }">
|
||||
<span>{{ getCategoryNameByValue(row.categoryId) }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 产品图标列 -->
|
||||
<!-- TODO @haohao:直接用 Image 组件,就 ok 了呀。在 data.ts 里 -->
|
||||
<template #icon="{ row }">
|
||||
<Button
|
||||
v-if="row.icon"
|
||||
@@ -257,7 +260,7 @@ onMounted(() => {
|
||||
</Button>
|
||||
<span v-else class="text-gray-400">-</span>
|
||||
</template>
|
||||
|
||||
<!-- TODO @haohao:直接用 Image 组件,就 ok 了呀。在 data.ts 里 -->
|
||||
<!-- 产品图片列 -->
|
||||
<template #picUrl="{ row }">
|
||||
<Button
|
||||
@@ -270,8 +273,6 @@ onMounted(() => {
|
||||
</Button>
|
||||
<span v-else class="text-gray-400">-</span>
|
||||
</template>
|
||||
|
||||
<!-- 操作列 -->
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
@@ -320,7 +321,9 @@ onMounted(() => {
|
||||
/>
|
||||
|
||||
<!-- 图片预览 -->
|
||||
<!-- TODO @haohao:tindwind -->
|
||||
<div style="display: none">
|
||||
<!-- TODO @haohao:是不是通过 Image 直接实现预览 -->
|
||||
<Image.PreviewGroup
|
||||
:preview="{
|
||||
visible: previewVisible,
|
||||
@@ -333,6 +336,7 @@ onMounted(() => {
|
||||
</Page>
|
||||
</template>
|
||||
<style scoped>
|
||||
/** TODO @haohao:貌似这 2 个 css 没啥用? */
|
||||
:deep(.vxe-toolbar div) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<!-- IoT 产品选择器,使用弹窗展示 -->
|
||||
<script setup lang="ts">
|
||||
// TODO @haohao:这个貌似暂时没看到,在哪里用?
|
||||
import type { IotProductApi } from '#/api/iot/product/product';
|
||||
|
||||
import { reactive, ref } from 'vue';
|
||||
@@ -28,6 +29,7 @@ interface Props {
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
title: '产品选择器',
|
||||
// TODO @haohao:handleConfirm 直接放到这里,不用单独声明
|
||||
onConfirm: handleConfirm,
|
||||
});
|
||||
|
||||
@@ -39,6 +41,7 @@ const queryParams = reactive({
|
||||
name: '',
|
||||
productKey: '',
|
||||
});
|
||||
// TODO @haohao:是不是 form 应该也在 Grid 里;
|
||||
|
||||
// 配置表格
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { IotProductApi } from '#/api/iot/product/product';
|
||||
|
||||
// TODO @haohao:detail 挪到 yudao-ui-admin-vben-v5/apps/web-antd/src/views/iot/product/product/detail 下。独立一个,不放在 modules 里。
|
||||
import { onMounted, provide, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
@@ -25,8 +26,7 @@ const loading = ref(true);
|
||||
const product = ref<IotProductApi.Product>({} as IotProductApi.Product);
|
||||
const activeTab = ref('info');
|
||||
|
||||
// 提供产品信息给子组件
|
||||
provide('product', product);
|
||||
provide('product', product); // 提供产品信息给子组件
|
||||
|
||||
/** 获取产品详情 */
|
||||
async function getProductData(productId: number) {
|
||||
@@ -44,13 +44,8 @@ async function getProductData(productId: number) {
|
||||
async function getDeviceCountData(productId: number) {
|
||||
try {
|
||||
return await getDeviceCount(productId);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Error fetching device count:',
|
||||
error,
|
||||
'productId:',
|
||||
productId,
|
||||
);
|
||||
} catch {
|
||||
message.error('获取设备数量失败');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -62,7 +57,6 @@ onMounted(async () => {
|
||||
router.back();
|
||||
return;
|
||||
}
|
||||
|
||||
await getProductData(id);
|
||||
|
||||
// 处理 tab 参数
|
||||
@@ -70,7 +64,6 @@ onMounted(async () => {
|
||||
if (tab) {
|
||||
activeTab.value = tab as string;
|
||||
}
|
||||
|
||||
// 查询设备数量
|
||||
if (product.value.id) {
|
||||
product.value.deviceCount = await getDeviceCountData(product.value.id);
|
||||
@@ -85,7 +78,6 @@ onMounted(async () => {
|
||||
:product="product"
|
||||
@refresh="() => getProductData(id)"
|
||||
/>
|
||||
|
||||
<Tabs v-model:active-key="activeTab" class="mt-4">
|
||||
<Tabs.TabPane key="info" tab="产品信息">
|
||||
<ProductDetailsInfo v-if="activeTab === 'info'" :product="product" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
// TODO @haohao:放到 detail/modules 里。然后名字就是 header.vue
|
||||
import type { IotProductApi } from '#/api/iot/product/product';
|
||||
|
||||
import { ref } from 'vue';
|
||||
@@ -51,8 +52,9 @@ function openForm(type: string, id?: number) {
|
||||
|
||||
/** 发布产品 */
|
||||
async function confirmPublish(id: number) {
|
||||
// TODO @haohao:最好类似;async function handleDeleteBatch() { 的做法:1)有个 confirm;2)有个 loading
|
||||
try {
|
||||
await updateProductStatus(id, 1);
|
||||
await updateProductStatus(id, 1); // TODO @好好】:1 和 0,最好用枚举;
|
||||
message.success('发布成功');
|
||||
emit('refresh');
|
||||
} catch {
|
||||
@@ -62,6 +64,7 @@ async function confirmPublish(id: number) {
|
||||
|
||||
/** 撤销发布 */
|
||||
async function confirmUnpublish(id: number) {
|
||||
// TODO @haohao:最好类似;async function handleDeleteBatch() { 的做法:1)有个 confirm;2)有个 loading
|
||||
try {
|
||||
await updateProductStatus(id, 0);
|
||||
message.success('撤销发布成功');
|
||||
@@ -126,6 +129,7 @@ async function confirmUnpublish(id: number) {
|
||||
</Card>
|
||||
|
||||
<!-- 表单弹窗 -->
|
||||
<!-- TODO @haohao:弹不出来;另外,应该用 index.vue 里,Form 的声明方式哈。 -->
|
||||
<ProductForm ref="formRef" @success="emit('refresh')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
// TODO @haohao:放到 detail/modules 里。然后名字就是 info.vue
|
||||
import type { IotProductApi } from '#/api/iot/product/product';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
@@ -23,6 +24,7 @@ function formatDate(date?: Date | string) {
|
||||
|
||||
<template>
|
||||
<Card title="产品信息">
|
||||
<!-- TODO @haohao:看看是不是用 description 组件 -->
|
||||
<Descriptions bordered :column="3" size="small">
|
||||
<Descriptions.Item label="产品名称">
|
||||
{{ product.name }}
|
||||
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
|
||||
import { getProductPage } from '#/api/iot/product/product';
|
||||
|
||||
// TODO @haohao:应该是 card-view.vue;
|
||||
|
||||
// TODO @haohao:命名不太对;可以简化下;
|
||||
defineOptions({ name: 'ProductCardView' });
|
||||
|
||||
@@ -48,6 +50,7 @@ const queryParams = ref({
|
||||
pageSize: 12,
|
||||
});
|
||||
|
||||
// TODO @haohao:注释的优化;
|
||||
// 获取分类名称
|
||||
function getCategoryName(categoryId: number) {
|
||||
const category = props.categoryList.find((c: any) => c.id === categoryId);
|
||||
@@ -85,11 +88,6 @@ function getDeviceTypeColor(deviceType: number) {
|
||||
return colors[deviceType] || 'default';
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
// 暴露方法供父组件调用
|
||||
defineExpose({
|
||||
reload: getList,
|
||||
search: () => {
|
||||
@@ -97,6 +95,11 @@ defineExpose({
|
||||
getList();
|
||||
},
|
||||
});
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -113,9 +116,11 @@ defineExpose({
|
||||
:lg="6"
|
||||
class="mb-4"
|
||||
>
|
||||
<!-- TODO @haohao:卡片之间的上下距离,太宽了。 -->
|
||||
<Card :body-style="{ padding: '20px' }" class="product-card h-full">
|
||||
<!-- 顶部标题区域 -->
|
||||
<div class="mb-4 flex items-start">
|
||||
<!-- TODO @haohao:图标太大了;看看是不是参考 vue3 + element-plus 搞小点;然后标题居中。 -->
|
||||
<div class="product-icon">
|
||||
<IconifyIcon
|
||||
:icon="item.icon || 'ant-design:inbox-outlined'"
|
||||
@@ -126,7 +131,6 @@ defineExpose({
|
||||
<div class="product-title">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<div class="mb-4 flex items-start">
|
||||
<div class="info-list flex-1">
|
||||
@@ -152,6 +156,7 @@ defineExpose({
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">产品标识</span>
|
||||
<!-- TODO @haohao:展示 ?有点奇怪,要不小手? -->
|
||||
<Tooltip :title="item.productKey || item.id" placement="top">
|
||||
<span class="info-value product-key">
|
||||
{{ item.productKey || item.id }}
|
||||
@@ -159,6 +164,8 @@ defineExpose({
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO @haohao:这里是不是有 image?然后默认 icon -->
|
||||
<!-- TODO @haohao:高度太高了。建议和左侧(产品分类 + 产品类型 + 产品标识)高度保持一致 -->
|
||||
<div class="product-3d-icon">
|
||||
<IconifyIcon
|
||||
icon="ant-design:box-plot-outlined"
|
||||
@@ -166,7 +173,6 @@ defineExpose({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 按钮组 -->
|
||||
<div class="action-buttons">
|
||||
<Button
|
||||
@@ -174,6 +180,7 @@ defineExpose({
|
||||
class="action-btn action-btn-edit"
|
||||
@click="emit('edit', item)"
|
||||
>
|
||||
<!-- TODO @haohao:按钮尽量用中立的按钮,方便迁移 ele; -->
|
||||
<IconifyIcon icon="ant-design:edit-outlined" class="mr-1" />
|
||||
编辑
|
||||
</Button>
|
||||
@@ -229,13 +236,13 @@ defineExpose({
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<Empty v-else description="暂无产品数据" class="my-20" />
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div v-if="list.length > 0" class="mt-6 flex justify-center">
|
||||
<!-- TODO @haohao:放到最右侧好点 -->
|
||||
<div v-if="list.length > 0" class="flex justify-center">
|
||||
<Pagination
|
||||
v-model:current="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
@@ -251,6 +258,7 @@ defineExpose({
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/** TODO @haohao:看看哪些可以 tindwind 掉 */
|
||||
.product-card-view {
|
||||
.product-card {
|
||||
height: 100%;
|
||||
|
||||
@@ -20,6 +20,8 @@ import {
|
||||
useBasicFormSchema,
|
||||
} from '../data';
|
||||
|
||||
// TODO @haohao:应该是 form.vue;
|
||||
|
||||
defineOptions({ name: 'IoTProductForm' });
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
@@ -30,10 +32,9 @@ const formData = ref<any>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id ? '编辑产品' : '新增产品';
|
||||
});
|
||||
const activeKey = ref<string[]>([]); // 折叠面板的激活 key,默认不展开
|
||||
|
||||
// 折叠面板的激活key,默认不展开
|
||||
const activeKey = ref<string[]>([]);
|
||||
|
||||
// TODO @haohao:每一行一个;
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
@@ -46,7 +47,7 @@ const [Form, formApi] = useVbenForm({
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
// 创建高级设置表单
|
||||
// TODO @haohao:每一行一个;
|
||||
const [AdvancedForm, advancedFormApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
@@ -59,7 +60,7 @@ const [AdvancedForm, advancedFormApi] = useVbenForm({
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
// 在 formApi 创建后设置 schema
|
||||
// TODO @haohao:看看是不是可以参考别的 form 模块,优化表单这块的逻辑;从 61 到 156 行。体感有点冗余、以及代码风格,不够统一;
|
||||
formApi.setState({ schema: useBasicFormSchema(formApi) });
|
||||
advancedFormApi.setState({ schema: useAdvancedFormSchema() });
|
||||
|
||||
|
||||
@@ -67,12 +67,6 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
title: '规则名称',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'productId',
|
||||
title: '所属产品',
|
||||
minWidth: 150,
|
||||
slots: { default: 'product' },
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '规则描述',
|
||||
@@ -87,6 +81,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
// TODO @haohao:这里是【数据源】【数据目的】
|
||||
{
|
||||
field: 'sinkCount',
|
||||
title: '数据流转数',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
// TODO @haohao:应该先有【规则】【目的】两个 tab;然后,在进行管理操作;类似,apps/web-antd/src/views/ai/chat/manager
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
@@ -42,10 +43,8 @@ async function handleDelete(row: any) {
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteDataRule(row.id);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
});
|
||||
await deleteDataRule(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
|
||||
@@ -25,12 +25,11 @@ const formRules: any = reactive({
|
||||
});
|
||||
const formRef = ref(); // 表单 Ref
|
||||
|
||||
// 获取上行消息方法列表
|
||||
const upstreamMethods = computed(() => {
|
||||
return Object.values(IotDeviceMessageMethodEnum).filter(
|
||||
(item) => item.upstream,
|
||||
);
|
||||
});
|
||||
}); // 获取上行消息方法列表
|
||||
|
||||
/** 根据产品 ID 过滤设备 */
|
||||
function getFilteredDevices(productId: number) {
|
||||
@@ -193,7 +192,7 @@ const columns = [
|
||||
{
|
||||
title: '操作',
|
||||
width: 80,
|
||||
fixed: 'right' as const,
|
||||
fixed: 'right',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -202,6 +201,8 @@ defineExpose({ validate, getData, setData });
|
||||
|
||||
<template>
|
||||
<Form ref="formRef" :model="{ data: formData }">
|
||||
<!-- TODO @haohao:貌似有告警。 -->
|
||||
<!-- TODO @haohao:是不是搞成 web-antd/src/views/erp/finance/receipt/modules/item-form.vue 的做法,通过 Grid;或 apps/web-antd/src/views/infra/demo/demo03/erp/modules/demo03-grade-list.vue;目的:后续 ele 通用性更好! -->
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data-source="formData"
|
||||
|
||||
@@ -21,6 +21,7 @@ const emit = defineEmits(['success']);
|
||||
const formData = ref<any>();
|
||||
const sourceConfigRef = ref();
|
||||
|
||||
// TODO @haohao:应该放到 modules
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['数据规则'])
|
||||
@@ -40,6 +41,7 @@ const [Form, formApi] = useVbenForm({
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
// TODO @haohao:这里需要优化下,参考别的模块写法;
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
|
||||
@@ -12,6 +12,8 @@ import { $t } from '#/locales';
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import DataRuleForm from './data-rule-form.vue';
|
||||
|
||||
// TODO @haohao:貌似和 apps/web-antd/src/views/iot/rule/data/index.vue 重复的。可能这个是对的。然后把 apps/web-antd/src/views/iot/rule/data/index.vue 搞成 tabs;
|
||||
|
||||
/** IoT 数据流转规则列表 */
|
||||
defineOptions({ name: 'IotDataRule' });
|
||||
|
||||
@@ -43,9 +45,7 @@ async function handleDelete(row: any) {
|
||||
});
|
||||
try {
|
||||
await deleteDataRule(row.id);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
});
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
|
||||
@@ -120,6 +120,7 @@ watch(
|
||||
<Form class="mx-4" />
|
||||
<div v-if="formData" class="mx-4 mt-4">
|
||||
<div class="mb-2 text-sm font-medium">配置信息</div>
|
||||
<!-- TODO @haohao:下面的 form,看看有没办法,搞成 form schema 的,方便后续 ele 的迁移! -->
|
||||
<HttpConfigForm
|
||||
v-if="IotDataSinkTypeEnum.HTTP === formData.type"
|
||||
v-model="formData.config"
|
||||
|
||||
@@ -12,7 +12,9 @@ import { $t } from '#/locales';
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import DataSinkForm from './data-sink-form.vue';
|
||||
|
||||
/** IoT 数据流转目的 列表 */
|
||||
// TODO @haohao:需要根据代码规范,在优化下这个模块。和别的模块的风格保持一致。
|
||||
|
||||
/** IoT 数据流转目的列表 */
|
||||
defineOptions({ name: 'IotDataSink' });
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
@@ -43,9 +45,7 @@ async function handleDelete(row: any) {
|
||||
});
|
||||
try {
|
||||
await deleteDataSink(row.id);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
});
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
defineOptions({ name: 'IotRuleDataBridge' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page description="物聯網規則引擎 - 數據橋接" title="數據橋接">
|
||||
<div class="p-4">
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/tree/master/src/views/iot/rule"
|
||||
>
|
||||
該功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/iot/rule/data/rule/index.vue"
|
||||
>
|
||||
可參考源代碼進行遷移!
|
||||
</Button>
|
||||
<div class="mt-4">
|
||||
<h3>功能說明:</h3>
|
||||
<p>規則引擎包括:</p>
|
||||
<ul>
|
||||
<li>數據規則配置</li>
|
||||
<li>數據轉發配置</li>
|
||||
<li>場景聯動配置</li>
|
||||
</ul>
|
||||
<h3 class="mt-4">待實現:</h3>
|
||||
<ul>
|
||||
<li>⚠️ API 接口定義</li>
|
||||
<li>⚠️ 頁面實現</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -23,16 +23,65 @@ const props = defineProps<{
|
||||
productId: number;
|
||||
}>();
|
||||
|
||||
// 产品信息
|
||||
const product = ref<IotProductApi.Product>({} as IotProductApi.Product);
|
||||
const product = ref<IotProductApi.Product>({} as IotProductApi.Product); // 产品信息
|
||||
|
||||
// 提供产品信息给子组件
|
||||
provide(IOT_PROVIDE_KEY.PRODUCT, product);
|
||||
provide(IOT_PROVIDE_KEY.PRODUCT, product); // 提供产品信息给子组件
|
||||
|
||||
// 组件引用
|
||||
// TODO @haohao:form 是不是用 web-antd/src/views/system/user/index.vue 里 open 的风格;
|
||||
const thingModelFormRef = ref();
|
||||
// TODO @haohao:thingModelTSLRef 应该是个 modal,也可以调整下风格;
|
||||
const thingModelTSLRef = ref();
|
||||
|
||||
// TODO @haohao:方法的顺序、注释、调整的和别的模块一致。
|
||||
|
||||
// 新增功能
|
||||
function handleCreate() {
|
||||
thingModelFormRef.value?.open('create');
|
||||
}
|
||||
|
||||
// 编辑功能
|
||||
function handleEdit(row: any) {
|
||||
thingModelFormRef.value?.open('update', row.id);
|
||||
}
|
||||
|
||||
// 删除功能
|
||||
async function handleDelete(row: any) {
|
||||
// TODO @haohao:应该有个 loading,类似别的模块写法;
|
||||
try {
|
||||
await deleteThingModel(row.id);
|
||||
message.success('删除成功');
|
||||
gridApi.reload();
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 打开 TSL
|
||||
function handleOpenTSL() {
|
||||
thingModelTSLRef.value?.open();
|
||||
}
|
||||
|
||||
// 获取数据类型标签
|
||||
// TODO @haohao:可以直接在 data.ts 就写掉这个逻辑;
|
||||
function getDataTypeLabel(row: any) {
|
||||
return getDataTypeOptionsLabel(row.property?.dataType) || '-';
|
||||
}
|
||||
|
||||
// 刷新表格
|
||||
function handleRefresh() {
|
||||
gridApi.reload();
|
||||
}
|
||||
|
||||
// 获取产品信息
|
||||
async function getProductData() {
|
||||
try {
|
||||
product.value = await getProduct(props.productId);
|
||||
} catch (error) {
|
||||
console.error('获取产品信息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @haohao:字段的顺序,调整成别的模块一直;
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
@@ -64,51 +113,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
},
|
||||
});
|
||||
|
||||
// 新增功能
|
||||
function handleCreate() {
|
||||
thingModelFormRef.value?.open('create');
|
||||
}
|
||||
|
||||
// 编辑功能
|
||||
function handleEdit(row: any) {
|
||||
thingModelFormRef.value?.open('update', row.id);
|
||||
}
|
||||
|
||||
// 删除功能
|
||||
async function handleDelete(row: any) {
|
||||
try {
|
||||
await deleteThingModel(row.id);
|
||||
message.success('删除成功');
|
||||
gridApi.reload();
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 打开 TSL
|
||||
function handleOpenTSL() {
|
||||
thingModelTSLRef.value?.open();
|
||||
}
|
||||
|
||||
// 获取数据类型标签
|
||||
function getDataTypeLabel(row: any) {
|
||||
return getDataTypeOptionsLabel(row.property?.dataType) || '-';
|
||||
}
|
||||
|
||||
// 刷新表格
|
||||
function handleRefresh() {
|
||||
gridApi.reload();
|
||||
}
|
||||
|
||||
// 获取产品信息
|
||||
async function getProductData() {
|
||||
try {
|
||||
product.value = await getProduct(props.productId);
|
||||
} catch (error) {
|
||||
console.error('获取产品信息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
await getProductData();
|
||||
@@ -116,11 +120,7 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page
|
||||
auto-content-height
|
||||
description="管理产品的物模型定义,包括属性、服务和事件"
|
||||
title="物模型管理"
|
||||
>
|
||||
<Page auto-content-height>
|
||||
<Grid>
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
@@ -134,23 +134,21 @@ onMounted(async () => {
|
||||
{
|
||||
label: 'TSL',
|
||||
type: 'default',
|
||||
color: 'success',
|
||||
color: 'success', // TODO @haohao:貌似 color 可以去掉?应该是不生效的哈。ps:另外,也给搞个 icon?
|
||||
onClick: handleOpenTSL,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 数据类型列 -->
|
||||
<template #dataType="{ row }">
|
||||
<span>{{ getDataTypeLabel(row) }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 数据定义列 -->
|
||||
<!-- TODO @haohao:可以在 data.ts 就写掉这个逻辑; -->
|
||||
<template #dataDefinition="{ row }">
|
||||
<DataDefinition :data="row" />
|
||||
</template>
|
||||
|
||||
<!-- 操作列 -->
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
@@ -178,8 +176,7 @@ onMounted(async () => {
|
||||
|
||||
<!-- 物模型表单 -->
|
||||
<ThingModelForm ref="thingModelFormRef" @success="handleRefresh" />
|
||||
|
||||
<!-- TSL 弹窗 -->
|
||||
<ThingModelTsl ref="thingModelTSLRef"/>
|
||||
<ThingModelTsl ref="thingModelTSLRef" />
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- TODO @haohao:如果是模块内用的,就用 modules 里。(等后面点在看,优先级:低) -->
|
||||
<script lang="ts" setup>
|
||||
import type { ThingModelData } from '#/api/iot/thingmodel';
|
||||
|
||||
@@ -17,7 +18,6 @@ defineOptions({ name: 'DataDefinition' });
|
||||
|
||||
const props = defineProps<{ data: ThingModelData }>();
|
||||
|
||||
// 格式化布尔值和枚举值列表为字符串
|
||||
const formattedDataSpecsList = computed(() => {
|
||||
if (
|
||||
!props.data.property?.dataSpecsList ||
|
||||
@@ -28,9 +28,8 @@ const formattedDataSpecsList = computed(() => {
|
||||
return props.data.property.dataSpecsList
|
||||
.map((item) => `${item.value}-${item.name}`)
|
||||
.join('、');
|
||||
});
|
||||
}); // 格式化布尔值和枚举值列表为字符串
|
||||
|
||||
// 显示的简短文本(第一个值)
|
||||
const shortText = computed(() => {
|
||||
if (
|
||||
!props.data.property?.dataSpecsList ||
|
||||
@@ -43,7 +42,7 @@ const shortText = computed(() => {
|
||||
return count > 1
|
||||
? `${first.value}-${first.name} 等${count}项`
|
||||
: `${first.value}-${first.name}`;
|
||||
});
|
||||
}); // 显示的简短文本(第一个值)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -100,9 +99,8 @@ const shortText = computed(() => {
|
||||
</template>
|
||||
<!-- 服务 -->
|
||||
<div v-if="Number(data.type) === IoTThingModelTypeEnum.SERVICE">
|
||||
调用方式:{{
|
||||
getThingModelServiceCallTypeLabel(data.service?.callType as any)
|
||||
}}
|
||||
调用方式:
|
||||
{{ getThingModelServiceCallTypeLabel(data.service?.callType as any) }}
|
||||
</div>
|
||||
<!-- 事件 -->
|
||||
<div v-if="Number(data.type) === IoTThingModelTypeEnum.EVENT">
|
||||
@@ -111,6 +109,7 @@ const shortText = computed(() => {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/** TODO @haohao:tindwind */
|
||||
.data-specs-text {
|
||||
cursor: help;
|
||||
border-bottom: 1px dashed #d9d9d9;
|
||||
|
||||
@@ -1 +1 @@
|
||||
export {default as DataDefinition} from './data-definition.vue';
|
||||
export { default as DataDefinition } from './data-definition.vue';
|
||||
|
||||
@@ -23,7 +23,7 @@ const props = defineProps<{ isStructDataSpecs?: boolean; modelValue: any }>();
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
const thingModelEvent = useVModel(props, 'modelValue', emits) as Ref<any>;
|
||||
|
||||
// 默认选中,INFO 信息
|
||||
/** 默认选中,INFO 信息 */
|
||||
watch(
|
||||
() => thingModelEvent.value.type,
|
||||
(val: string | undefined) =>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
// TODO @haohao:使用 form.vue;
|
||||
import type { IotProductApi } from '#/api/iot/product/product';
|
||||
import type { ThingModelData } from '#/api/iot/thingmodel';
|
||||
|
||||
@@ -61,6 +62,7 @@ const formData = ref<any>({
|
||||
const formRef = ref(); // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
// TODO @haohao:Modal 的写法。
|
||||
async function open(type: string, id?: number) {
|
||||
dialogVisible.value = true;
|
||||
// 设置标题:create -> 新增,update -> 编辑
|
||||
@@ -244,6 +246,7 @@ function resetForm() {
|
||||
:confirm-loading="formLoading"
|
||||
@ok="submitForm"
|
||||
>
|
||||
<!-- TODO @haohao:这个可以改造成 data.ts schema 形式么?可能是有一定成本,后续迁移 ele 版本,会容易很多。 -->
|
||||
<Form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Modal, Radio, Textarea } from 'ant-design-vue';
|
||||
import { getThingModelTSL } from '#/api/iot/thingmodel';
|
||||
import { IOT_PROVIDE_KEY } from '#/views/iot/utils/constants';
|
||||
|
||||
defineOptions({name: 'ThingModelTsl'});
|
||||
defineOptions({ name: 'ThingModelTsl' });
|
||||
|
||||
const dialogVisible = ref(false); // 弹窗的是否展示
|
||||
const dialogTitle = ref('物模型 TSL'); // 弹窗的标题
|
||||
|
||||
1
packages/constants/src/biz-iot-enum.ts
Normal file
1
packages/constants/src/biz-iot-enum.ts
Normal file
@@ -0,0 +1 @@
|
||||
// TODO @haohao:枚举可以放到这里;
|
||||
@@ -2,6 +2,7 @@ export * from './biz-ai-enum';
|
||||
export * from './biz-bpm-enum';
|
||||
export * from './biz-erp-enum';
|
||||
export * from './biz-infra-enum';
|
||||
export * from './biz-iot-enum';
|
||||
export * from './biz-mall-enum';
|
||||
export * from './biz-mp-enum';
|
||||
export * from './biz-pay-enum';
|
||||
|
||||
Reference in New Issue
Block a user