feat:太阳能网控设置,新增功能。
This commit is contained in:
@@ -16,6 +16,19 @@ export const listDevice = (query?: DeviceQuery): AxiosPromise<DeviceVO[]> => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询太阳能网控设备列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const listSolarDevice = (query?: DeviceQuery): AxiosPromise<DeviceVO[]> => {
|
||||
return request({
|
||||
url: '/fishery/device/solar/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询设备管理详细
|
||||
* @param id
|
||||
|
||||
@@ -234,6 +234,11 @@ export interface DeviceVO {
|
||||
*/
|
||||
pondName?: string;
|
||||
|
||||
/**
|
||||
* 在线状态(online/offline)
|
||||
*/
|
||||
onlineStatus?: string;
|
||||
|
||||
/**
|
||||
* 创建时间(激活时间)
|
||||
*/
|
||||
|
||||
38
src/api/iot/index.ts
Normal file
38
src/api/iot/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
export interface SolarPropertyCommand {
|
||||
id: string | number;
|
||||
properties: Record<string, any>;
|
||||
cache?: boolean;
|
||||
}
|
||||
|
||||
export interface SolarPropertyCommandResult {
|
||||
deviceId: string | number;
|
||||
serialNum: string;
|
||||
online: boolean;
|
||||
statusCode?: number;
|
||||
properties: Record<string, any>;
|
||||
mode: 'direct' | 'cached';
|
||||
cached: boolean;
|
||||
sent: boolean;
|
||||
}
|
||||
|
||||
export const getDeviceStatus = (devicetype: number, serialnum: string): AxiosPromise<number> => {
|
||||
return request({
|
||||
url: '/iot/device/status',
|
||||
method: 'get',
|
||||
params: {
|
||||
devicetype,
|
||||
serialnum
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const sendSolarPropertyCommand = (data: SolarPropertyCommand): AxiosPromise<SolarPropertyCommandResult> => {
|
||||
return request({
|
||||
url: '/iot/device/solar/property-command',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
2
src/types/components.d.ts
vendored
2
src/types/components.d.ts
vendored
@@ -13,6 +13,7 @@ declare module 'vue' {
|
||||
Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default']
|
||||
DictTag: typeof import('./../components/DictTag/index.vue')['default']
|
||||
Editor: typeof import('./../components/Editor/index.vue')['default']
|
||||
ElAlert: typeof import('element-plus/es')['ElAlert']
|
||||
ElBadge: typeof import('element-plus/es')['ElBadge']
|
||||
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
||||
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
||||
@@ -44,6 +45,7 @@ declare module 'vue' {
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElOptionGroup: typeof import('element-plus/es')['ElOptionGroup']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||
|
||||
612
src/views/fishery/solarControl/index.vue
Normal file
612
src/views/fishery/solarControl/index.vue
Normal file
@@ -0,0 +1,612 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
|
||||
<el-form-item label="用户信息" prop="params.userKeyword">
|
||||
<el-input v-model="queryParams.params.userKeyword" placeholder="请输入用户名或手机号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="设备编号" prop="serialNum">
|
||||
<el-input v-model="queryParams.serialNum" placeholder="请输入设备编号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="设备名称" prop="deviceName">
|
||||
<el-input v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="塘口名称" prop="params.pondKeyword">
|
||||
<el-input v-model="queryParams.params.pondKeyword" placeholder="请输入塘口名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="在线状态" prop="params.onlineStatus">
|
||||
<el-select v-model="queryParams.params.onlineStatus" placeholder="请选择在线状态" clearable style="width: 180px" @change="handleQuery">
|
||||
<el-option v-for="item in onlineStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="deviceList" row-key="id">
|
||||
<el-table-column label="用户名" align="center" prop="userName" min-width="120" />
|
||||
<el-table-column label="手机号" align="center" prop="mobilePhone" min-width="120" />
|
||||
<el-table-column label="设备编号" align="center" prop="serialNum" min-width="150" />
|
||||
<el-table-column label="设备名称" align="center" prop="deviceName" min-width="130" />
|
||||
<el-table-column label="塘口名称" align="center" prop="pondName" min-width="120" />
|
||||
<el-table-column label="在线状态" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row)" effect="light">
|
||||
{{ getStatusText(scope.row) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="盐度补偿" align="center" prop="salinityCompensation" width="120">
|
||||
<template #default="scope">
|
||||
{{ formatValue(scope.row.salinityCompensation) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="线性补偿" align="center" prop="phaseCompensation" width="120">
|
||||
<template #default="scope">
|
||||
{{ formatValue(scope.row.phaseCompensation) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="相位差补偿" align="center" prop="phasedifCompensation" width="120">
|
||||
<template #default="scope">
|
||||
{{ formatValue(scope.row.phasedifCompensation) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="温度补偿" align="center" prop="temperatureCompensation" width="120">
|
||||
<template #default="scope">
|
||||
{{ formatValue(scope.row.temperatureCompensation) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最后上数时间" align="center" prop="lastReportTime" min-width="170">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.lastReportTime, '{y}-{m}-{d} {h}:{i}:{s}') || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="170">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Refresh" :loading="statusLoadingId === scope.row.id" @click="refreshStatus(scope.row)">刷新状态</el-button>
|
||||
<el-button link type="primary" icon="Position" @click="openCommand(scope.row)" v-hasPermi="['fishery:solarControl:command']">下发指令</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="commandOpen" :title="commandTitle" width="680px" append-to-body destroy-on-close>
|
||||
<template v-if="currentDevice">
|
||||
<div class="device-summary">
|
||||
<div>
|
||||
<div class="device-summary__name">{{ currentDevice.deviceName || '-' }}</div>
|
||||
<div class="device-summary__serial">{{ currentDevice.serialNum }}</div>
|
||||
</div>
|
||||
<el-tag :type="getStatusTagType(currentDevice)" size="large">{{ getStatusText(currentDevice) }}</el-tag>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
class="mb-[12px]"
|
||||
:title="currentDevice.online ? '设备在线,将直接下发到太阳能网控。' : '设备离线,将写入缓存,待设备上线后自动下发。'"
|
||||
:type="currentDevice.online ? 'success' : 'warning'"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
|
||||
<el-form ref="commandFormRef" :model="commandForm" :rules="commandRules" label-width="130px">
|
||||
<el-form-item label="下发属性" prop="identifier">
|
||||
<el-select v-model="commandForm.identifier" filterable placeholder="请选择属性" style="width: 100%" @change="handlePropertyChange">
|
||||
<el-option-group v-for="group in propertyGroups" :key="group.label" :label="group.label">
|
||||
<el-option v-for="item in group.options" :key="item.identifier" :label="item.name" :value="item.identifier">
|
||||
<span>{{ item.name }}</span>
|
||||
<span class="property-option-code">{{ item.identifier }}</span>
|
||||
</el-option>
|
||||
</el-option-group>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="selectedProperty">
|
||||
<el-form-item v-if="selectedProperty.type === 'enum'" :label="selectedProperty.name" prop="value">
|
||||
<el-select v-model="commandForm.value" placeholder="请选择值" style="width: 100%">
|
||||
<el-option v-for="option in selectedProperty.options" :key="String(option.value)" :label="option.label" :value="option.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<template v-else-if="selectedProperty.type === 'struct'">
|
||||
<el-form-item
|
||||
v-for="field in selectedProperty.fields"
|
||||
:key="field.identifier"
|
||||
:label="field.name"
|
||||
:prop="`structValue.${field.identifier}`"
|
||||
:rules="[{ required: true, message: `${field.name}不能为空`, trigger: 'blur' }]"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="commandForm.structValue[field.identifier]"
|
||||
:min="field.min"
|
||||
:max="field.max"
|
||||
:step="field.step"
|
||||
:precision="field.precision"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item v-else :label="selectedProperty.name" prop="value">
|
||||
<el-input-number
|
||||
v-model="commandForm.value"
|
||||
:min="selectedProperty.min"
|
||||
:max="selectedProperty.max"
|
||||
:step="selectedProperty.step"
|
||||
:precision="selectedProperty.precision"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<div class="property-meta">
|
||||
<span>{{ selectedProperty.identifier }}</span>
|
||||
<span v-if="selectedProperty.rangeText">{{ selectedProperty.rangeText }}</span>
|
||||
<span v-if="selectedProperty.unit">{{ selectedProperty.unit }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="command-footer">
|
||||
<el-button @click="commandOpen = false">取消</el-button>
|
||||
<el-button
|
||||
v-if="currentDevice?.online"
|
||||
type="primary"
|
||||
icon="Position"
|
||||
:loading="submitLoading"
|
||||
@click="submitCommand(false)"
|
||||
>
|
||||
直接下发
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
type="warning"
|
||||
icon="Clock"
|
||||
:loading="submitLoading"
|
||||
@click="submitCommand(true)"
|
||||
>
|
||||
缓存下发
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="SolarControl" lang="ts">
|
||||
import { listSolarDevice } from '@/api/fishery/device';
|
||||
import { DeviceQuery, DeviceVO } from '@/api/fishery/device/types';
|
||||
import { getDeviceStatus, sendSolarPropertyCommand } from '@/api/iot';
|
||||
|
||||
type PropertyType = 'float' | 'double' | 'int' | 'enum' | 'struct';
|
||||
|
||||
interface PropertyOption {
|
||||
identifier: string;
|
||||
name: string;
|
||||
type: PropertyType;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
precision?: number;
|
||||
unit?: string;
|
||||
rangeText?: string;
|
||||
options?: Array<{ label: string; value: number }>;
|
||||
fields?: Array<Required<Pick<PropertyOption, 'identifier' | 'name' | 'min' | 'max' | 'step' | 'precision'>> & { unit?: string }>;
|
||||
defaultValue?: number;
|
||||
}
|
||||
|
||||
interface SolarDeviceVO extends DeviceVO {
|
||||
online?: boolean;
|
||||
statusCode?: number;
|
||||
}
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const commandFormRef = ref<ElFormInstance>();
|
||||
|
||||
const deviceList = ref<SolarDeviceVO[]>([]);
|
||||
const loading = ref(false);
|
||||
const showSearch = ref(true);
|
||||
const total = ref(0);
|
||||
const commandOpen = ref(false);
|
||||
const currentDevice = ref<SolarDeviceVO>();
|
||||
const submitLoading = ref(false);
|
||||
const statusLoadingId = ref<string | number>();
|
||||
|
||||
const onlineStatusOptions = [
|
||||
{ label: '在线', value: 'online' },
|
||||
{ label: '离线', value: 'offline' }
|
||||
];
|
||||
|
||||
const data = reactive<PageData<{}, DeviceQuery>>({
|
||||
form: {},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
serialNum: undefined,
|
||||
deviceName: undefined,
|
||||
deviceType: 3,
|
||||
params: {
|
||||
userKeyword: undefined,
|
||||
onlineStatus: undefined,
|
||||
pondKeyword: undefined
|
||||
}
|
||||
},
|
||||
rules: {}
|
||||
});
|
||||
|
||||
const { queryParams } = toRefs(data);
|
||||
|
||||
const propertyGroups = [
|
||||
{
|
||||
label: '常用设置',
|
||||
options: [
|
||||
numberProperty('salinitySet', '盐度设置', 'float', 0, 100, 0.01, undefined, 0),
|
||||
enumProperty('PowerMode', '电源模式', {
|
||||
0: '太阳能网控常驻模式',
|
||||
1: '太阳能网控节能模式',
|
||||
2: '适配器网控'
|
||||
}),
|
||||
numberProperty('WakeUpTime', '休眠时长', 'int', 1, 2147483647, 1, 'min', 10),
|
||||
numberProperty('BootToSleepTimeout', '工作时长', 'int', 2, 2147483647, 1, 'min', 2),
|
||||
numberProperty('updatatime', '数据上报时间', 'int', 0, 65535, 1, 's', 60),
|
||||
numberProperty('FirstBootToSleepTimeout', '首次上电工作时间', 'int', 3, 2147483647, 1, 'min', 3)
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '校准与补偿',
|
||||
options: [
|
||||
enumProperty('correct', '校准', {
|
||||
1: '下发溶解氧传感器校准指令',
|
||||
2: '下发盐度传感器校准指令',
|
||||
11: '氧探头饱和校准',
|
||||
12: '氧探头无氧校准',
|
||||
13: '氧探头低温系数设置',
|
||||
14: '氧探头高温系数设置',
|
||||
31: '下发pH传感器校准第1步',
|
||||
32: '下发pH传感器校准第2步'
|
||||
}),
|
||||
numberProperty('phasedif_compensation', '相位差补偿', 'float', -10, 100, 0.001, '°', 0),
|
||||
numberProperty('phase_compensation', '线性补偿', 'float', 0, 10, 0.0001, undefined, 1),
|
||||
numberProperty('temperature_compensation', '温度补偿', 'float', -10, 10, 0.001, '°C', 0),
|
||||
numberProperty('stdsatphase', '标准饱和相位差', 'double', 0, 360, 0.01, '°', 0),
|
||||
numberProperty('stdphaseinterval', '标准相位区间', 'double', 0, 360, 0.01, '°', 0)
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '高级参数',
|
||||
options: [
|
||||
numberProperty('SaturationPoint', '饱和相位差测量值', 'float', -1000, 1000, 0.01, '°', 0),
|
||||
numberProperty('AnaerobicPoint', '无氧相位差测量值', 'float', -1000, 1000, 0.01, '°', 0),
|
||||
numberProperty('LowtempCOEF', '低温系数', 'float', -1000, 1000, 0.0001, undefined, 0),
|
||||
numberProperty('HightempCOEF', '高温系数', 'float', -1000, 1000, 0.0001, undefined, 0),
|
||||
numberProperty('ammonia_nitrogen', '氨氮', 'float', 0, 1000, 0.01, 'mg/L', 0),
|
||||
numberProperty('TempPobeOffSet1', '温度偏移量通道1', 'float', -12.7, 12.7, 0.1, '°C', 0),
|
||||
numberProperty('TempPobeOffSet2', '温度偏移量通道2', 'float', -12.7, 12.7, 1, '°C', 0),
|
||||
structProperty('CorrectCharge', '电量校准', [
|
||||
numberProperty('lowvoltage', '低压', 'float', 0, 10, 0.01, 'V', 0),
|
||||
numberProperty('highvoltage', '高压', 'float', 0, 10, 0.01, 'V', 0)
|
||||
])
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const propertyList = computed(() => propertyGroups.flatMap((group) => group.options));
|
||||
const selectedProperty = computed(() => propertyList.value.find((item) => item.identifier === commandForm.identifier));
|
||||
const commandTitle = computed(() => `太阳能网控指令 - ${currentDevice.value?.deviceName || ''}`);
|
||||
|
||||
const commandForm = reactive({
|
||||
identifier: 'PowerMode',
|
||||
value: undefined as number | undefined,
|
||||
structValue: {} as Record<string, number | undefined>
|
||||
});
|
||||
|
||||
const commandRules = {
|
||||
identifier: [{ required: true, message: '请选择下发属性', trigger: 'change' }],
|
||||
value: [{ required: true, message: '属性值不能为空', trigger: 'blur' }]
|
||||
};
|
||||
|
||||
function numberProperty(
|
||||
identifier: string,
|
||||
name: string,
|
||||
type: Extract<PropertyType, 'float' | 'double' | 'int'>,
|
||||
min: number,
|
||||
max: number,
|
||||
step: number,
|
||||
unit?: string,
|
||||
defaultValue?: number
|
||||
): PropertyOption {
|
||||
return {
|
||||
identifier,
|
||||
name,
|
||||
type,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
unit,
|
||||
precision: getPrecision(step),
|
||||
rangeText: `${min} - ${max}`,
|
||||
defaultValue
|
||||
};
|
||||
}
|
||||
|
||||
function enumProperty(identifier: string, name: string, options: Record<number, string>): PropertyOption {
|
||||
const values = Object.entries(options).map(([value, label]) => ({ value: Number(value), label }));
|
||||
return {
|
||||
identifier,
|
||||
name,
|
||||
type: 'enum',
|
||||
options: values,
|
||||
defaultValue: values[0]?.value
|
||||
};
|
||||
}
|
||||
|
||||
function structProperty(identifier: string, name: string, fields: PropertyOption[]): PropertyOption {
|
||||
return {
|
||||
identifier,
|
||||
name,
|
||||
type: 'struct',
|
||||
fields: fields.map((field) => ({
|
||||
identifier: field.identifier,
|
||||
name: field.name,
|
||||
min: field.min ?? 0,
|
||||
max: field.max ?? 0,
|
||||
step: field.step ?? 1,
|
||||
precision: field.precision ?? 0,
|
||||
unit: field.unit
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
function getPrecision(step: number) {
|
||||
const text = String(step);
|
||||
if (!text.includes('.')) {
|
||||
return 0;
|
||||
}
|
||||
return text.split('.')[1].length;
|
||||
}
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
queryParams.value.deviceType = 3;
|
||||
const res = await listSolarDevice(queryParams.value);
|
||||
deviceList.value = ((res.rows || []) as SolarDeviceVO[]).map(initStatusFromList);
|
||||
total.value = res.total;
|
||||
await refreshVisibleStatuses();
|
||||
sortDeviceListByOnline();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const initStatusFromList = (item: SolarDeviceVO) => {
|
||||
if (item.onlineStatus?.toLowerCase() === 'online') {
|
||||
item.online = true;
|
||||
item.statusCode = 1;
|
||||
} else {
|
||||
item.online = false;
|
||||
item.statusCode = 3;
|
||||
}
|
||||
return item;
|
||||
};
|
||||
|
||||
const refreshVisibleStatuses = async () => {
|
||||
await Promise.all(
|
||||
deviceList.value.map(async (item) => {
|
||||
try {
|
||||
await refreshStatus(item, false);
|
||||
} catch {
|
||||
// 列表已有后端状态,单台云端状态刷新失败时不影响整页展示。
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const refreshStatus = async (row: SolarDeviceVO, showMessage = true) => {
|
||||
if (!row.serialNum) {
|
||||
row.online = false;
|
||||
row.statusCode = 0;
|
||||
return;
|
||||
}
|
||||
statusLoadingId.value = row.id;
|
||||
try {
|
||||
const res = await getDeviceStatus(3, row.serialNum);
|
||||
row.statusCode = res.data;
|
||||
row.online = res.data === 1;
|
||||
if (currentDevice.value?.id === row.id) {
|
||||
currentDevice.value = row;
|
||||
}
|
||||
sortDeviceListByOnline();
|
||||
if (showMessage) {
|
||||
ElMessage.success('状态已刷新');
|
||||
}
|
||||
} finally {
|
||||
statusLoadingId.value = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
queryParams.value.deviceType = 3;
|
||||
queryParams.value.params = {
|
||||
userKeyword: undefined,
|
||||
onlineStatus: undefined,
|
||||
pondKeyword: undefined
|
||||
};
|
||||
getList();
|
||||
};
|
||||
|
||||
const openCommand = async (row: SolarDeviceVO) => {
|
||||
currentDevice.value = row;
|
||||
resetCommandForm();
|
||||
commandOpen.value = true;
|
||||
await refreshStatus(row, false);
|
||||
};
|
||||
|
||||
const resetCommandForm = () => {
|
||||
commandForm.identifier = 'PowerMode';
|
||||
handlePropertyChange('PowerMode');
|
||||
nextTick(() => commandFormRef.value?.clearValidate());
|
||||
};
|
||||
|
||||
const handlePropertyChange = (identifier: string) => {
|
||||
const property = propertyList.value.find((item) => item.identifier === identifier);
|
||||
commandForm.value = property?.defaultValue;
|
||||
commandForm.structValue = {};
|
||||
if (property?.type === 'struct') {
|
||||
property.fields?.forEach((field) => {
|
||||
commandForm.structValue[field.identifier] = 0;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const submitCommand = async (cache: boolean) => {
|
||||
if (!currentDevice.value || !selectedProperty.value) {
|
||||
return;
|
||||
}
|
||||
await commandFormRef.value?.validate();
|
||||
const property = selectedProperty.value;
|
||||
const value = property.type === 'struct' ? { ...commandForm.structValue } : commandForm.value;
|
||||
submitLoading.value = true;
|
||||
try {
|
||||
await sendSolarPropertyCommand({
|
||||
id: currentDevice.value.id,
|
||||
properties: {
|
||||
[property.identifier]: value
|
||||
},
|
||||
cache
|
||||
});
|
||||
ElMessage.success(cache ? '已写入缓存,设备上线后自动下发' : '下发成功');
|
||||
commandOpen.value = false;
|
||||
await getList();
|
||||
} finally {
|
||||
submitLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = (row: SolarDeviceVO) => {
|
||||
if (row.online) {
|
||||
return '在线';
|
||||
}
|
||||
if (row.statusCode === 3) {
|
||||
return '离线';
|
||||
}
|
||||
if (row.statusCode === 8) {
|
||||
return '禁用';
|
||||
}
|
||||
if (row.statusCode === 0) {
|
||||
return '未激活';
|
||||
}
|
||||
return '未知';
|
||||
};
|
||||
|
||||
const getStatusTagType = (row: SolarDeviceVO) => {
|
||||
if (row.online) {
|
||||
return 'success';
|
||||
}
|
||||
if (row.statusCode === 3) {
|
||||
return 'warning';
|
||||
}
|
||||
if (row.statusCode === 8) {
|
||||
return 'danger';
|
||||
}
|
||||
return 'info';
|
||||
};
|
||||
|
||||
const formatValue = (value: unknown) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return '-';
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const sortDeviceListByOnline = () => {
|
||||
deviceList.value = deviceList.value
|
||||
.map((item, index) => ({ item, index }))
|
||||
.sort((a, b) => {
|
||||
const onlineDiff = Number(Boolean(b.item.online)) - Number(Boolean(a.item.online));
|
||||
if (onlineDiff !== 0) {
|
||||
return onlineDiff;
|
||||
}
|
||||
return a.index - b.index;
|
||||
})
|
||||
.map(({ item }) => item);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.device-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 12px 14px;
|
||||
margin-bottom: 12px;
|
||||
background: #f6f8fb;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.device-summary__name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.device-summary__serial {
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.property-option-code {
|
||||
float: right;
|
||||
margin-left: 16px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.property-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-left: 130px;
|
||||
margin-bottom: 16px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.command-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user