electripper-v2-ui/src/views/bst/device/index.vue
2025-04-19 16:11:35 +08:00

849 lines
28 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

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

<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="SN" prop="sn">
<el-input
v-model="queryParams.sn"
placeholder="请输入设备SN号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="MAC" prop="mac">
<el-input
v-model="queryParams.mac"
placeholder="请输入MAC号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input
v-model="queryParams.deviceName"
placeholder="请输入设备名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="型号" prop="modelName">
<el-input
v-model="queryParams.modelName"
placeholder="请输入型号名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="车牌号" prop="vehicleNum">
<el-input
v-model="queryParams.vehicleNum"
placeholder="请输入车牌号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="运营区" prop="areaId">
<el-input
v-model="queryParams.areaName"
placeholder="请输入运营区名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="在线状态" prop="onlineStatus">
<el-select v-model="queryParams.onlineStatus" placeholder="请选择在线状态" clearable @change="handleQuery">
<el-option
v-for="dict in dict.type.device_online_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="车辆状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择车辆状态" clearable @change="handleQuery">
<el-option
v-for="dict in dict.type.device_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="锁状态" prop="lockStatus">
<el-select v-model="queryParams.lockStatus" placeholder="请选择锁状态" clearable @change="handleQuery">
<el-option
v-for="dict in dict.type.device_lock_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="运营商" prop="mchName">
<el-input
v-model="queryParams.mchName"
placeholder="请输入运营商"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="物联网状态" prop="iotStatus" label-width="6em">
<el-select v-model="queryParams.iotStatus" placeholder="请选择物联网状态" clearable @change="handleQuery">
<el-option
v-for="dict in dict.type.device_iot_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-has-permi="['bst:device:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-has-permi="['bst:device:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-has-permi="['bst:device:export']"
>导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-upload"
size="mini"
@click="handleOut(null)"
:disabled="multiple"
v-has-permi="['bst:device:out']"
>一键出仓</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-download"
size="mini"
@click="handleIn(null)"
:disabled="multiple"
v-has-permi="['bst:device:in']"
>一键入仓</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-close"
size="mini"
@click="handleDisable(null)"
:disabled="multiple"
v-has-permi="['bst:device:disable']"
>一键禁用</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-check"
size="mini"
@click="handleEnable(null)"
:disabled="multiple"
v-has-permi="['bst:device:enable']"
>一键启用</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-sort"
size="mini"
@click="handleTransfer(null)"
:disabled="multiple"
v-has-permi="['bst:device:transfer']"
>一键划拨</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-connection"
size="mini"
@click="handleUnbindMch(null)"
:disabled="multiple"
v-has-permi="['bst:device:unbindMch']"
>一键解绑商户</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-map-location"
size="mini"
@click="handleUnbindArea(null)"
:disabled="multiple"
v-has-permi="['bst:device:unbindArea']"
>一键解绑运营区</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList(true)" :columns="columns"></right-toolbar>
</el-row>
<el-table size="mini" v-loading="loading" :data="deviceList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="onSortChange">
<el-table-column type="selection" width="55" align="center" />
<template v-for="column of showColumns">
<el-table-column
:key="column.key"
:label="column.label"
:prop="column.key"
:align="column.align"
:min-width="column.minWidth"
:sort-orders="orderSorts"
:sortable="column.sortable"
:show-overflow-tooltip="column.overflow"
:width="column.width"
>
<template slot-scope="d">
<template v-if="column.key === 'id'">
{{d.row[column.key]}}
</template>
<template v-else-if="column.key === 'sn'">
{{d.row.sn | dv}}
| {{d.row.mac | dv}}
<device-sn :sn="d.row.sn"/>
<br/>
<dict-tag :options="dict.type.device_online_status" :value="d.row.onlineStatus" size="mini"/>
<dict-tag :options="dict.type.device_status" :value="d.row.status" size="mini" style="margin-left: 4px;"/>
<dict-tag :options="dict.type.device_iot_status" :value="d.row.iotStatus" size="mini" style="margin-left: 4px;"/>
</template>
<template v-else-if="column.key === 'mchName'">
{{d.row.mchName | dv}}<br/>
{{d.row.areaName | dv}}
</template>
<template v-else-if="column.key === 'lockStatus'">
<el-tooltip content="开锁:共享模块有开锁的信号输出;电门开:车子处于开锁可骑行状态" placement="top">
<div>
<dict-tag :options="dict.type.device_lock_status" :value="d.row.lockStatus" size="mini"/><br/>
<dict-tag :options="dict.type.device_quality" :value="d.row.quality" size="mini"/>
</div>
</el-tooltip>
</template>
<template v-else-if="column.key === 'iotStatus'">
<dict-tag :options="dict.type.device_iot_status" :value="d.row[column.key]" size="mini"/>
</template>
<template v-else-if="column.key === 'remainingPower'">
{{d.row.remainingPower | fix2 | dv}} % <br/>
{{d.row.remainEndurance | dv}} KM
</template>
<template v-else-if="column.key === 'vehicleNum'">
{{d.row.vehicleNum | dv}}<br/>
{{d.row.modelName | dv}}
</template>
<template v-else-if="column.key === 'voltage'">
{{d.row.voltage | fix2 | dv}} V
</template>
<template v-else-if="column.key === 'music'">
<dict-tag :options="dict.type.device_music" :value="d.row[column.key]" size="mini"/>
</template>
<template v-else-if="column.key === 'hardwareVersion'">
{{d.row.hardwareVersion | dv}} | {{d.row.softwareVersion | dv}}
</template>
<template v-else>
{{d.row[column.key]}}
</template>
</template>
</el-table-column>
</template>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="300">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click="handleView(scope.row)"
v-has-permi="['bst:device:query']"
>详情</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-has-permi="['bst:device:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-upload"
@click="handleOut(scope.row)"
v-has-permi="['bst:device:out']"
v-show="DeviceStatus.canOut().includes(scope.row.status)"
>出仓</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-download"
@click="handleIn(scope.row)"
v-has-permi="['bst:device:in']"
v-show="DeviceStatus.canIn().includes(scope.row.status)"
>入仓</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-close"
@click="handleDisable(scope.row)"
v-has-permi="['bst:device:disable']"
v-show="DeviceStatus.canDisable().includes(scope.row.status)"
>禁用</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-check"
@click="handleEnable(scope.row)"
v-has-permi="['bst:device:enable']"
v-show="DeviceStatus.canEnable().includes(scope.row.status)"
>启用</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-unlock"
@click="handleUnlock(scope.row)"
v-has-permi="['bst:device:unlock']"
v-show="canUnlock(scope.row)"
>开锁</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-lock"
@click="handleLock(scope.row)"
v-has-permi="['bst:device:lock']"
v-show="canAdminLock(scope.row)"
>锁车</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-bell"
@click="handleRing(scope.row)"
v-has-permi="['bst:device:ring']"
>响铃</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-refresh"
@click="handleReboot(scope.row)"
v-has-permi="['bst:device:reboot']"
>重启</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-unlock"
@click="handleUnlockSeat(scope.row)"
v-has-permi="['bst:device:unlockSeat']"
>开坐垫锁</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-refresh"
@click="handleRefresh(scope.row)"
v-has-permi="['bst:device:refresh']"
>刷新</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-sort"
@click="handleTransfer(scope.row)"
v-has-permi="['bst:device:transfer']"
>划拨</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-connection"
@click="handleUnbindMch(scope.row)"
v-has-permi="['bst:device:unbindMch']"
v-show="scope.row.mchId != null"
>解绑商户</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-map-location"
@click="handleUnbindArea(scope.row)"
v-has-permi="['bst:device:unbindArea']"
v-show="scope.row.areaId != null"
>解绑运营区</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-has-permi="['bst:device:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList(true)"
/>
<!-- 添加或修改设备对话框 -->
<device-edit-dialog
:visible.sync="open"
:id="editId"
@success="getList(true)"
/>
<device-transfer-dialog
:visible.sync="showTransferDialog"
:ids="transferIds"
@success="getList(true)"
/>
</div>
</template>
<script>
import { listDevice, delDevice, inDevice, outDevice, disableDevice, enableDevice, unbindDeviceMch, unbindDeviceArea} from "@/api/bst/device";
import { unlockDevice, lockDevice, ringDevice, rebootDevice, unlockSeatDevice, refreshDevice} from "@/api/bst/deviceIot";
import { $showColumns } from '@/utils/mixins';
import FormCol from "@/components/FormCol/index.vue";
import DeviceEditDialog from './components/DeviceEditDialog.vue';
import BooleanTag from '@/components/BooleanTag/index.vue';
import { DeviceStatus } from '@/utils/enums';
import { $device } from '@/views/bst/device/mixins';
import DeviceTransferDialog from '@/views/bst/device/components/DeviceTransferDialog.vue';
import DeviceSn from '@/views/bst/device/components/DeviceSn.vue';
// 默认排序字段
const defaultSort = {
prop: "createTime",
order: "descending"
}
export default {
name: "Device",
mixins: [$showColumns, $device],
dicts: ['device_status', 'device_lock_status', 'device_iot_status', 'device_online_status', 'device_quality', 'device_music'],
components: {FormCol, DeviceEditDialog, BooleanTag, DeviceTransferDialog, DeviceSn},
props: {
query: {
type: Object,
default: () => ({})
}
},
data() {
return {
DeviceStatus,
span: 24,
// 字段列表
columns: [
{key: 'id', visible: false, label: 'ID', minWidth: null, sortable: true, overflow: false, align: 'center', width: "80"},
{key: 'sn', visible: true, label: '设备', minWidth: null, sortable: true, overflow: false, align: 'left', width: "220"},
{key: 'vehicleNum', visible: true, label: '车辆', minWidth: null, sortable: true, overflow: false, align: 'left', width: null},
{key: 'mchName', visible: true, label: '归属', minWidth: null, sortable: true, overflow: false, align: 'left', width: null},
{key: 'hardwareVersion', visible: true, label: '版本', minWidth: null, sortable: true, overflow: false, align: 'left', width: null},
{key: 'signalStrength', visible: true, label: '信号', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'satellites', visible: true, label: '卫星', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'lockStatus', visible: true, label: '锁状态', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'voltage', visible: true, label: '电压', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'remainingPower', visible: true, label: '续航', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'music', visible: true, label: '声音', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'remark', visible: true, label: '备注', minWidth: null, sortable: true, overflow: true, align: 'center', width: null},
{key: 'orderNo', visible: true, label: '订单', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'createTime', visible: true, label: '创建', minWidth: null, sortable: true, overflow: false, align: 'center', width: "90"},
],
// 排序方式
orderSorts: ['ascending', 'descending', null],
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 设备表格数据
deviceList: [],
// 是否显示弹出层
open: false,
// 编辑ID
editId: null,
defaultSort,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 20,
orderByColumn: defaultSort.prop,
isAsc: defaultSort.order,
refresh: true,
id: null,
modelId: null,
deviceName: null,
mac: null,
sn: null,
vehicleNum: null,
areaId: null,
onlineStatus: null,
remark: null,
status: null,
lockStatus: null,
location: null,
gps: null,
lastLocationTime: null,
hardwareVersionId: null,
mchId: null,
iotStatus: null,
isSound: null
},
// 表单参数
form: {},
// 表单校验
rules: {
modelId: [
{ required: true, message: "型号id不能为空", trigger: "blur" }
],
mac: [
{ required: true, message: "设备Mac号不能为空", trigger: "blur" }
],
sn: [
{ required: true, message: "设备SN号不能为空", trigger: "blur" }
],
createTime: [
{ required: true, message: "创建时间不能为空", trigger: "blur" }
],
},
showTransferDialog: false,
transferIds: [],
};
},
created() {
Object.assign(this.queryParams, this.query);
this.getList(true);
},
methods: {
// 划拨运营区
handleTransfer(row) {
this.transferIds = this.ids;
if (row != null) {
this.transferIds = [row.id];
}
this.showTransferDialog = true;
},
// 管理员开锁
handleUnlock(row) {
this.$confirm('是否确认操作设备【' + row.sn + '】开锁?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
unlockDevice({ id: row.id }).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功设备已开锁");
this.getList(true);
}
})
})
},
// 管理员锁车
handleLock(row) {
this.$confirm('是否确认操作设备【' + row.sn + '】锁车?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
lockDevice({ id: row.id }).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功设备已锁车");
this.getList(true);
}
})
})
},
// 管理员响铃寻车
handleRing(row) {
this.$confirm('是否确认操作设备【' + row.sn + '】响铃寻车?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
ringDevice({ id: row.id }).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功设备已响铃");
this.getList(true);
}
})
})
},
// 管理员重启
handleReboot(row) {
this.$confirm('是否确认操作设备【' + row.sn + '】重启?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
rebootDevice({ id: row.id }).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功设备已重启");
this.getList(true);
}
})
})
},
// 管理员开坐垫锁
handleUnlockSeat(row) {
this.$confirm('是否确认操作设备【' + row.sn + '】开坐垫锁?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
unlockSeatDevice({ id: row.id }).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功坐垫锁已开");
this.getList(true);
}
})
})
},
// 管理员刷新
handleRefresh(row) {
this.$confirm('是否确认刷新设备【' + row.sn + '】?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
refreshDevice({ id: row.id }).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功设备已刷新");
this.getList(false);
}
})
})
},
// 一键入仓
handleOut(row) {
let msg = '是否确认一键出仓?';
let ids = this.ids;
if (row != null) {
ids = [row.id];
msg = '是否确认出仓设备【' + row.sn + '】?';
}
this.$confirm(msg, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
outDevice(ids).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功共出仓" + res.data + "台设备");
this.getList(true);
}
})
})
},
// 一键出仓
handleIn(row) {
let msg = '是否确认一键入仓?';
let ids = this.ids;
if (row != null) {
ids = [row.id];
msg = '是否确认入仓设备【' + row.sn + '】?';
}
this.$confirm(msg, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
inDevice(ids).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功共入仓" + res.data + "台设备");
this.getList(true);
}
})
})
},
// 一键禁用
handleDisable(row) {
let msg = '是否确认一键禁用?';
let ids = this.ids;
if (row != null) {
ids = [row.id];
msg = '是否确认禁用设备【' + row.sn + '】?';
}
this.$confirm(msg, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
disableDevice(ids).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功共禁用" + res.data + "台设备");
this.getList(true);
}
})
})
},
// 一键启用
handleEnable(row) {
let msg = '是否确认一键启用?';
let ids = this.ids;
if (row != null) {
ids = [row.id];
msg = '是否确认启用设备【' + row.sn + '】?';
}
this.$confirm(msg, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
enableDevice(ids).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功共启用" + res.data + "台设备");
this.getList();
}
})
})
},
/** 当排序按钮被点击时触发 **/
onSortChange(column) {
if (column.order == null) {
this.queryParams.orderByColumn = defaultSort.prop;
this.queryParams.isAsc = defaultSort.order;
} else {
this.queryParams.orderByColumn = column.prop;
this.queryParams.isAsc = column.order;
}
this.getList();
},
/** 查询设备列表 */
getList(refresh = false) {
this.loading = true;
this.queryParams.refresh = refresh;
listDevice(this.queryParams).then(response => {
this.deviceList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList(true);
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.editId = null;
this.open = true;
},
/** 详情按钮操作 */
handleView(row) {
this.$router.push(`/view/device/${row.id}`);
},
/** 修改按钮操作 */
handleUpdate(row) {
this.editId = row.id || this.ids;
this.open = true;
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除设备编号为"' + ids + '"的数据项?').then(function() {
return delDevice(ids);
}).then(() => {
this.getList(true);
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
this.download('bst/device/export', {
...this.queryParams
}, `device_${new Date().getTime()}.xlsx`)
},
// 一键解绑商户
handleUnbindMch(row) {
let msg = '是否确认一键解绑商户?';
let ids = this.ids;
if (row != null) {
ids = [row.id];
msg = '是否确认解绑设备【' + row.sn + '】的商户?';
}
this.$confirm(msg, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
unbindDeviceMch(ids).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功共解绑" + res.data + "台设备");
this.getList(true);
}
})
})
},
// 一键解绑运营区
handleUnbindArea(row) {
let msg = '是否确认一键解绑运营区?';
let ids = this.ids;
if (row != null) {
ids = [row.id];
msg = '是否确认解绑设备【' + row.sn + '】的运营区?';
}
this.$confirm(msg, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
unbindDeviceArea(ids).then((res) => {
if (res.code === 200) {
this.$message.success("操作成功共解绑" + res.data + "台设备");
this.getList(true);
}
})
})
},
}
};
</script>