356 lines
10 KiB
Vue
356 lines
10 KiB
Vue
<template>
|
|
<el-dialog
|
|
:title="$t('device.sub-device-create.title')"
|
|
:visible.sync="visible"
|
|
width="720px"
|
|
append-to-body
|
|
:close-on-click-modal="false"
|
|
@close="handleClose"
|
|
>
|
|
<div v-loading="loading" class="sub-device-create">
|
|
<div class="section-title">
|
|
<i class="el-icon-s-grid" style="margin-right: 4px"></i>
|
|
{{ $t('device.sub-device-create.productListTitle') }}
|
|
</div>
|
|
|
|
<div v-if="subProductList.length === 0 && !loading" class="empty-tip">
|
|
{{ $t('device.sub-device-create.noSubProducts') }}
|
|
</div>
|
|
|
|
<el-collapse v-model="activeNames">
|
|
<el-collapse-item
|
|
v-for="(item, index) in subProductList"
|
|
:key="item.subProductId"
|
|
:name="index"
|
|
>
|
|
<template slot="title">
|
|
<div class="product-header">
|
|
<i class="el-icon-cpu" style="color: #409eff; margin-right: 6px"></i>
|
|
<span class="product-name">{{ item.subProductName }}</span>
|
|
<el-tag v-if="item.enabled" size="mini" type="success" effect="plain" style="margin-left: 8px">
|
|
{{ $t('device.sub-device-create.enabled') }}
|
|
</el-tag>
|
|
<el-tag v-if="item.enabled" size="mini" effect="plain" style="margin-left: 4px">
|
|
x{{ item.devices.length }}
|
|
</el-tag>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="product-body">
|
|
<el-row :gutter="24" class="config-row">
|
|
<el-col :span="10">
|
|
<div class="config-item">
|
|
<span class="config-label">{{ $t('device.sub-device-create.deviceConfig') }}</span>
|
|
<el-switch v-model="item.enabled" active-color="#409eff" @change="onEnableChange(index, $event)" />
|
|
</div>
|
|
</el-col>
|
|
<el-col :span="14">
|
|
<div class="config-item">
|
|
<span class="config-label">{{ $t('device.sub-device-create.deviceCount') }}</span>
|
|
<el-input-number
|
|
v-model="item.count"
|
|
:min="1"
|
|
:max="10"
|
|
:disabled="!item.enabled"
|
|
size="small"
|
|
@change="onCountChange(index)"
|
|
@input.native="onCountInput(index, $event)"
|
|
/>
|
|
</div>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<div v-if="item.enabled && item.devices.length > 0" class="device-list-section">
|
|
<div class="device-list-title">
|
|
<i class="el-icon-menu" style="margin-right: 4px; color: #409eff"></i>
|
|
{{ $t('device.sub-device-create.deviceListTitle') }}
|
|
</div>
|
|
<el-collapse v-model="item.expandedDevices" class="device-collapse">
|
|
<el-collapse-item
|
|
v-for="(dev, dIdx) in item.devices"
|
|
:key="dIdx"
|
|
:name="dIdx"
|
|
>
|
|
<template slot="title">
|
|
<div class="device-header">
|
|
<i class="el-icon-monitor" style="color: #909399; margin-right: 6px"></i>
|
|
<span>{{ dev.deviceName || ($t('device.sub-device-create.devicePrefix') + (dIdx + 1)) }}</span>
|
|
</div>
|
|
</template>
|
|
<div class="device-form">
|
|
<el-row :gutter="16">
|
|
<el-col :span="14">
|
|
<div class="device-form-item">
|
|
<span class="device-form-label">{{ $t('device.sub-device-create.deviceNameLabel') }}</span>
|
|
<el-input
|
|
v-model="dev.deviceName"
|
|
size="small"
|
|
:placeholder="$t('device.sub-device-create.deviceNamePlaceholder')"
|
|
/>
|
|
</div>
|
|
</el-col>
|
|
<el-col :span="10">
|
|
<div class="device-form-item">
|
|
<span class="device-form-label">{{ $t('device.sub-device-create.slaveIdLabel') }}</span>
|
|
<el-input-number
|
|
v-model="dev.slaveId"
|
|
:min="1"
|
|
:max="255"
|
|
size="small"
|
|
:placeholder="$t('device.sub-device-create.slaveIdPlaceholder')"
|
|
controls-position="right"
|
|
/>
|
|
</div>
|
|
</el-col>
|
|
</el-row>
|
|
</div>
|
|
</el-collapse-item>
|
|
</el-collapse>
|
|
</div>
|
|
</div>
|
|
</el-collapse-item>
|
|
</el-collapse>
|
|
|
|
<div v-if="totalDeviceCount > 0" class="summary-bar">
|
|
<i class="el-icon-info" style="color: #409eff; margin-right: 4px"></i>
|
|
{{ $t('device.sub-device-create.summary', [enabledProductCount, totalDeviceCount]) }}
|
|
</div>
|
|
</div>
|
|
|
|
<div slot="footer" class="dialog-footer">
|
|
<el-button type="primary" :loading="submitting" :disabled="totalDeviceCount === 0" @click="handleConfirm">
|
|
{{ $t('confirm') }}
|
|
</el-button>
|
|
<el-button @click="handleClose">{{ $t('cancel') }}</el-button>
|
|
</div>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<script>
|
|
import { gatewayProductList, createSubDeviceByProducts } from '@/api/iot/gateway';
|
|
|
|
export default {
|
|
name: 'sub-device-create',
|
|
props: {
|
|
gateway: {
|
|
type: Object,
|
|
default: null,
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
visible: false,
|
|
loading: false,
|
|
submitting: false,
|
|
subProductList: [],
|
|
activeNames: [],
|
|
};
|
|
},
|
|
computed: {
|
|
enabledProducts() {
|
|
return this.subProductList.filter((p) => p.enabled);
|
|
},
|
|
enabledProductCount() {
|
|
return this.enabledProducts.length;
|
|
},
|
|
totalDeviceCount() {
|
|
return this.enabledProducts.reduce((sum, p) => sum + (p.devices ? p.devices.length : 0), 0);
|
|
},
|
|
},
|
|
methods: {
|
|
open(productId) {
|
|
this.visible = true;
|
|
this.subProductList = [];
|
|
this.activeNames = [];
|
|
this.getSubProducts(productId);
|
|
},
|
|
getSubProducts(productId) {
|
|
this.loading = true;
|
|
gatewayProductList({ gwProductId: productId, pageNum: 1, pageSize: 100 })
|
|
.then((response) => {
|
|
const rows = response.rows || [];
|
|
this.subProductList = rows.map((item) => ({
|
|
...item,
|
|
enabled: false,
|
|
count: 1,
|
|
devices: [],
|
|
expandedDevices: [],
|
|
}));
|
|
this.activeNames = rows.map((_, idx) => idx);
|
|
})
|
|
.finally(() => {
|
|
this.loading = false;
|
|
});
|
|
},
|
|
onEnableChange(index, val) {
|
|
const item = this.subProductList[index];
|
|
if (val) {
|
|
this.rebuildDevices(index);
|
|
} else {
|
|
this.$set(item, 'devices', []);
|
|
this.$set(item, 'expandedDevices', []);
|
|
}
|
|
},
|
|
onCountChange(index) {
|
|
this.rebuildDevices(index);
|
|
},
|
|
onCountInput(index, e) {
|
|
this.$nextTick(() => {
|
|
this.rebuildDevices(index);
|
|
});
|
|
},
|
|
rebuildDevices(index) {
|
|
const item = this.subProductList[index];
|
|
if (!item.enabled) return;
|
|
const count = Math.max(1, Math.min(10, item.count || 1));
|
|
const baseSlaveId = item.slaveId || 1;
|
|
const oldDevices = item.devices || [];
|
|
const devices = [];
|
|
for (let i = 0; i < count; i++) {
|
|
if (i < oldDevices.length) {
|
|
devices.push(oldDevices[i]);
|
|
} else {
|
|
devices.push({
|
|
deviceName: item.subProductName + '_' + (i + 1),
|
|
slaveId: baseSlaveId + i,
|
|
});
|
|
}
|
|
}
|
|
this.$set(item, 'devices', devices);
|
|
this.$set(item, 'expandedDevices', devices.map((_, i) => i));
|
|
},
|
|
handleConfirm() {
|
|
if (this.totalDeviceCount === 0) {
|
|
this.$modal.msgWarning(this.$t('device.sub-device-create.selectTip'));
|
|
return;
|
|
}
|
|
const items = this.enabledProducts.map((p) => ({
|
|
subProductId: p.subProductId,
|
|
devices: (p.devices || []).map((d) => ({
|
|
deviceName: d.deviceName,
|
|
slaveId: d.slaveId,
|
|
})),
|
|
}));
|
|
this.submitting = true;
|
|
createSubDeviceByProducts({
|
|
gwDeviceId: this.gateway.gwDeviceId,
|
|
items: items,
|
|
})
|
|
.then((response) => {
|
|
this.$modal.msgSuccess(response.msg || this.$t('device.sub-device-create.success'));
|
|
this.visible = false;
|
|
this.$emit('addSuccess');
|
|
})
|
|
.finally(() => {
|
|
this.submitting = false;
|
|
});
|
|
},
|
|
handleClose() {
|
|
this.visible = false;
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.sub-device-create {
|
|
max-height: 520px;
|
|
overflow-y: auto;
|
|
}
|
|
.section-title {
|
|
font-weight: 600;
|
|
font-size: 15px;
|
|
color: #303133;
|
|
margin-bottom: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.empty-tip {
|
|
text-align: center;
|
|
color: #909399;
|
|
padding: 40px 0;
|
|
font-size: 14px;
|
|
}
|
|
.product-header {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
}
|
|
.product-name {
|
|
color: #303133;
|
|
}
|
|
.product-body {
|
|
padding: 8px 0 4px;
|
|
}
|
|
.config-row {
|
|
margin-bottom: 12px;
|
|
}
|
|
.config-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
.config-label {
|
|
font-size: 13px;
|
|
color: #606266;
|
|
white-space: nowrap;
|
|
}
|
|
.device-list-section {
|
|
margin-top: 8px;
|
|
}
|
|
.device-list-title {
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: #303133;
|
|
margin-bottom: 6px;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.device-collapse {
|
|
border: 1px solid #ebeef5;
|
|
border-radius: 4px;
|
|
}
|
|
.device-collapse >>> .el-collapse-item__header {
|
|
padding-left: 14px;
|
|
height: 40px;
|
|
line-height: 40px;
|
|
font-size: 13px;
|
|
background: #fafafa;
|
|
}
|
|
.device-collapse >>> .el-collapse-item__wrap {
|
|
border-bottom: 1px solid #f2f6fc;
|
|
}
|
|
.device-header {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 13px;
|
|
color: #606266;
|
|
}
|
|
.device-form {
|
|
padding: 12px 14px 8px;
|
|
}
|
|
.device-form-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-bottom: 4px;
|
|
}
|
|
.device-form-label {
|
|
font-size: 12px;
|
|
color: #909399;
|
|
white-space: nowrap;
|
|
min-width: 56px;
|
|
}
|
|
.summary-bar {
|
|
margin-top: 16px;
|
|
padding: 10px 14px;
|
|
background: #ecf5ff;
|
|
border-radius: 4px;
|
|
font-size: 13px;
|
|
color: #409eff;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
</style>
|