【愚公系列】2022年09月 微信小程序-图片裁剪功能实现
【摘要】 前言1、图片裁剪相关api属性类型缺省值取值描述必填imgSrcString无无限制图片地址(如果是网络图片需配置安全域名)否disable_rotateBooleanfalsetrue/false禁止用户旋转(为false时建议同时设置limit_move为false)否limit_moveBooleanfalsetrue/false限制图片移动范围(裁剪框始终在图片内)(为true时建...
前言
1、图片裁剪相关api
属性 | 类型 | 缺省值 | 取值 | 描述 | 必填 |
---|---|---|---|---|---|
imgSrc | String | 无 | 无限制 | 图片地址(如果是网络图片需配置安全域名) | 否 |
disable_rotate | Boolean | false | true/false | 禁止用户旋转(为false时建议同时设置limit_move为false) | 否 |
limit_move | Boolean | false | true/false | 限制图片移动范围(裁剪框始终在图片内)(为true时建议同时设置disable_rotate为true) | 否 |
width | Number | 200 | 超过屏幕宽度自动转为屏幕宽度 | 裁剪框宽度 | 否 |
height | Number | 200 | 超过屏幕高度自动转为屏幕高度 | 裁剪框高度 | 否 |
max_width | Number | 300 | 裁剪框最大宽度 | 裁剪框最大宽度 | 否 |
max_height | Number | 300 | 裁剪框最大高度 | 裁剪框最大高度 | 否 |
min_width | Number | 100 | 裁剪框最小宽度 | 裁剪框最小宽度 | 否 |
min_height | Number | 100 | 裁剪框最小高度 | 裁剪框最小高度 | 否 |
disable_width | Boolean | false | true/false | 锁定裁剪框宽度 | 否 |
disable_height | Boolean | false | true/false | 锁定裁剪框高度 | 否 |
disable_ratio | Boolean | false | true/false | 锁定裁剪框比例 | 否 |
export_scale | Number | 3 | 无限制 | 输出图片的比例(相对于裁剪框尺寸) | 否 |
quality | Number | 1 | 0-1 | 生成的图片质量 | 否 |
cut_top | Number | 居中 | 始终在屏幕内 | 裁剪框上边距 | 否 |
cut_left | Number | 居中 | 始终在屏幕内 | 裁剪框左边距 | 否 |
img_width | Number | 宽高都不设置,最小边填满裁剪框 | 支持%(不加单位为px)(只设置宽度,高度自适应) | 图片宽度 | 否 |
img_height | Number | 宽高都不设置,最小边填满裁剪框 支持%(不加单位为px)(只设置高度,宽度自适应) | 图片高度 | 否 | |
scale | Number | 1 | 无限制 | 图片的缩放比 | 否 |
angle | Number | 0 | (limit_move=true时angle=n*90) | 图片的旋转角度 | 否 |
min_scale | Number | 0.5 | 无限制 | 图片的最小缩放比 | 否 |
max_scale | Number | 2 | 无限制 | 图片的最大缩放比 | 否 |
bindload | Function | null | 函数名称 | cropper初始化完成 | 否 |
bindimageload | Function | null | 函数名称 | 图片加载完成,返回值Object{width,height,path,type等} | 否 |
bindtapcut | Function | null | 函数名称 | 点击中间裁剪框,返回值Object{src,width,height} | 否 |
2、函数说明
函数名 | 参数 | 返回值 | 描述 | 参数必填 |
---|---|---|---|---|
upload | 无 | 无 | 调起wx上传图片接口并开始剪裁 | 否 |
pushImg | src | 无 | 放入图片开始裁剪 | 是 |
getImg | Function(回调函数) | Object{url,width,height} | 裁剪并获取图片(图片尺寸 = 图片宽高 * export_scale) | 是 |
setCutXY | X、Y | 无 | 设置裁剪框位置 | 是 |
setCutSize | width、height | 无 | 设置裁剪框大小 | 是 |
setCutCenter | 无 | 无 | 设置裁剪框居中 | 否 |
setScale | scale | 无 | 设置图片缩放比例(不受min_scale、max_scale影响) | 是 |
setAngle | deg | 无 | 设置图片旋转角度(带过渡效果) | 是 |
setTransform | {x,y,angle,scale,cutX,cutY} | 无 | 图片在原有基础上的变化(scale受min_scale、max_scale影响) | 根据需要传参 |
imgReset | 无 | 无 | 重置图片的角度、缩放、位置(可以在onloadImage回调里使用) | 否 |
一、相关代码封装
1.组件的封装
1.1 wxml
<view class='image-cropper' catchtouchmove='_preventTouchMove'>
<view class='main' bindtouchend="_cutTouchEnd" bindtouchstart="_cutTouchStart" bindtouchmove="_cutTouchMove" bindtap="_click">
<view class='content'>
<view class='content_top bg_gray {{_flag_bright?"":"bg_black"}}' style="height:{{cut_top}}px;transition-property:{{_cut_animation?'':'background'}}"></view>
<view class='content_middle' style="height:{{height}}px;">
<view class='content_middle_left bg_gray {{_flag_bright?"":"bg_black"}}' style="width:{{cut_left}}px;transition-property:{{_cut_animation?'':'background'}}"></view>
<view class='content_middle_middle' style="width:{{width}}px;height:{{height}}px;transition-duration: .3s;transition-property:{{_cut_animation?'':'background'}};">
<view class="border border-top-left"></view>
<view class="border border-top-right"></view>
<view class="border border-right-top"></view>
<view class="border border-right-bottom"></view>
<view class="border border-bottom-right"></view>
<view class="border border-bottom-left"></view>
<view class="border border-left-bottom"></view>
<view class="border border-left-top"></view>
</view>
<view class='content_middle_right bg_gray {{_flag_bright?"":"bg_black"}}' style="transition-property:{{_cut_animation?'':'background'}}"></view>
</view>
<view class='content_bottom bg_gray {{_flag_bright?"":"bg_black"}}' style="transition-property:{{_cut_animation?'':'background'}}"></view>
</view>
<image bindload="imageLoad" bindtouchstart="_start" bindtouchmove="_move" bindtouchend="_end" style="width:{{img_width ? img_width + 'px' : 'auto'}};height:{{img_height ? img_height + 'px' : 'auto'}};transform:translate3d({{_img_left-img_width/2}}px,{{_img_top-img_height/2}}px,0) scale({{scale}}) rotate({{angle}}deg);transition-duration:{{_cut_animation?.4:0}}s;" class='img' src='{{imgSrc}}'></image>
</view>
<canvas canvas-id='image-cropper' disable-scroll="true" style="width:{{_canvas_width * export_scale}}px;height:{{_canvas_height * export_scale}}px;left:{{canvas_left}}px;top:{{canvas_top}}px" class='image-cropper-canvas'></canvas>
</view>
1.2 js
Component({
properties: {
/**
* 图片路径
*/
'imgSrc': {
type: String
},
/**
* 裁剪框高度
*/
'height': {
type: Number,
value: 200
},
/**
* 裁剪框宽度
*/
'width': {
type: Number,
value: 200
},
/**
* 裁剪框最小尺寸
*/
'min_width': {
type: Number,
value: 100
},
'min_height': {
type: Number,
value: 100
},
/**
* 裁剪框最大尺寸
*/
'max_width': {
type: Number,
value: 300
},
'max_height': {
type: Number,
value: 300
},
/**
* 裁剪框禁止拖动
*/
'disable_width': {
type: Boolean,
value: false
},
'disable_height': {
type: Boolean,
value: false
},
/**
* 锁定裁剪框比例
*/
'disable_ratio':{
type: Boolean,
value: false
},
/**
* 生成的图片尺寸相对剪裁框的比例
*/
'export_scale': {
type: Number,
value: 3
},
/**
* 生成的图片质量0-1
*/
'quality': {
type: Number,
value: 1
},
'cut_top': {
type: Number,
value: null
},
'cut_left': {
type: Number,
value: null
},
/**
* canvas上边距(不设置默认不显示)
*/
'canvas_top': {
type: Number,
value: null
},
/**
* canvas左边距(不设置默认不显示)
*/
'canvas_left': {
type: Number,
value: null
},
/**
* 图片宽度
*/
'img_width': {
type: null,
value: null
},
/**
* 图片高度
*/
'img_height': {
type: null,
value: null
},
/**
* 图片缩放比
*/
'scale': {
type: Number,
value: 1
},
/**
* 图片旋转角度
*/
'angle': {
type: Number,
value: 0
},
/**
* 最小缩放比
*/
'min_scale': {
type: Number,
value: 0.5
},
/**
* 最大缩放比
*/
'max_scale': {
type: Number,
value: 2
},
/**
* 是否禁用旋转
*/
'disable_rotate': {
type: Boolean,
value: false
},
/**
* 是否限制移动范围(剪裁框只能在图片内)
*/
'limit_move':{
type: Boolean,
value: false
}
},
data: {
el: 'image-cropper', //暂时无用
info: wx.getSystemInfoSync(),
MOVE_THROTTLE:null,//触摸移动节流settimeout
MOVE_THROTTLE_FLAG: true,//节流标识
INIT_IMGWIDTH: 0, //图片设置尺寸,此值不变(记录最初设定的尺寸)
INIT_IMGHEIGHT: 0, //图片设置尺寸,此值不变(记录最初设定的尺寸)
TIME_BG: null,//背景变暗延时函数
TIME_CUT_CENTER:null,
_touch_img_relative: [{
x: 0,
y: 0
}], //鼠标和图片中心的相对位置
_flag_cut_touch:false,//是否是拖动裁剪框
_hypotenuse_length: 0, //双指触摸时斜边长度
_flag_img_endtouch: false, //是否结束触摸
_flag_bright: true, //背景是否亮
_canvas_overflow:true,//canvas缩略图是否在屏幕外面
_canvas_width:200,
_canvas_height:200,
origin_x: 0.5, //图片旋转中心
origin_y: 0.5, //图片旋转中心
_cut_animation: false,//是否开启图片和裁剪框过渡
_img_top: wx.getSystemInfoSync().windowHeight / 2, //图片上边距
_img_left: wx.getSystemInfoSync().windowWidth / 2, //图片左边距
watch: {
//监听截取框宽高变化
width(value, that) {
if (value < that.data.min_width){
that.setData({
width: that.data.min_width
});
}
that._computeCutSize();
},
height(value, that) {
if (value < that.data.min_height) {
that.setData({
height: that.data.min_height
});
}
that._computeCutSize();
},
angle(value, that){
//停止居中裁剪框,继续修改图片位置
that._moveStop();
if(that.data.limit_move){
if (that.data.angle % 90) {
that.setData({
angle: Math.round(that.data.angle / 90) * 90
});
return;
}
}
},
_cut_animation(value, that){
//开启过渡300毫秒之后自动关闭
clearTimeout(that.data._cut_animation_time);
if (value){
that.data._cut_animation_time = setTimeout(()=>{
that.setData({
_cut_animation:false
});
},300)
}
},
limit_move(value, that){
if (value) {
if (that.data.angle%90){
that.setData({
angle: Math.round(that.data.angle / 90)*90
});
}
that._imgMarginDetectionScale();
!that.data._canvas_overflow && that._draw();
}
},
canvas_top(value, that){
that._canvasDetectionPosition();
},
canvas_left(value, that){
that._canvasDetectionPosition();
},
imgSrc(value, that){
that.pushImg();
},
cut_top(value, that) {
that._cutDetectionPosition();
if (that.data.limit_move) {
!that.data._canvas_overflow && that._draw();
}
},
cut_left(value, that) {
that._cutDetectionPosition();
if (that.data.limit_move) {
!that.data._canvas_overflow && that._draw();
}
}
}
},
attached() {
this.data.info = wx.getSystemInfoSync();
//启用数据监听
this._watcher();
this.data.INIT_IMGWIDTH = this.data.img_width;
this.data.INIT_IMGHEIGHT = this.data.img_height;
this.setData({
_canvas_height: this.data.height,
_canvas_width: this.data.width,
});
this._initCanvas();
this.data.imgSrc && (this.data.imgSrc = this.data.imgSrc);
//根据开发者设置的图片目标尺寸计算实际尺寸
this._initImageSize();
//设置裁剪框大小>设置图片尺寸>绘制canvas
this._computeCutSize();
//检查裁剪框是否在范围内
this._cutDetectionPosition();
//检查canvas是否在范围内
this._canvasDetectionPosition();
//初始化完成
this.triggerEvent('load', {
cropper: this
});
},
methods: {
/**
* 上传图片
*/
upload() {
let that = this;
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success(res) {
const tempFilePaths = res.tempFilePaths[0];
that.pushImg(tempFilePaths);
wx.showLoading({
title: '加载中...'
})
}
})
},
/**
* 检测剪裁框位置是否在允许的范围内(屏幕内)
*/
_cutDetectionPosition(){
let _cutDetectionPositionTop = () => {
//检测上边距是否在范围内
if (this.data.cut_top < 0) {
this.setData({
cut_top: 0
});
}
if (this.data.cut_top > this.data.info.windowHeight - this.data.height) {
this.setData({
cut_top: this.data.info.windowHeight - this.data.height
});
}
}, _cutDetectionPositionLeft = () => {
//检测左边距是否在范围内
if (this.data.cut_left < 0) {
this.setData({
cut_left: 0
});
}
if (this.data.cut_left > this.data.info.windowWidth - this.data.width) {
this.setData({
cut_left: this.data.info.windowWidth - this.data.width
});
}
};
//裁剪框坐标处理(如果只写一个参数则另一个默认为0,都不写默认居中)
if (this.data.cut_top == null && this.data.cut_left == null) {
this._setCutCenter();
} else if (this.data.cut_top != null && this.data.cut_left != null){
_cutDetectionPositionTop();
_cutDetectionPositionLeft();
} else if (this.data.cut_top != null && this.data.cut_left == null) {
_cutDetectionPositionTop();
this.setData({
cut_left: (this.data.info.windowWidth - this.data.width) / 2
});
} else if (this.data.cut_top == null && this.data.cut_left != null) {
_cutDetectionPositionLeft();
this.setData({
cut_top: (this.data.info.windowHeight - this.data.height) / 2
});
}
},
//渲染
_draw(callback) {
if (!this.data.imgSrc) return;
let draw = () => {
//图片实际大小
let img_width = this.data.img_width * this.data.scale * this.data.export_scale;
let img_height = this.data.img_height * this.data.scale * this.data.export_scale;
//canvas和图片的相对距离
var xpos = this.data._img_left - this.data.cut_left;
var ypos = this.data._img_top - this.data.cut_top;
//旋转画布
this.data.ctx.translate(xpos * this.data.export_scale, ypos * this.data.export_scale);
this.data.ctx.rotate(this.data.angle * Math.PI / 180);
this.data.ctx.drawImage(this.data.imgSrc, -img_width / 2, -img_height / 2, img_width, img_height);
this.data.ctx.draw(false, () => {
callback && callback();
});
}
if (this.data.ctx.width != this.data.width || this.data.ctx.height != this.data.height){
//优化拖动裁剪框,所以必须把宽高设置放在离用户触发渲染最近的地方
this.setData({
_canvas_height: this.data.height,
_canvas_width: this.data.width,
},()=>{
//延迟40毫秒防止点击过快出现拉伸或裁剪过多
setTimeout(() => {
draw();
}, 40);
});
}else{
draw();
}
},
//裁剪框处理
_cutTouchMove(e) {
if (this.data._flag_cut_touch && this.data.MOVE_THROTTLE_FLAG) {
if (this.data.disable_ratio && (this.data.disable_width || this.data.disable_height)) return;
//节流
this.data.MOVE_THROTTLE_FLAG = false;
this._move_throttle();
let width = this.data.width,
height = this.data.height,
cut_top = this.data.cut_top,
cut_left = this.data.cut_left,
size_correct = () => {
width = width <= this.data.max_width ? width >= this.data.min_width ? width : this.data.min_width : this.data.max_width;
height = height <= this.data.max_height ? height >= this.data.min_height ? height : this.data.min_height : this.data.max_height;
},
size_inspect = () => {
if ((width > this.data.max_width || width < this.data.min_width || height > this.data.max_height || height < this.data.min_height) && this.data.disable_ratio) {
size_correct();
return false;
} else {
size_correct();
return true;
}
};
height = this.data.CUT_START.height + ((this.data.CUT_START.corner > 1 && this.data.CUT_START.corner < 4 ? 1 : -1) * (this.data.CUT_START.y - e.touches[0].clientY));
switch (this.data.CUT_START.corner) {
case 1:
width = this.data.CUT_START.width + this.data.CUT_START.x - e.touches[0].clientX;
if (this.data.disable_ratio) {
height = width / (this.data.width / this.data.height)
}
if (!size_inspect()) return;
cut_left = this.data.CUT_START.cut_left - (width - this.data.CUT_START.width);
break
case 2:
width = this.data.CUT_START.width + this.data.CUT_START.x - e.touches[0].clientX;
if (this.data.disable_ratio) {
height = width / (this.data.width / this.data.height)
}
if (!size_inspect()) return;
cut_top = this.data.CUT_START.cut_top - (height - this.data.CUT_START.height)
cut_left = this.data.CUT_START.cut_left - (width - this.data.CUT_START.width)
break
case 3:
width = this.data.CUT_START.width - this.data.CUT_START.x + e.touches[0].clientX;
if (this.data.disable_ratio) {
height = width / (this.data.width / this.data.height)
}
if (!size_inspect()) return;
cut_top = this.data.CUT_START.cut_top - (height - this.data.CUT_START.height);
break
case 4:
width = this.data.CUT_START.width - this.data.CUT_START.x + e.touches[0].clientX;
if (this.data.disable_ratio) {
height = width / (this.data.width / this.data.height)
}
if (!size_inspect()) return;
break
}
if (!this.data.disable_width && !this.data.disable_height) {
this.setData({
width: width,
cut_left: cut_left,
height: height,
cut_top: cut_top,
})
} else if (!this.data.disable_width) {
this.setData({
width: width,
cut_left: cut_left
})
} else if (!this.data.disable_height) {
this.setData({
height: height,
cut_top: cut_top
})
}
this._imgMarginDetectionScale();
}
},
_cutTouchStart(e) {
let currentX = e.touches[0].clientX;
let currentY = e.touches[0].clientY;
let cutbox_top4 = this.data.cut_top + this.data.height - 30;
let cutbox_bottom4 = this.data.cut_top + this.data.height + 20;
let cutbox_left4 = this.data.cut_left + this.data.width - 30;
let cutbox_right4 = this.data.cut_left + this.data.width + 30;
let cutbox_top3 = this.data.cut_top - 30;
let cutbox_bottom3 = this.data.cut_top + 30;
let cutbox_left3 = this.data.cut_left + this.data.width - 30;
let cutbox_right3 = this.data.cut_left + this.data.width + 30;
let cutbox_top2 = this.data.cut_top - 30;
let cutbox_bottom2 = this.data.cut_top + 30;
let cutbox_left2 = this.data.cut_left - 30;
let cutbox_right2 = this.data.cut_left + 30;
let cutbox_top1 = this.data.cut_top + this.data.height - 30;
let cutbox_bottom1 = this.data.cut_top + this.data.height + 30;
let cutbox_left1 = this.data.cut_left - 30;
let cutbox_right1 = this.data.cut_left + 30;
if (currentX > cutbox_left4 && currentX < cutbox_right4 && currentY > cutbox_top4 && currentY < cutbox_bottom4) {
this._moveDuring();
this.data._flag_cut_touch = true;
this.data._flag_img_endtouch = true;
this.data.CUT_START = {
width: this.data.width,
height: this.data.height,
x: currentX,
y: currentY,
corner: 4
}
} else if (currentX > cutbox_left3 && currentX < cutbox_right3 && currentY > cutbox_top3 && currentY < cutbox_bottom3) {
this._moveDuring();
this.data._flag_cut_touch = true;
this.data._flag_img_endtouch = true;
this.data.CUT_START = {
width: this.data.width,
height: this.data.height,
x: currentX,
y: currentY,
cut_top: this.data.cut_top,
cut_left: this.data.cut_left,
corner: 3
}
} else if (currentX > cutbox_left2 && currentX < cutbox_right2 && currentY > cutbox_top2 && currentY < cutbox_bottom2) {
this._moveDuring();
this.data._flag_cut_touch = true;
this.data._flag_img_endtouch = true;
this.data.CUT_START = {
width: this.data.width,
height: this.data.height,
cut_top: this.data.cut_top,
cut_left: this.data.cut_left,
x: currentX,
y: currentY,
corner: 2
}
} else if (currentX > cutbox_left1 && currentX < cutbox_right1 && currentY > cutbox_top1 && currentY < cutbox_bottom1) {
this._moveDuring();
this.data._flag_cut_touch = true;
this.data._flag_img_endtouch = true;
this.data.CUT_START = {
width: this.data.width,
height: this.data.height,
cut_top: this.data.cut_top,
cut_left: this.data.cut_left,
x: currentX,
y: currentY,
corner: 1
}
}
},
_cutTouchEnd(e) {
this._moveStop();
this.data._flag_cut_touch = false;
},
//停止移动时需要做的操作
_moveStop() {
//清空之前的自动居中延迟函数并添加最新的
clearTimeout(this.data.TIME_CUT_CENTER);
this.data.TIME_CUT_CENTER = setTimeout(() => {
//动画启动
if (!this.data._cut_animation) {
this.setData({
_cut_animation: true
});
}
this.setCutCenter();
}, 1000)
//清空之前的背景变化延迟函数并添加最新的
clearTimeout(this.data.TIME_BG);
this.data.TIME_BG = setTimeout(() => {
if (this.data._flag_bright) {
this.setData({
_flag_bright: false
});
}
}, 2000)
},
//移动中
_moveDuring() {
//清空之前的自动居中延迟函数
clearTimeout(this.data.TIME_CUT_CENTER);
//清空之前的背景变化延迟函数
clearTimeout(this.data.TIME_BG);
//高亮背景
if (!this.data._flag_bright) {
this.setData({
_flag_bright: true
});
}
},
//监听器
_watcher() {
Object.keys(this.data).forEach(v => {
this._observe(this.data, v, this.data.watch[v]);
})
},
_observe(obj, key, watchFun) {
var val = obj[key];
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
set:(value) => {
val = value;
watchFun && watchFun(val, this);
},
get() {
if (val && '_img_top|img_left||width|height|min_width|max_width|min_height|max_height|export_scale|cut_top|cut_left|canvas_top|canvas_left|img_width|img_height|scale|angle|min_scale|max_scale'.indexOf(key)!=-1){
let ret = parseFloat(parseFloat(val).toFixed(3));
if (typeof val == "string" && val.indexOf("%") != -1){
ret+='%';
}
return ret;
}
return val;
}
})
},
_preventTouchMove() {
}
}
})
2.组件的使用
2.1 wxml
<!--miniprogram/pages/2.14/cut/index.wxml-->
<view class="page-section">
<text class="page-section__title">图片裁剪</text>
<image-cropper id="image-cropper" limit_move="{{true}}" disable_rotate="{{true}}" width="{{width}}" height="{{height}}" imgSrc="{{src}}" bindload="cropperload" bindimageload="loadimage" bindtapcut="clickcut"></image-cropper>
</view>
2.2 js
// miniprogram/pages/2.14/cut/index.js
Page({
/**
* 页面的初始数据
*/
data: {
src: '',
width: 250, //宽度
height: 250, //高度
},
startCuting() {
//获取到image-cropper对象
this.cropper = this.selectComponent("#image-cropper");
//开始裁剪
this.setData({
src: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2Ftp09%2F21031FKU44S6-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664885017&t=c57f23cee60a265d5c3d70ebbaa57e30",
});
wx.showLoading({
title: '加载中'
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.startCuting()
},
cropperload(e) {
console.log("cropper初始化完成");
},
loadimage(e) {
console.log("图片加载完成", e.detail);
wx.hideLoading();
//重置图片角度、缩放、位置
this.cropper.imgReset();
},
clickcut(e) {
console.log(e.detail);
console.log(e.detail.url)
//点击裁剪框阅览图片
wx.previewImage({
current: e.detail.url, // 当前显示图片的http链接
urls: [e.detail.url] // 需要预览的图片http链接列表
})
},
})
2.3 效果
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)