物联网问题解决,只差最后测试完善
This commit is contained in:
@@ -214,8 +214,35 @@
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
<div class="host-box" v-if="data.serverIds != ''">
|
||||
<!-- <div class="ear-box">
|
||||
<div class="title">智能主机</div>
|
||||
<el-table
|
||||
:data="data.collarRows"
|
||||
border
|
||||
v-loading="data.collarDataListLoading"
|
||||
element-loading-text="数据加载中..."
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column label="智能主机编号" prop="deviceId"></el-table-column>
|
||||
<el-table-column label="设备电量" prop="battery">
|
||||
<template #default="scope"> {{ scope.row.battery || scope.row.deviceVoltage || '-' }}% </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="步数" prop="steps">
|
||||
<template #default="scope"> {{ scope.row.steps || scope.row.walkSteps || '-' }}步</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备温度" prop="temperature">
|
||||
<template #default="scope"> {{ scope.row.temperature || scope.row.deviceTemp || '-' }}℃ </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据最后更新时间" prop="time">
|
||||
<template #default="scope"> {{ scope.row.time || scope.row.updateTime || scope.row.createTime || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" prop="">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="collarLogClick(scope.row)">日志</el-button>
|
||||
<el-button link type="primary" @click="collarTrackClick(scope.row)">运动轨迹</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-descriptions :column="1">
|
||||
<el-descriptions-item label="主机编号:">
|
||||
{{ data.serverIds }}
|
||||
@@ -227,7 +254,7 @@
|
||||
>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="ear-box">
|
||||
<div class="title">智能项圈</div>
|
||||
<el-table
|
||||
@@ -237,17 +264,19 @@
|
||||
element-loading-text="数据加载中..."
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column label="智能项圈编号" prop="sn"></el-table-column>
|
||||
<el-table-column label="智能项圈编号" prop="deviceId"></el-table-column>
|
||||
<el-table-column label="设备电量" prop="battery">
|
||||
<template #default="scope"> {{ scope.row.battery }}% </template>
|
||||
<template #default="scope"> {{ scope.row.battery || scope.row.deviceVoltage || '-' }}% </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="步数" prop="steps">
|
||||
<template #default="scope"> {{ scope.row.steps }}步</template>
|
||||
<template #default="scope"> {{ scope.row.steps || scope.row.walkSteps || '-' }}步</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备温度" prop="temperature">
|
||||
<template #default="scope"> {{ scope.row.temperature }}/℃ </template>
|
||||
<template #default="scope"> {{ scope.row.temperature || scope.row.deviceTemp || '-' }}℃ </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据最后更新时间" prop="time">
|
||||
<template #default="scope"> {{ scope.row.time || scope.row.updateTime || scope.row.createTime || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据最后更新时间" prop="time"></el-table-column>
|
||||
<el-table-column label="操作" prop="">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="collarLogClick(scope.row)">日志</el-button>
|
||||
@@ -267,15 +296,17 @@
|
||||
<el-table :data="data.rows" border v-loading="data.dataListLoading" element-loading-text="数据加载中..." style="width: 100%">
|
||||
<el-table-column label="智能耳标编号" prop="deviceId"></el-table-column>
|
||||
<el-table-column label="设备电量" prop="deviceVoltage">
|
||||
<template #default="scope"> {{ scope.row.deviceVoltage }}% </template>
|
||||
<template #default="scope"> {{ scope.row.deviceVoltage || scope.row.battery || '-' }}% </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="步数" prop="walkSteps">
|
||||
<template #default="scope"> {{ scope.row.walkSteps }}步</template>
|
||||
<template #default="scope"> {{ scope.row.walkSteps || scope.row.steps || '-' }}步</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备温度" prop="deviceTemp">
|
||||
<template #default="scope"> {{ scope.row.deviceTemp }}/℃ </template>
|
||||
<template #default="scope"> {{ scope.row.deviceTemp || scope.row.temperature || '-' }}℃ </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据最后更新时间" prop="updateTime">
|
||||
<template #default="scope"> {{ scope.row.updateTime || scope.row.createTime || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据最后更新时间" prop="updateTime"></el-table-column>
|
||||
<el-table-column label="操作" prop="">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="earLogClick(scope.row)">日志</el-button>
|
||||
@@ -387,15 +418,17 @@
|
||||
<el-table :data="data.earLogRows" border v-loading="data.logListLoading" element-loading-text="数据加载中..." style="width: 100%">
|
||||
<el-table-column label="智能耳标编号" prop="deviceId"></el-table-column>
|
||||
<el-table-column label="设备电量" prop="deviceVoltage">
|
||||
<template #default="scope"> {{ scope.row.deviceVoltage }}% </template>
|
||||
<template #default="scope"> {{ scope.row.deviceVoltage || scope.row.battery || '-' }}% </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="步数" prop="walkSteps">
|
||||
<template #default="scope"> {{ scope.row.walkSteps }}步</template>
|
||||
<template #default="scope"> {{ scope.row.walkSteps || scope.row.steps || '-' }}步</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备温度" prop="deviceTemp">
|
||||
<template #default="scope"> {{ scope.row.deviceTemp }}/℃ </template>
|
||||
<template #default="scope"> {{ scope.row.deviceTemp || scope.row.temperature || '-' }}℃ </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据最后更新时间" prop="updateTime" width="200">
|
||||
<template #default="scope"> {{ scope.row.updateTime || scope.row.createTime || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据最后更新时间" prop="updateTime" width="200"></el-table-column>
|
||||
<el-table-column label="操作" prop="">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="locateClick(scope.row)">定位</el-button>
|
||||
@@ -409,15 +442,17 @@
|
||||
<el-table :data="data.collarLogRows" border v-loading="data.logListLoading" element-loading-text="数据加载中..." style="width: 100%">
|
||||
<el-table-column label="智能项圈编号" prop="sn"></el-table-column>
|
||||
<el-table-column label="设备电量" prop="battery">
|
||||
<template #default="scope"> {{ scope.row.battery }}% </template>
|
||||
<template #default="scope"> {{ scope.row.battery || scope.row.deviceVoltage || '-' }}% </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="步数" prop="steps">
|
||||
<template #default="scope"> {{ scope.row.steps }}步</template>
|
||||
<template #default="scope"> {{ scope.row.steps || scope.row.walkSteps || '-' }}步</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备温度" prop="temperature">
|
||||
<template #default="scope"> {{ scope.row.temperature }}/℃ </template>
|
||||
<template #default="scope"> {{ scope.row.temperature || scope.row.deviceTemp || '-' }}℃ </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据最后更新时间" prop="time" width="200">
|
||||
<template #default="scope"> {{ scope.row.time || scope.row.updateTime || scope.row.createTime || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据最后更新时间" prop="time" width="200"></el-table-column>
|
||||
<el-table-column label="操作" prop="">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="collarlocateClick(scope.row)">定位</el-button>
|
||||
@@ -440,7 +475,7 @@
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { earList, hostLocation, hostTrack, waybillDetail, collarList, collarLogList, earLogList, testDeliveryDevices } from '@/api/abroad.js';
|
||||
import { earList, hostLocation, hostTrack, waybillDetail, collarList, collarLogList, earLogList, testDeliveryDevices, pageDeviceList } from '@/api/abroad.js';
|
||||
import startIcon from '../../assets/images/qi.png';
|
||||
import endIcon from '../../assets/images/zhong.png';
|
||||
import TrackDialog from '../hardware/trackDialog.vue';
|
||||
@@ -548,6 +583,9 @@ const getDetail = () => {
|
||||
carBehindPhoto: data.baseInfo.carBehindPhoto,
|
||||
driverId: data.baseInfo.driverId
|
||||
});
|
||||
|
||||
// 使用新的统一API获取智能主机信息
|
||||
getHostDeviceInfo();
|
||||
} else {
|
||||
ElMessage.error(res.msg);
|
||||
}
|
||||
@@ -555,6 +593,43 @@ const getDetail = () => {
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 获取智能主机信息
|
||||
const getHostDeviceInfo = () => {
|
||||
if (!route.query.id) {
|
||||
console.warn('=== 警告:deliveryId为空,跳过主机设备查询');
|
||||
return;
|
||||
}
|
||||
|
||||
pageDeviceList({
|
||||
pageNum: 1,
|
||||
pageSize: 100, // 获取所有主机设备
|
||||
deliveryId: parseInt(route.query.id),
|
||||
deviceType: 1, // 智能主机设备类型
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('=== 主机设备API返回结果:', res);
|
||||
if (res.code === 200) {
|
||||
console.log('=== 主机设备数据:', res.data);
|
||||
// 新API返回的是数组格式,过滤出智能主机设备
|
||||
const hostDevices = res.data.filter(device => device.deviceType === 1 || device.deviceType === '1');
|
||||
if (hostDevices.length > 0) {
|
||||
// 如果有主机设备,取第一个作为主要主机
|
||||
data.serverIds = hostDevices[0].deviceId || hostDevices[0].sn || '';
|
||||
console.log('=== 设置后的serverIds:', data.serverIds);
|
||||
} else {
|
||||
data.serverIds = '';
|
||||
}
|
||||
} else {
|
||||
console.warn('获取主机设备信息失败:', res.msg);
|
||||
data.serverIds = '';
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('获取主机设备信息异常:', err);
|
||||
data.serverIds = '';
|
||||
});
|
||||
};
|
||||
|
||||
// 查看主机定位
|
||||
const locationClick = (item) => {
|
||||
getHostLocation(item);
|
||||
@@ -626,18 +701,21 @@ const getEarList = () => {
|
||||
}
|
||||
|
||||
data.dataListLoading = true;
|
||||
earList({
|
||||
pageDeviceList({
|
||||
pageNum: form.pageNum,
|
||||
pageSize: form.pageSize,
|
||||
deliveryId: route.query.id,
|
||||
deliveryId: parseInt(route.query.id),
|
||||
deviceType: 2, // 智能耳标设备类型
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('=== 耳标设备API返回结果:', res);
|
||||
data.dataListLoading = false;
|
||||
if (res.code === 200) {
|
||||
console.log('=== 耳标设备数据:', res.data);
|
||||
data.rows = res.data.rows || [];
|
||||
data.total = res.data.total || 0;
|
||||
// 新API返回的是数组格式,需要过滤出智能耳标设备
|
||||
const earDevices = res.data.filter(device => device.deviceType === 2 || device.deviceType === '2');
|
||||
data.rows = earDevices || [];
|
||||
data.total = earDevices.length || 0;
|
||||
console.log('=== 设置后的rows:', data.rows);
|
||||
console.log('=== 设置后的total:', data.total);
|
||||
} else {
|
||||
@@ -650,7 +728,7 @@ const getEarList = () => {
|
||||
});
|
||||
};
|
||||
const earLogClick = (row) => {
|
||||
data.deviceId = row.deviceId;
|
||||
data.deviceId = row.deviceId || row.sn || '';
|
||||
data.earLogDialogVisible = true;
|
||||
getEarLogList();
|
||||
};
|
||||
@@ -663,18 +741,21 @@ const getCollarList = () => {
|
||||
}
|
||||
|
||||
data.collarDataListLoading = true;
|
||||
collarList({
|
||||
pageDeviceList({
|
||||
pageNum: collarForm.pageNum,
|
||||
pageSize: collarForm.pageSize,
|
||||
deliveryId: route.query.id,
|
||||
deliveryId: parseInt(route.query.id),
|
||||
deviceType: 4, // 智能项圈设备类型
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('=== 项圈设备API返回结果:', res);
|
||||
data.collarDataListLoading = false;
|
||||
if (res.code === 200) {
|
||||
console.log('=== 项圈设备数据:', res.data);
|
||||
data.collarRows = res.data.rows || [];
|
||||
data.collarTotal = res.data.total || 0;
|
||||
// 新API返回的是数组格式,需要过滤出智能项圈设备
|
||||
const collarDevices = res.data.filter(device => device.deviceType === 4 || device.deviceType === '4');
|
||||
data.collarRows = collarDevices || [];
|
||||
data.collarTotal = collarDevices.length || 0;
|
||||
console.log('=== 设置后的collarRows:', data.collarRows);
|
||||
console.log('=== 设置后的collarTotal:', data.collarTotal);
|
||||
} else {
|
||||
@@ -688,7 +769,7 @@ const getCollarList = () => {
|
||||
});
|
||||
};
|
||||
const collarLogClick = (row) => {
|
||||
data.sn = row.sn;
|
||||
data.sn = row.sn || row.deviceId || '';
|
||||
data.collarDialogVisible = true;
|
||||
getCollarLogList();
|
||||
};
|
||||
@@ -716,14 +797,14 @@ const goToList = () => {
|
||||
const locateClick = (row) => {
|
||||
data.center.lng = row.longitude;
|
||||
data.center.lat = row.latitude;
|
||||
data.updateTime = row.updateTime;
|
||||
data.updateTime = row.updateTime || row.createTime || '';
|
||||
data.dialogVisible = true;
|
||||
};
|
||||
//
|
||||
const collarlocateClick = (row) => {
|
||||
data.center.lng = row.longitude;
|
||||
data.center.lat = row.latitude;
|
||||
data.updateTime = row.time;
|
||||
data.updateTime = row.time || row.updateTime || row.createTime || '';
|
||||
data.dialogVisible = true;
|
||||
};
|
||||
// 耳标日志列表
|
||||
@@ -777,7 +858,7 @@ const collarTrackClick = (row) => {
|
||||
if (TrackDialogRef.value) {
|
||||
const info = {
|
||||
deliveryId: route.query.id,
|
||||
deviceId: row.sn,
|
||||
deviceId: row.sn || row.deviceId || '',
|
||||
type: 'order',
|
||||
};
|
||||
TrackDialogRef.value.onShowTrackDialog(info);
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
|
||||
<div class="main-container" style="margin-top: 10px">
|
||||
<el-table :data="form.tableData" style="width: 100%" border>
|
||||
<el-table-column label="项圈编号" prop="sn" />
|
||||
<el-table-column label="项圈编号" prop="deviceId" />
|
||||
<el-table-column label="设备电量" prop="battery">
|
||||
<template #default="scope"> {{ scope.row.battery }}% </template>
|
||||
<template #default="scope"> {{ calculateBatteryPercentage(scope.row.battery) }}% </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备温度" prop="temperature">
|
||||
<template #default="scope"> {{ scope.row.temperature }}°C</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="delivery_number" label="所属运单号" />
|
||||
<el-table-column prop="license_plate" label="车牌号" />
|
||||
<el-table-column prop="uptime" label="绑定时间" />
|
||||
<el-table-column prop="deliveryNumber" label="运单号" />
|
||||
<el-table-column prop="carNumber" label="车牌号" />
|
||||
<el-table-column prop="uptime" label="更新时间" />
|
||||
<el-table-column label="操作" width="180">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="showLocationDialog(scope.row)">定位</el-button>
|
||||
@@ -46,7 +46,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { collarList } from '~/api/hardware.js';
|
||||
import { iotDeviceQueryList } from '~/api/hardware.js';
|
||||
import LocationDialog from './locationDialog.vue';
|
||||
import TrackDialog from './trackDialog.vue';
|
||||
|
||||
@@ -63,21 +63,66 @@ const form = reactive({
|
||||
endNo: '',
|
||||
});
|
||||
|
||||
// 计算电量百分比:3V为100%,2.4V为0%
|
||||
const calculateBatteryPercentage = (voltage) => {
|
||||
if (!voltage || voltage === '' || voltage === '0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const voltageValue = parseFloat(voltage);
|
||||
if (isNaN(voltageValue)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 线性插值计算:3V = 100%, 2.4V = 0%
|
||||
const minVoltage = 2.4;
|
||||
const maxVoltage = 3.0;
|
||||
|
||||
if (voltageValue >= maxVoltage) {
|
||||
return 100;
|
||||
} else if (voltageValue <= minVoltage) {
|
||||
return 0;
|
||||
} else {
|
||||
const percentage = ((voltageValue - minVoltage) / (maxVoltage - minVoltage)) * 100;
|
||||
return Math.round(percentage);
|
||||
}
|
||||
};
|
||||
|
||||
const getList = async () => {
|
||||
const { pageSize, pageNum, sn, startNo, endNo } = form;
|
||||
// 为了获取所有项圈设备,使用更大的pageSize
|
||||
const params = {
|
||||
pageSize,
|
||||
pageNum,
|
||||
pageSize: 100, // 使用更大的页面大小确保能获取所有设备
|
||||
pageNum: 1,
|
||||
sn,
|
||||
startNo,
|
||||
endNo,
|
||||
};
|
||||
const res = await collarList(params);
|
||||
const { data = {}, code } = res;
|
||||
const { rows = [], total = 0 } = data;
|
||||
if (code === 200) {
|
||||
form.tableData = rows;
|
||||
form.total = total;
|
||||
|
||||
try {
|
||||
const res = await iotDeviceQueryList(params);
|
||||
const { data = {}, code } = res;
|
||||
|
||||
if (code === 200) {
|
||||
// 后端已经过滤了organName为'牛只运输跟踪系统'的数据
|
||||
// 前端根据设备类型过滤项圈数据(type=4)
|
||||
const allData = data.rows || [];
|
||||
const filteredData = allData.filter(item => item.type === 4);
|
||||
|
||||
form.tableData = filteredData;
|
||||
form.total = filteredData.length;
|
||||
|
||||
// 重新计算分页,因为我们现在显示的是过滤后的数据
|
||||
form.pageNum = 1; // 重置到第一页
|
||||
} else {
|
||||
console.error('API调用失败:', res.msg);
|
||||
form.tableData = [];
|
||||
form.total = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('API调用异常:', error);
|
||||
form.tableData = [];
|
||||
form.total = 0;
|
||||
}
|
||||
};
|
||||
const searchClick = async () => {
|
||||
|
||||
@@ -22,15 +22,15 @@
|
||||
<div class="main-container" style="margin-top: 10px">
|
||||
<el-table :data="form.tableData" style="width: 100%" border>
|
||||
<el-table-column prop="deviceId" label="耳标编号" />
|
||||
<el-table-column label="设备电量" prop="deviceVoltage">
|
||||
<template #default="scope"> {{ scope.row.deviceVoltage }}% </template>
|
||||
<el-table-column label="设备电量" prop="voltage">
|
||||
<template #default="scope"> {{ calculateBatteryPercentage(scope.row.voltage) }}% </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备温度" prop="deviceTemp">
|
||||
<template #default="scope"> {{ scope.row.deviceTemp }}°C</template>
|
||||
<el-table-column label="设备温度" prop="temperature">
|
||||
<template #default="scope"> {{ scope.row.temperature }}°C</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="deliveryNumber" label="所属运单号" />
|
||||
<el-table-column prop="licensePlate" label="车牌号" />
|
||||
<el-table-column prop="deliveryCreateTime" label="绑定时间" />
|
||||
<el-table-column prop="deliveryNumber" label="运单号" />
|
||||
<el-table-column prop="carNumber" label="车牌号" />
|
||||
<el-table-column prop="uptime" label="更新时间" />
|
||||
</el-table>
|
||||
<pagination v-model:limit="form.pageSize" v-model:page="form.pageNum" :total="form.total" @pagination="getList" />
|
||||
</div>
|
||||
@@ -38,19 +38,48 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { jbqClientList } from '~/api/hardware.js';
|
||||
import { iotDeviceQueryList } from '~/api/hardware.js';
|
||||
|
||||
const formRef = ref();
|
||||
const form = reactive({
|
||||
tableData: [],
|
||||
pageSize: 10,
|
||||
pageSize: 20,
|
||||
pageNum: 1,
|
||||
total: 0,
|
||||
deviceId: '',
|
||||
startNo: '',
|
||||
endNo: '',
|
||||
obj:{
|
||||
deviceId: '',
|
||||
orgId: 385082,
|
||||
type: '',
|
||||
subtype: ''
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// 计算电量百分比:3V为100%,2.4V为0%
|
||||
const calculateBatteryPercentage = (voltage) => {
|
||||
if (!voltage || voltage === '' || voltage === '0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const voltageValue = parseFloat(voltage);
|
||||
if (isNaN(voltageValue)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 线性插值计算:3V = 100%, 2.4V = 0%
|
||||
const minVoltage = 2.4;
|
||||
const maxVoltage = 3.0;
|
||||
|
||||
if (voltageValue >= maxVoltage) {
|
||||
return 100;
|
||||
} else if (voltageValue <= minVoltage) {
|
||||
return 0;
|
||||
} else {
|
||||
const percentage = ((voltageValue - minVoltage) / (maxVoltage - minVoltage)) * 100;
|
||||
return Math.round(percentage);
|
||||
}
|
||||
};
|
||||
|
||||
const getList = async () => {
|
||||
const { pageSize, pageNum, deviceId, startNo, endNo } = form;
|
||||
const params = {
|
||||
@@ -60,12 +89,28 @@ const getList = async () => {
|
||||
startNo,
|
||||
endNo,
|
||||
};
|
||||
const res = await jbqClientList(params);
|
||||
const { data = {}, code } = res;
|
||||
const { rows = [], total = 0 } = data;
|
||||
if (code === 200) {
|
||||
form.tableData = rows;
|
||||
form.total = total;
|
||||
|
||||
try {
|
||||
const res = await iotDeviceQueryList(params);
|
||||
const { data = {}, code } = res;
|
||||
|
||||
if (code === 200) {
|
||||
// 后端已经过滤了organName为'牛只运输跟踪系统'的数据
|
||||
// 前端根据设备类型过滤耳标数据(type=2)
|
||||
const allData = data.rows || [];
|
||||
const filteredData = allData.filter(item => item.type === 2);
|
||||
|
||||
form.tableData = filteredData;
|
||||
form.total = filteredData.length;
|
||||
} else {
|
||||
console.error('API调用失败:', res.msg);
|
||||
form.tableData = [];
|
||||
form.total = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('API调用异常:', error);
|
||||
form.tableData = [];
|
||||
form.total = 0;
|
||||
}
|
||||
};
|
||||
const searchClick = async () => {
|
||||
|
||||
@@ -17,16 +17,15 @@
|
||||
<div class="main-container" style="margin-top: 10px">
|
||||
<el-table :data="form.tableData" style="width: 100%" border>
|
||||
<el-table-column prop="deviceId" label="主机编号" />
|
||||
<el-table-column label="设备电量" prop="deviceVoltage">
|
||||
<template #default="scope"> {{ scope.row.deviceVoltage }}% </template>
|
||||
<el-table-column label="设备电量" prop="voltage">
|
||||
<template #default="scope"> {{ calculateBatteryPercentage(scope.row.voltage) }}% </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备温度" prop="deviceTemp">
|
||||
<template #default="scope"> {{ scope.row.deviceTemp }}°C</template>
|
||||
<el-table-column label="设备温度" prop="temperature">
|
||||
<template #default="scope"> {{ scope.row.temperature }}°C</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="onlineStatusDesc" label="联网状态" />
|
||||
<el-table-column prop="deliveryNumber" label="所属运单号" />
|
||||
<el-table-column prop="licensePlate" label="车牌号" />
|
||||
<el-table-column prop="deliveryCreateTime" label="绑定时间" />
|
||||
<el-table-column prop="deliveryNumber" label="运单号" />
|
||||
<el-table-column prop="carNumber" label="车牌号" />
|
||||
<el-table-column prop="uptime" label="更新时间" />
|
||||
</el-table>
|
||||
<pagination v-model:limit="form.pageSize" v-model:page="form.pageNum" :total="form.total" @pagination="getList" />
|
||||
</div>
|
||||
@@ -34,7 +33,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { jbqServerList } from '~/api/hardware.js';
|
||||
import { iotDeviceQueryList } from '~/api/hardware.js';
|
||||
|
||||
const formRef = ref();
|
||||
const form = reactive({
|
||||
@@ -45,6 +44,31 @@ const form = reactive({
|
||||
deviceId: '',
|
||||
});
|
||||
|
||||
// 计算电量百分比:3V为100%,2.4V为0%
|
||||
const calculateBatteryPercentage = (voltage) => {
|
||||
if (!voltage || voltage === '' || voltage === '0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const voltageValue = parseFloat(voltage);
|
||||
if (isNaN(voltageValue)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 线性插值计算:3V = 100%, 2.4V = 0%
|
||||
const minVoltage = 2.4;
|
||||
const maxVoltage = 3.0;
|
||||
|
||||
if (voltageValue >= maxVoltage) {
|
||||
return 100;
|
||||
} else if (voltageValue <= minVoltage) {
|
||||
return 0;
|
||||
} else {
|
||||
const percentage = ((voltageValue - minVoltage) / (maxVoltage - minVoltage)) * 100;
|
||||
return Math.round(percentage);
|
||||
}
|
||||
};
|
||||
|
||||
const getList = async () => {
|
||||
const { pageSize, pageNum, deviceId } = form;
|
||||
const params = {
|
||||
@@ -52,12 +76,28 @@ const getList = async () => {
|
||||
pageNum,
|
||||
deviceId,
|
||||
};
|
||||
const res = await jbqServerList(params);
|
||||
const { data = {}, code } = res;
|
||||
const { rows = [], total = 0 } = data;
|
||||
if (code === 200) {
|
||||
form.tableData = rows;
|
||||
form.total = total;
|
||||
|
||||
try {
|
||||
const res = await iotDeviceQueryList(params);
|
||||
const { data = {}, code } = res;
|
||||
|
||||
if (code === 200) {
|
||||
// 后端已经过滤了organName为'牛只运输跟踪系统'的数据
|
||||
// 前端根据设备类型过滤主机数据(type=1)
|
||||
const allData = data.rows || [];
|
||||
const filteredData = allData.filter(item => item.type === 1);
|
||||
|
||||
form.tableData = filteredData;
|
||||
form.total = filteredData.length;
|
||||
} else {
|
||||
console.error('API调用失败:', res.msg);
|
||||
form.tableData = [];
|
||||
form.total = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('API调用异常:', error);
|
||||
form.tableData = [];
|
||||
form.total = 0;
|
||||
}
|
||||
};
|
||||
const searchClick = async () => {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { collarLocation } from '~/api/hardware.js';
|
||||
import { iotDeviceLocation } from '~/api/hardware.js';
|
||||
|
||||
const data = reactive({
|
||||
dialogVisible: false,
|
||||
@@ -50,16 +50,15 @@ const data = reactive({
|
||||
// 查询定位
|
||||
const getLocation = () => {
|
||||
data.loactionLoading = true;
|
||||
collarLocation({
|
||||
deliveryId: data.deliveryId,
|
||||
xqDeviceId: data.xqDeviceId,
|
||||
iotDeviceLocation({
|
||||
deviceId: data.xqDeviceId,
|
||||
})
|
||||
.then((res) => {
|
||||
data.loactionLoading = false;
|
||||
if (res.code === 200) {
|
||||
data.mapShow = true;
|
||||
data.center.lng = res.data.longitude;
|
||||
data.center.lat = res.data.latitude;
|
||||
data.center.lng = parseFloat(res.data.longitude);
|
||||
data.center.lat = parseFloat(res.data.latitude);
|
||||
data.time = `最后定位时间:${res.data.updateTime}`;
|
||||
} else {
|
||||
ElMessage.error(res.msg);
|
||||
|
||||
@@ -55,6 +55,7 @@ import { reactive, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { login, getSmsCodeByPhone } from '~/api/sys.js';
|
||||
import { useUserStore } from '~/store/user';
|
||||
import usePermissionStore from '~/store/permission.js';
|
||||
import { setToken } from '@/utils/auth';
|
||||
import { checkMobile } from '@/utils/validateFuns.js';
|
||||
import { getUserMenu } from '@/api/sys.js';
|
||||
@@ -244,15 +245,25 @@ const generateRoutes = async () => {
|
||||
console.log('=== 没有找到有效菜单,跳转到默认页面 ===', targetPath);
|
||||
}
|
||||
|
||||
// 执行跳转
|
||||
// 等待路由完全生成后再执行跳转
|
||||
await nextTick();
|
||||
|
||||
// 确保权限store的路由生成完成
|
||||
const permissionStore = usePermissionStore();
|
||||
if (!permissionStore.routeFlag) {
|
||||
console.log('=== 等待路由生成完成 ===');
|
||||
await permissionStore.generateRoutes();
|
||||
}
|
||||
|
||||
// 使用replace而不是push,避免路由警告
|
||||
try {
|
||||
await router.push({ path: targetPath });
|
||||
await router.replace({ path: targetPath });
|
||||
console.log('=== 成功跳转到目标页面 ===', targetPath);
|
||||
} catch (error) {
|
||||
console.warn('Failed to navigate to', targetPath, 'error:', error);
|
||||
// 如果跳转失败,尝试跳转到首页
|
||||
try {
|
||||
await router.push({ path: '/' });
|
||||
await router.replace({ path: '/' });
|
||||
console.log('=== 跳转到首页 ===');
|
||||
} catch (homeError) {
|
||||
console.error('Failed to navigate to home:', homeError);
|
||||
|
||||
@@ -3,23 +3,27 @@
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧:用户列表 -->
|
||||
<el-col :span="8">
|
||||
<el-card class="role-card">
|
||||
<el-card class="user-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">用户列表</span>
|
||||
<el-tag type="success" size="small" style="margin-left: 10px;">
|
||||
用户专属权限
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
:data="paginatedRoleList"
|
||||
:data="paginatedUserList"
|
||||
highlight-current-row
|
||||
@current-change="handleRoleChange"
|
||||
v-loading="roleLoading"
|
||||
@current-change="handleUserChange"
|
||||
v-loading="userLoading"
|
||||
style="width: 100%"
|
||||
max-height="500"
|
||||
>
|
||||
<el-table-column prop="name" label="用户名称" width="120" />
|
||||
<el-table-column prop="mobile" label="手机号" />
|
||||
<el-table-column prop="roleId" label="角色ID" width="80" />
|
||||
</el-table>
|
||||
|
||||
<!-- 分页器 -->
|
||||
@@ -28,7 +32,7 @@
|
||||
v-model:current-page="pagination.currentPage"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="roleList.length"
|
||||
:total="userList.length"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
@@ -43,13 +47,16 @@
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
{{ currentRole ? `${currentRole.name} - 菜单访问权限` : '请选择角色' }}
|
||||
{{ currentUser ? `${currentUser.name} - 菜单访问权限` : '请选择用户' }}
|
||||
</span>
|
||||
<el-tag v-if="currentUser" type="success" size="small" style="margin-right: 10px">
|
||||
用户ID: {{ currentUser.id }}
|
||||
</el-tag>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleSaveMenuPermissions"
|
||||
:disabled="!currentRole"
|
||||
:disabled="!currentUser"
|
||||
:loading="saveLoading"
|
||||
>
|
||||
保存菜单权限
|
||||
@@ -58,23 +65,42 @@
|
||||
type="success"
|
||||
size="small"
|
||||
@click="handleQuickAssignAll"
|
||||
:disabled="!currentRole"
|
||||
:disabled="!currentUser"
|
||||
:loading="quickAssignLoading"
|
||||
style="margin-left: 10px"
|
||||
>
|
||||
一键分配全部权限
|
||||
一键分配全部菜单权限
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="handleClearUserPermissions"
|
||||
:disabled="!currentUser"
|
||||
:loading="clearLoading"
|
||||
style="margin-left: 10px"
|
||||
>
|
||||
清空用户权限
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="currentRole" v-loading="permissionLoading">
|
||||
<div v-if="currentUser" v-loading="permissionLoading">
|
||||
<el-alert
|
||||
title="提示"
|
||||
title="用户专属菜单权限管理"
|
||||
type="info"
|
||||
:closable="false"
|
||||
style="margin-bottom: 20px"
|
||||
>
|
||||
勾选菜单和按钮后,该用户登录系统时可以访问这些菜单页面和执行相应的操作
|
||||
<template #default>
|
||||
<div>
|
||||
<p><strong>当前系统使用基于用户的菜单权限管理</strong></p>
|
||||
<p>• 修改菜单权限只影响当前选择的用户</p>
|
||||
<p>• 当前用户: <strong>{{ currentUser.name }}</strong> (ID: {{ currentUser.id }})</p>
|
||||
<p>• 角色ID: <strong>{{ currentUser.roleId }}</strong></p>
|
||||
<p>• 勾选菜单后,该用户可以访问相应的菜单页面</p>
|
||||
<p>• 按钮权限请在"操作权限管理"页面中设置</p>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<el-tree
|
||||
@@ -137,15 +163,17 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import {
|
||||
getUserList,
|
||||
getMenuTree,
|
||||
getRoleMenuIds,
|
||||
assignRoleMenus,
|
||||
getUserMenuIds,
|
||||
assignUserMenus,
|
||||
clearUserMenus,
|
||||
getMenuList,
|
||||
} from '@/api/permission.js';
|
||||
import usePermissionStore from '@/store/permission.js';
|
||||
|
||||
// 角色相关数据
|
||||
const roleLoading = ref(false);
|
||||
const roleList = ref([]);
|
||||
const currentRole = ref(null);
|
||||
// 用户相关数据
|
||||
const userLoading = ref(false);
|
||||
const userList = ref([]);
|
||||
const currentUser = ref(null);
|
||||
|
||||
// 分页数据
|
||||
const pagination = reactive({
|
||||
@@ -154,16 +182,17 @@ const pagination = reactive({
|
||||
});
|
||||
|
||||
// 计算分页后的用户列表
|
||||
const paginatedRoleList = computed(() => {
|
||||
const paginatedUserList = computed(() => {
|
||||
const start = (pagination.currentPage - 1) * pagination.pageSize;
|
||||
const end = start + pagination.pageSize;
|
||||
return roleList.value.slice(start, end);
|
||||
return userList.value.slice(start, end);
|
||||
});
|
||||
|
||||
// 权限相关数据
|
||||
const permissionLoading = ref(false);
|
||||
const saveLoading = ref(false);
|
||||
const quickAssignLoading = ref(false);
|
||||
const clearLoading = ref(false);
|
||||
const menuTree = ref([]);
|
||||
const menuTreeRef = ref(null);
|
||||
const checkedMenuIds = ref([]);
|
||||
@@ -174,42 +203,48 @@ const treeProps = {
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
loadRoleList();
|
||||
loadUserList();
|
||||
});
|
||||
|
||||
// 加载用户列表
|
||||
const loadRoleList = async () => {
|
||||
roleLoading.value = true;
|
||||
const loadUserList = async () => {
|
||||
userLoading.value = true;
|
||||
try {
|
||||
const res = await getUserList();
|
||||
if (res.code === 200) {
|
||||
roleList.value = res.data || [];
|
||||
userList.value = res.data || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载用户列表失败:', error);
|
||||
ElMessage.error('加载用户列表失败');
|
||||
} finally {
|
||||
roleLoading.value = false;
|
||||
userLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 角色选择改变
|
||||
const handleRoleChange = async (row) => {
|
||||
// 用户选择改变
|
||||
const handleUserChange = async (row) => {
|
||||
if (!row) return;
|
||||
|
||||
currentRole.value = row;
|
||||
console.log('=== 菜单权限管理 - 用户选择改变 ===');
|
||||
console.log('选择的用户:', row);
|
||||
console.log('用户ID:', row.id);
|
||||
|
||||
currentUser.value = row;
|
||||
await loadMenuTree();
|
||||
await loadRoleMenus(row.roleId);
|
||||
await loadUserMenus(row.id);
|
||||
};
|
||||
|
||||
// 加载菜单树(显示所有菜单,包括菜单和按钮)
|
||||
// 加载菜单树(只显示菜单,不显示按钮)
|
||||
const loadMenuTree = async () => {
|
||||
permissionLoading.value = true;
|
||||
try {
|
||||
const res = await getMenuTree();
|
||||
if (res.code === 200) {
|
||||
// 显示所有菜单和按钮,不进行过滤
|
||||
menuTree.value = res.data || [];
|
||||
// 过滤掉按钮权限(type=2),只保留菜单(type=0,1)
|
||||
const filteredTree = filterMenuTree(res.data || []);
|
||||
menuTree.value = filteredTree;
|
||||
console.log('=== 菜单权限管理 - 过滤后的菜单树 ===', filteredTree);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载菜单树失败:', error);
|
||||
@@ -219,12 +254,42 @@ const loadMenuTree = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 加载角色已分配的菜单
|
||||
const loadRoleMenus = async (roleId) => {
|
||||
// 过滤菜单树,只保留菜单项(type=0,1),移除按钮(type=2)
|
||||
const filterMenuTree = (tree) => {
|
||||
return tree.map(node => {
|
||||
const filteredNode = { ...node };
|
||||
|
||||
// 如果当前节点是按钮(type=2),返回null(会被过滤掉)
|
||||
if (node.type === 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果有子节点,递归过滤
|
||||
if (node.children && node.children.length > 0) {
|
||||
const filteredChildren = filterMenuTree(node.children).filter(child => child !== null);
|
||||
filteredNode.children = filteredChildren;
|
||||
}
|
||||
|
||||
return filteredNode;
|
||||
}).filter(node => node !== null);
|
||||
};
|
||||
|
||||
// 加载用户已分配的菜单(只加载菜单权限,不加载按钮权限)
|
||||
const loadUserMenus = async (userId) => {
|
||||
try {
|
||||
const res = await getRoleMenuIds(roleId);
|
||||
const res = await getUserMenuIds(userId);
|
||||
if (res.code === 200) {
|
||||
checkedMenuIds.value = res.data || [];
|
||||
const allMenuIds = res.data || [];
|
||||
|
||||
// 过滤掉按钮权限,只保留菜单权限
|
||||
const menuOnlyIds = await filterMenuOnlyIds(allMenuIds);
|
||||
checkedMenuIds.value = menuOnlyIds;
|
||||
|
||||
console.log('=== 菜单权限管理 - 过滤后的菜单权限 ===', {
|
||||
userId: userId,
|
||||
allMenuIds: allMenuIds,
|
||||
menuOnlyIds: menuOnlyIds
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
if (menuTreeRef.value) {
|
||||
@@ -232,8 +297,33 @@ const loadRoleMenus = async (roleId) => {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载角色菜单失败:', error);
|
||||
ElMessage.error('加载角色菜单失败');
|
||||
console.error('加载用户菜单失败:', error);
|
||||
ElMessage.error('加载用户菜单失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 过滤菜单ID列表,只保留菜单项(type=0,1),移除按钮(type=2)
|
||||
const filterMenuOnlyIds = async (menuIds) => {
|
||||
try {
|
||||
// 获取所有菜单信息
|
||||
const menuListRes = await getMenuList();
|
||||
if (menuListRes.code !== 200) {
|
||||
return menuIds; // 如果获取失败,返回原始列表
|
||||
}
|
||||
|
||||
const allMenus = menuListRes.data || [];
|
||||
const menuMap = new Map(allMenus.map(menu => [menu.id, menu]));
|
||||
|
||||
// 过滤掉按钮权限
|
||||
const menuOnlyIds = menuIds.filter(id => {
|
||||
const menu = menuMap.get(id);
|
||||
return menu && menu.type !== 2; // 只保留菜单项(type=0,1)
|
||||
});
|
||||
|
||||
return menuOnlyIds;
|
||||
} catch (error) {
|
||||
console.error('过滤菜单权限失败:', error);
|
||||
return menuIds; // 如果过滤失败,返回原始列表
|
||||
}
|
||||
};
|
||||
|
||||
@@ -247,27 +337,64 @@ const handleCurrentChange = (page) => {
|
||||
pagination.currentPage = page;
|
||||
};
|
||||
|
||||
// 保存菜单权限
|
||||
// 保存菜单权限(只保存菜单权限,不保存按钮权限)
|
||||
const handleSaveMenuPermissions = async () => {
|
||||
if (!currentRole.value) {
|
||||
if (!currentUser.value) {
|
||||
ElMessage.warning('请先选择用户');
|
||||
return;
|
||||
}
|
||||
|
||||
// 确认对话框,让用户明确知道影响范围
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`您即将为用户 ${currentUser.value.name} (ID: ${currentUser.value.id}) 设置菜单权限。\n\n这将只影响当前选择的用户。\n\n确定要继续吗?`,
|
||||
'确认菜单权限修改',
|
||||
{
|
||||
confirmButtonText: '确定修改',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
dangerouslyUseHTMLString: false
|
||||
}
|
||||
);
|
||||
} catch {
|
||||
// 用户取消操作
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取选中的节点(包括半选中的父节点)
|
||||
const checkedKeys = menuTreeRef.value.getCheckedKeys();
|
||||
const halfCheckedKeys = menuTreeRef.value.getHalfCheckedKeys();
|
||||
const allKeys = [...checkedKeys, ...halfCheckedKeys];
|
||||
|
||||
// 过滤掉按钮权限,只保留菜单权限
|
||||
const menuOnlyIds = await filterMenuOnlyIds(allKeys);
|
||||
|
||||
console.log('=== 保存菜单权限 ===', {
|
||||
user: currentUser.value,
|
||||
allKeys: allKeys,
|
||||
menuOnlyIds: menuOnlyIds
|
||||
});
|
||||
|
||||
saveLoading.value = true;
|
||||
try {
|
||||
const res = await assignRoleMenus({
|
||||
roleId: currentRole.value.roleId,
|
||||
menuIds: allKeys,
|
||||
const res = await assignUserMenus({
|
||||
userId: currentUser.value.id,
|
||||
menuIds: menuOnlyIds, // 只保存菜单权限
|
||||
});
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('菜单权限保存成功');
|
||||
ElMessage.success(`用户 ${currentUser.value.name} 的菜单权限保存成功,共保存 ${menuOnlyIds.length} 个菜单权限。`);
|
||||
|
||||
// 权限保存成功后,刷新权限数据
|
||||
try {
|
||||
const permissionStore = usePermissionStore();
|
||||
await permissionStore.refreshPermissions();
|
||||
ElMessage.success('权限已保存并刷新成功!');
|
||||
console.log('权限数据已刷新');
|
||||
} catch (error) {
|
||||
console.error('刷新权限失败:', error);
|
||||
ElMessage.warning('权限已保存,但刷新失败,请手动刷新页面');
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败');
|
||||
}
|
||||
@@ -279,9 +406,9 @@ const handleSaveMenuPermissions = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 一键分配全部权限
|
||||
// 一键分配全部菜单权限(只分配菜单权限,不分配按钮权限)
|
||||
const handleQuickAssignAll = async () => {
|
||||
if (!currentRole.value) {
|
||||
if (!currentUser.value) {
|
||||
ElMessage.warning('请先选择用户');
|
||||
return;
|
||||
}
|
||||
@@ -289,10 +416,10 @@ const handleQuickAssignAll = async () => {
|
||||
try {
|
||||
// 确认操作
|
||||
const confirmed = await ElMessageBox.confirm(
|
||||
`确定要为用户 ${currentRole.value.name} (${currentRole.value.mobile}) 分配所有菜单权限吗?`,
|
||||
'确认分配权限',
|
||||
`您即将为用户 ${currentUser.value.name} (ID: ${currentUser.value.id}) 分配所有菜单权限。\n\n这将只影响当前选择的用户。\n\n注意:此操作只分配菜单权限,按钮权限请在"操作权限管理"页面中设置。`,
|
||||
'确认分配菜单权限',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
confirmButtonText: '确定分配',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
@@ -311,38 +438,111 @@ const handleQuickAssignAll = async () => {
|
||||
}
|
||||
|
||||
const allMenus = menuListRes.data || [];
|
||||
const allMenuIds = allMenus.map(menu => menu.id);
|
||||
|
||||
// 过滤掉按钮权限,只保留菜单权限
|
||||
const menuOnlyMenus = allMenus.filter(menu => menu.type !== 2);
|
||||
const menuOnlyIds = menuOnlyMenus.map(menu => menu.id);
|
||||
|
||||
console.log('=== 一键分配全部权限 ===', {
|
||||
user: currentRole.value,
|
||||
console.log('=== 一键分配全部菜单权限 ===', {
|
||||
user: currentUser.value,
|
||||
totalMenus: allMenus.length,
|
||||
menuIds: allMenuIds
|
||||
menuOnlyMenus: menuOnlyMenus.length,
|
||||
menuOnlyIds: menuOnlyIds
|
||||
});
|
||||
|
||||
// 分配所有菜单权限
|
||||
const res = await assignRoleMenus({
|
||||
roleId: currentRole.value.roleId,
|
||||
menuIds: allMenuIds,
|
||||
const res = await assignUserMenus({
|
||||
userId: currentUser.value.id,
|
||||
menuIds: menuOnlyIds,
|
||||
});
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success(`成功为用户 ${currentRole.value.name} 分配了 ${allMenuIds.length} 个菜单权限`);
|
||||
ElMessage.success(`成功为用户 ${currentUser.value.name} 分配了 ${menuOnlyIds.length} 个菜单权限。`);
|
||||
|
||||
// 重新加载权限显示
|
||||
await loadRoleMenus(currentRole.value.roleId);
|
||||
await loadUserMenus(currentUser.value.id);
|
||||
|
||||
// 权限保存成功后,刷新权限数据
|
||||
try {
|
||||
const permissionStore = usePermissionStore();
|
||||
await permissionStore.refreshPermissions();
|
||||
ElMessage.success('权限已保存并刷新成功!');
|
||||
console.log('权限数据已刷新');
|
||||
} catch (error) {
|
||||
console.error('刷新权限失败:', error);
|
||||
ElMessage.warning('权限已保存,但刷新失败,请手动刷新页面');
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.msg || '分配失败');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('一键分配权限失败:', error);
|
||||
console.error('一键分配菜单权限失败:', error);
|
||||
ElMessage.error(`分配失败: ${error.message || error}`);
|
||||
}
|
||||
} finally {
|
||||
quickAssignLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 清空用户权限
|
||||
const handleClearUserPermissions = async () => {
|
||||
if (!currentUser.value) {
|
||||
ElMessage.warning('请先选择用户');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 确认操作
|
||||
const confirmed = await ElMessageBox.confirm(
|
||||
`您即将清空用户 ${currentUser.value.name} (ID: ${currentUser.value.id}) 的所有权限。\n\n清空后,该用户将无法访问任何菜单。\n\n确定要继续吗?`,
|
||||
'确认清空用户权限',
|
||||
{
|
||||
confirmButtonText: '确定清空',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
);
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearLoading.value = true;
|
||||
|
||||
// 清空用户权限
|
||||
const res = await clearUserMenus(currentUser.value.id);
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success(`用户 ${currentUser.value.name} 的权限已清空!`);
|
||||
|
||||
// 重新加载权限显示
|
||||
await loadUserMenus(currentUser.value.id);
|
||||
|
||||
// 权限清空后,刷新权限数据
|
||||
try {
|
||||
const permissionStore = usePermissionStore();
|
||||
await permissionStore.refreshPermissions();
|
||||
ElMessage.success('权限已清空并刷新成功!');
|
||||
console.log('权限数据已刷新');
|
||||
} catch (error) {
|
||||
console.error('刷新权限失败:', error);
|
||||
ElMessage.warning('权限已清空,但刷新失败,请手动刷新页面');
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.msg || '清空失败');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('清空用户权限失败:', error);
|
||||
ElMessage.error(`清空失败: ${error.message || error}`);
|
||||
}
|
||||
} finally {
|
||||
clearLoading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -350,7 +550,7 @@ const handleQuickAssignAll = async () => {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.role-card,
|
||||
.user-card,
|
||||
.permission-card {
|
||||
border-radius: 8px;
|
||||
min-height: 600px;
|
||||
|
||||
@@ -3,23 +3,27 @@
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧:用户列表 -->
|
||||
<el-col :span="8">
|
||||
<el-card class="role-card">
|
||||
<el-card class="user-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">用户列表</span>
|
||||
<el-tag type="success" size="small" style="margin-left: 10px;">
|
||||
用户专属权限
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
:data="paginatedRoleList"
|
||||
:data="paginatedUserList"
|
||||
highlight-current-row
|
||||
@current-change="handleRoleChange"
|
||||
v-loading="roleLoading"
|
||||
@current-change="handleUserChange"
|
||||
v-loading="userLoading"
|
||||
style="width: 100%"
|
||||
max-height="500"
|
||||
>
|
||||
<el-table-column prop="name" label="用户名称" width="120" />
|
||||
<el-table-column prop="mobile" label="手机号" />
|
||||
<el-table-column prop="roleId" label="角色ID" width="80" />
|
||||
</el-table>
|
||||
|
||||
<!-- 分页器 -->
|
||||
@@ -28,7 +32,7 @@
|
||||
v-model:current-page="pagination.currentPage"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="roleList.length"
|
||||
:total="userList.length"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
@@ -37,39 +41,62 @@
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧:操作权限分配 -->
|
||||
<!-- 右侧:用户权限分配 -->
|
||||
<el-col :span="16">
|
||||
<el-card class="permission-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
{{ currentRole ? `${currentRole.name} - 操作权限分配` : '请选择角色' }}
|
||||
{{ currentUser ? `${currentUser.name} - 操作权限分配` : '请选择用户' }}
|
||||
</span>
|
||||
<el-tag type="success" size="small" style="margin-left: 10px;">
|
||||
用户ID: {{ currentUser ? currentUser.id : '-' }}
|
||||
</el-tag>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
v-hasPermi="['permission:operation:assign']"
|
||||
@click="handleSavePermissions"
|
||||
:disabled="!currentRole"
|
||||
@click="handleSaveUserPermissions"
|
||||
:disabled="!currentUser"
|
||||
:loading="saveLoading"
|
||||
style="margin-left: 10px;"
|
||||
>
|
||||
保存操作权限
|
||||
保存用户权限
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
v-hasPermi="['permission:operation:assign']"
|
||||
@click="handleClearUserPermissions"
|
||||
:disabled="!currentUser"
|
||||
:loading="clearLoading"
|
||||
style="margin-left: 10px;"
|
||||
>
|
||||
清空用户权限
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="currentRole" v-loading="permissionLoading">
|
||||
<div v-if="currentUser" v-loading="permissionLoading">
|
||||
<el-alert
|
||||
title="提示"
|
||||
title="用户专属操作权限管理"
|
||||
type="info"
|
||||
:closable="false"
|
||||
style="margin-bottom: 20px"
|
||||
>
|
||||
勾选操作权限后,该角色可以执行相应的按钮操作(新增、编辑、删除等)
|
||||
<template #default>
|
||||
<div>
|
||||
<p><strong>当前系统使用基于用户的操作权限管理</strong></p>
|
||||
<p>• 修改权限只影响当前选择的用户</p>
|
||||
<p>• 当前用户: <strong>{{ currentUser.name }}</strong> (ID: {{ currentUser.id }})</p>
|
||||
<p>• 角色ID: <strong>{{ currentUser.roleId }}</strong></p>
|
||||
<p>• 勾选操作权限后,该用户可以执行相应的按钮操作(新增、编辑、删除等)</p>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<el-tree
|
||||
ref="permissionTreeRef"
|
||||
ref="userPermissionTreeRef"
|
||||
:data="permissionTree"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
@@ -82,39 +109,11 @@
|
||||
<el-icon v-if="data.icon" style="margin-right: 5px">
|
||||
<component :is="data.icon" />
|
||||
</el-icon>
|
||||
<span>{{ node.label }}</span>
|
||||
<el-tag
|
||||
v-if="data.type === 1"
|
||||
type="info"
|
||||
size="small"
|
||||
style="margin-left: 10px"
|
||||
>
|
||||
菜单
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="data.type === 2"
|
||||
type="warning"
|
||||
size="small"
|
||||
style="margin-left: 10px"
|
||||
>
|
||||
按钮
|
||||
</el-tag>
|
||||
<span
|
||||
v-if="data.authority"
|
||||
style="margin-left: 10px; color: #999; font-size: 12px"
|
||||
>
|
||||
{{ data.authority }}
|
||||
</span>
|
||||
<span>{{ data.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
|
||||
<el-empty
|
||||
v-else
|
||||
description="请从左侧选择一个角色,为其分配操作权限"
|
||||
:image-size="100"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -123,18 +122,20 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, nextTick, computed } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import {
|
||||
getUserList,
|
||||
getMenuTree,
|
||||
getRoleMenuIds,
|
||||
assignRoleMenus,
|
||||
getUserMenuIds,
|
||||
assignUserMenus,
|
||||
clearUserMenus,
|
||||
} from '@/api/permission.js';
|
||||
import usePermissionStore from '@/store/permission.js';
|
||||
|
||||
// 用户相关数据
|
||||
const roleLoading = ref(false);
|
||||
const roleList = ref([]);
|
||||
const currentRole = ref(null);
|
||||
const userLoading = ref(false);
|
||||
const userList = ref([]);
|
||||
const currentUser = ref(null);
|
||||
|
||||
// 分页数据
|
||||
const pagination = reactive({
|
||||
@@ -143,18 +144,18 @@ const pagination = reactive({
|
||||
});
|
||||
|
||||
// 计算分页后的用户列表
|
||||
const paginatedRoleList = computed(() => {
|
||||
const paginatedUserList = computed(() => {
|
||||
const start = (pagination.currentPage - 1) * pagination.pageSize;
|
||||
const end = start + pagination.pageSize;
|
||||
return roleList.value.slice(start, end);
|
||||
return userList.value.slice(start, end);
|
||||
});
|
||||
|
||||
// 权限相关数据
|
||||
const permissionLoading = ref(false);
|
||||
const saveLoading = ref(false);
|
||||
const clearLoading = ref(false);
|
||||
const permissionTree = ref([]);
|
||||
const permissionTreeRef = ref(null);
|
||||
const checkedPermissionIds = ref([]);
|
||||
const userPermissionTreeRef = ref(null);
|
||||
const treeProps = {
|
||||
children: 'children',
|
||||
label: 'label',
|
||||
@@ -162,46 +163,194 @@ const treeProps = {
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
loadRoleList();
|
||||
loadUserList();
|
||||
});
|
||||
|
||||
// 加载用户列表
|
||||
const loadRoleList = async () => {
|
||||
roleLoading.value = true;
|
||||
const loadUserList = async () => {
|
||||
userLoading.value = true;
|
||||
try {
|
||||
const res = await getUserList();
|
||||
if (res.code === 200) {
|
||||
roleList.value = res.data || [];
|
||||
userList.value = res.data || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载用户列表失败:', error);
|
||||
ElMessage.error('加载用户列表失败');
|
||||
} finally {
|
||||
roleLoading.value = false;
|
||||
userLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 角色选择改变
|
||||
const handleRoleChange = async (row) => {
|
||||
// 用户选择改变
|
||||
const handleUserChange = async (row) => {
|
||||
if (!row) return;
|
||||
|
||||
console.log('=== 用户选择改变 ===');
|
||||
console.log('=== 操作权限管理 - 用户选择改变 ===');
|
||||
console.log('选择的用户:', row);
|
||||
console.log('用户roleId:', row.roleId);
|
||||
console.log('用户ID:', row.id);
|
||||
|
||||
currentRole.value = row;
|
||||
currentUser.value = row;
|
||||
await loadPermissionTree();
|
||||
await loadRolePermissions(row.roleId);
|
||||
await loadUserPermissions(row.id);
|
||||
};
|
||||
|
||||
// 分页处理
|
||||
const handleSizeChange = (size) => {
|
||||
pagination.pageSize = size;
|
||||
pagination.currentPage = 1;
|
||||
// 加载用户已分配的权限
|
||||
const loadUserPermissions = async (userId) => {
|
||||
console.log('=== 加载用户权限 ===');
|
||||
console.log('userId:', userId);
|
||||
|
||||
try {
|
||||
const res = await getUserMenuIds(userId);
|
||||
console.log('用户权限API响应:', res);
|
||||
|
||||
if (res.code === 200) {
|
||||
const menuIds = res.data || [];
|
||||
console.log('已分配的用户权限IDs:', menuIds);
|
||||
|
||||
// 设置权限树选中状态
|
||||
await nextTick();
|
||||
if (userPermissionTreeRef.value) {
|
||||
userPermissionTreeRef.value.setCheckedKeys(menuIds);
|
||||
console.log('用户权限树已设置选中状态');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载用户权限失败:', error);
|
||||
ElMessage.error('加载用户权限失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCurrentChange = (page) => {
|
||||
pagination.currentPage = page;
|
||||
// 保存用户权限
|
||||
const handleSaveUserPermissions = async () => {
|
||||
if (!currentUser.value) {
|
||||
ElMessage.warning('请先选择用户');
|
||||
return;
|
||||
}
|
||||
|
||||
// 确认对话框
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`您即将为用户 ${currentUser.value.name} (ID: ${currentUser.value.id}) 设置操作权限。\n\n这将只影响当前选择的用户。\n\n确定要继续吗?`,
|
||||
'确认用户权限修改',
|
||||
{
|
||||
confirmButtonText: '确定修改',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
dangerouslyUseHTMLString: false
|
||||
}
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('=== 保存用户权限 ===');
|
||||
console.log('当前用户:', currentUser.value);
|
||||
|
||||
// 获取选中的节点
|
||||
const checkedKeys = userPermissionTreeRef.value.getCheckedKeys();
|
||||
const halfCheckedKeys = userPermissionTreeRef.value.getHalfCheckedKeys();
|
||||
const allKeys = [...checkedKeys, ...halfCheckedKeys];
|
||||
|
||||
console.log('选中的权限IDs:', checkedKeys);
|
||||
console.log('半选中的权限IDs:', halfCheckedKeys);
|
||||
console.log('所有权限IDs:', allKeys);
|
||||
|
||||
const saveData = {
|
||||
userId: currentUser.value.id,
|
||||
menuIds: allKeys,
|
||||
};
|
||||
console.log('保存数据:', saveData);
|
||||
|
||||
saveLoading.value = true;
|
||||
try {
|
||||
const res = await assignUserMenus(saveData);
|
||||
console.log('保存API响应:', res);
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success(`用户 ${currentUser.value.name} 的操作权限保存成功!`);
|
||||
console.log('用户权限保存成功');
|
||||
|
||||
// 权限保存成功后,刷新权限数据
|
||||
try {
|
||||
const permissionStore = usePermissionStore();
|
||||
await permissionStore.refreshPermissions();
|
||||
ElMessage.success('权限已保存并刷新成功!');
|
||||
console.log('权限数据已刷新');
|
||||
} catch (error) {
|
||||
console.error('刷新权限失败:', error);
|
||||
ElMessage.warning('权限已保存,但刷新失败,请手动刷新页面');
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败');
|
||||
console.error('用户权限保存失败:', res);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存用户权限失败:', error);
|
||||
ElMessage.error('保存失败');
|
||||
} finally {
|
||||
saveLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 清空用户权限
|
||||
const handleClearUserPermissions = async () => {
|
||||
if (!currentUser.value) {
|
||||
ElMessage.warning('请先选择用户');
|
||||
return;
|
||||
}
|
||||
|
||||
// 确认对话框
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`您即将清空用户 ${currentUser.value.name} (ID: ${currentUser.value.id}) 的所有权限。\n\n清空后,该用户将无法执行任何操作。\n\n确定要继续吗?`,
|
||||
'确认清空用户权限',
|
||||
{
|
||||
confirmButtonText: '确定清空',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
dangerouslyUseHTMLString: false
|
||||
}
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('=== 清空用户权限 ===');
|
||||
console.log('当前用户:', currentUser.value);
|
||||
|
||||
clearLoading.value = true;
|
||||
try {
|
||||
const res = await clearUserMenus(currentUser.value.id);
|
||||
console.log('清空API响应:', res);
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success(`用户 ${currentUser.value.name} 的权限已清空!`);
|
||||
console.log('用户权限清空成功');
|
||||
|
||||
// 重新加载用户权限(显示空权限)
|
||||
await loadUserPermissions(currentUser.value.id);
|
||||
|
||||
// 权限清空后,刷新权限数据
|
||||
try {
|
||||
const permissionStore = usePermissionStore();
|
||||
await permissionStore.refreshPermissions();
|
||||
ElMessage.success('权限已清空并刷新成功!');
|
||||
console.log('权限数据已刷新');
|
||||
} catch (error) {
|
||||
console.error('刷新权限失败:', error);
|
||||
ElMessage.warning('权限已清空,但刷新失败,请手动刷新页面');
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.msg || '清空失败');
|
||||
console.error('用户权限清空失败:', res);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('清空用户权限失败:', error);
|
||||
ElMessage.error('清空失败');
|
||||
} finally {
|
||||
clearLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 加载权限树(包含所有菜单和按钮)
|
||||
@@ -220,88 +369,14 @@ const loadPermissionTree = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 加载角色已分配的权限
|
||||
const loadRolePermissions = async (roleId) => {
|
||||
console.log('=== 加载角色权限 ===');
|
||||
console.log('roleId:', roleId);
|
||||
|
||||
try {
|
||||
const res = await getRoleMenuIds(roleId);
|
||||
console.log('权限API响应:', res);
|
||||
|
||||
if (res.code === 200) {
|
||||
checkedPermissionIds.value = res.data || [];
|
||||
console.log('已分配的权限IDs:', checkedPermissionIds.value);
|
||||
|
||||
await nextTick();
|
||||
if (permissionTreeRef.value) {
|
||||
permissionTreeRef.value.setCheckedKeys(checkedPermissionIds.value);
|
||||
console.log('权限树已设置选中状态');
|
||||
|
||||
// 验证权限树的实际选中状态
|
||||
setTimeout(() => {
|
||||
const actualCheckedKeys = permissionTreeRef.value.getCheckedKeys();
|
||||
const actualHalfCheckedKeys = permissionTreeRef.value.getHalfCheckedKeys();
|
||||
console.log('=== 权限树实际选中状态验证 ===');
|
||||
console.log('实际选中的权限IDs:', actualCheckedKeys);
|
||||
console.log('实际半选中的权限IDs:', actualHalfCheckedKeys);
|
||||
console.log('期望的权限IDs:', checkedPermissionIds.value);
|
||||
console.log('选中状态是否一致:', JSON.stringify(actualCheckedKeys.sort()) === JSON.stringify(checkedPermissionIds.value.sort()));
|
||||
}, 100);
|
||||
}
|
||||
} else {
|
||||
console.error('权限API返回错误:', res);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载角色权限失败:', error);
|
||||
ElMessage.error('加载角色权限失败');
|
||||
}
|
||||
// 分页处理
|
||||
const handleSizeChange = (size) => {
|
||||
pagination.pageSize = size;
|
||||
pagination.currentPage = 1;
|
||||
};
|
||||
|
||||
// 保存操作权限
|
||||
const handleSavePermissions = async () => {
|
||||
if (!currentRole.value) {
|
||||
ElMessage.warning('请先选择用户');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('=== 保存操作权限 ===');
|
||||
console.log('当前用户:', currentRole.value);
|
||||
console.log('用户roleId:', currentRole.value.roleId);
|
||||
|
||||
// 获取选中的节点(包括半选中的父节点)
|
||||
const checkedKeys = permissionTreeRef.value.getCheckedKeys();
|
||||
const halfCheckedKeys = permissionTreeRef.value.getHalfCheckedKeys();
|
||||
const allKeys = [...checkedKeys, ...halfCheckedKeys];
|
||||
|
||||
console.log('选中的权限IDs:', checkedKeys);
|
||||
console.log('半选中的权限IDs:', halfCheckedKeys);
|
||||
console.log('所有权限IDs:', allKeys);
|
||||
|
||||
const saveData = {
|
||||
roleId: currentRole.value.roleId,
|
||||
menuIds: allKeys,
|
||||
};
|
||||
console.log('保存数据:', saveData);
|
||||
|
||||
saveLoading.value = true;
|
||||
try {
|
||||
const res = await assignRoleMenus(saveData);
|
||||
console.log('保存API响应:', res);
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('操作权限保存成功');
|
||||
console.log('权限保存成功');
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败');
|
||||
console.error('权限保存失败:', res);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存操作权限失败:', error);
|
||||
ElMessage.error('保存失败');
|
||||
} finally {
|
||||
saveLoading.value = false;
|
||||
}
|
||||
const handleCurrentChange = (page) => {
|
||||
pagination.currentPage = page;
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -310,30 +385,24 @@ const handleSavePermissions = async () => {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.role-card,
|
||||
.permission-card {
|
||||
border-radius: 8px;
|
||||
min-height: 600px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__content) {
|
||||
height: 36px;
|
||||
.user-card,
|
||||
.permission-card {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -14,7 +14,8 @@
|
||||
<el-select v-model="data.deviceType" placeholder="选择设备类型" style="width: 150px; margin-right: 10px;" @change="onDeviceTypeChange">
|
||||
<el-option label="全部设备" value="" />
|
||||
<el-option label="智能耳标" value="2" />
|
||||
<el-option label="智能项圈" value="3" />
|
||||
<el-option label="智能项圈" value="4" />
|
||||
<el-option label="智能主机" value="1" />
|
||||
</el-select>
|
||||
<el-input v-model="data.deviceId" placeholder="请输入设备编号" style="width: 200px" />
|
||||
<div class="search-right">
|
||||
@@ -35,9 +36,10 @@
|
||||
<el-table-column label="设备编号" prop="deviceId"></el-table-column>
|
||||
<el-table-column label="设备类型" prop="deviceType">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.deviceType === 2" type="success">智能耳标</el-tag>
|
||||
<el-tag v-else-if="scope.row.deviceType === 3" type="primary">智能项圈</el-tag>
|
||||
<el-tag v-else type="info">未知类型</el-tag>
|
||||
<el-tag v-if="scope.row.deviceType === 1" type="info">智能主机</el-tag>
|
||||
<el-tag v-else-if="scope.row.deviceType === 2" type="success">智能耳标</el-tag>
|
||||
<el-tag v-else-if="scope.row.deviceType === 4" type="primary">智能项圈</el-tag>
|
||||
<el-tag v-else type="warning">未知类型</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备状态" prop="status">
|
||||
@@ -58,7 +60,8 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { deviceList, deviceAssign } from '@/api/shipping.js';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { iotDeviceAssignableList, iotDeviceAssign } from '@/api/hardware.js';
|
||||
import request from '@/utils/request.js';
|
||||
|
||||
const emits = defineEmits();
|
||||
@@ -72,7 +75,7 @@ const data = reactive({
|
||||
ratedQuantity: 0, // 装车数量
|
||||
selectTotal: 0,
|
||||
deviceId: '',
|
||||
deviceType: '', // 设备类型:2=智能耳标,3=智能项圈
|
||||
deviceType: '', // 设备类型:1=智能主机,2=智能耳标,4=智能项圈
|
||||
deviceIds: [],
|
||||
licensePlate: '', // 车牌号
|
||||
});
|
||||
@@ -101,119 +104,68 @@ const searchClick = () => {
|
||||
};
|
||||
const getDataList = async () => {
|
||||
data.dataListLoading = true;
|
||||
const allDevices = [];
|
||||
|
||||
try {
|
||||
console.log('开始查询设备列表...');
|
||||
console.log('开始查询可分配设备列表...');
|
||||
|
||||
// 查询智能耳标(如果选择了全部设备或智能耳标)
|
||||
if (!data.deviceType || data.deviceType === '2') {
|
||||
const earTagParams = {
|
||||
pageNum: form.pageNum,
|
||||
pageSize: form.pageSize,
|
||||
deviceId: data.deviceId || '',
|
||||
};
|
||||
const params = {
|
||||
pageNum: form.pageNum,
|
||||
pageSize: form.pageSize,
|
||||
deviceId: data.deviceId || '',
|
||||
deviceType: data.deviceType ? parseInt(data.deviceType) : null,
|
||||
};
|
||||
|
||||
console.log('查询参数:', params);
|
||||
|
||||
const res = await iotDeviceAssignableList(params);
|
||||
console.log('API返回结果:', res);
|
||||
|
||||
if (res.code === 200) {
|
||||
const rawData = res.data?.rows || [];
|
||||
const total = res.data?.total || 0;
|
||||
|
||||
console.log('查询智能耳标参数:', earTagParams);
|
||||
|
||||
try {
|
||||
const earTagRes = await request({
|
||||
url: '/jbqClient/list',
|
||||
method: 'POST',
|
||||
data: earTagParams,
|
||||
});
|
||||
console.log('智能耳标查询结果:', earTagRes);
|
||||
// 处理数据:添加设备类型名称
|
||||
data.rows = rawData.map(item => {
|
||||
const processedItem = { ...item };
|
||||
|
||||
if (earTagRes.code === 200 && earTagRes.data && earTagRes.data.rows) {
|
||||
// 过滤出未分配的设备
|
||||
const unassignedEarTags = earTagRes.data.rows.filter(item => {
|
||||
return !item.deliveryNumber ||
|
||||
item.deliveryNumber === '' ||
|
||||
item.deliveryNumber === '未分配' ||
|
||||
item.deliveryNumber === 'δ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>';
|
||||
});
|
||||
|
||||
console.log('智能耳标原始数量:', earTagRes.data.rows.length);
|
||||
console.log('智能耳标未分配数量:', unassignedEarTags.length);
|
||||
|
||||
unassignedEarTags.forEach(item => {
|
||||
allDevices.push({
|
||||
...item,
|
||||
deviceType: 2,
|
||||
deviceTypeName: '智能耳标',
|
||||
deliveryNumber: '未分配',
|
||||
licensePlate: '未分配'
|
||||
});
|
||||
});
|
||||
// 根据设备类型自动添加设备类型名称
|
||||
switch (item.deviceType) {
|
||||
case 1: // 主机
|
||||
processedItem.deviceTypeName = '智能主机';
|
||||
break;
|
||||
case 2: // 耳标
|
||||
processedItem.deviceTypeName = '智能耳标';
|
||||
break;
|
||||
case 4: // 项圈
|
||||
processedItem.deviceTypeName = '智能项圈';
|
||||
break;
|
||||
default:
|
||||
processedItem.deviceTypeName = '未知设备';
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('智能耳标查询失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 查询智能项圈(如果选择了全部设备或智能项圈)
|
||||
if (!data.deviceType || data.deviceType === '3') {
|
||||
const collarParams = {
|
||||
pageNum: form.pageNum,
|
||||
pageSize: form.pageSize,
|
||||
sn: data.deviceId || '',
|
||||
};
|
||||
|
||||
console.log('查询智能项圈参数:', collarParams);
|
||||
|
||||
try {
|
||||
const collarRes = await request({
|
||||
url: '/xqClient/list',
|
||||
method: 'POST',
|
||||
data: collarParams,
|
||||
});
|
||||
console.log('智能项圈查询结果:', collarRes);
|
||||
|
||||
if (collarRes.code === 200 && collarRes.data && collarRes.data.rows) {
|
||||
// 过滤出未分配的设备
|
||||
const unassignedCollars = collarRes.data.rows.filter(item => {
|
||||
return !item.delivery_number ||
|
||||
item.delivery_number === '' ||
|
||||
item.delivery_number === '未分配' ||
|
||||
item.delivery_number === 'δ<><CEB4><EFBFBD><EFBFBD>';
|
||||
});
|
||||
|
||||
console.log('智能项圈原始数量:', collarRes.data.rows.length);
|
||||
console.log('智能项圈未分配数量:', unassignedCollars.length);
|
||||
|
||||
unassignedCollars.forEach(item => {
|
||||
allDevices.push({
|
||||
...item,
|
||||
deviceId: item.sn || item.deviceId, // 优先使用sn字段
|
||||
deviceType: 3,
|
||||
deviceTypeName: '智能项圈',
|
||||
deliveryNumber: '未分配',
|
||||
licensePlate: '未分配'
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('智能项圈查询失败:', error);
|
||||
}
|
||||
console.log(`处理设备 ${item.deviceId}:`, {
|
||||
deviceType: item.deviceType,
|
||||
deviceTypeName: processedItem.deviceTypeName,
|
||||
isAssigned: item.isAssigned
|
||||
});
|
||||
|
||||
return processedItem;
|
||||
});
|
||||
|
||||
data.total = total;
|
||||
data.dataListLoading = false;
|
||||
|
||||
console.log('最终可分配设备列表:', data.rows);
|
||||
console.log('总设备数量:', data.total);
|
||||
|
||||
} else {
|
||||
console.error('API返回错误:', res);
|
||||
data.dataListLoading = false;
|
||||
data.rows = [];
|
||||
data.total = 0;
|
||||
}
|
||||
|
||||
// 去重处理,确保deviceId唯一
|
||||
const uniqueRows = [];
|
||||
const deviceIdSet = new Set();
|
||||
allDevices.forEach(item => {
|
||||
if (!deviceIdSet.has(item.deviceId)) {
|
||||
deviceIdSet.add(item.deviceId);
|
||||
uniqueRows.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
data.rows = uniqueRows;
|
||||
data.total = uniqueRows.length;
|
||||
data.dataListLoading = false;
|
||||
|
||||
console.log('最终未分配设备列表:', uniqueRows);
|
||||
console.log('总设备数量:', uniqueRows.length);
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询设备列表失败:', error);
|
||||
data.dataListLoading = false;
|
||||
@@ -237,15 +189,23 @@ const onClickSave = () => {
|
||||
ElMessage.error('请选择设备编号');
|
||||
return;
|
||||
}
|
||||
|
||||
// 提取设备ID列表
|
||||
const deviceIdList = data.deviceIds.map(item => item.deviceId);
|
||||
|
||||
const params = {
|
||||
deliveryId: data.deliveryId,
|
||||
deviceIds: data.deviceIds,
|
||||
licensePlate: data.licensePlate, // 添加车牌号
|
||||
deviceIds: deviceIdList,
|
||||
deliveryId: parseInt(data.deliveryId), // 装车订单ID
|
||||
carNumber: data.licensePlate, // 车牌号
|
||||
};
|
||||
|
||||
console.log('设备分配参数:', params);
|
||||
|
||||
data.saveLoading = true;
|
||||
deviceAssign(params)
|
||||
iotDeviceAssign(params)
|
||||
.then((res) => {
|
||||
data.saveLoading = false;
|
||||
console.log('设备分配结果:', res);
|
||||
if (res.code === 200) {
|
||||
ElMessage({
|
||||
message: res.msg,
|
||||
@@ -265,7 +225,9 @@ const onClickSave = () => {
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('设备分配失败:', err);
|
||||
data.saveLoading = false;
|
||||
ElMessage.error('设备分配失败');
|
||||
});
|
||||
};
|
||||
const handleClose = () => {
|
||||
|
||||
@@ -13,15 +13,15 @@
|
||||
? '智能耳标'
|
||||
: scope.row.deviceType == '1'
|
||||
? '智能主机'
|
||||
: scope.row.deviceType == '3'
|
||||
: scope.row.deviceType == '4'
|
||||
? '智能项圈'
|
||||
: '--'
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="所属用户" prop="deviceUserName">
|
||||
<el-table-column label="所属租户" prop="tenantName">
|
||||
<template #default="scope">
|
||||
{{ scope.row.deviceUserName || '--' }}
|
||||
{{ scope.row.tenantName || '--' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否佩戴" prop="gpsState">
|
||||
|
||||
517
pc-cattle-transportation/src/views/shipping/shippingList.vue
Normal file
517
pc-cattle-transportation/src/views/shipping/shippingList.vue
Normal file
@@ -0,0 +1,517 @@
|
||||
<template>
|
||||
<div>
|
||||
<base-search :formItemList="formItemList" @search="searchFrom" ref="baseSearchRef"> </base-search>
|
||||
<!-- 横向滚动操作栏 -->
|
||||
<div class="operation-scroll-bar">
|
||||
<div class="operation-scroll-container">
|
||||
<el-button type="primary" v-hasPermi="['delivery:add']" @click="showAddDialog(null)">新增运送清单</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-container">
|
||||
<el-table :data="rows" :key="data.tableKey" border v-loading="data.dataListLoading" element-loading-text="数据加载中..." style="width: 100%">
|
||||
<el-table-column label="运送清单编号" prop="deliveryNumber">
|
||||
<template #default="scope">
|
||||
{{ scope.row.deliveryNumber || '--' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="清单标题" prop="deliveryTitle">
|
||||
<template #default="scope">
|
||||
{{ scope.row.deliveryTitle || '--' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="起始地" prop="startLocation">
|
||||
<template #default="scope">
|
||||
{{ scope.row.startLocation || '--' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="目的地" prop="endLocation">
|
||||
<template #default="scope">
|
||||
{{ scope.row.endLocation || '--' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="车牌号" prop="licensePlate">
|
||||
<template #default="scope">
|
||||
{{ scope.row.licensePlate || '--' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="司机姓名" prop="driverName">
|
||||
<template #default="scope">
|
||||
{{ scope.row.driverName || '--' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="司机手机号" prop="driverMobile">
|
||||
<template #default="scope">
|
||||
{{ scope.row.driverMobile || '--' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="status">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusType(scope.row.status)">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime">
|
||||
<template #default="scope">
|
||||
{{ scope.row.createTime || '--' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" size="small" v-hasPermi="['delivery:view']" @click="showDetailDialog(scope.row)">详情</el-button>
|
||||
<el-button type="warning" size="small" v-hasPermi="['delivery:export']" @click="handleDownload(scope.row)">下载文件</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<el-pagination
|
||||
v-model:current-page="form.pageNum"
|
||||
v-model:page-size="form.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="data.total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 对话框 -->
|
||||
<OrderDialog ref="OrderDialogRef" @success="getDataList" />
|
||||
<LookDialog ref="LookDialogRef" />
|
||||
<DetailDialog ref="DetailDialogRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import baseSearch from '@/components/common/searchCustom/index.vue';
|
||||
import { shippingList, orderDel } from '@/api/shipping.js';
|
||||
import OrderDialog from './orderDialog.vue';
|
||||
import LookDialog from './lookDialog.vue';
|
||||
import DetailDialog from './detailDialog.vue';
|
||||
|
||||
const baseSearchRef = ref();
|
||||
const OrderDialogRef = ref();
|
||||
const LookDialogRef = ref();
|
||||
const DetailDialogRef = ref();
|
||||
|
||||
const formItemList = reactive([
|
||||
{
|
||||
label: '运单号',
|
||||
type: 'input',
|
||||
param: 'deliveryNumber',
|
||||
span: 6,
|
||||
placeholder: '请输入运单号',
|
||||
},
|
||||
{
|
||||
label: '清单标题',
|
||||
type: 'input',
|
||||
param: 'deliveryTitle',
|
||||
span: 6,
|
||||
placeholder: '请输入清单标题',
|
||||
},
|
||||
{
|
||||
label: '目的地',
|
||||
type: 'input',
|
||||
param: 'endLocation',
|
||||
span: 6,
|
||||
placeholder: '请输入目的地',
|
||||
},
|
||||
{
|
||||
label: '车牌号',
|
||||
type: 'input',
|
||||
param: 'licensePlate',
|
||||
span: 6,
|
||||
placeholder: '请输入车牌号',
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
type: 'select',
|
||||
param: 'status',
|
||||
span: 6,
|
||||
placeholder: '请选择状态',
|
||||
selectOptions: [
|
||||
{ text: '待装车', value: 1 },
|
||||
{ text: '已装车/待资金方付款', value: 2 },
|
||||
{ text: '待核验/资金方已付款', value: 3 },
|
||||
{ text: '已核验/待买家付款', value: 4 },
|
||||
{ text: '买家已付款', value: 5 }
|
||||
],
|
||||
labelKey: 'text',
|
||||
valueKey: 'value',
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
type: 'daterange',
|
||||
param: 'createTimeRange',
|
||||
span: 6,
|
||||
startPlaceholder: '开始日期',
|
||||
endPlaceholder: '结束日期',
|
||||
},
|
||||
]);
|
||||
|
||||
const data = reactive({
|
||||
total: 0,
|
||||
dataListLoading: false,
|
||||
tableKey: 0,
|
||||
});
|
||||
|
||||
const rows = ref([]);
|
||||
const form = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const searchFrom = () => {
|
||||
console.log('=== 运送清单搜索功能被触发 ===');
|
||||
form.pageNum = 1;
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 获取运送清单列表
|
||||
const getDataList = () => {
|
||||
data.dataListLoading = true;
|
||||
const searchParams = baseSearchRef.value.penetrateParams();
|
||||
|
||||
const params = {
|
||||
...form,
|
||||
...searchParams,
|
||||
};
|
||||
|
||||
// 处理日期范围参数
|
||||
if (searchParams.createTimeRange && Array.isArray(searchParams.createTimeRange) && searchParams.createTimeRange.length === 2) {
|
||||
params.startTime = searchParams.createTimeRange[0];
|
||||
params.endTime = searchParams.createTimeRange[1];
|
||||
delete params.createTimeRange;
|
||||
}
|
||||
|
||||
console.log('运送清单列表查询参数:', params);
|
||||
shippingList(params)
|
||||
.then((res) => {
|
||||
console.log('运送清单列表返回结果:', res);
|
||||
data.dataListLoading = false;
|
||||
|
||||
if (res.data.rows && res.data.rows.length > 0) {
|
||||
rows.value = res.data.rows;
|
||||
data.total = res.data.total;
|
||||
} else {
|
||||
rows.value = [];
|
||||
data.total = 0;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('运送清单列表查询失败:', error);
|
||||
data.dataListLoading = false;
|
||||
ElMessage.error('查询失败');
|
||||
});
|
||||
};
|
||||
|
||||
// 分页处理
|
||||
const handleSizeChange = (val) => {
|
||||
form.pageSize = val;
|
||||
getDataList();
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
form.pageNum = val;
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 状态相关方法
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
1: '待装车',
|
||||
2: '已装车/待资金方付款',
|
||||
3: '待核验/资金方已付款',
|
||||
4: '已核验/待买家付款',
|
||||
5: '买家已付款'
|
||||
};
|
||||
return statusMap[status] || '未知状态';
|
||||
};
|
||||
|
||||
const getStatusType = (status) => {
|
||||
const typeMap = {
|
||||
1: 'warning',
|
||||
2: 'info',
|
||||
3: 'primary',
|
||||
4: 'success',
|
||||
5: 'success'
|
||||
};
|
||||
return typeMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 对话框方法
|
||||
const showAddDialog = (row) => {
|
||||
OrderDialogRef.value.onShowDialog(row);
|
||||
};
|
||||
|
||||
const showLookDialog = (row) => {
|
||||
LookDialogRef.value.onShowLookDialog(row);
|
||||
};
|
||||
|
||||
const showDetailDialog = (row) => {
|
||||
DetailDialogRef.value.onShowDetailDialog(row);
|
||||
};
|
||||
|
||||
// 下载方法 - 生成牛只验收单
|
||||
const handleDownload = async (row) => {
|
||||
try {
|
||||
// 计算字段
|
||||
const landingWeight = parseFloat(row.landingEntruckWeight || 0);
|
||||
const emptyWeight = parseFloat(row.emptyWeight || 0);
|
||||
const totalWeight = ((landingWeight - emptyWeight) / 2).toFixed(2);
|
||||
const unitPrice = (parseFloat(row.firmPrice || 0) / 2).toFixed(2);
|
||||
const totalAmount = (parseFloat(totalWeight) * parseFloat(unitPrice)).toFixed(2);
|
||||
|
||||
// 准备数据 - 使用回退机制
|
||||
const data = {
|
||||
supplierName: row.supplierName || row.supplierMobile || '',
|
||||
buyerName: row.buyerName || row.buyerMobile || '',
|
||||
startLocation: row.startLocation || '',
|
||||
createTime: row.createTime || '',
|
||||
endLocation: row.endLocation || '',
|
||||
driverName: row.driverName || '',
|
||||
driverMobile: row.driverMobile || '',
|
||||
licensePlate: row.licensePlate || '',
|
||||
ratedQuantity: row.ratedQuantity || '',
|
||||
totalWeight: totalWeight,
|
||||
unitPrice: unitPrice,
|
||||
totalAmount: totalAmount
|
||||
};
|
||||
|
||||
console.log('生成牛只验收单数据:', data);
|
||||
|
||||
// 生成HTML内容
|
||||
const htmlContent = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>牛只发车验收单</title>
|
||||
<style>
|
||||
@media print {
|
||||
body { margin: 0; }
|
||||
.no-print { display: none; }
|
||||
}
|
||||
body {
|
||||
font-family: "Microsoft YaHei", "SimSun", Arial, sans-serif;
|
||||
margin: 20px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 2px solid #000;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.order-number {
|
||||
text-align: right;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.info-grid {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.info-grid td {
|
||||
border: 1px solid #000;
|
||||
padding: 10px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.info-grid .label {
|
||||
background-color: #f5f5f5;
|
||||
font-weight: bold;
|
||||
width: 20%;
|
||||
text-align: center;
|
||||
}
|
||||
.cattle-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.cattle-table th, .cattle-table td {
|
||||
border: 1px solid #000;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.cattle-table th {
|
||||
background-color: #f5f5f5;
|
||||
font-weight: bold;
|
||||
}
|
||||
.signature-section {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.signature-section td {
|
||||
border: 1px solid #000;
|
||||
padding: 10px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.signature-section .label {
|
||||
background-color: #f5f5f5;
|
||||
font-weight: bold;
|
||||
width: 25%;
|
||||
text-align: center;
|
||||
}
|
||||
.print-button {
|
||||
background-color: #409EFF;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.print-button:hover {
|
||||
background-color: #66b1ff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">牛只发车验收单</div>
|
||||
<div class="order-number">订单编号: </div>
|
||||
|
||||
<table class="info-grid">
|
||||
<tr>
|
||||
<td class="label">供货单位</td>
|
||||
<td>${data.supplierName}</td>
|
||||
<td class="label">收货单位</td>
|
||||
<td>${data.buyerName}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">发车地点</td>
|
||||
<td>${data.startLocation}</td>
|
||||
<td class="label">发车时间</td>
|
||||
<td>${data.createTime}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">到达地点</td>
|
||||
<td>${data.endLocation}</td>
|
||||
<td class="label">动物检疫合格证明编号</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">司机姓名及联系方式</td>
|
||||
<td>${data.driverName} ${data.driverMobile}</td>
|
||||
<td class="label">装车车牌号</td>
|
||||
<td>${data.licensePlate}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="cattle-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>活牛品种</th>
|
||||
<th>单只体重范围 (斤)</th>
|
||||
<th>下车总数量 (头)</th>
|
||||
<th>下车总重量 (斤)</th>
|
||||
<th>单价 (元/斤)</th>
|
||||
<th>总金额 (元)</th>
|
||||
<th>备注</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>${data.ratedQuantity}</td>
|
||||
<td>${data.totalWeight}</td>
|
||||
<td>${data.unitPrice}</td>
|
||||
<td>${data.totalAmount}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="signature-section">
|
||||
<tr>
|
||||
<td class="label">已支付货款时间</td>
|
||||
<td></td>
|
||||
<td class="label">已支付货款金额</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">应支付尾款时间</td>
|
||||
<td></td>
|
||||
<td class="label">应支付尾款金额</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">验收结论</td>
|
||||
<td></td>
|
||||
<td class="label">验收时间</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">供货单位指定验收人签字及联系方式</td>
|
||||
<td></td>
|
||||
<td class="label">收货单位指定验收人签字及联系方式</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">供货单位盖章</td>
|
||||
<td></td>
|
||||
<td class="label">收货单位盖章</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="no-print">
|
||||
<button class="print-button" onclick="window.print()">打印/保存为PDF</button>
|
||||
<p style="color: #666; font-size: 12px;">
|
||||
提示:点击"打印/保存为PDF"按钮可以将此文档打印或保存为PDF格式。
|
||||
在打印对话框中,您也可以选择"另存为PDF"来保存文档。
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
// 在新窗口中打开HTML文档
|
||||
const newWindow = window.open('', '_blank');
|
||||
newWindow.document.write(htmlContent);
|
||||
newWindow.document.close();
|
||||
|
||||
ElMessage.success('牛只验收单已生成,可以在新窗口中查看和打印');
|
||||
|
||||
} catch (error) {
|
||||
console.error('生成牛只验收单失败:', error);
|
||||
ElMessage.error('生成牛只验收单失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDataList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.operation-scroll-bar {
|
||||
overflow-x: auto;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.operation-scroll-container {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
min-width: max-content;
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -60,7 +60,7 @@ import { ref, reactive, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { deviceList, deviceAssign, deviceDel, tenantList } from '@/api/sys.js';
|
||||
import { queryEarTagList, queryHostList } from '@/api/device.js';
|
||||
import { collarList } from '@/api/abroad.js';
|
||||
import { iotDeviceAssignableList, iotDeviceAssign, iotDeviceAssignToTenant, iotDeviceUnassignFromTenant } from '@/api/hardware.js';
|
||||
|
||||
const emits = defineEmits(['success']);
|
||||
const multipleTableUnRef = ref(null);
|
||||
@@ -74,11 +74,15 @@ const data = reactive({
|
||||
rows: [],
|
||||
dataListLoading: false,
|
||||
total: 0,
|
||||
deviceType: '', // 设备类型1:耳标,2:项圈,3:主机
|
||||
deviceType: '', // 设备类型1:主机,2:耳标,4:项圈
|
||||
allotType: '', // 是否已分配标识,0:未分配,1:已分配
|
||||
tenantId: '', // 租户id
|
||||
assignLoading: false,
|
||||
delLoading: false,
|
||||
deliveryId: null, // 装车订单ID
|
||||
deliveryNumber: '', // 运单号
|
||||
carNumber: '', // 车牌号
|
||||
mode: 'delivery', // 分配模式:'delivery' 或 'tenant'
|
||||
});
|
||||
const form = reactive({
|
||||
pageNum: 1,
|
||||
@@ -98,34 +102,45 @@ const handleClick = (tab, event) => {
|
||||
const getDataList = () => {
|
||||
console.log('=== getDataList 开始执行 ===');
|
||||
data.dataListLoading = true;
|
||||
const params = {
|
||||
...form,
|
||||
deviceType: data.deviceType,
|
||||
allotType: data.allotType,
|
||||
tenantId: data.tenantId,
|
||||
};
|
||||
|
||||
let params;
|
||||
if (data.mode === 'tenant') {
|
||||
// 租户分配模式
|
||||
if (data.allotType === '0') {
|
||||
// 未分配标签页:查询未分配给任何租户的设备
|
||||
params = {
|
||||
...form,
|
||||
deviceType: data.deviceType,
|
||||
allotType: data.allotType,
|
||||
// 不传tenantId,让后端查询tenant_id为空的设备
|
||||
};
|
||||
console.log('=== 租户分配模式-未分配标签页参数 ===', params);
|
||||
} else {
|
||||
// 已分配标签页:查询已分配给该租户的设备
|
||||
params = {
|
||||
...form,
|
||||
deviceType: data.deviceType,
|
||||
allotType: data.allotType,
|
||||
tenantId: data.tenantId,
|
||||
};
|
||||
console.log('=== 租户分配模式-已分配标签页参数 ===', params);
|
||||
}
|
||||
} else {
|
||||
// 装车订单分配模式:查询未分配给装车订单的设备
|
||||
params = {
|
||||
...form,
|
||||
deviceType: data.deviceType,
|
||||
allotType: data.allotType,
|
||||
tenantId: data.tenantId,
|
||||
};
|
||||
console.log('=== 装车订单分配模式参数 ===', params);
|
||||
}
|
||||
|
||||
console.log('=== 请求参数 ===', params);
|
||||
|
||||
// 根据设备类型调用不同的API
|
||||
let apiCall;
|
||||
switch (data.deviceType) {
|
||||
case '1': // 耳标
|
||||
console.log('=== 调用耳标API ===');
|
||||
apiCall = queryEarTagList(params);
|
||||
break;
|
||||
case '2': // 项圈
|
||||
console.log('=== 调用项圈API ===');
|
||||
apiCall = collarList(params);
|
||||
break;
|
||||
case '3': // 主机
|
||||
console.log('=== 调用主机API ===');
|
||||
apiCall = queryHostList(params);
|
||||
break;
|
||||
default:
|
||||
console.log('=== 调用默认API ===');
|
||||
apiCall = deviceList(params);
|
||||
break;
|
||||
}
|
||||
// 使用新的IoT设备API
|
||||
console.log('=== 调用IoT设备API ===');
|
||||
const apiCall = iotDeviceAssignableList(params);
|
||||
|
||||
apiCall
|
||||
.then((res) => {
|
||||
@@ -154,29 +169,37 @@ const getDataList = () => {
|
||||
|
||||
// 根据设备类型自动添加设备类型名称
|
||||
switch (data.deviceType) {
|
||||
case '1': // 耳标
|
||||
case '1': // 主机
|
||||
processedItem.deviceTypeName = '智能主机';
|
||||
break;
|
||||
case '2': // 耳标
|
||||
processedItem.deviceTypeName = '智能耳标';
|
||||
break;
|
||||
case '2': // 项圈
|
||||
case '4': // 项圈
|
||||
processedItem.deviceTypeName = '智能项圈';
|
||||
break;
|
||||
case '3': // 主机
|
||||
processedItem.deviceTypeName = '智能主机';
|
||||
break;
|
||||
default:
|
||||
processedItem.deviceTypeName = '未知设备';
|
||||
break;
|
||||
}
|
||||
|
||||
// 根据 deliveryNumber/delivery_number 判断分配状态(兼容两种格式)
|
||||
const deliveryNumber = item.deliveryNumber || item.delivery_number;
|
||||
processedItem.isAssigned = !!(deliveryNumber && deliveryNumber.trim() !== '');
|
||||
// 根据模式判断分配状态
|
||||
if (data.mode === 'tenant') {
|
||||
// 租户模式:根据tenantId判断分配状态
|
||||
processedItem.isAssigned = !!(item.tenantId && item.tenantId !== null);
|
||||
} else {
|
||||
// 装车订单模式:根据deliveryNumber判断分配状态
|
||||
const deliveryNumber = item.deliveryNumber || item.delivery_number;
|
||||
processedItem.isAssigned = !!(deliveryNumber && deliveryNumber.trim() !== '');
|
||||
}
|
||||
|
||||
console.log(`=== 处理设备 ${item.deviceId || item.sn} ===`, {
|
||||
deviceType: data.deviceType,
|
||||
deviceTypeName: processedItem.deviceTypeName,
|
||||
deliveryNumber: deliveryNumber,
|
||||
isAssigned: processedItem.isAssigned
|
||||
tenantId: item.tenantId,
|
||||
deliveryNumber: item.deliveryNumber || item.delivery_number,
|
||||
isAssigned: processedItem.isAssigned,
|
||||
mode: data.mode
|
||||
});
|
||||
|
||||
return processedItem;
|
||||
@@ -217,12 +240,28 @@ const batchAssign = () => {
|
||||
return;
|
||||
}
|
||||
data.assignLoading = true;
|
||||
const params = {
|
||||
deviceIds: data.unassignedSelect,
|
||||
deviceType: data.deviceType,
|
||||
tenantId: data.tenantId,
|
||||
};
|
||||
deviceAssign(params)
|
||||
|
||||
let params;
|
||||
let apiCall;
|
||||
|
||||
if (data.mode === 'tenant') {
|
||||
// 租户分配模式
|
||||
params = {
|
||||
deviceIds: data.unassignedSelect,
|
||||
tenantId: data.tenantId,
|
||||
};
|
||||
apiCall = iotDeviceAssignToTenant(params);
|
||||
} else {
|
||||
// 装车订单分配模式
|
||||
params = {
|
||||
deviceIds: data.unassignedSelect,
|
||||
deliveryId: data.deliveryId,
|
||||
carNumber: data.carNumber,
|
||||
};
|
||||
apiCall = iotDeviceAssign(params);
|
||||
}
|
||||
|
||||
apiCall
|
||||
.then((res) => {
|
||||
data.assignLoading = false;
|
||||
if (res.code === 200) {
|
||||
@@ -231,12 +270,14 @@ const batchAssign = () => {
|
||||
type: 'success',
|
||||
});
|
||||
getDataList();
|
||||
emits('success'); // 通知父组件刷新
|
||||
} else {
|
||||
ElMessage.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
data.assignLoading = false;
|
||||
ElMessage.error('设备分配失败');
|
||||
});
|
||||
};
|
||||
//
|
||||
@@ -246,12 +287,25 @@ const assignedSelected = (val) => {
|
||||
};
|
||||
// 单个分配
|
||||
const assignClick = (deviceId) => {
|
||||
const params = {
|
||||
deviceIds: [deviceId],
|
||||
deviceType: data.deviceType,
|
||||
tenantId: data.tenantId,
|
||||
};
|
||||
deviceAssign(params)
|
||||
let apiCall;
|
||||
if (data.mode === 'tenant') {
|
||||
// 租户模式:分配设备到租户
|
||||
const params = {
|
||||
deviceIds: [deviceId],
|
||||
tenantId: data.tenantId,
|
||||
};
|
||||
apiCall = iotDeviceAssignToTenant(params);
|
||||
} else {
|
||||
// 装车订单模式:分配设备到装车订单
|
||||
const params = {
|
||||
deviceIds: [deviceId],
|
||||
deviceType: data.deviceType,
|
||||
tenantId: data.tenantId,
|
||||
};
|
||||
apiCall = deviceAssign(params);
|
||||
}
|
||||
|
||||
apiCall
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
ElMessage({
|
||||
@@ -259,31 +313,44 @@ const assignClick = (deviceId) => {
|
||||
type: 'success',
|
||||
});
|
||||
getDataList();
|
||||
emits('success'); // 通知父组件刷新
|
||||
} else {
|
||||
ElMessage.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
// 批量删除
|
||||
// 批量删除(解绑)
|
||||
const batchDel = () => {
|
||||
if (data.assignedSelect.length === 0) {
|
||||
ElMessage.error('请选择设备编号');
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm('请确认是否批量删除', '提示', {
|
||||
ElMessageBox.confirm('请确认是否批量解绑', '提示', {
|
||||
cancelButtonText: '取消',
|
||||
confirmButtonText: '确定',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
data.delLoading = true;
|
||||
const params = {
|
||||
deviceIds: data.assignedSelect,
|
||||
deviceType: data.deviceType,
|
||||
tenantId: data.tenantId,
|
||||
};
|
||||
|
||||
deviceDel(params).then((res) => {
|
||||
|
||||
let apiCall;
|
||||
if (data.mode === 'tenant') {
|
||||
// 租户模式:解绑设备(将tenant_id设置为空)
|
||||
const params = {
|
||||
deviceIds: data.assignedSelect,
|
||||
};
|
||||
apiCall = iotDeviceUnassignFromTenant(params);
|
||||
} else {
|
||||
// 装车订单模式:取消分配设备
|
||||
const unassignParams = {
|
||||
deviceIds: data.assignedSelect,
|
||||
deliveryId: null, // 设置为null表示取消分配
|
||||
carNumber: null,
|
||||
};
|
||||
apiCall = iotDeviceAssign(unassignParams);
|
||||
}
|
||||
|
||||
apiCall.then((res) => {
|
||||
data.delLoading = false;
|
||||
if (res.code === 200) {
|
||||
ElMessage({
|
||||
@@ -304,18 +371,31 @@ const delClick = (id) => {
|
||||
confirmButtonText: '确定',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
const params = {
|
||||
deviceIds: [id],
|
||||
deviceType: data.deviceType,
|
||||
tenantId: data.tenantId,
|
||||
};
|
||||
deviceDel(params).then((res) => {
|
||||
let apiCall;
|
||||
if (data.mode === 'tenant') {
|
||||
// 租户模式:解绑设备(将tenant_id设置为空)
|
||||
const params = {
|
||||
deviceIds: [id],
|
||||
};
|
||||
apiCall = iotDeviceUnassignFromTenant(params);
|
||||
} else {
|
||||
// 装车订单模式:取消分配设备
|
||||
const unassignParams = {
|
||||
deviceIds: [id],
|
||||
deliveryId: null, // 设置为null表示取消分配
|
||||
carNumber: null,
|
||||
};
|
||||
apiCall = iotDeviceAssign(unassignParams);
|
||||
}
|
||||
|
||||
apiCall.then((res) => {
|
||||
if (res.code === 200) {
|
||||
ElMessage({
|
||||
message: res.msg,
|
||||
type: 'success',
|
||||
});
|
||||
getDataList();
|
||||
emits('success'); // 通知父组件刷新
|
||||
} else {
|
||||
ElMessage.error(res.msg);
|
||||
}
|
||||
@@ -325,17 +405,33 @@ const delClick = (id) => {
|
||||
const getRowKey = (row) => {
|
||||
return row.id;
|
||||
};
|
||||
const onShowDialog = (tenantId, deviceType) => {
|
||||
console.log('=== onShowDialog 被调用 ===', { tenantId, deviceType });
|
||||
const onShowDialog = (tenantId, deviceType, deliveryId, deliveryNumber, carNumber, mode = 'delivery') => {
|
||||
console.log('=== onShowDialog 被调用 ===', { tenantId, deviceType, deliveryId, deliveryNumber, carNumber, mode });
|
||||
data.dialogVisible = true;
|
||||
data.activeName = 'first';
|
||||
data.deviceType = deviceType;
|
||||
data.allotType = '0';
|
||||
data.tenantId = tenantId;
|
||||
data.deliveryId = deliveryId;
|
||||
data.deliveryNumber = deliveryNumber;
|
||||
data.carNumber = carNumber;
|
||||
data.mode = mode; // 新增:分配模式 'delivery' 或 'tenant'
|
||||
|
||||
// 根据模式设置标题
|
||||
if (mode === 'tenant') {
|
||||
data.title = '租户设备分配';
|
||||
} else {
|
||||
data.title = '设备分配';
|
||||
}
|
||||
|
||||
console.log('=== 设置后的数据 ===', {
|
||||
deviceType: data.deviceType,
|
||||
allotType: data.allotType,
|
||||
tenantId: data.tenantId
|
||||
tenantId: data.tenantId,
|
||||
deliveryId: data.deliveryId,
|
||||
deliveryNumber: data.deliveryNumber,
|
||||
carNumber: data.carNumber,
|
||||
mode: data.mode
|
||||
});
|
||||
getDataList();
|
||||
if (multipleTableUnRef.value) {
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="showAddDialog(scope.row)">编辑</el-button>
|
||||
<el-button link type="primary" @click="del(scope.row.id)">删除</el-button>
|
||||
<el-button link type="primary" @click="assign(scope.row.id, '1')">耳标分配</el-button>
|
||||
<el-button link type="primary" @click="assign(scope.row.id, '2')">项圈分配</el-button>
|
||||
<el-button link type="primary" @click="assign(scope.row.id, '3')">主机分配</el-button>
|
||||
<el-button link type="primary" @click="assign(scope.row.id, '2')">耳标分配</el-button>
|
||||
<el-button link type="primary" @click="assign(scope.row.id, '4')">项圈分配</el-button>
|
||||
<el-button link type="primary" @click="assign(scope.row.id, '1')">主机分配</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
@@ -36,6 +36,7 @@
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import baseSearch from '@/components/common/searchCustom/index.vue';
|
||||
import { tenantList, tenantDel } from '@/api/sys.js';
|
||||
import { iotDeviceAssignableList, iotDeviceAssignToTenant } from '@/api/hardware.js';
|
||||
import TenantDialog from './tenantDialog.vue';
|
||||
import AssignDialog from './assignDevice.vue';
|
||||
|
||||
@@ -95,9 +96,11 @@ const del = (id) => {
|
||||
});
|
||||
};
|
||||
// 分配弹层
|
||||
const assign = (id, type) => {
|
||||
const assign = (tenantId, deviceType) => {
|
||||
if (AssignDialogRef.value) {
|
||||
AssignDialogRef.value.onShowDialog(id, type);
|
||||
// 传递租户ID和设备类型到分配对话框
|
||||
// onShowDialog(tenantId, deviceType, deliveryId, deliveryNumber, carNumber, mode)
|
||||
AssignDialogRef.value.onShowDialog(tenantId, deviceType, null, '', '', 'tenant');
|
||||
}
|
||||
};
|
||||
const showAddDialog = (row) => {
|
||||
|
||||
Reference in New Issue
Block a user