设备型号版本功能完善

This commit is contained in:
SjS 2025-04-17 17:00:08 +08:00
parent 3637f31f3f
commit b7379ee51b
24 changed files with 870 additions and 158 deletions

View File

@ -201,6 +201,8 @@ qiniu:
bucket: autosprout
# 过期时间(秒)
expireSeconds: 86400
# 七牛云token缓存
cacheKey: qiniu-token
# 七牛云GET请求域名
domain: https://lxnapi.ccttiot.com
xinzhi:

View File

@ -2,8 +2,11 @@ package com.ruoyi.common.utils.qiniu;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.spring.SpringUtils;
import java.util.concurrent.TimeUnit;
/**
* 七牛云工具类
* @author wjh
@ -16,15 +19,32 @@ public class QiNiuUtils {
public static final String BUCKET = SpringUtils.getRequiredProperty("qiniu.bucket");
public static final Long EXPIRE_SECONDS = Long.parseLong(SpringUtils.getRequiredProperty("qiniu.expireSeconds")); // 过期时间
public static final String DOMAIN = SpringUtils.getRequiredProperty("qiniu.domain"); // 域名
private static final RedisCache REDIS_CACHE = SpringUtils.getBean(RedisCache.class);
private static final String CACHE_KEY = SpringUtils.getRequiredProperty("qiniu.cacheKey");
/**
* 获取文件上传的token
* @return token
*/
// public static String getToken() {
// StringMap putPolicy = new StringMap();
// Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
// return auth.uploadToken(BUCKET,null, EXPIRE_SECONDS, putPolicy);
// }
/**
* 获取文件上传的token
* @return token
*/
public static String getToken() {
StringMap putPolicy = new StringMap();
Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
return auth.uploadToken(BUCKET,null, EXPIRE_SECONDS, putPolicy);
String tokenCache = REDIS_CACHE.getCacheObject(CACHE_KEY);
if (tokenCache == null) {
StringMap putPolicy = new StringMap();
Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
tokenCache = auth.uploadToken(BUCKET,null, EXPIRE_SECONDS, putPolicy);
REDIS_CACHE.setCacheObject(CACHE_KEY, tokenCache, EXPIRE_SECONDS.intValue(), TimeUnit.SECONDS);
}
return tokenCache;
}
// public static void main(String[] args) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

View File

@ -10,7 +10,7 @@
:on-exceed="handleExceed"
:on-success="handleUploadSuccess"
:show-file-list="false"
:data="uploadData"
:headers="headers"
class="upload-file-uploader"
ref="fileUpload"
>
@ -28,9 +28,13 @@
<!-- 文件列表 -->
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
<li :key="file.url" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
<el-link :href="`${file.url}`" :underline="false" target="_blank">
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
</el-link>
<div class="file-link">
<image-preview v-if="isImage(file.url)" :src="file.url" :width="32" :height="32" style="margin-right: 10px"/>
<i class="el-icon-document" v-else/>
<el-link :href="realUrl(file.url)" :underline="false" target="_blank">
{{ getFileName(file.name) }}
</el-link>
</div>
<div class="ele-upload-list__item-content-action">
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
</div>
@ -40,8 +44,7 @@
</template>
<script>
import { getToken } from "@/utils/auth";
import { getQiniuToken } from "@/api/common/common";
import { getToken } from '@/utils/auth'
export default {
name: "FileUpload",
@ -56,13 +59,12 @@ export default {
// (MB)
fileSize: {
type: Number,
default: 5,
default: 10,
},
// , ['png', 'jpg', 'jpeg']
fileType: {
type: Array,
// default: () => ["doc", "docx", "xls", "ppt", "txt", "pdf","bin"],
default: () => ["bin"],
default: () => ["doc", "xls", "ppt", "txt", "pdf", "xlsx", "jpg", "jpeg", "png"],
},
//
isShowTip: {
@ -75,24 +77,13 @@ export default {
number: 0,
uploadList: [],
baseUrl: process.env.VUE_APP_BASE_API,
uploadData: {token:''},
domain:"",
uploadFileUrl: "https://up-z2.qiniup.com", //
// headers: {
// Authorization: "Bearer " + getToken(),
// },
uploadFileUrl: process.env.VUE_APP_BASE_API + "/common/upload", //
headers: {
Authorization: "Bearer " + getToken(),
},
fileList: [],
};
},
mounted() {
getQiniuToken().then((res)=> {
console.log("七牛云获取token"+JSON.stringify(res));
if (res.code === 200) {
this.uploadData.token = res.token
this.domain = res.domain;
}
})
},
watch: {
value: {
handler(val) {
@ -122,8 +113,21 @@ export default {
showTip() {
return this.isShowTip && (this.fileType || this.fileSize);
},
// url
realUrl() {
return (url) => {
if (url.startsWith('http')) {
return url;
}
return this.baseUrl + url;
}
}
},
methods: {
//
isImage(url) {
return url.endsWith('.png') || url.endsWith('.jpg') || url.endsWith('.jpeg');
},
//
handleBeforeUpload(file) {
//
@ -155,24 +159,20 @@ export default {
//
handleUploadError(err) {
this.$modal.msgError("上传文件失败,请重试");
this.$modal.closeLoading()
this.$modal.closeLoading();
},
//
handleUploadSuccess(res, file) {
console.log("七牛云获取res"+JSON.stringify(res));
console.log("七牛云获取file"+JSON.stringify(file));
this.uploadList.push({ name: res.hash, url: this.domain+"/"+res.hash });
this.uploadedSuccessfully();
// if (res.code === 200) {
// this.uploadList.push({ name: res.fileName, url: res.fileName });
// this.uploadedSuccessfully();
// } else {
// this.number--;
// this.$modal.closeLoading();
// this.$modal.msgError(res.msg);
// this.$refs.fileUpload.handleRemove(file);
// this.uploadedSuccessfully();
// }
if (res.code === 200) {
this.uploadList.push({ name: res.fileName, url: res.fileName });
this.uploadedSuccessfully();
} else {
this.number--;
this.$modal.closeLoading();
this.$modal.msgError(res.msg);
this.$refs.fileUpload.handleRemove(file);
this.uploadedSuccessfully();
}
},
//
handleDelete(index) {
@ -181,9 +181,7 @@ export default {
},
//
uploadedSuccessfully() {
console.log("this.number"+this.number);
console.log("this.uploadList.length"+this.uploadList.length);
if (this.number > 0) {
if (this.number > 0 && this.uploadList.length === this.number) {
this.fileList = this.fileList.concat(this.uploadList);
this.uploadList = [];
this.number = 0;
@ -193,10 +191,11 @@ export default {
},
//
getFileName(name) {
// url
if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1);
} else {
return "";
return name;
}
},
//
@ -231,4 +230,9 @@ export default {
.ele-upload-list__item-content-action .el-link {
margin-right: 10px;
}
.file-link {
display: flex;
align-items: center;
flex-direction: row;
}
</style>

View File

@ -1,5 +1,9 @@
<template>
<div class="component-upload-image">
<div
class="component-upload-image"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
>
<el-upload
multiple
:action="uploadImgUrl"
@ -12,39 +16,85 @@
ref="imageUpload"
:on-remove="handleDelete"
:show-file-list="true"
:data='uploadData'
:headers="headers"
:file-list="fileList"
:on-preview="handlePictureCardPreview"
:class="{hide: this.fileList.length >= this.limit}"
class="image-upload"
:style="cssVars"
:data="uploadData"
drag
>
<!-- 粘贴区域隐藏 -->
<textarea
ref="pasteArea"
class="paste-area"
@paste="handlePaste"
></textarea>
<!-- 上传按钮 -->
<i class="el-icon-plus"></i>
<!-- 文件列表 -->
<template #file="{ file }">
<div :class="['el-upload-list__item', { 'is-file': !isImage(file.url) }]">
<!-- 图片 -->
<img v-if="isImage(file.url)" class="el-upload-list__item-thumbnail " :src="file.url" alt=""/>
<!-- 视频 -->
<video v-else-if="isVideo(file.url)" class="el-upload-list__item-thumbnail" :src="file.url" alt=""/>
<!-- 文件图标 -->
<img v-else class="el-upload-list__item-thumbnail" :src="getFileIcon(file.url)" alt=""/>
<!-- 操作按钮 -->
<span class="el-upload-list__item-actions">
<span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
<i class="el-icon-zoom-in"></i>
</span>
<span class="el-upload-list__item-delete" @click="handleDelete(file)">
<i class="el-icon-delete"></i>
</span>
</span>
<!-- 文件名 -->
<div class="file-name-container">
<el-tooltip :content="getDisplayFileName(file)" placement="bottom" :disabled="getDisplayFileName(file).length <= 10">
<span class="file-name" :class="{'edit-name': editName}" @click="editFileName(file)">
<i v-if="editName" class="el-icon-edit"></i>
{{ getDisplayFileName(file) }}
</span>
</el-tooltip>
</div>
</div>
</template>
</el-upload>
<!-- 上传提示 -->
<div class="el-upload__tip" slot="tip" v-if="showTip">
<div v-if="uploadType === 'image'" style="color: #909399; margin-bottom: 8px;">
建议尺寸700×286
</div>
请上传
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
<template v-if="fileSize">大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b></template>
<template v-if="fileType">格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b></template>
的文件
<br/>
支持
<b style="color: #f56c6c">拖动上传</b>
和鼠标移入后
<b style="color: #f56c6c">Ctrl+V</b>
粘贴上传
</div>
<el-dialog
:visible.sync="dialogVisible"
title="预览"
width="800"
append-to-body
>
<img
:src="dialogImageUrl"
style="display: block; max-width: 100%; margin: 0 auto"
/>
<!-- 预览 -->
<el-dialog :visible.sync="dialogVisible" title="预览" width="800" append-to-body>
<video v-if="isVideo(dialogImageUrl)" :src="dialogImageUrl" controls style="display: block; max-width: 100%; margin: 0 auto"/>
<img v-else :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto"/>
</el-dialog>
</div>
</template>
<script>
import { getToken } from "@/utils/auth";
import {getQiniuToken} from "@/api/common/common";
import { getToken } from '@/utils/auth'
import { getQiniuToken } from "@/api/tool/common";
import axios from 'axios'
import { getRealUrl, getFileIcon, getExt, isImage, getFileNameWithTime, isVideo } from '@/utils'
export default {
props: {
@ -57,7 +107,11 @@ export default {
// (MB)
fileSize: {
type: Number,
default: 5,
default: 200,
},
uploadType: {
type: String,
default: 'image' // image/video
},
// , ['png', 'jpg', 'jpeg']
fileType: {
@ -68,6 +122,35 @@ export default {
isShowTip: {
type: Boolean,
default: true
},
width: {
type: String,
default: "148px"
},
height: {
type: String,
default: "148px"
},
// localqiniu
host: {
type: String,
default: "qiniu",
validator: function (value) {
return ['local', 'qiniu'].includes(value);
}
},
//
editName: {
type: Boolean,
default: false
},
// string array-string array-object
valueType: {
type: String,
default: 'string',
validator: function (value) {
return ['string', 'array-string', 'array-object'].includes(value);
}
}
},
data() {
@ -78,37 +161,35 @@ export default {
dialogVisible: false,
hideUpload: false,
baseUrl: process.env.VUE_APP_BASE_API,
uploadData: {token:''},
domain:"",
uploadImgUrl:"https://up-z2.qiniup.com", //
// headers: {
// Authorization: "Bearer " + getToken(),
// },
fileList: []
fileList: [],
isListening: false,
qiniuToken: null,
uploadData: {},
domain: null,
};
},
mounted(){
getQiniuToken().then(res => {
debugger
console.log("获取到七牛云token"+JSON.stringify(res));
if (res.code === 200) {
this.uploadData.token = res.token;
this.domain = res.domain;
}
})
},
watch: {
value: {
handler(val) {
if (val) {
//
const list = Array.isArray(val) ? val : this.value.split(',');
let list = [];
if (Array.isArray(val)) {
list = val;
} else {
list = val.split(',');
}
//
this.fileList = list.map(item => {
if (typeof item === "string") {
item = { name: item, url: item };
return {
name: item, //
url: this.getRealUrl(item)
};
} else {
return item;
}
return item;
});
} else {
this.fileList = [];
@ -124,38 +205,198 @@ export default {
showTip() {
return this.isShowTip && (this.fileType || this.fileSize);
},
//
cssVars() {
return {
'--width': this.width,
'--height': this.height
}
},
//
getFileIcon() {
return (url) => {
return getFileIcon(url);
}
},
//
isImage() {
return (url) => {
return isImage(url);
}
},
//
isVideo() {
return (url) => {
return isVideo(url);
}
},
//
getDisplayFileName() {
return (file) => {
let fileName = file.name;
// URL
if (fileName.includes('/')) {
fileName = fileName.split('/').pop();
}
return fileName;
}
},
//
uploadImgUrl() {
if (this.host === 'qiniu') {
return 'https://up-z2.qiniup.com';
}
return process.env.VUE_APP_BASE_API + "/common/upload";
},
//
headers() {
if (this.host === 'local') {
return {
Authorization: "Bearer " + getToken(),
};
}
return {};
},
},
methods: {
// loading
handleBeforeUpload(file) {
let isImg = false;
if (this.fileType.length) {
let fileExtension = "";
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
editFileName(file) {
if (!this.editName) {
return;
}
this.$prompt('请输入文件名', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /^[^<>]*$/,
inputErrorMessage: '文件名不能包含<或>',
inputValue: file.name,
}).then(({ value }) => {
file.name = value;
this.emitInputValue();
});
},
// url
getRealUrl,
//
handleMouseEnter() {
if (!this.isListening) {
if (this.$refs.pasteArea) {
this.$refs.pasteArea.focus();
}
isImg = this.fileType.some(type => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
});
} else {
isImg = file.type.indexOf("image") > -1;
this.isListening = true;
}
},
//
handleMouseLeave() {
if (this.isListening) {
if (this.$refs.pasteArea) {
this.$refs.pasteArea.blur();
}
this.isListening = false;
}
},
//
handlePaste(event) {
//
event.preventDefault();
const items = event.clipboardData?.items;
if (!items) {
this.$modal.msgError('当前浏览器不支持粘贴上传');
return;
}
if (!isImg) {
this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join("/")}图片格式文件!`);
let file = null;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
file = items[i].getAsFile();
break;
}
}
if (!file) {
this.$modal.msgError('粘贴内容中不包含图片');
return;
}
//
if (this.fileList.length >= this.limit) {
this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
return;
}
this.uploadFile(file);
},
//
async uploadFile(file) {
// 使 handleBeforeUpload
const valid = await this.handleBeforeUpload(file);
if (!valid) {
return;
}
// formData
const formData = new FormData();
formData.append('file', file);
if (this.host === 'qiniu') {
formData.append('key', this.uploadData.key);
formData.append('token', this.uploadData.token);
}
//
try {
const response = await axios.post(this.uploadImgUrl, formData, {
headers: {
...this.headers,
'Content-Type': 'multipart/form-data'
}
});
this.handleUploadSuccess(response.data, file);
} catch (error) {
this.handleUploadError();
} finally {
this.$modal.closeLoading();
}
},
// loading
async handleBeforeUpload(file) {
let isValid = false;
let fileExtension = getExt(file.name);
//
isValid = this.fileType.some(type => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension === type.toLowerCase()) return true;
return false;
});
if (!isValid) {
this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);
return false;
}
if (this.fileSize) {
const isLt = file.size / 1024 / 1024 < this.fileSize;
if (!isLt) {
this.$modal.msgError(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
this.$modal.msgError(`上传文件大小不能超过 ${this.fileSize} MB!`);
return false;
}
}
this.$modal.loading("正在上传图片,请稍候...");
this.number++;
this.$modal.loading("正在上传文件,请稍候...");
// token
if (this.host === 'qiniu') {
let tokenRes = await getQiniuToken();
console.log("tokenRes", tokenRes)
if (tokenRes.code === 200) {
this.uploadData.key = getFileNameWithTime(file.name);
this.uploadData.token = tokenRes.token;
this.domain = tokenRes.domain;
}
}
return true;
},
//
handleExceed() {
@ -163,49 +404,86 @@ export default {
},
//
handleUploadSuccess(res, file) {
console.log("上传成功回调token"+JSON.stringify(res));
console.log("上传成功回调file"+JSON.stringify(file));
// if (res.code === 200) {
this.uploadList.push({ name: res.hash, url: this.domain+"/"+res.hash });
this.uploadedSuccessfully();
// } else {
// this.number--;
// this.$modal.closeLoading();
// this.$modal.msgError(res.msg);
// this.$refs.imageUpload.handleRemove(file);
// this.uploadedSuccessfully();
// }
if (this.host === 'qiniu') {
//
this.uploadList.push({
name: res.key,
url: this.domain + '/' + res.key //
});
this.uploadedSuccessfully();
} else {
if (res.code === 200) {
this.uploadList.push({
name: file.name, //
url: res.fileName // URL
});
this.uploadedSuccessfully();
} else {
this.number--;
this.$modal.closeLoading();
this.$modal.msgError(res.msg);
this.$refs.imageUpload.handleRemove(file);
this.uploadedSuccessfully();
}
}
},
//
handleDelete(file) {
const findex = this.fileList.map(f => f.name).indexOf(file.name);
if(findex > -1) {
if (findex > -1) {
this.fileList.splice(findex, 1);
this.$emit("input", this.listToString(this.fileList));
this.emitInputValue();
}
},
// input
emitInputValue() {
// valuefileList
if (this.valueType === 'string') {
this.$emit("input", this.listToString(this.fileList));
}
// valuefileList
else if (this.valueType === 'array-string') {
this.$emit("input", this.fileList.map(f => f.url));
}
// value
else if (this.valueType === 'array-object') {
this.$emit("input", this.fileList);
}
},
//
handleUploadError() {
this.$modal.msgError("上传图片失败,请重试");
this.$modal.msgError("上传文件失败,请重试");
this.$modal.closeLoading();
},
//
uploadedSuccessfully() {
console.log("this.number"+this.number);
console.log("this.uploadList.length"+this.uploadList.length);
if (this.number > 0) {
if (this.number > 0 && this.uploadList.length === this.number) {
this.fileList = this.fileList.concat(this.uploadList);
this.uploadList = [];
this.number = 0;
this.$emit("input", this.listToString(this.fileList));
this.emitInputValue();
this.$modal.closeLoading();
}
},
//
handlePictureCardPreview(file) {
console.log("预览:"+JSON.stringify(file));
this.dialogImageUrl = file.url;
this.dialogVisible = true;
const fileUrl = file.url;
if (!this.isImage(file.name) && !this.isVideo(file.name)) {
this.downloadFile(file);
} else {
// 使
this.dialogImageUrl = fileUrl;
this.dialogVisible = true;
}
},
//
downloadFile(file) {
const link = document.createElement('a');
link.href = file.url;
link.download = this.getDisplayFileName(file);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
//
listToString(list, separator) {
@ -222,19 +500,155 @@ export default {
};
</script>
<style scoped lang="scss">
// .el-upload--picture-card
::v-deep.hide .el-upload--picture-card {
display: none;
}
//
::v-deep .el-list-enter-active,
::v-deep .el-list-leave-active {
transition: all 0s;
/* 上传区域基础样式 */
::v-deep .el-upload {
display: flex;
justify-content: center;
align-items: center;
}
::v-deep .el-list-enter, .el-list-leave-active {
/* 上传按钮样式 */
::v-deep .el-upload--picture-card {
width: var(--width);
height: var(--height);
}
/* 隐藏上传按钮 */
::v-deep.hide .el-upload--picture-card {
display: none;
}
/* 已上传图片项样式 */
::v-deep .el-upload-list__item {
position: relative;
overflow: hidden;
width: var(--width);
height: var(--height);
.el-upload-list__item-thumbnail {
object-fit: cover;
object-position: center;
}
&.is-file {
.el-upload-list__item-thumbnail {
padding: 20px;
background-color: #f5f7fa;
object-fit: contain;
}
}
&:hover {
.el-upload-list__item-actions {
opacity: 1;
}
}
.el-upload-list__item-actions {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
opacity: 0;
transform: translateY(0);
font-size: 20px;
background-color: rgba(0, 0, 0, 0.5);
cursor: default;
text-align: center;
transition: opacity .3s;
.el-upload-list__item-delete {
position: static;
font-size: inherit;
color: inherit;
margin: 0 8px;
}
.el-icon-delete {
cursor: pointer;
}
.el-icon-zoom-in {
cursor: pointer;
}
}
}
/* 拖拽上传区域样式 */
::v-deep .el-upload-dragger {
width: var(--width);
height: var(--height);
display: flex;
justify-content: center;
align-items: center;
}
/* 文件类型图标样式 */
.file-icon {
width: 48px;
height: 48px;
margin: 0 auto;
}
/* 文件名容器样式 */
.file-name-container {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.5);
padding: 4px;
text-align: center;
z-index: 1;
}
/* 文件名样式 */
.file-name {
color: #fff;
font-size: 12px;
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 4px;
}
.edit-name {
cursor: pointer;
&:hover {
color: #409EFF;
}
}
/* 调整列表动画时间 */
::v-deep .el-list-enter-active {
transition: all 0.1s;
}
::v-deep .el-list-leave-active {
transition: all 0.1s;
}
/* 确保列表项立即靠左显示 */
::v-deep .el-upload-list--picture-card {
.el-upload-list__item {
transition: none;
}
}
/* 粘贴区域样式 */
.paste-area {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
z-index: 1;
}
</style>

View File

@ -0,0 +1,19 @@
// 文件类型
export const FileType = {
// 图片
IMAGE: ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp', 'ico'],
// 办公
OFFICE: ['doc', 'docx', 'xls', 'xlsx', 'pdf', 'ppt', 'pptx'],
// 音频
AUDIO: ['mp3', 'wav', 'm4a', 'ogg', 'flac', 'aac'],
// 视频
VIDEO: ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mpeg', 'mpg', 'm4v', 'webm', 'mkv'],
// 压缩文件
ZIP: ['zip', 'rar', '7z'],
// 其他
OTHER: ['exe'],
// 全部
all() {
return [...this.IMAGE, ...this.OFFICE, ...this.AUDIO, ...this.VIDEO, ...this.OTHER, ...this.ZIP];
}
}

View File

@ -1,16 +1,17 @@
import { parseTime } from './ruoyi'
import {FileType} from "@/utils/constants";
/**
* 表格时间格式化
*/
export function formatDate(cellValue) {
if (cellValue == null || cellValue == "") return "";
var date = new Date(cellValue)
var date = new Date(cellValue)
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
}
@ -77,6 +78,64 @@ export function getQueryObject(url) {
return obj
}
// 获取真实url
export function getRealUrl(url) {
if (url == null ) {
return url;
}
if (url.startsWith('http')) {
return url;
}
return `${process.env.VUE_APP_BASE_API}${url}`;
}
// 获取文件图标
export function getFileIcon(fileName) {
const ext = getExt(fileName);
if(['doc', 'docx'].includes(ext)) {
return require('@/assets/fileicons/word.png');
} else if(['xls', 'xlsx'].includes(ext)) {
return require('@/assets/fileicons/excel.png');
} else if(['ppt', 'pptx'].includes(ext)) {
return require('@/assets/fileicons/ppt.png');
} else if(ext === 'pdf') {
return require('@/assets/fileicons/pdf.png');
} else if(FileType.VIDEO.includes(ext)) {
return require('@/assets/fileicons/video.png');
} else if(FileType.ZIP.includes(ext)) {
return require('@/assets/fileicons/zip.png');
}
return require('@/assets/fileicons/unknown.png');
}
// 获取文件扩展名
export function getExt(fileName) {
let fileExtension = '';
if (fileName.lastIndexOf(".") > -1) {
fileExtension = fileName.slice(fileName.lastIndexOf(".") + 1).toLowerCase();
}
return fileExtension;
}
// 是否为图片
export function isImage(url) {
const ext = getExt(url);
return FileType.IMAGE.includes(ext);
}
// 是否为视频
export function isVideo(url) {
const ext = getExt(url);
return FileType.VIDEO.includes(ext);
}
// 为文件名在后缀和名称之间拼接时间戳
export function getFileNameWithTime(fileName) {
const ext = getExt(fileName);
const name = fileName.slice(0, fileName.lastIndexOf("."));
return `${name}-${Date.now()}.${ext}`;
}
/**
* @param {string} input value
* @returns {number} output value
@ -330,7 +389,7 @@ export function makeMap(str, expectsLowerCase) {
? val => map[val.toLowerCase()]
: val => map[val]
}
export const exportDefault = 'export default '
export const beautifierConf = {
@ -387,4 +446,4 @@ export function camelCase(str) {
export function isNumberStr(str) {
return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
}

View File

@ -94,7 +94,7 @@
<el-table-column label="id" align="center" prop="modelId" />
<el-table-column label="名称" align="center" prop="modelName" />
<el-table-column label="型号" align="center" prop="model" />
<el-table-column label="固件版本" align="center" prop="version" />
<el-table-column label="最新固件" align="center" prop="version" />
<el-table-column label="图片" align="center" prop="picture" width="100">
<template slot-scope="scope">
<image-preview :src="scope.row.picture" :width="50" :height="50"/>
@ -144,6 +144,9 @@
<el-form-item label="型号" prop="model">
<el-input v-model="form.model" placeholder="请输入型号" />
</el-form-item>
<el-form-item label="型号前缀" prop="pre">
<el-input v-model="form.pre" type="textarea" placeholder="请输入型号前缀,多个前缀用逗号分隔" />
</el-form-item>
<el-form-item label="分类" prop="classifyId">
<el-select v-model="form.classifyId" filterable clearable placeholder="请选择" >
<el-option
@ -154,6 +157,7 @@
</el-option>
</el-select>
</el-form-item>
<!-- <el-form-item label="版本" prop="versionId">-->
<!-- <el-select v-model="form.versionId" filterable clearable placeholder="请绑定固件后再选择版本" >-->
<!-- <el-option-->
@ -164,11 +168,29 @@
<!-- </el-option>-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<el-form-item label="图片" prop="picture">
<image-upload v-model="form.picture"/>
<el-form-item label="型号说明" prop="articleId">
<el-select v-model="form.articleId" clearable placeholder="请选择" >
<el-option
v-for="item in articleOptions"
:key="item.articleId"
:label="item.title"
:value="item.articleId">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="型号前缀" prop="pre">
<el-input v-model="form.pre" type="textarea" placeholder="请输入型号前缀,多个前缀用逗号分隔" />
<el-form-item label="图片" prop="picture">
<image-upload
v-model="form.picture"
upload-type="image"
/>
</el-form-item>
<el-form-item label="介绍视频" prop="video">
<image-upload
v-model="form.video"
upload-type="video"
:file-size="1024"
:file-type="['mp4','avi','mov','wmv','flv','mkv', 'png']"
/>
</el-form-item>
<!-- <el-form-item label="S/N" prop="idCode">-->
<!-- <el-input v-model="form.idCode" placeholder="请输入S/N" />-->
@ -192,6 +214,7 @@
import { listModel, getModel, delModel, addModel, updateModel } from "@/api/device/model";
import {listClassify} from "@/api/device/classify";
import {listVersion} from "@/api/device/version";
import {listArticle} from "@/api/system/article";
export default {
name: "Model",
@ -201,6 +224,8 @@ export default {
classifyOptions: [],
//
versionOptions: [],
//
articleOptions: [],
//
loading: true,
//
@ -226,6 +251,7 @@ export default {
modelName: null,
model: null,
picture: null,
video:null,
idCode: null,
classifyName: null,
versionId: null,
@ -288,6 +314,11 @@ export default {
this.loading = false;
});
});
// id
listArticle({classifyId:103}).then(response => {
this.articleOptions = response.rows;
this.loading = true;
});
},
//
cancel() {
@ -300,6 +331,7 @@ export default {
modelId: null,
classifyId: null,
modelName: null,
articleId: null,
model: null,
picture: null,
idCode: null,

View File

@ -25,6 +25,9 @@ public class AsModel extends BaseEntity
/** 分类ID */
private Long classifyId;
@Excel(name = "文章id")
private String articleId;
/** 名称 */
@Excel(name = "名称")
private String modelName;
@ -73,4 +76,7 @@ public class AsModel extends BaseEntity
@Excel(name = "固件版本号")
private String version;
@Excel(name = "介绍视频")
private String video;
}

View File

@ -80,4 +80,5 @@ public interface AsModelMapper extends BaseMapper<AsModel>
public AsModel checkModelUnique(String model);
public AsModelVO checkModelByPre(AsModel asModel);
}

View File

@ -0,0 +1,59 @@
package com.ruoyi.device.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.device.domain.AsModel;
import java.util.List;
/**
* 型号列表Mapper接口
*
* @author qiuzhenzhao
* @date 2023-11-11
*/
public interface AsModelVideoMapper extends BaseMapper<AsModel>
{
/**
* 查询型号列表
*
* @param id 型号列表主键
* @return 型号列表
*/
public AsModel selectAsModeVideolById(Long id);
/**
* 新增型号列表
*
* @param asModel 型号列表
* @return 结果
*/
public int insertAsModelVideo(AsModel asModel);
/**
* 修改型号列表
*
* @param asModel 型号列表
* @return 结果
*/
public int updateAsModelVideo(AsModel asModel);
/**
* 删除型号列表
*
* @param modelId 型号列表主键
* @return 结果
*/
public int deleteAsModelVideoByModelId(Long modelId);
/**
* 批量删除型号列表
*
* @param modelIds 需要删除的数据主键集合
* @return 结果
*/
public int deleteAsModelVideoByModelIds(Long[] modelIds);
}

View File

@ -42,7 +42,7 @@ public interface IAsModelService
* @param asModel 型号列表
* @return 结果
*/
public int insertAsModel(AsModel asModel);
public Boolean insertAsModel(AsModel asModel);
/**
* 修改型号列表

View File

@ -511,9 +511,19 @@ public class AsDeviceServiceImpl extends ServiceImpl<AsDeviceMapper, AsDevice> i
asDevice.setDeviceName(defaultName);
int i = bandSn(asDevice);
ServiceUtil.assertion(i == 0, "录入失败!");
AsDevice device = setDeviceValue(asDevice);
i = asDeviceMapper.insertAsDevice(device);
ServiceUtil.assertion(i == 0, "绑定失败!");
AsDevice vo = asDeviceMapper.selectAsDeviceByMac(asDevice.getMac());
if (vo != null) {
if (vo.getUserId().equals(0L)){
device.setDeviceId(vo.getDeviceId());
i = asDeviceMapper.updateAsDevice(device);
}
}
else {
i = asDeviceMapper.insertAsDevice(device);
}
ServiceUtil.assertion(i == 0, "绑定失败!");
// 切换默认设备
// toggleDevice(asDevice.getUserId(), asDevice.getDeviceId());
logger.info("=================【绑定设备】成功==================");
@ -665,7 +675,9 @@ public class AsDeviceServiceImpl extends ServiceImpl<AsDeviceMapper, AsDevice> i
public int bandSn(AsDevice asDevice) {
AsDevice device = asDeviceMapper.selectAsDeviceByMac(asDevice.getMac());
if(ObjectUtils.isNotEmpty(device)){
throw new ServiceException("该MAC号已经存在");
if(!device.getUserId().equals(0L)){
throw new ServiceException("该设备已经被绑定");
}
}else{
// 调用onenet接口
CreateDeviceVo createDeviceVo = new CreateDeviceVo();
@ -681,6 +693,7 @@ public class AsDeviceServiceImpl extends ServiceImpl<AsDeviceMapper, AsDevice> i
throw new ServiceException(code+"-----"+ paramsObj.getString("msg"));
}
}
return 1;
}

View File

@ -9,20 +9,21 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.device.domain.*;
import com.ruoyi.device.domain.vo.AsModelVO;
import com.ruoyi.device.mapper.AsDeviceClassifyMapper;
import com.ruoyi.device.mapper.AsDeviceMapper;
import com.ruoyi.device.mapper.AsDeviceVersionMapper;
import com.ruoyi.device.mapper.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.device.mapper.AsModelMapper;
import com.ruoyi.device.service.IAsModelService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Resource;
import javax.annotation.Tainted;
/**
* 型号列表Service业务层处理
@ -44,6 +45,10 @@ public class AsModelServiceImpl extends ServiceImpl<AsModelMapper, AsModel> impl
@Resource
private AsDeviceVersionMapper versionMapper;
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private AsModelVideoMapper asModelVideoMapper;
/**
@ -114,12 +119,26 @@ public class AsModelServiceImpl extends ServiceImpl<AsModelMapper, AsModel> impl
* @return 结果
*/
@Override
public int insertAsModel(AsModel asModel)
public Boolean insertAsModel(AsModel asModel)
{
asModel.setCreateTime(DateUtils.getNowDate());
AsDeviceClassify deviceClassify = classifyMapper.selectAsDeviceClassifyByClassifyId(asModel.getClassifyId());
asModel.setClassifyName(deviceClassify.getClassifyName());
return asModelMapper.insertAsModel(asModel);
Boolean execute = transactionTemplate.execute(e -> {
// 插入型号数据
int i = asModelMapper.insertAsModel(asModel);
ServiceUtil.assertion(i == 0, "绑定失败!");
// 构建型号 - 介绍视频表
AsModel model = new AsModel();
model.setModelId(asModel.getModelId());
model.setVideo(asModel.getVideo());
i = asModelVideoMapper.insertAsModelVideo(model);
ServiceUtil.assertion(i == 0, "绑定失败!");
return Boolean.TRUE;
});
if(Boolean.FALSE.equals(execute))throw new ServiceException("绑定失败");
return true;
}
/**
@ -136,6 +155,7 @@ public class AsModelServiceImpl extends ServiceImpl<AsModelMapper, AsModel> impl
String classifyName = deviceClassify.getClassifyName();
asModel.setClassifyName(classifyName);
int i = asModelMapper.updateAsModel(asModel);
asModelVideoMapper.updateAsModelVideo(asModel);
AsDeviceQuery device = new AsDeviceQuery();
device.setModelId(asModel.getModelId());
List<AsDevice> asDevices = deviceMapper.selectAsDeviceList(device);
@ -153,8 +173,12 @@ public class AsModelServiceImpl extends ServiceImpl<AsModelMapper, AsModel> impl
* @return 结果
*/
@Override
@Transactional
public int deleteAsModelByModelIds(Long[] modelIds)
{
for (Long modelId : modelIds) {
asModelVideoMapper.deleteAsModelVideoByModelId(modelId);
}
return asModelMapper.deleteAsModelByModelIds(modelIds);
}
@ -165,8 +189,10 @@ public class AsModelServiceImpl extends ServiceImpl<AsModelMapper, AsModel> impl
* @return 结果
*/
@Override
public int deleteAsModelByModelId(Long modelId)
{
return asModelMapper.deleteAsModelByModelId(modelId);
}

View File

@ -80,7 +80,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="modelId != null">model_id,</if>
<if test="version != null">version,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="createLocalDateTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="size != null">size,</if>
@ -92,7 +92,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="modelId != null">#{modelId},</if>
<if test="version != null">#{version},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="createLocalDateTime != null">#{createLocalDateTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="size != null">#{size},</if>

View File

@ -26,6 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
am.model_id,
am.classify_id,
am.model_name,
am.article_id,
am.model,
am.picture,
am.id_code,
@ -38,9 +39,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
am.update_time,
am.pre,
am.remark,
adv.version
adv.version,
amv.video,
aa.title
from as_model am
left join as_device_version adv on am.version_id = adv.version_id
left join as_model_video amv on am.model_id = amv.model_id
left join as_article aa on am.article_id = aa.article_id
</sql>
<select id="selectAsModelList" parameterType="AsModel" resultMap="AsModelResult">
@ -53,7 +58,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="classifyName != null and classifyName != ''"> and am.classify_name like concat('%', #{classifyName}, '%')</if>
<if test="versionId != null "> and am.version_id = #{versionId}</if>
<if test="introduce != null and introduce != ''"> and am.introduce = #{introduce}</if>
<if test="pre != null and introduce != ''"> and am.pre = #{pre}</if>
</where>
</select>
@ -83,6 +87,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
insert into as_model
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="classifyId != null">classify_id,</if>
<if test="articleId != null">article_id,</if>
<if test="modelName != null">model_name,</if>
<if test="model != null">model,</if>
<if test="picture != null">picture,</if>
@ -99,6 +104,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="classifyId != null">#{classifyId},</if>
<if test="articleId != null">#{articleId},</if>
<if test="modelName != null">#{modelName},</if>
<if test="model != null">#{model},</if>
<if test="picture != null">#{picture},</if>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.device.mapper.AsModelVideoMapper">
<resultMap type="AsModel" id="AsModelResult">
<result property="modelId" column="model_id" />
<result property="video" column="video" />
</resultMap>
<select id="selectAsModeVideolById" resultType="AsModel">
select
amv.model_id,
amv.video
from as_model_video amv
where amv.id = #{id}
</select>
<insert id="insertAsModelVideo">
insert into as_model_video
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="modelId !=null">model_id,</if>
<if test="video !=null">video,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="modelId !=null">#{modelId},</if>
<if test="video !=null">#{video},</if>
</trim>
</insert>
<update id="updateAsModelVideo" parameterType="AsModel">
update as_model_video
<trim prefix="SET" suffixOverrides=",">
<if test="modelId != null">model_id = #{modelId},</if>
<if test="video != null">video = #{video},</if>
</trim>
where model_id = #{modelId}
</update>
<delete id="deleteAsModelVideoByModelId" parameterType="Long">
delete from as_model_video where model_id = #{modelId}
</delete>
<delete id="deleteAsModelVideoByModelIds" parameterType="String">
delete from as_model_video where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{modelId}
</foreach>
</delete>
</mapper>