add:设备基础信息
This commit is contained in:
@@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
|
||||||
*
|
|
||||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
|
||||||
*
|
|
||||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
|
||||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
|
||||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
|
||||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
|
||||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
|
||||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
|
||||||
*/
|
|
||||||
package org.nl.modular.device.controller;
|
|
||||||
|
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaIgnore;
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import org.nl.common.pojo.CommonResult;
|
|
||||||
import org.nl.modular.device.service.dto.DeviceInfo;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Tag(name = "设备模块")
|
|
||||||
@RestController()
|
|
||||||
@RequestMapping("/api/device")
|
|
||||||
@Validated
|
|
||||||
@SaIgnore
|
|
||||||
public class DeviceController {
|
|
||||||
static {
|
|
||||||
System.out.println("初始化AGV模块------------");
|
|
||||||
}
|
|
||||||
@GetMapping("list")
|
|
||||||
public CommonResult<List> status() {
|
|
||||||
List<DeviceInfo> list = new ArrayList<>();
|
|
||||||
JSONObject object = new JSONObject();
|
|
||||||
object.put("action","action");
|
|
||||||
object.put("signal","signal");
|
|
||||||
object.put("power","power");
|
|
||||||
for (int i = 0; i < 20; i++) {
|
|
||||||
DeviceInfo build = DeviceInfo.builder()
|
|
||||||
.name("设备1"+i)
|
|
||||||
.code(i + "sdfsdf")
|
|
||||||
.id(String.valueOf(i))
|
|
||||||
.icon("/Users/mima0000/Desktop/car.png")
|
|
||||||
.description("设备介绍")
|
|
||||||
.editParam(object)
|
|
||||||
.type(i%2==1?"货架":"专机")
|
|
||||||
.build();
|
|
||||||
list.add(build);
|
|
||||||
}
|
|
||||||
return CommonResult.data(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package org.nl.modular.device.service.dto;
|
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Builder
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class DeviceInfo {
|
|
||||||
/**
|
|
||||||
* 设备Id
|
|
||||||
*/
|
|
||||||
private String id;
|
|
||||||
/**
|
|
||||||
* 设备Id
|
|
||||||
*/
|
|
||||||
private String code;
|
|
||||||
/**
|
|
||||||
* 车辆类型
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
/**
|
|
||||||
* 设备描述
|
|
||||||
*/
|
|
||||||
private String description;
|
|
||||||
/**
|
|
||||||
* 设备类型
|
|
||||||
*/
|
|
||||||
private String type;
|
|
||||||
/**
|
|
||||||
* 设备编辑时存储信息
|
|
||||||
*/
|
|
||||||
private JSONObject editParam;
|
|
||||||
/**
|
|
||||||
* 设备图标
|
|
||||||
*/
|
|
||||||
private String icon="/Users/mima0000/Desktop/car.png";
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||||
|
*
|
||||||
|
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||||
|
*
|
||||||
|
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||||
|
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||||
|
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||||
|
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||||
|
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||||
|
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||||
|
*/
|
||||||
|
package org.nl.module.device.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.nl.common.pojo.CommonResult;
|
||||||
|
import org.nl.module.device.entity.BaseDataDevice;
|
||||||
|
import org.nl.module.device.service.BaseDataDeviceService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备信息表Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "设备信息管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/baseData/device")
|
||||||
|
@Validated
|
||||||
|
public class BaseDataDeviceController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BaseDataDeviceService baseDataDeviceService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设备列表
|
||||||
|
*/
|
||||||
|
@Operation(summary = "获取设备列表")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public CommonResult list(@RequestParam(required = false) String searchKey,
|
||||||
|
@RequestParam(required = false) String region,
|
||||||
|
@RequestParam(defaultValue = "1") Integer current,
|
||||||
|
@RequestParam(defaultValue = "10") Integer size) {
|
||||||
|
LambdaQueryWrapper<BaseDataDevice> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
|
||||||
|
// 关键词搜索(设备名称或编码)
|
||||||
|
if (StringUtils.isNotBlank(searchKey)) {
|
||||||
|
queryWrapper.and(wrapper -> wrapper
|
||||||
|
.like(BaseDataDevice::getName, searchKey)
|
||||||
|
.or()
|
||||||
|
.like(BaseDataDevice::getCode, searchKey)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 区域搜索
|
||||||
|
if (StringUtils.isNotBlank(region)) {
|
||||||
|
queryWrapper.like(BaseDataDevice::getRegion, region);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按创建时间倒序
|
||||||
|
queryWrapper.orderByDesc(BaseDataDevice::getCreateTime);
|
||||||
|
|
||||||
|
Page<BaseDataDevice> page = new Page<>(current, size);
|
||||||
|
Page<BaseDataDevice> result = baseDataDeviceService.page(page, queryWrapper);
|
||||||
|
|
||||||
|
return CommonResult.data(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增设备信息
|
||||||
|
*/
|
||||||
|
@Operation(summary = "新增设备信息")
|
||||||
|
@PostMapping("/add")
|
||||||
|
public CommonResult add(@RequestBody BaseDataDevice device) {
|
||||||
|
baseDataDeviceService.save(device);
|
||||||
|
return CommonResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改设备信息
|
||||||
|
*/
|
||||||
|
@Operation(summary = "修改设备信息")
|
||||||
|
@PostMapping("/edit")
|
||||||
|
public CommonResult edit(@RequestBody BaseDataDevice device) {
|
||||||
|
baseDataDeviceService.updateById(device);
|
||||||
|
return CommonResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除设备信息
|
||||||
|
*/
|
||||||
|
@Operation(summary = "删除设备信息")
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public CommonResult delete(@RequestParam Integer id) {
|
||||||
|
baseDataDeviceService.removeById(id);
|
||||||
|
return CommonResult.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package org.nl.module.device.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备信息表实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("base_data_device")
|
||||||
|
@Builder
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Schema(description = "设备信息表")
|
||||||
|
public class BaseDataDevice implements Serializable {
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
@Schema(description = "主键ID")
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Schema(description = "编码")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Schema(description = "设备名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "所属区域")
|
||||||
|
private String region;
|
||||||
|
|
||||||
|
@Schema(description = "x坐标")
|
||||||
|
private Integer x;
|
||||||
|
|
||||||
|
@Schema(description = "y坐标")
|
||||||
|
private Integer y;
|
||||||
|
|
||||||
|
@Schema(description = "角度")
|
||||||
|
private Integer angle;
|
||||||
|
|
||||||
|
@Schema(description = "放大比例")
|
||||||
|
private Integer size;
|
||||||
|
|
||||||
|
@Schema(description = "图标地址")
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
@Schema(description = "创建用户")
|
||||||
|
private String createName;
|
||||||
|
|
||||||
|
@Schema(description = "修改时间")
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
@Schema(description = "修改用户")
|
||||||
|
private String updateName;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.nl.module.device.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.nl.module.device.entity.BaseDataDevice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备信息表Mapper
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface BaseDataDeviceMapper extends BaseMapper<BaseDataDevice> {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.nl.module.device.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import org.nl.module.device.entity.BaseDataDevice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备信息表Service接口
|
||||||
|
*/
|
||||||
|
public interface BaseDataDeviceService extends IService<BaseDataDevice> {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.nl.module.device.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import org.nl.module.device.entity.BaseDataDevice;
|
||||||
|
import org.nl.module.device.mapper.BaseDataDeviceMapper;
|
||||||
|
import org.nl.module.device.service.BaseDataDeviceService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备信息表Service实现类
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class BaseDataDeviceServiceImpl extends ServiceImpl<BaseDataDeviceMapper, BaseDataDevice> implements BaseDataDeviceService {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,13 +10,13 @@
|
|||||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||||
*/
|
*/
|
||||||
package org.nl.modular.point;
|
package org.nl.module.point;
|
||||||
|
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaIgnore;
|
import cn.dev33.satoken.annotation.SaIgnore;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.nl.common.pojo.CommonResult;
|
import org.nl.common.pojo.CommonResult;
|
||||||
import org.nl.modular.point.dto.PointStatus;
|
import org.nl.module.point.dto.PointStatus;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.nl.modular.point.dto;
|
package org.nl.module.point.dto;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package org.nl.common.util;
|
|
||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @author ZZQ
|
|
||||||
* @Date 2022/11/29 2:55 下午
|
|
||||||
*/
|
|
||||||
public class MapOf implements Serializable {
|
|
||||||
|
|
||||||
public static <K> HashMap of(K... key) {
|
|
||||||
HashMap map = new HashMap<>();
|
|
||||||
for (int i = 0; i < (key.length & ~1); i = i + 2) {
|
|
||||||
map.put(key[i], key[i + 1]);
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -214,7 +214,9 @@ public class DevFileLocalUtil {
|
|||||||
*/
|
*/
|
||||||
public static void storageFile(String bucketName, String key, InputStream inputStream) {
|
public static void storageFile(String bucketName, String key, InputStream inputStream) {
|
||||||
initClient();
|
initClient();
|
||||||
FileUtil.writeFromStream(inputStream, getUploadFileFolder() + FileUtil.FILE_SEPARATOR + bucketName + FileUtil.FILE_SEPARATOR + key);
|
String path = getUploadFileFolder() + FileUtil.FILE_SEPARATOR + bucketName + FileUtil.FILE_SEPARATOR + key;
|
||||||
|
System.out.println("=--filepath--="+path);
|
||||||
|
FileUtil.writeFromStream(inputStream, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
25
nl-vue/src/api/baseData/deviceApi.js
Normal file
25
nl-vue/src/api/baseData/deviceApi.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 设备管理API
|
||||||
|
*/
|
||||||
|
import { baseRequest } from '@/utils/request'
|
||||||
|
|
||||||
|
const request = (url, ...arg) => baseRequest(`/api/baseData/device/` + url, ...arg)
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// 获取设备列表
|
||||||
|
list(data) {
|
||||||
|
return request('list', data, 'get')
|
||||||
|
},
|
||||||
|
// 新增设备信息
|
||||||
|
add(data) {
|
||||||
|
return request('add', data)
|
||||||
|
},
|
||||||
|
// 修改设备信息
|
||||||
|
edit(data) {
|
||||||
|
return request('edit', data)
|
||||||
|
},
|
||||||
|
// 删除设备信息
|
||||||
|
delete(data) {
|
||||||
|
return request('delete', data, 'post')
|
||||||
|
}
|
||||||
|
}
|
||||||
315
nl-vue/src/views/nl_base_data/device/form.vue
Normal file
315
nl-vue/src/views/nl_base_data/device/form.vue
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
<template>
|
||||||
|
<xn-form-container
|
||||||
|
:title="formData.id ? '编辑设备信息' : '新增设备信息'"
|
||||||
|
:width="900"
|
||||||
|
:visible="visible"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<div class="form-content">
|
||||||
|
<!-- 左侧:图片预览区域 -->
|
||||||
|
<div class="image-preview-section" v-if="formData.id && imagePreviewUrl">
|
||||||
|
<div class="preview-title">设备图片预览</div>
|
||||||
|
<div class="preview-container">
|
||||||
|
<a-spin :spinning="imageLoading" tip="加载中...">
|
||||||
|
<a-image v-if="imagePreviewUrl" :src="imagePreviewUrl" :preview="true" class="device-image" />
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧:表单区域 -->
|
||||||
|
<div class="form-section" :class="{ 'full-width': !formData.id || !imagePreviewUrl }">
|
||||||
|
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
||||||
|
<a-form-item label="设备编码:" name="code">
|
||||||
|
<a-input v-model:value="formData.code" placeholder="请输入设备编码" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="设备名称:" name="name">
|
||||||
|
<a-input v-model:value="formData.name" placeholder="请输入设备名称" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="所属区域:" name="region">
|
||||||
|
<a-input v-model:value="formData.region" placeholder="请输入所属区域" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="设备图片:" name="icon">
|
||||||
|
<a-upload
|
||||||
|
v-model:file-list="fileList"
|
||||||
|
name="file"
|
||||||
|
list-type="picture-card"
|
||||||
|
class="device-uploader"
|
||||||
|
:show-upload-list="true"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
:custom-request="handleUpload"
|
||||||
|
@remove="handleRemove"
|
||||||
|
@preview="handlePreview"
|
||||||
|
:max-count="1"
|
||||||
|
>
|
||||||
|
<div v-if="fileList.length < 1">
|
||||||
|
<plus-outlined />
|
||||||
|
<div style="margin-top: 8px">上传图片</div>
|
||||||
|
</div>
|
||||||
|
</a-upload>
|
||||||
|
<div class="upload-tip">支持jpg、png格式,建议尺寸200x200像素</div>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<a-button class="xn-mr8" @click="onClose">关闭</a-button>
|
||||||
|
<a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
|
||||||
|
</template>
|
||||||
|
</xn-form-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="nlBaseDataDeviceForm">
|
||||||
|
import { required } from '@/utils/formRules'
|
||||||
|
import deviceApi from '@/api/baseData/deviceApi'
|
||||||
|
import fileApi from '@/api/dev/fileApi'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
|
||||||
|
// 默认是关闭状态
|
||||||
|
const visible = ref(false)
|
||||||
|
const emit = defineEmits({ successful: null })
|
||||||
|
const formRef = ref()
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = ref({})
|
||||||
|
const submitLoading = ref(false)
|
||||||
|
const fileList = ref([])
|
||||||
|
const imagePreviewUrl = ref('') // 图片预览URL(blob格式)
|
||||||
|
const imageLoading = ref(false) // 图片加载状态
|
||||||
|
|
||||||
|
// 根据文件ID获取图片blob并转换为URL
|
||||||
|
const loadImageById = (fileId, updateFileList = false) => {
|
||||||
|
if (!fileId) return
|
||||||
|
|
||||||
|
imageLoading.value = true
|
||||||
|
const param = {
|
||||||
|
storageId: fileId
|
||||||
|
}
|
||||||
|
fileApi
|
||||||
|
.fileDownload(param)
|
||||||
|
.then((response) => {
|
||||||
|
const blob = response.data
|
||||||
|
if (blob && blob instanceof Blob) {
|
||||||
|
// 将blob转换为URL用于预览
|
||||||
|
const blobUrl = URL.createObjectURL(blob)
|
||||||
|
imagePreviewUrl.value = blobUrl
|
||||||
|
|
||||||
|
// 如果需要更新文件列表(编辑时)
|
||||||
|
if (updateFileList) {
|
||||||
|
fileList.value = [
|
||||||
|
{
|
||||||
|
uid: fileId,
|
||||||
|
name: 'device-image.png',
|
||||||
|
status: 'done',
|
||||||
|
url: blobUrl,
|
||||||
|
thumbUrl: blobUrl
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.error('图片格式错误')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
message.error('图片加载失败: ' + (error.message || error))
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
imageLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开抽屉
|
||||||
|
const onOpen = (record) => {
|
||||||
|
visible.value = true
|
||||||
|
formData.value = {}
|
||||||
|
fileList.value = []
|
||||||
|
imagePreviewUrl.value = ''
|
||||||
|
|
||||||
|
if (record) {
|
||||||
|
formData.value = Object.assign({}, record)
|
||||||
|
// 如果有图片ID,加载图片并设置文件列表
|
||||||
|
if (record.icon) {
|
||||||
|
loadImageById(record.icon, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭抽屉
|
||||||
|
const onClose = () => {
|
||||||
|
formRef.value.resetFields()
|
||||||
|
fileList.value = []
|
||||||
|
// 释放blob URL
|
||||||
|
if (imagePreviewUrl.value) {
|
||||||
|
URL.revokeObjectURL(imagePreviewUrl.value)
|
||||||
|
imagePreviewUrl.value = ''
|
||||||
|
}
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认要校验的
|
||||||
|
const formRules = {
|
||||||
|
name: [required('请输入设备名称')],
|
||||||
|
region: [required('请输入所属区域')],
|
||||||
|
x: [required('请输入X坐标')],
|
||||||
|
y: [required('请输入Y坐标')],
|
||||||
|
angle: [required('请输入角度')],
|
||||||
|
size: [required('请输入放大比例')],
|
||||||
|
icon: [required('请上传设备图片')]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传前校验
|
||||||
|
const beforeUpload = (file) => {
|
||||||
|
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
|
||||||
|
if (!isJpgOrPng) {
|
||||||
|
message.error('只能上传 JPG/PNG 格式的图片!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const isLt5M = file.size / 1024 / 1024 < 5
|
||||||
|
if (!isLt5M) {
|
||||||
|
message.error('图片大小不能超过 5MB!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义上传
|
||||||
|
const handleUpload = ({ file, onSuccess, onError }) => {
|
||||||
|
const uploadFormData = new FormData()
|
||||||
|
uploadFormData.append('file', file)
|
||||||
|
|
||||||
|
fileApi
|
||||||
|
.fileUploadDynamicReturnId(uploadFormData)
|
||||||
|
.then((res) => {
|
||||||
|
if (res) {
|
||||||
|
// 保存文件ID到表单数据
|
||||||
|
const fileId = res.id || res
|
||||||
|
formData.value.icon = fileId
|
||||||
|
message.success('上传成功')
|
||||||
|
|
||||||
|
// 加载新上传的图片预览
|
||||||
|
loadImageById(fileId)
|
||||||
|
|
||||||
|
onSuccess(res)
|
||||||
|
} else {
|
||||||
|
message.error('上传失败')
|
||||||
|
onError(new Error('上传失败'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
message.error('上传失败: ' + error.message)
|
||||||
|
onError(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除图片
|
||||||
|
const handleRemove = () => {
|
||||||
|
formData.value.icon = ''
|
||||||
|
// 释放blob URL
|
||||||
|
if (imagePreviewUrl.value) {
|
||||||
|
URL.revokeObjectURL(imagePreviewUrl.value)
|
||||||
|
imagePreviewUrl.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览图片
|
||||||
|
const handlePreview = async (file) => {
|
||||||
|
// 如果已经有预览URL,直接使用
|
||||||
|
if (imagePreviewUrl.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 如果是新上传的文件,使用file对象创建预览
|
||||||
|
if (file.originFileObj) {
|
||||||
|
imagePreviewUrl.value = URL.createObjectURL(file.originFileObj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证并提交数据
|
||||||
|
const onSubmit = () => {
|
||||||
|
formRef.value
|
||||||
|
.validate()
|
||||||
|
.then(() => {
|
||||||
|
submitLoading.value = true
|
||||||
|
const apiMethod = formData.value.id ? deviceApi.edit : deviceApi.add
|
||||||
|
apiMethod(formData.value)
|
||||||
|
.then(() => {
|
||||||
|
message.success('保存成功')
|
||||||
|
onClose()
|
||||||
|
emit('successful')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
submitLoading.value = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||||
|
defineExpose({
|
||||||
|
onOpen
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.form-content {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview-section {
|
||||||
|
flex: 0 0 350px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.preview-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.device-image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 400px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
&.full-width {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-uploader {
|
||||||
|
:deep(.ant-upload-select-picture-card) {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-upload-list-picture-card-container) {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-tip {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
223
nl-vue/src/views/nl_base_data/device/index.vue
Normal file
223
nl-vue/src/views/nl_base_data/device/index.vue
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
<template>
|
||||||
|
<a-card :bordered="false" :body-style="{ 'padding-bottom': '0px' }" class="mb-2">
|
||||||
|
<a-form ref="searchFormRef" name="advanced_search" :model="searchFormState" class="ant-advanced-search-form">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="关键词" name="searchKey">
|
||||||
|
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入设备名称或编码" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="所属区域" name="region">
|
||||||
|
<a-input v-model:value="searchFormState.region" placeholder="请输入所属区域" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button type="primary" @click="tableRef.refresh(true)">查询</a-button>
|
||||||
|
<a-button class="xn-mg08" @click="reset">重置</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
<a-card :bordered="false">
|
||||||
|
<s-table
|
||||||
|
ref="tableRef"
|
||||||
|
:columns="columns"
|
||||||
|
:data="loadData"
|
||||||
|
:alert="options.alert.show"
|
||||||
|
:row-key="(record) => record.id"
|
||||||
|
:tool-config="toolConfig"
|
||||||
|
:row-selection="options.rowSelection"
|
||||||
|
>
|
||||||
|
<template #operator class="table-operator">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="formRef.onOpen()">
|
||||||
|
<template #icon><plus-outlined /></template>
|
||||||
|
新增设备
|
||||||
|
</a-button>
|
||||||
|
<xn-batch-button
|
||||||
|
buttonName="批量删除"
|
||||||
|
icon="DeleteOutlined"
|
||||||
|
buttonDanger
|
||||||
|
:selectedRowKeys="selectedRowKeys"
|
||||||
|
@batchCallBack="deleteBatchDevice"
|
||||||
|
/>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'code'">
|
||||||
|
<span>{{ record.code }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'icon'">
|
||||||
|
<a-image v-if="record.icon && getImageUrl(record.icon)" :width="50" :src="getImageUrl(record.icon)" />
|
||||||
|
<a-spin v-else-if="record.icon" size="small" />
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'action'">
|
||||||
|
<a-space>
|
||||||
|
<a @click="formRef.onOpen(record)">编辑</a>
|
||||||
|
<a-divider type="vertical" />
|
||||||
|
<a-popconfirm title="确定要删除此设备吗?" @confirm="deleteDevice(record)">
|
||||||
|
<a-button type="link" danger size="small">删除</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</s-table>
|
||||||
|
</a-card>
|
||||||
|
<Form ref="formRef" @successful="tableRef.refresh(true)" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="nlBaseDataDevice">
|
||||||
|
import Form from './form.vue'
|
||||||
|
import deviceApi from '@/api/baseData/deviceApi'
|
||||||
|
import fileApi from '@/api/dev/fileApi'
|
||||||
|
|
||||||
|
const searchFormState = ref({})
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const tableRef = ref()
|
||||||
|
const formRef = ref()
|
||||||
|
const toolConfig = { refresh: true, height: true, columnSetting: false, striped: false }
|
||||||
|
|
||||||
|
// 存储图片blob URL的映射
|
||||||
|
const imageUrlMap = ref(new Map())
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '设备编码',
|
||||||
|
dataIndex: 'code'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备名称',
|
||||||
|
dataIndex: 'name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '所属区域',
|
||||||
|
dataIndex: 'region'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备图片',
|
||||||
|
dataIndex: 'icon'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'X坐标',
|
||||||
|
dataIndex: 'x'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Y坐标',
|
||||||
|
dataIndex: 'y'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '角度',
|
||||||
|
dataIndex: 'angle'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '放大比例',
|
||||||
|
dataIndex: 'size'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
align: 'center',
|
||||||
|
width: '150px'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
let selectedRowKeys = ref([])
|
||||||
|
|
||||||
|
// 列表选择配置
|
||||||
|
const options = {
|
||||||
|
alert: {
|
||||||
|
show: false,
|
||||||
|
clear: () => {
|
||||||
|
selectedRowKeys = ref([])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rowSelection: {
|
||||||
|
onChange: (selectedRowKey, selectedRows) => {
|
||||||
|
selectedRowKeys.value = selectedRowKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据文件ID加载图片
|
||||||
|
const loadImageById = (fileId) => {
|
||||||
|
if (!fileId || imageUrlMap.value.has(fileId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const param = {
|
||||||
|
storageId: fileId
|
||||||
|
}
|
||||||
|
|
||||||
|
fileApi
|
||||||
|
.fileDownload(param)
|
||||||
|
.then((response) => {
|
||||||
|
const blob = response.data
|
||||||
|
if (blob && blob instanceof Blob) {
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
imageUrlMap.value.set(fileId, url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('图片加载失败:', error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取图片URL
|
||||||
|
const getImageUrl = (fileId) => {
|
||||||
|
return imageUrlMap.value.get(fileId) || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadData = (parameter) => {
|
||||||
|
return deviceApi.list(Object.assign(parameter, searchFormState.value)).then((res) => {
|
||||||
|
console.log('API返回的原始数据:', res)
|
||||||
|
// 加载所有图片
|
||||||
|
if (res.records && res.records.length > 0) {
|
||||||
|
res.records.forEach((item) => {
|
||||||
|
if (item.icon) {
|
||||||
|
loadImageById(item.icon)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 转换为表格需要的格式(s-table 需要 rows 字段)
|
||||||
|
const result = {
|
||||||
|
rows: res.records || [],
|
||||||
|
total: res.total || 0,
|
||||||
|
size: res.size || 10,
|
||||||
|
current: res.current || 1
|
||||||
|
}
|
||||||
|
console.log('转换后的数据:', result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
searchFormRef.value.resetFields()
|
||||||
|
tableRef.value.refresh(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const deleteDevice = (record) => {
|
||||||
|
deviceApi.delete({ id: record.id }).then(() => {
|
||||||
|
tableRef.value.refresh(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
const deleteBatchDevice = (params) => {
|
||||||
|
const ids = params.map((item) => item.id)
|
||||||
|
Promise.all(ids.map((id) => deviceApi.delete({ id }))).then(() => {
|
||||||
|
tableRef.value.clearRefreshSelected()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件卸载时清理blob URL
|
||||||
|
onUnmounted(() => {
|
||||||
|
imageUrlMap.value.forEach((url) => {
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
})
|
||||||
|
imageUrlMap.value.clear()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
server:
|
server:
|
||||||
port: 82
|
port: 8081
|
||||||
spring:
|
spring:
|
||||||
main:
|
main:
|
||||||
allow-circular-references: true
|
allow-circular-references: true
|
||||||
@@ -12,9 +12,9 @@ spring:
|
|||||||
datasource:
|
datasource:
|
||||||
master:
|
master:
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
url: jdbc:mysql://localhost:3306/acs3.0?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true&rewriteBatchedStatements=true
|
url: jdbc:mysql://192.168.81.251:3306/acs3.0?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true&rewriteBatchedStatements=true
|
||||||
username: root
|
username: root
|
||||||
password: "123456"
|
password: "P@ssw0rd."
|
||||||
strict: true
|
strict: true
|
||||||
public-key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMWiTVtdXFVrgFHDDKELZM0SywkWY3KjugN90eY5Sogon1j8Y0ClPF7nx3FuE7pAeBKiv7ChIS0vvx/59WUpKmUCAwEAAQ==
|
public-key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMWiTVtdXFVrgFHDDKELZM0SywkWY3KjugN90eY5Sogon1j8Y0ClPF7nx3FuE7pAeBKiv7ChIS0vvx/59WUpKmUCAwEAAQ==
|
||||||
druid:
|
druid:
|
||||||
|
|||||||
Reference in New Issue
Block a user