init: 初始化 hgmj 后台管理项目

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 17:48:09 +08:00
co-authored by Claude Opus 4.7
commit 397398b746
645 changed files with 179153 additions and 0 deletions
+198
View File
@@ -0,0 +1,198 @@
<template>
<div class="upload-img-box">
<el-upload class="avatar-uploader" action="'string'" name="upload" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" :on-error="uploadError" accept="image/jpeg,image/png,image/jpg, image/gif, image/webp" :on-change="handleCrop" :style="{width: width,height: height}" :auto-upload="false" :http-request="upload" ref="multipleUpload" list-type="picture">
<img v-if="cover" :src="imgUrl + cover" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon" :style="{lineHeight: height}"></i>
</el-upload>
<p class="desc">图片大小不能超过1M,且宽高度为{{wSize + '*' + hSize}}</p>
<el-dialog :visible.sync="cropperModel" width="1100px" class="dialog-popup" append-to-body :title="'图片裁剪大小' + wSize + '*' + hSize">
<Cropper :img-file="imgFile" ref="vueCropper" :fixedNumber="[wSize,hSize]" @upload="upload">
</Cropper>
</el-dialog>
</div>
</template>
<script>
// import { mapGetters } from 'vuex'
import baseConfig from '@/utils/serverUrlConfig'
import Cropper from './cropper'
import api from '@/api'
export default {
data() {
return {
imgUrl: baseConfig.imgUrl,
cropperModel: false,
// 当前选中的文件(未压缩裁剪之前)
checkedFile: '',
// 传递到裁剪框中的图片路径
imgFile: '',
}
},
props: ['cover', 'action', 'width', 'height', 'id', 'wSize', 'hSize'],
components: {
Cropper
},
methods: {
// 图片选择过后
handleCrop(file) {
this.cropperModel = true
this.imgFile = URL.createObjectURL(file.raw)
this.checkedFile = file
},
// 图片上传前验证
beforeAvatarUpload(file) {
const isLt3M = file.size / 1024 / 1024 < 1.024;
if (!isLt3M) {
this.$message.error("封面上传大小不能超过1M");
return false
}
//const isSize = new Promise(function (resolve, reject) {
// let width = this.wSize;
// let height = this.hSize;
// let _URL = window.URL || window.webkitURL;
// let img = new Image();
// img.onload = function () {
// let valid = img.width === width && img.height === height;
// valid ? resolve() : reject();
// }
// img.src = _URL.createObjectURL(file);
//}).then(() => {
// return file;
//}, () => {
// this.$message.error('上传的图片宽高必须为' + this.wSize + '*' + this.hSize + '!');
// return Promise.reject();
//});
//return isSize;
},
// 视频封面上传成功回掉
handleAvatarSuccess(res) {
if (res.code === 200) {
this.$emit("successCallback", res)
} else {
this.$message.error('上传封面图失败')
}
},
// 图片上传错误
uploadError() {
this.$message.error('上传封面图失败')
},
// 图片上传
upload(data) {
this.cropperModel = false;
let img = new Image();
img.src = data;
img.onload = async () => {
// 将base64转换成blob
let blob = this.dataURItoBlob(data);
let formData = new FormData();
formData.append('upload', blob, this.checkedFile.name); // 有的后台需要传文件名,不然会报错
if (this.id) {
formData.append('id', this.id)
}
// 判断图片大小 超过500kb自动打包
if (formData.get('upload').size / 1024 / 1024 > 1.024) {
this.compress(img)
} else {
this.imgUpload(formData);
}
};
},
async imgUpload(formData) {
api.uploadImg(formData).then(res => {
if (res.code === 200) {
this.$emit("successCallback", res)
this.$message({
message: '上传成功',
type: 'success'
});
} else {
this.$message.error('上传失败');
}
this.uploading = false;
this.$refs.vueCropper.flag = false;
}).catch(() => {
this.uploading = false;
this.$message.error('上传失败');
});
},
// 压缩图片
compress(img) {
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
// let initSize = img.src.length;
let width = img.width;
let height = img.height;
canvas.width = width;
canvas.height = height;
// 铺底色
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, width, height);
// 进行压缩
let ndata = canvas.toDataURL('image/jpeg', 0.98);
this.upload(ndata)
},
// base64转成bolb对象
dataURItoBlob(base64Data) {
let byteString;
if (base64Data.split(',')[0].indexOf('base64') >= 0) { byteString = atob(base64Data.split(',')[1]); } else { byteString = unescape(base64Data.split(',')[1]); }
let mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0];
let ia = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], { type: mimeString });
}
}
}
</script>
<style lang="scss">
.dialog-popup {
.el-dialog {
margin-top: 0px !important;
}
}
.upload-img-box {
.avatar-uploader {
width: 150px;
height: 150px;
}
.el-icon-plus {
height: 100% !important;
}
img {
width: 100%;
height: 100%;
}
.el-upload {
width: 100%;
height: 100%;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
&:hover {
border: 1px dashed #e63765;
}
}
.desc {
line-height: 1;
margin-top: 10px;
color: #999;
font-size: 12px;
}
.avatar-uploader-icon {
width: 100%;
height: 100%;
font-size: 28px;
color: #8c939d;
line-height: 150px;
text-align: center;
&:hover {
color: 1px dashed #e63765 !important;
}
}
}
</style>