element-ui 上传组件el-upload的再次封装 并支持图片的上传后的裁剪功能
1 <!-- @format --> 2 3 <template> 4 <div> 5 <!-- <input type="file" v-show="false" @change="change" /> --> 6 <!-- 生产环境 --> 7 <!-- action="http://cystorage.cycore.cn/v2/files/upload" --> 8 <!-- 测试环境 --> 9 <!-- action="http://cystorage.whhlj2test.changyan.cn/v2/files/upload" --> 10 <el-upload 11 ref="elUpload" 12 :class="className" 13 :action="url" 14 :accept="accept" 15 :before-upload="beforeUpload" 16 :limit="limit" 17 :multiple="multiple" 18 :file-list='fileList' 19 :data='token' 20 :show-file-list="showFileList" 21 :list-type="listType" 22 :on-exceed="handleExceed" 23 :on-progress="handleProgress" 24 :on-success='handleFileSuccess' 25 :on-preview="handlePictureCardPreview" 26 :on-remove="handleRemove" 27 :on-error="handleError" 28 :on-change="handleFileChange"> 29 <slot></slot> 30 </el-upload> 31 </div> 32 </template> 33 34 <script> 35 import axios from '@/plugins/axios'; 36 export default { 37 props: { 38 uploadType: { 39 type: String, 40 default: 'uploadToken' 41 }, 42 listType: { 43 type: String, 44 default: '' 45 }, 46 type: { 47 type: String, 48 default: '' 49 }, 50 accept: { 51 type: String, 52 default: '' 53 }, 54 limit: { 55 type: Number 56 }, 57 multiple: { 58 type: Boolean, 59 default: false 60 }, 61 className: { 62 type: String, 63 default: 'upload-demo' 64 }, 65 showFileList: { 66 type: Boolean, 67 default: true 68 }, 69 fileList: { 70 type: Array, 71 default: () => { 72 return []; 73 } 74 }, 75 beforeAvatarUpload: { 76 type: Function 77 } 78 }, 79 data() { 80 return { 81 token: {}, 82 url: 'http://cystorage.whhlj2test.changyan.cn/v2/files/upload' 83 }; 84 }, 85 methods: { 86 async beforeUpload(file) { 87 let rules = false; 88 let fileType = file.name.substring(file.name.lastIndexOf('.') + 1); 89 rules = this.beforeAvatarUpload(file, fileType); 90 if (!rules) { 91 return Promise.reject(); 92 } 93 let tokenize = await this.$api['common/tokenize']({ 94 fileExt: fileType 95 }); 96 this.token = tokenize['uploadToken']; 97 98 return Promise.resolve(); 99 }, 100 handleFileSuccess(res, file, fileList) { 101 this.$emit('handleFileSuccess', res, file, fileList, this.type); 102 }, 103 handlePictureCardPreview(file) { 104 if (this.type == 'cover') { 105 this.$emit('handlePictureCardPreview', file); 106 } 107 }, 108 handleFileChange(file, fileList) { 109 if (this.type == 'cover') { 110 this.$emit('handleFileChange', file, fileList); 111 } else { 112 this.$emit('handleUploadChange', file, fileList); 113 } 114 }, 115 handleExceed(files, fileList) { 116 this.$emit('handleExceed', files, fileList); 117 }, 118 handleRemove(file, fileList) { 119 this.$emit('handleRemove', file, fileList, this.type); 120 }, 121 handleProgress(event, file, fileList) { 122 this.$emit('handleProgress', event, file, fileList); 123 }, 124 handleError(err, file, fileList) { 125 this.$emit('handleError', err, file, fileList); 126 }, 127 handleUrl() { 128 let baseUrl = window.location.href; 129 let baseUrlArr = []; 130 baseUrlArr = baseUrl.split('#'); 131 baseUrl = baseUrlArr[0]; 132 if (baseUrl.indexOf('jichugroup') > -1) { 133 this.url = 'http://cystorage.cycore.cn/v2/files/upload'; 134 } else { 135 this.url = 'http://cystorage.sdfztest.changyan.cn/v2/files/upload'; 136 } 137 } 138 }, 139 created() { 140 this.handleUrl(); 141 } 142 }; 143 </script> 144 145 <style lang="less" scoped> 146 </style>
在调用该组件的地方 这样写
1 <!-- @format --> 2 <!-- 创建活动 --> 3 4 <template> 5 <div> 6 <el-form class="create-form" 7 :model="dataForm" 8 :rules="dataRule" 9 ref="ruleForm" 10 label-width="110px"> 11 <FromBlock :blockObj="{title:'基础设置',tip:'',showBtn:false}"> 12 <div> 13 <el-form-item label="活动主题:" prop="title"> 14 <el-input v-model="dataForm.title" placeholder="请输入活动主题"></el-input> 15 </el-form-item> 16 <el-form-item label="封面图片:" prop="cover"> 17 <MyUpload 18 v-show="!cutImgShow" 19 :type="'cover'" 20 :className="'avatar-uploader'" 21 :listType="'picture-card'" 22 :accept="'jpg,png,jpeg,bpm'" 23 :fileList="coverList" 24 :beforeAvatarUpload='beforeAvatarUploadImg' 25 @handleFileSuccess="handleFileSuccess" 26 @handlePictureCardPreview="handlePictureCardPreview" 27 @handleRemove="handleRemove" 28 @handleFileChange="handleFileChange" 29 > 30 <i class="el-icon-plus avatar-uploader-icon"></i> 31 <span slot="tip" class="el-upload__tip upload-tip">封面尺寸比例应为77:20</span> 32 </MyUpload> 33 <el-dialog :visible.sync="dialogVisible"> 34 <img width="100%" :src="dialogImageUrl" alt=""> 35 </el-dialog> 36 <div v-if="cutImgShow"> 37 <cropper v-if="dataForm.cover.length===0" :Setting="setting" @cutImg="getCutUrl"></cropper> 38 <div v-if="dataForm.cover.length >0 "> 39 <img class="cut-img-view" :src="dataForm.cover" alt="头像"> 40 </div> 41 <div class="btn-wrap" :class="{'handle-cutimg-btn':dataForm.cover.length>0}"> 42 <a class="form-btn btn-submit" v-if="dataForm.cover.length===0 && !showSubmit" @click="keepCutImg">保存</a> 43 <a class="form-btn btn-submit1" v-if="showSubmit">裁剪中...</a> 44 <a class="form-btn btn-cancel2" @click="cancelCutImg">取消</a> 45 </div> 46 </div> 47 </el-form-item> 48 <el-form-item label="活动时间:" prop="activityTime"> 49 <el-date-picker 50 v-model="dataForm.activityTime" 51 type="datetimerange" 52 align="right" 53 @change="handleActivityTime" 54 start-placeholder="开始日期" 55 end-placeholder="结束日期" 56 format="yyyy-MM-dd HH:mm" 57 value-format="yyyy-MM-dd HH:mm:ss"> 58 </el-date-picker> 59 </el-form-item> 60 <el-form-item label="活动地址:"> 61 <el-input 62 v-model="dataForm.address" 63 placeholder="请输入活动地址" 64 maxlength="50" 65 show-word-limit></el-input> 66 </el-form-item> 67 <el-form-item label="活动类型:" prop="typeList"> 68 <el-select v-model="dataForm.typeList" multiple :multiple-limit="3" placeholder="请添加活动类型"> 69 <el-option 70 v-for="(item,index) in activityType" 71 :key="index" 72 :label="item.name" 73 :value="item.id"> 74 </el-option> 75 </el-select> 76 <span class="icon-set" @click="openAddType"></span> 77 </el-form-item> 78 <el-form-item label="活动介绍:"> 79 <el-input 80 type="textarea" 81 placeholder="请输入活动介绍" 82 v-model="dataForm.introduce" 83 maxlength="1000" 84 :rows="6" 85 show-word-limit 86 > 87 </el-input> 88 </el-form-item> 89 <el-form-item label="活动对象:"> 90 <el-input 91 v-model="dataForm.actGroup" 92 maxlength="40" 93 show-word-limit 94 placeholder="请输入活动对象 "> 95 </el-input> 96 <span class="tip-message">需要强制限制报名范围,需在限制报名范围中设置</span> 97 </el-form-item> 98 <el-form-item label="活动人数:" prop="joinNum"> 99 <el-input-number class="input-number" v-model="dataForm.joinNum" :precision="0" :min="0" :step="10" :max="10000"></el-input-number> 100 <span class="tip-message ml10">不填则不限制人数</span> 101 </el-form-item> 102 <el-form-item label="活动附件:"> 103 <MyUpload 104 class="file-name" 105 :listType="''" 106 :fileList="fileList" 107 :limit="10" 108 :beforeAvatarUpload='beforeAvatarUpload' 109 @handleExceed="handleExceed" 110 @handleFileSuccess="handleFileSuccess" 111 @handleRemove="handleRemove" 112 > 113 <el-button size="small" type="primary">点击上传</el-button> 114 <div slot="tip" class="el-upload__tip">支持扩展名:rar、zip、doc、docx、pdf、jpg......</div> 115 </MyUpload> 116 </el-form-item> 117 </div> 118 </FromBlock> 119 <FromBlock 120 :blockObj="{title:'限制报名范围',tip:'不填则默认全集团校范围下均可自由报名',showBtn:true}" 121 @handleCheckChange="handleCheckChange" 122 > 123 <div> 124 <el-tabs type="border-card"> 125 <el-tab-pane label="添加学生"> 126 <div class="tree-box clearfix"> 127 <div class="tree-left fl"> 128 <div>全体学生</div> 129 <div> 130 <!-- <el-input 131 style="width:260px;" 132 placeholder="请输入老师名字" 133 suffix-icon="el-icon-search" 134 v-model="namVal"> 135 </el-input> --> 136 <el-tree 137 :data="classTree" 138 :filter-node-method="filterNode" 139 show-checkbox 140 default-expand-all 141 node-key="id" 142 ref="classTree" 143 highlight-current 144 :default-checked-keys="checkedKeys" 145 @check="handleCheck" 146 :props="defaultProps"> 147 </el-tree> 148 </div> 149 </div> 150 <div class="tree-center fl"></div> 151 <div class="tree-right fl"> 152 <div>已选中</div> 153 <div> 154 <el-table 155 :data="tableData" 156 style="width: 100%;"> 157 <el-table-column 158 prop="schoolName" 159 label="学校名称" 160 width="140"> 161 </el-table-column> 162 <el-table-column 163 prop="areaName" 164 label="校区名称" 165 width="140"> 166 </el-table-column> 167 <el-table-column 168 prop="grade" 169 label="年级"> 170 </el-table-column> 171 <el-table-column 172 prop="class" 173 label="班级"> 174 </el-table-column> 175 <el-table-column 176 width="60" 177 label="操作"> 178 <template slot-scope="scope"> 179 <span @click="handleDel(scope.row.id,scope.$index)" class="icon-del"></span> 180 </template> 181 </el-table-column> 182 </el-table> 183 </div> 184 </div> 185 </div> 186 </el-tab-pane> 187 </el-tabs> 188 </div> 189 </FromBlock> 190 <FromBlock :blockObj="{title:'高级设置',tip:'不填则无限制条件',showBtn:true}"> 191 <div> 192 <el-form-item label="报名开始时间:"> 193 <el-date-picker 194 v-model="dataForm.enterBeginTime" 195 type="datetime" 196 placeholder="请选择报名开始时间" 197 value-format="yyyy-MM-dd HH:mm:ss"> 198 </el-date-picker> 199 </el-form-item> 200 <el-form-item label="报名结束时间:"> 201 <el-date-picker 202 v-model="dataForm.enterEndTime" 203 type="datetime" 204 placeholder="请选择报名结束时间" 205 value-format="yyyy-MM-dd HH:mm:ss"> 206 </el-date-picker> 207 </el-form-item> 208 </div> 209 </FromBlock> 210 <FromBlock :blockObj="{title:'评审设置',tip:'不填则默认没有评审流程',showBtn:true}"> 211 <div> 212 <el-form-item label="作品评审老师:"> 213 <span v-for="(item,index) in teacherArr" :key="index" class="block-teacher">{{item.name}} 214 <i class="name-delete" @click="handleDeleteTeaName(item,index)"></i> 215 </span> 216 <el-button @click="openTree"> <i class="icon-small-add"></i> 添加</el-button> 217 </el-form-item> 218 <el-form-item label="评审截止时间:" prop="evaluateOpenTime"> 219 <el-date-picker 220 v-model="dataForm.evaluateOpenTime" 221 type="datetime" 222 placeholder="请选择评审截止时间" 223 value-format="yyyy-MM-dd HH:mm:ss"> 224 </el-date-picker> 225 </el-form-item> 226 </div> 227 </FromBlock> 228 <FromBlock :blockObj="{title:'隐私设置',tip:'',showBtn:true}"> 229 <div class="set-group"> 230 <el-checkbox v-model="dataForm.openJoin">公开报名名单</el-checkbox> 231 <el-checkbox v-model="dataForm.openComment">打开活动评论功能</el-checkbox> 232 <el-checkbox v-model="dataForm.openUpload">上传活动材料</el-checkbox> 233 <el-checkbox v-model="dataForm.openPicture">照片墙</el-checkbox> 234 </div> 235 </FromBlock> 236 <div class="submit-box"> 237 <el-button @click="handleCancel">取消</el-button> 238 <el-button :disabled="submitFlag" type="primary" @click="dataFormSubmit">{{activeId?'修改':'发布'}}</el-button> 239 </div> 240 </el-form> 241 <AddActivityType 242 v-if="showAddType" 243 @addType="addType" 244 :showAddType='showAddType' 245 :activityType="activityType" 246 ref="addType" 247 /> 248 <TeacherTree 249 v-if="showTree" 250 ref="showTree" 251 :configInfo="{title:'添加活动评审老师',treeTitle:'全体老师'}" 252 :teacherTree="teacherTree" 253 :checkedArr="dataForm.evaluateList" 254 @addManage="addTeacher"/> 255 </div> 256 </template> 257 258 <script> 259 import FromBlock from './from-block'; 260 import AddActivityType from './add-activity-type'; 261 import { timeFormat } from '@/utils/index'; 262 import MyUpload from '@/components/common/myUpload'; 263 import TeacherTree from '@/components/common/teacher-tree'; 264 import cropper from '@/components/common/img-cropper/index'; 265 export default { 266 name: 'creat-activity', 267 components: { 268 FromBlock, 269 MyUpload, 270 AddActivityType, 271 TeacherTree, 272 cropper 273 }, 274 data() { 275 let validateJoinNum = (rule, value, callback) => { 276 if (value === 0) { 277 return callback(new Error('参加人数不能为0')); 278 } else { 279 return callback(); 280 } 281 }; 282 let evaluateOpenTime = (rule, value, callback) => { 283 if (this.dataForm.evaluateList.length > 0) { 284 if (value == '' || value == null) { 285 return callback(new Error('请选择评审截止时间!')); 286 } else { 287 return callback(); 288 } 289 } else { 290 return callback(); 291 } 292 }; 293 return { 294 submitFlag: false, 295 activeId: this.$route.query.id, 296 dialogImageUrl: '', 297 dialogVisible: false, 298 showAddType: false, // 是否展示新增活动类型弹窗 299 coverList: [], 300 fileList: [], 301 activityType: [], 302 imageUrl: '', 303 showSubmit: false, 304 setting: { 305 photoUrl: '', 306 //背景图片的路径 307 backgroundUrl: '', 308 //图片操作区域宽度 309 width: 300, 310 //图片操作区域高度 311 height: 360, 312 //预览框的大小及可见性 313 target: { 314 w: 208, // 宽度 315 h: 117, // 高度 316 visible: true //是否显示 317 }, 318 //拖拽框位置 319 cutPos: { 320 w: 0, // 宽度 321 h: 0, // 高度 322 x: 0, //相对父级左边距离 大于0有效 323 y: 0 //相对父级顶部距离 大于0有效 324 }, // 拖拽框的范围为照片的边界,如果xy过大超出边界后,以边界值为准 325 // 按钮样式 326 btnStyle: {}, 327 limitSize: 0, //限制图片大小 328 format: [] //限制图片格式 329 }, 330 imgPosition: { 331 x: '', 332 y: '', 333 width: '', 334 height: '' 335 }, 336 cutImgUrl: '', 337 cutImgShow: false, //裁剪插件是否显示 338 fileExt: null, 339 dataForm: { 340 title: '', 341 cover: '', 342 coverName: '', 343 activityTime: '', 344 beginTime: '', 345 endTime: '', 346 address: '', 347 introduce: '', 348 typeList: [], 349 actGroup: '', 350 joinNum: undefined, 351 clazzList: [], //班级列表 352 evaluateList: [], //评审教师Id数组 353 attachmentList: [], //附件列表 354 enterBeginTime: '', //报名开始时间 355 enterEndTime: '', //报名结束时间 356 evaluateOpenTime: '', //评审截止时间 357 openJoin: false, 358 openComment: false, 359 openUpload: false, 360 openPicture: false 361 }, 362 dataRule: { 363 title: [ 364 { required: true, message: '活动主题名称不能为空', trigger: 'blur' } 365 ], 366 cover: [ 367 { required: true, message: '封面图片不能为空', trigger: 'change' } 368 ], 369 activityTime: [ 370 { required: true, message: '活动时间不能为空', trigger: 'change' } 371 ], 372 typeList: [ 373 { required: true, message: '活动类型不能为空', trigger: 'change' } 374 ], 375 evaluateOpenTime: [ 376 { 377 validator: evaluateOpenTime, 378 trigger: 'change' 379 } 380 ], 381 joinNum: [{ validator: validateJoinNum, trigger: 'change' }] 382 }, 383 classTree: [], 384 checkedKeys: [], 385 namVal: '', //树的搜索框 386 teacherTree: [], 387 teacherArr: [], 388 showTree: false, 389 defaultProps: { 390 children: 'child', 391 label: 'name', 392 value: 'id' 393 }, 394 pickerOptions: { 395 disabledDate(time) { 396 // return time.getTime() > Date.now(); 397 } 398 }, 399 tableData: [], 400 treeChooseArr: [], 401 groupList: [] // 分组列表 402 }; 403 }, 404 watch: { 405 namVal(val) { 406 this.$refs.classTree.filter(val); 407 } 408 }, 409 computed: { 410 ruleTimeFlag() { 411 return this.dataForm.evaluateList.length > 0; 412 } 413 }, 414 created() { 415 this.getActivityType(); 416 this.getClassTree(); 417 if (this.activeId) { 418 this.getFindTreeInfo(); 419 } else { 420 this.getTeacherTree(); 421 } 422 }, 423 mounted() {}, 424 methods: { 425 /* 获取班级树 */ 426 getClassTree() { 427 this.$api['common/classTree']() 428 .then(res => { 429 console.log(res); 430 let arr = [ 431 { 432 name: '山东大学基础教育集团', 433 id: 0, 434 child: res['山东大学基础教育集团'] 435 } 436 ]; 437 this.classTree = arr; 438 console.log(this.classTree); 439 }) 440 .catch(err => { 441 this.$message.error(err); 442 }); 443 }, 444 /* 获取老师树 */ 445 getTeacherTree() { 446 let self = this; 447 return new Promise((resolve, reject) => { 448 this.$api['common/teacherTree']() 449 .then(res => { 450 console.log(res); 451 let arr = [ 452 { 453 name: '山东大学基础教育集团', 454 id: 0, 455 teacherChild: res['山东大学基础教育集团'] 456 } 457 ]; 458 console.log(arr); 459 self.teacherTree = arr; 460 resolve(); 461 }) 462 .catch(err => { 463 self.$message.error(err); 464 reject(); 465 }); 466 }); 467 }, 468 /* 获取活动类型 */ 469 getActivityType() { 470 this.$api['activity/getType']() 471 .then(res => { 472 console.log(res); 473 this.activityType = res; 474 }) 475 .catch(err => { 476 this.$message.error(err); 477 }); 478 }, 479 /** 480 * 获取活动详情 481 */ 482 getActivityDetail() { 483 let self = this; 484 return new Promise((resolve, reject) => { 485 self.$api['activity/detail']({ 486 actId: self.activeId 487 }) 488 .then(res => { 489 console.log(res); 490 self.dataForm = { ...self.dataForm, ...res }; 491 self.coverList.push({ 492 url: self.dataForm.cover 493 }); 494 res.attachmentList.forEach(item => { 495 let temp = { 496 name: item.fileName, 497 response: item, 498 ...item 499 }; 500 self.fileList.push(temp); 501 }); 502 self.checkedKeys = res.clazzList; 503 self.dataForm.evaluateList = res.evaluateList; 504 self.$set(self.dataForm, 'activityTime', [ 505 self.dataForm.beginTime, 506 self.dataForm.endTime 507 ]); 508 if (!self.dataForm.joinNum) { 509 self.dataForm.joinNum = undefined; 510 } 511 resolve(); 512 }) 513 .catch(err => { 514 self.$message.error(err); 515 reject(); 516 }); 517 }); 518 }, 519 handleFileSuccess(res, file, fileList, type) { 520 console.log(res); 521 console.log(file); 522 console.log(fileList); 523 if (type === 'cover') { 524 this.cutImgShow = true; 525 this.cutImgUrl = res.url; 526 this.dataForm.coverName = res.filename; 527 this.dataForm.cover = ''; 528 this.setting.photoUrl = res.url; 529 } else { 530 if (fileList.length > 0) { 531 this.dataForm.attachmentList = []; 532 fileList.forEach(item => { 533 let param = { 534 fileName: item.response.filename, 535 ...item.response 536 }; 537 this.dataForm.attachmentList.push(param); 538 }); 539 } 540 } 541 542 // this.imageUrl = URL.createObjectURL(file.raw); 543 // // this.fileList.push(file); 544 // this.dataForm.cover = this.imageUrl; 545 }, 546 handleExceed(files, fileList) { 547 this.$message.warning('最多可上传10个附件!'); 548 }, 549 handleFileChange(file, fileList) { 550 console.log(file); 551 console.log(fileList); 552 if (fileList.length > 1) { 553 fileList.splice(0, 1); 554 } 555 }, 556 handlePictureCardPreview(file) { 557 this.dialogImageUrl = file.url; 558 this.dialogVisible = true; 559 }, 560 beforeAvatarUploadImg(file, fileType) { 561 console.log(fileType); 562 let fileTypeArr = [ 563 'png', 564 'jpeg', 565 'jpg', 566 'bmp', 567 'PNG', 568 'JPEG', 569 'JPG', 570 'BMP' 571 ]; 572 let ruleType = false; 573 if (fileTypeArr.indexOf(fileType) != -1) { 574 ruleType = true; 575 } else { 576 this.$message.error('仅可上传png/jpeg/jpg/bmp格式附件!'); 577 } 578 let fileSize = file.size / 1024 / 1024 < 20; 579 if (!fileSize) { 580 this.$message.error('上传的封面图片不能超过 20MB!'); 581 } 582 if (fileSize && ruleType) { 583 this.fileExt = fileType; 584 } else { 585 this.fileExt = null; 586 } 587 return fileSize && ruleType; 588 }, 589 beforeAvatarUpload(file, fileType) { 590 let fileTypeArr = [ 591 'doc', 592 'ppt', 593 'docx', 594 'pptx', 595 'zip', 596 '7z', 597 'rar' 598 // 'xls', 599 // 'xlsx' 600 ]; 601 let ruleType = false; 602 if (fileTypeArr.indexOf(fileType) != -1) { 603 ruleType = true; 604 } else { 605 this.$message.error('仅可上传doc/ppt/docx/pptx/zip/7z/rar格式附件!'); 606 } 607 let fileSize = 0; 608 if (fileType == '7z' || fileType == 'rar') { 609 fileSize = file.size / 1024 / 1024 < 50; 610 if (!fileSize) { 611 this.$message.error('上传的压缩包不能超过 50MB!'); 612 } 613 } else { 614 fileSize = file.size / 1024 / 1024 < 10; 615 if (!fileSize) { 616 this.$message.error('上传的文件不能超过 10MB!'); 617 } 618 } 619 620 return fileSize && ruleType; 621 }, 622 handleRemove(file, fileList, type) { 623 if (type === 'cover') { 624 this.dataForm.cover = ''; 625 this.dataForm.coverName = ''; 626 } else { 627 if (fileList.length > 0) { 628 this.dataForm.attachmentList = []; 629 fileList.forEach(item => { 630 let param = { 631 fileName: item.response.filename, 632 ...item.response 633 }; 634 this.dataForm.attachmentList.push(param); 635 }); 636 } 637 } 638 console.log(file, fileList); 639 }, 640 641 /* 取消活动 */ 642 handleCancel() { 643 if (this.activeId) { 644 this.$router.go(-1); 645 } else { 646 this.$router.push({ 647 path: '/activityPage/managePage/manageActivity' 648 }); 649 } 650 }, 651 /** 652 * 打开新增类型弹窗 653 */ 654 openAddType(id) { 655 let self = this; 656 self.showAddType = true; 657 }, 658 /** 659 * 新增活动类型方法 660 */ 661 addType(dataForm) { 662 console.log(dataForm); 663 let self = this; 664 }, 665 /* 活动时间 change */ 666 handleActivityTime(val) { 667 this.dataForm.beginTime = val[0]; 668 this.dataForm.endTime = val[1]; 669 }, 670 dataFormSubmit() { 671 let self = this; 672 console.log(this.dataForm); 673 this.$refs.ruleForm.validate(valid => { 674 if (valid) { 675 this.submitFlag = true; 676 //发送请求 677 self.$api['activity/addOrUpdate'](this.dataForm) 678 .then(res => { 679 if (this.activeId) { 680 self.$message.success('修改成功!'); 681 } else { 682 self.$message.success('创建成功!'); 683 } 684 self.$router.push({ 685 path: '/activityPage/managePage/manageActivity' 686 }); 687 this.submitFlag = false; 688 }) 689 .catch(err => { 690 this.submitFlag = false; 691 self.$message.error(err); 692 }); 693 } else { 694 console.log('error submit!!'); 695 return false; 696 } 697 }); 698 }, 699 /* 树的过滤方法 */ 700 filterNode(value, data) { 701 if (!value) return true; 702 return data.name.indexOf(value) !== -1; 703 }, 704 handleCheckChange() { 705 let self = this; 706 if (self.checkedKeys.length > 0) { 707 // setTimeout(function() {}, 1000); 708 self.handleCheck(); 709 } 710 // this.handleCheck(); 711 }, 712 /* 班级树处理方法 */ 713 handleCheck() { 714 let self = this; 715 self.tableData = []; 716 let keysTemp = []; 717 this.dataForm.clazzList = []; 718 keysTemp = this.$refs.classTree.getCheckedKeys(true); 719 keysTemp.forEach(item => { 720 console.log(self.getParent(self.classTree, item)); 721 let obj = { 722 schoolName: '', 723 areaName: '', 724 grade: '', 725 class: '', 726 id: '' 727 }; 728 let treeArr = self.getParent(this.classTree, item); 729 if (treeArr.length === 4) { 730 obj.schoolName = treeArr[0].name; 731 obj.areaName = treeArr[1].name; 732 obj.grade = treeArr[2].name; 733 obj.class = treeArr[3].name; 734 obj.id = treeArr[3].id; 735 this.dataForm.clazzList.push(treeArr[3].id); 736 self.tableData.push(obj); 737 } 738 }); 739 }, 740 /*递归 根据子元素找到父级元素 */ 741 getParent(data2, nodeId2) { 742 var arrRes = []; 743 if (data2.length == 0) { 744 if (nodeId2) { 745 arrRes.unshift(data2); 746 } 747 return arrRes; 748 } 749 let rev = (data, nodeId) => { 750 for (var i = 0, length = data.length; i < length; i++) { 751 let node = data[i]; 752 if (node.id == nodeId) { 753 arrRes.unshift(node); 754 rev(data2, node.pid); 755 break; 756 } else { 757 if (node.child) { 758 rev(node.child, nodeId); 759 } 760 } 761 } 762 return arrRes; 763 }; 764 arrRes = rev(data2, nodeId2); 765 return arrRes; 766 }, 767 /* 删除管理人员 */ 768 handleDel(id, index) { 769 this.tableData.splice(index, 1); 770 let idIndex = this.dataForm.clazzList.indexOf(id); 771 if (idIndex != -1) { 772 this.dataForm.clazzList.splice(idIndex, 1); 773 } 774 this.$refs.classTree.setCheckedKeys(this.dataForm.clazzList); 775 this.$message.success('删除成功'); 776 }, 777 /* 添加评审老师 */ 778 addTeacher(param) { 779 let self = this; 780 this.teacherArr = []; 781 this.dataForm.evaluateList = []; 782 if (param.length > 0) { 783 param.forEach(item => { 784 this.teacherArr.push({ name: item.userName, id: item.id }); 785 this.dataForm.evaluateList.push(item.id); 786 }); 787 } 788 }, 789 openTree() { 790 this.showTree = true; 791 }, 792 /* 删除评审老师 */ 793 handleDeleteTeaName(item, index) { 794 this.teacherArr.splice(index, 1); 795 this.dataForm.evaluateList.splice(index, 1); 796 }, 797 getFindTreeInfo() { 798 let self = this; 799 Promise.all([self.getActivityDetail(), self.getTeacherTree()]).then( 800 data => { 801 self.getFindNode(self.teacherTree, self.dataForm.evaluateList); 802 } 803 ); 804 return; 805 }, 806 /*递归 根据子元素找到父级元素 */ 807 getFindNode(data2, ids) { 808 let self = this; 809 if (ids.length > 0) { 810 ids.forEach(nodeId2 => { 811 let arrRes = {}; 812 if (data2.length == 0) { 813 if (nodeId2) { 814 arrRes = {}; 815 } 816 return arrRes; 817 } 818 let rev = (data, nodeId) => { 819 for (var i = 0, length = data.length; i < length; i++) { 820 let node = data[i]; 821 if (node.id == nodeId) { 822 arrRes = node; 823 // rev(data2, node.pid); 824 break; 825 } else { 826 if (node.teacherChild) { 827 rev(node.teacherChild, nodeId); 828 } 829 } 830 } 831 return arrRes; 832 }; 833 arrRes = rev(data2, nodeId2); 834 let obj = { id: arrRes.id, name: arrRes.userName }; 835 self.teacherArr.push(obj); 836 // return arrRes; 837 }); 838 } 839 console.log('________________________________', self.teacherArr); 840 }, 841 keepCutImg() { 842 this.showSubmit = true; 843 this.$api['common/cutImage']({ 844 url: this.cutImgUrl, 845 fileName: this.dataForm.coverName, 846 fileExt: this.fileExt, 847 xPoint: this.imgPosition.x, 848 yPoint: this.imgPosition.y, 849 hPoint: this.imgPosition.height, 850 wPoint: this.imgPosition.width 851 }) 852 .then(res => { 853 this.dataForm.cover = res.fileUrl; 854 this.dataForm.coverName = res.fileName; 855 this.showSubmit = false; 856 this.$refs.ruleForm.validateField('cover'); 857 }) 858 .catch(err => { 859 this.$message.error('剪切失败了!'); 860 this.showSubmit = false; 861 }); 862 }, 863 cancelCutImg() { 864 this.showSubmit = false; 865 this.cutImgShow = false; 866 this.coverList = []; 867 this.cutImgUrl = ''; 868 this.dataForm.cover = ''; 869 this.dataForm.coverName = ''; 870 this.setting.photoUrl = ''; 871 }, 872 /** 873 * 获取裁剪图片区域坐标 874 */ 875 getCutUrl(data) { 876 this.imgPosition.x = data.left; 877 this.imgPosition.y = data.top; 878 this.imgPosition.height = data.height; 879 this.imgPosition.width = data.width; 880 } 881 } 882 }; 883 </script> 884 885 <style scoped lang="less"> 886 .avatar-uploader .el-upload:hover { 887 border-color: #409eff; 888 } 889 // .avatar-uploader-icon { 890 // width: 132px; 891 // height: 132px; 892 // background-color: #f5f8fa; 893 // font-size: 28px; 894 // color: #8c939d; 895 // line-height: 132px; 896 // text-align: center; 897 // } 898 .avatar { 899 width: 132px; 900 height: 132px; 901 display: block; 902 } 903 .upload-tip { 904 display: inline-block; 905 padding-left: 18px; 906 background: url('../../../../assets/images/icon-tip.png') no-repeat left 2px; 907 margin-left: 10px; 908 position: absolute; 909 top: 110px; 910 color: #999999; 911 } 912 .icon-set { 913 display: inline-block; 914 width: 30px; 915 height: 30px; 916 box-sizing: border-box; 917 position: absolute; 918 margin-left: 10px; 919 background: url('../../../../assets/images/icon-set.png') no-repeat center 920 center; 921 cursor: pointer; 922 } 923 /* 树结构 */ 924 .tree-box { 925 text-align: left; 926 .tree-left { 927 width: 300px; 928 height: 370px; 929 overflow-y: auto; 930 border: 1px solid #e8ecf0; 931 border-radius: 2px; 932 box-sizing: border-box; 933 padding: 10px; 934 } 935 .tree-center { 936 width: 50px; 937 height: 370px; 938 background: url('../../../../assets/images/icon-chuansuo.png') no-repeat 939 center center; 940 } 941 .tree-right { 942 width: 550px; 943 height: 370px; 944 overflow-y: auto; 945 border: 1px solid #e8ecf0; 946 border-radius: 2px; 947 box-sizing: border-box; 948 padding: 10px; 949 .icon-del { 950 display: inline-block; 951 width: 16px; 952 height: 16px; 953 cursor: pointer; 954 background: url('../../../../assets/images/icon-default-delete.png') 955 no-repeat center center; 956 } 957 .icon-del:hover { 958 background: url('../../../../assets/images/icon-active-delete.png') 959 no-repeat center center; 960 } 961 } 962 } 963 964 /* 添加老师*/ 965 .tip-teacher-choose { 966 display: block; 967 } 968 .block-teacher { 969 display: inline-block; 970 background-color: #f1f5f8; 971 border-radius: 2px; 972 height: 30px; 973 line-height: 30px; 974 font-size: 14px; 975 color: #3d3e40; 976 padding: 0 10px; 977 margin-right: 10px; 978 .name-delete { 979 width: 14px; 980 height: 18px; 981 background: url('../../../../assets/images/icon-delete.png') center 9px 982 no-repeat; 983 cursor: pointer; 984 } 985 } 986 .icon-small-add { 987 width: 12px; 988 height: 12px; 989 background: url('../../../../assets/images/icon-small-add.png') center center 990 no-repeat; 991 } 992 993 /* 隐私设置 */ 994 .set-group { 995 text-align: left; 996 padding-bottom: 20px; 997 } 998 999 /* 最下方的 操作按钮 */ 1000 .submit-box { 1001 margin: 30px; 1002 } 1003 .file-name { 1004 width: 530px; 1005 } 1006 </style> 1007 <style lang="less"> 1008 .create-form { 1009 .avatar-uploader /deep/.el-upload { 1010 border: 1px dashed #d9d9d9; 1011 border-radius: 6px; 1012 cursor: pointer; 1013 position: relative; 1014 overflow: hidden; 1015 } 1016 .el-input, 1017 .el-textarea { 1018 width: 530px; 1019 } 1020 .el-input-number { 1021 line-height: 30px; 1022 height: 30px; 1023 } 1024 .el-input-number .el-input { 1025 width: 180px; 1026 } 1027 .el-input-number__decrease, 1028 .el-input-number__increase { 1029 height: 28px; 1030 line-height: 28px; 1031 } 1032 } 1033 .btn-wrap { 1034 margin-top: 50px; 1035 text-align: center; 1036 a { 1037 display: inline-block; 1038 cursor: pointer; 1039 height: 30px; 1040 line-height: 30px; 1041 border-radius: 2px; 1042 } 1043 .btn-submit { 1044 width: 60px; 1045 background: #228cf9; 1046 color: #fff; 1047 } 1048 .btn-submit1 { 1049 width: 60px; 1050 background: #228cf9; 1051 color: #fff; 1052 cursor: not-allowed; 1053 } 1054 .btn-cancel2 { 1055 width: 60px; 1056 margin-left: 12px; 1057 border: 1px solid #228cf9; 1058 color: #228cf9; 1059 } 1060 } 1061 .cut-img-view { 1062 width: 160px; 1063 height: 90px; 1064 } 1065 .handle-cutimg-btn { 1066 display: inline; 1067 } 1068 </style>
二.图片裁剪 功能如下:
1.createDom.js
1 /** 2 * @format 3 * @description 创建dom元素 4 * @param config 添加对象及内部元素 5 * @param refs 6 */ 7 8 function createDOM(config, refs) { 9 if (!config) return null; 10 var dom, childElement; 11 if (config.tag) { 12 dom = document.createElement(config.tag); 13 for (var prop in config) { 14 if (config.hasOwnProperty(prop)) { 15 if (prop === 'content' || prop === 'tag') continue; 16 if (prop === 'key' && refs) { 17 var key = config[prop]; 18 if (key) { 19 refs[key] = dom; 20 } 21 } 22 dom[prop] = config[prop]; 23 } 24 } 25 var content = config.content; 26 if (content instanceof Array) { 27 for (var i = 0, j = content.length; i < j; i++) { 28 var child = content[i]; 29 childElement = createDOM(child, refs); 30 dom.appendChild(childElement); 31 } 32 } else if (typeof content === 'string') { 33 childElement = document.createTextNode(content); 34 dom.appendChild(childElement); 35 } 36 } 37 return dom; 38 } 39 40 export default createDOM;
2.cropper.js
1 /** @format */ 2 3 import createDOM from './createDOM'; 4 import Resizer from './resize'; 5 6 //预加载元素 7 var preLoadElement; 8 9 //ie浏览器版本 10 var ieVersion = Number(document.documentMode); 11 12 /** 13 * @description 获取图片大小 14 * @param {*} src 图片路径 15 * @param {*} callback 回调函数 16 */ 17 function getImageSize(src, callback) { 18 if (ieVersion < 10) { 19 if (!preLoadElement) { 20 preLoadElement = document.createElement('div'); 21 preLoadElement.style.position = 'absolute'; 22 preLoadElement.style.width = '1px'; 23 preLoadElement.style.height = '1px'; 24 preLoadElement.style.left = '-9999px'; 25 preLoadElement.style.top = '-9999px'; 26 //filter 用于定于元素(通常是 <img>)的可视效果。 27 preLoadElement.style.filter = 28 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=image)'; 29 document.body.insertBefore(preLoadElement, document.body.firstChild); 30 } 31 32 preLoadElement.filters.item( 33 'DXImageTransform.Microsoft.AlphaImageLoader' 34 ).src = src; 35 36 var size = { 37 width: preLoadElement.offsetWidth, 38 height: preLoadElement.offsetHeight 39 }; 40 41 if (typeof callback === 'function') { 42 callback(size); 43 } 44 } else { 45 var image = new Image(); 46 image.onload = function() { 47 var size = { 48 width: image.width, 49 height: image.height 50 }; 51 if (typeof callback === 'function') { 52 callback(size); 53 } 54 }; 55 image.src = src; 56 } 57 } 58 59 /** 60 * @description 创建对象 61 * @param {*} options 配置选项 62 */ 63 function Cropper(options) { 64 //实例化对象 65 var cropper = this; 66 if (!(this instanceof Cropper)) { 67 cropper = new Cropper(); 68 } 69 70 //对象属性拷贝 71 for (var prop in options) { 72 if (options.hasOwnProperty(prop)) cropper[prop] = options[prop]; 73 } 74 75 if (cropper.element) { 76 cropper.render(cropper.element); 77 } 78 79 //默认宽高比为1 80 if (!cropper.aspectRatio) { 81 cropper.aspectRatio = 1; 82 } 83 84 return cropper; 85 } 86 87 /** 88 * @description 设置拖拽框大小及位置 89 */ 90 Cropper.prototype.resetResizer = function() { 91 //拖拽框 92 var resizer = this.resizer; 93 //外部框框 94 var cropperRect = this.cropperRect; 95 //宽高比 96 var aspectRatio = this.aspectRatio; 97 //宽高像素比不为数字时给一个默认值 98 if (typeof aspectRatio !== 'number') { 99 aspectRatio = 1; 100 } 101 102 //设定宽高,参数无效时默认值为图片的一半,超出图片区域时默认为图片的宽度 103 var width; 104 if (this.width > 0 && typeof this.width === 'number') { 105 width = this.width > cropperRect.width ? cropperRect.width : this.width; 106 } else { 107 width = cropperRect.width / 2; 108 } 109 var height; 110 if (this.height > 0 && typeof this.height === 'number') { 111 height = 112 this.height > cropperRect.height ? cropperRect.height : this.height; 113 } else { 114 height = width / aspectRatio; 115 } 116 117 //设置拖拽框的大小 118 var resizerDom = resizer.dom; 119 resizerDom.style.width = width + 'px'; 120 resizerDom.style.height = height + 'px'; 121 122 //如果配置了拖拽框的位置就按配置的来否则就居中 123 //x 124 if (this.x > 0 && typeof this.x === 'number') { 125 //如果x设置超出了图片的区域则放置在图片边上 126 if (this.x > cropperRect.width - width) { 127 resizerDom.style.left = cropperRect.width - width + 'px'; 128 } else { 129 resizerDom.style.left = this.x + 'px'; 130 } 131 } else if (cropperRect) { 132 resizerDom.style.left = (cropperRect.width - width) / 2 + 'px'; 133 } else { 134 resizerDom.style.left = ''; 135 } 136 137 //y 138 if (this.y > 0 && typeof this.y === 'number') { 139 //如果y设置超出了图片的区域则放置在图片底部 140 if (this.y > cropperRect.height - height) { 141 resizerDom.style.top = cropperRect.height - height + 'px'; 142 } else { 143 resizerDom.style.top = this.y + 'px'; 144 } 145 } else if (cropperRect) { 146 resizerDom.style.top = (cropperRect.height - height) / 2 + 'px'; 147 } else { 148 resizerDom.style.top = ''; 149 } 150 151 resizer.doOnStateChange(); 152 resizer.doOnDragEnd(); 153 }; 154 155 //设置父级元素的图片源 156 Cropper.prototype.setImage = function(src) { 157 var element = this.element; 158 var sourceImage = element.querySelector('img'); 159 var resizeImage = this.refs.image; 160 161 var self = this; 162 163 //图片为空时 164 if (src === undefined || src === null) { 165 resizeImage.src = sourceImage.src = ''; 166 resizeImage.style.width = resizeImage.style.height = resizeImage.style.left = resizeImage.style.top = 167 ''; 168 sourceImage.style.width = sourceImage.style.height = sourceImage.style.left = sourceImage.style.top = 169 ''; 170 171 //更新预览视图 172 self.updatePreview(''); 173 174 self.dom.style.display = 'none'; 175 self.resetResizer(); 176 177 self.dom.style.left = self.dom.style.top = ''; 178 self.dom.style.width = element.offsetWidth + 'px'; 179 self.dom.style.height = element.offsetHeight + 'px'; 180 181 self.croppedRect = { 182 width: 0, 183 height: 0, 184 left: 0, 185 top: 0 186 }; 187 188 self.onCroppedRectChange && self.onCroppedRectChange(self.croppedRect); 189 190 return; 191 } 192 193 //获取图片大小后渲染预览图 194 getImageSize(src, function(size) { 195 if (ieVersion < 10) { 196 //ie9以下使用css渲染本地图片方式 197 resizeImage.src = sourceImage.src = ''; 198 resizeImage.style.filter = sourceImage.style.filter = 199 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)'; 200 sourceImage.filters.item( 201 'DXImageTransform.Microsoft.AlphaImageLoader' 202 ).src = src; 203 resizeImage.filters.item( 204 'DXImageTransform.Microsoft.AlphaImageLoader' 205 ).src = src; 206 } else { 207 //其他浏览器直接复制src 208 resizeImage.src = sourceImage.src = src; 209 } 210 211 self.imageSize = size; 212 213 var elementWidth = element.offsetWidth; 214 var elementHeight = element.offsetHeight; 215 216 var dom = self.dom; 217 218 var cropperRect = {}; 219 //图片大小等比缩放到父级容器的宽度之内 220 if (size.width / size.height > elementWidth / elementHeight) { 221 cropperRect.width = elementWidth; 222 cropperRect.height = (elementWidth * size.height) / size.width; 223 cropperRect.top = (elementHeight - cropperRect.height) / 2; 224 cropperRect.left = 0; 225 } else { 226 cropperRect.height = elementHeight; 227 cropperRect.width = (elementHeight * size.width) / size.height; 228 cropperRect.top = 0; 229 cropperRect.left = (elementWidth - cropperRect.width) / 2; 230 } 231 232 self.cropperRect = cropperRect; 233 234 for (var style in cropperRect) { 235 if (cropperRect.hasOwnProperty(style)) { 236 dom.style[style] = sourceImage.style[style] = resizeImage.style[style] = 237 cropperRect[style] + 'px'; 238 } 239 } 240 241 self.dom.style.display = ''; 242 self.resetResizer(); 243 self.updatePreview(src); 244 }); 245 }; 246 247 //添加预览对象 248 Cropper.prototype.addPreview = function(preview) { 249 var previews = this.previews; 250 if (!previews) { 251 previews = this.previews = []; 252 } 253 previews.push(preview); 254 }; 255 256 //渲染 257 Cropper.prototype.render = function(container) { 258 var resizer = new Resizer({ 259 aspectRatio: this.aspectRatio 260 }); 261 var refs = {}; 262 //创建遮罩层 263 var dom = createDOM( 264 { 265 tag: 'div', 266 className: 'cropper', 267 content: [ 268 { 269 tag: 'div', 270 className: 'mask' 271 } 272 ] 273 }, 274 refs 275 ); 276 277 var resizerDom = resizer.render(dom); 278 279 // 创建图片区域 280 var img = createDOM( 281 { 282 tag: 'div', 283 className: 'wrapper', 284 content: [ 285 { 286 tag: 'img', 287 key: 'image', 288 src: '' 289 } 290 ] 291 }, 292 refs 293 ); 294 295 var self = this; 296 self.refs = refs; 297 //拖拽时更新预览图片 298 resizer.doOnStateChange = function() { 299 var left = parseInt(resizerDom.style.left, 10) || 0; 300 var top = parseInt(resizerDom.style.top, 10) || 0; 301 302 var image = refs.image; 303 304 image.style.left = -left + 'px'; 305 image.style.top = -top + 'px'; 306 307 self.updatePreview(); 308 }; 309 310 resizer.doOnDragEnd = function() { 311 var left = parseInt(resizerDom.style.left, 10) || 0; 312 var top = parseInt(resizerDom.style.top, 10) || 0; 313 var resizerWidth = resizerDom.offsetWidth; 314 var resizerHeight = resizerDom.offsetHeight; 315 316 var imageSize = self.imageSize; 317 var cropperRect = self.cropperRect; 318 //预览部分进行等比缩放 319 if (cropperRect) { 320 var scale = cropperRect.width / imageSize.width; 321 322 self.croppedRect = { 323 width: Math.floor(resizerWidth / scale), 324 height: Math.floor(resizerHeight / scale), 325 left: Math.floor(left / scale), 326 top: Math.floor(top / scale) 327 }; 328 329 self.onCroppedRectChange && self.onCroppedRectChange(self.croppedRect); 330 } 331 }; 332 self.resizer = resizer; 333 self.dom = dom; 334 335 resizerDom.insertBefore(img, resizerDom.firstChild); 336 337 container.appendChild(dom); 338 339 self.dom.style.display = 'none'; 340 }; 341 342 //更新预览图片 343 Cropper.prototype.updatePreview = function(src) { 344 var imageSize = this.imageSize; 345 var cropperRect = this.cropperRect; 346 if (!imageSize || !cropperRect) return; 347 348 var previews = this.previews || []; 349 350 var resizerDom = this.resizer.dom; 351 var resizerLeft = parseInt(resizerDom.style.left, 10) || 0; 352 var resizerTop = parseInt(resizerDom.style.top, 10) || 0; 353 354 var resizerWidth = resizerDom.offsetWidth; 355 var resizerHeight = resizerDom.offsetHeight; 356 357 for (var i = 0, j = previews.length; i < j; i++) { 358 var previewElement = previews[i]; 359 var previewImage = previewElement.querySelector('img'); 360 var previewWrapper = previewElement.querySelector('div'); 361 362 if (!previewImage) continue; 363 364 if (src === '') { 365 previewImage.style.width = previewImage.style.height = previewImage.style.left = previewImage.style.top = 366 ''; 367 previewImage.src = ''; 368 } else { 369 if (ieVersion < 10) { 370 if (src) { 371 previewImage.src = ''; 372 previewImage.style.filter = 373 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)'; 374 previewImage.filters.item( 375 'DXImageTransform.Microsoft.AlphaImageLoader' 376 ).src = src; 377 previewImage.style.width = cropperRect.width + 'px'; 378 previewImage.style.height = cropperRect.height + 'px'; 379 } 380 } else if (src) { 381 previewImage.src = src; 382 } 383 384 var elementWidth = previewElement.offsetWidth; 385 var elementHeight = previewElement.offsetHeight; 386 387 var scale = elementWidth / resizerWidth; 388 389 if (previewWrapper) { 390 var elementRatio = elementWidth / elementHeight; 391 var resizerRatio = resizerWidth / resizerHeight; 392 393 if (elementRatio < resizerRatio) { 394 previewWrapper.style.width = elementWidth + 'px'; 395 previewWrapper.style.height = 396 (resizerHeight * elementWidth) / resizerWidth + 'px'; 397 previewWrapper.style.top = 398 (elementHeight - previewWrapper.clientHeight) / 2 + 'px'; 399 previewWrapper.style.left = ''; 400 } else { 401 var visibleWidth = (resizerWidth * elementHeight) / resizerHeight; 402 scale = visibleWidth / resizerWidth; 403 previewWrapper.style.height = elementHeight + 'px'; 404 previewWrapper.style.width = visibleWidth + 'px'; 405 previewWrapper.style.left = 406 (elementWidth - previewWrapper.clientWidth) / 2 + 'px'; 407 previewWrapper.style.top = ''; 408 } 409 } 410 411 previewImage.style.width = scale * cropperRect.width + 'px'; 412 previewImage.style.height = scale * cropperRect.height + 'px'; 413 previewImage.style.left = -resizerLeft * scale + 'px'; 414 previewImage.style.top = -resizerTop * scale + 'px'; 415 } 416 } 417 }; 418 419 export default Cropper;
3.index.js最后输出的是这个js文件
1 /** @format */ 2 3 import 'babel-polyfill'; 4 import component from './cropper.vue'; 5 6 export function install(Vue) { 7 if (install.installed) return; 8 install.installed = true; 9 Vue.component('cropper', component); 10 } 11 12 const plugin = { 13 install 14 }; 15 16 var GlobalVue = null; 17 if (typeof window !== 'undefined') { 18 GlobalVue = window.Vue; 19 } else if (typeof global !== 'undefined') { 20 GlobalVue = global.Vue; 21 } 22 if (GlobalVue) { 23 GlobalVue.use(plugin); 24 } 25 26 export default component;
4.resize.js
1 /** @format */ 2 3 'use strict'; 4 import createDOM from './createDOM'; 5 6 //是否正在拖拽 7 var isDragging = false; 8 9 //判断是否为ie8 10 var isIE8 = Number(document.documentMode) < 9; 11 12 //定义移动方向 13 var configDirection = { 14 n: { 15 top: true, 16 height: -1 17 }, //上 18 w: { 19 left: true, 20 width: -1 21 }, //左 22 e: { 23 width: 1 24 }, //右 25 s: { 26 height: 1 27 }, //下 28 nw: { 29 left: true, 30 top: true, 31 width: -1, 32 height: -1 33 }, //左下 34 ne: { 35 top: true, 36 width: 1, 37 height: -1 38 }, // 39 sw: { 40 left: true, 41 width: -1, 42 height: 1 43 }, 44 se: { 45 width: 1, 46 height: 1 47 } 48 }; 49 50 /** 51 * @description 为元素绑定on事件 52 * @param element 指定元素 53 * @param event 事件名称(mouseon...) 54 * @param fn 绑定事件 55 */ 56 function bindEvent(element, event, fn) { 57 //attachEvent为ie特有 58 if (element.attachEvent) { 59 element.attachEvent('on' + event, fn); 60 } else { 61 element.addEventListener(event, fn, false); 62 } 63 } 64 65 /** 66 * @description 为元素解除绑定on事件 67 * @param element 指定元素 68 * @param event 事件名称(mouseon...) 69 * @param fn 绑定事件 70 */ 71 function unbindEvent(element, event, fn) { 72 //attachEvent为ie特有 73 if (element.detachEvent) { 74 element.detachEvent('on' + event, fn); 75 } else { 76 element.removeEventListener(event, fn); 77 } 78 } 79 80 /** 81 * @description 校正ie8浏览器钟x.y位置 82 * @param event 事件对象 83 */ 84 function adjustEvent(event) { 85 var scrollTop = Math.max( 86 window.scrollY || 0, 87 document.documentElement.scrollTop || 0 88 ); 89 var scrollLeft = Math.max( 90 window.scrollX || 0, 91 document.documentElement.scrollLeft || 0 92 ); 93 94 event.target = event.srcElement; 95 event.pageX = scrollLeft + event.clientX; 96 event.pageY = scrollTop + event.clientY; 97 } 98 99 /** 100 * @description 拖拽事件 101 * @param {} element 元素 102 * @param {*} options 元素属性 103 */ 104 function draggable(element, options) { 105 var moveFn = function(event) { 106 if (isIE8) { 107 adjustEvent(event); 108 } 109 if (options.drag) { 110 options.drag(event); 111 } 112 }; 113 var upFn = function(event) { 114 if (isIE8) { 115 adjustEvent(event); 116 } 117 unbindEvent(document, 'mousemove', moveFn); 118 unbindEvent(document, 'mouseup', upFn); 119 document.onselectstart = null; 120 document.ondragstart = null; 121 122 isDragging = false; 123 124 if (options.end) { 125 options.end(event); 126 } 127 }; 128 bindEvent(element, 'mousedown', function(event) { 129 if (isIE8) { 130 adjustEvent(event); 131 } 132 if (isDragging) return; 133 document.onselectstart = function() { 134 return false; 135 }; 136 document.ondragstart = function() { 137 return false; 138 }; 139 140 bindEvent(document, 'mousemove', moveFn); 141 bindEvent(document, 'mouseup', upFn); 142 isDragging = true; 143 144 if (options.start) { 145 options.start(event); 146 } 147 }); 148 } 149 150 /** 151 * @description 获取元素相对父元素的位置 152 * @param {dom} element 元素对象 153 */ 154 function getPosition(element) { 155 var selfRect = element.getBoundingClientRect(); 156 var parentRect = element.offsetParent.getBoundingClientRect(); 157 158 return { 159 left: selfRect.left - parentRect.left, 160 top: selfRect.top - parentRect.top 161 }; 162 } 163 164 /** 165 * @description 重新设置框框大小 166 * @param {*} options 167 */ 168 function Resizer(options) { 169 for (var prop in options) { 170 if (options.hasOwnProperty(prop)) this[prop] = options[prop]; 171 } 172 } 173 174 Resizer.prototype.doOnStateChange = function() {}; 175 176 Resizer.prototype.makeDraggable = function(dom) { 177 var self = this; 178 var dragState = {}; 179 var containment; 180 181 draggable(dom, { 182 start: function(event) { 183 var parentNode = dom.parentNode; 184 containment = { 185 left: 0, 186 top: 0, 187 width: parentNode.clientWidth, 188 height: parentNode.clientHeight, 189 right: parentNode.clientWidth, 190 bottom: parentNode.clientHeight 191 }; 192 193 dragState.startLeft = event.clientX; 194 dragState.startTop = event.clientY; 195 196 var position = getPosition(dom); 197 198 dragState.resizerStartLeft = position.left; 199 dragState.resizerStartTop = position.top; 200 dragState.resizerStartWidth = dom.offsetWidth; 201 dragState.resizerStartHeight = dom.offsetHeight; 202 }, 203 drag: function(event) { 204 var offsetLeft = event.clientX - dragState.startLeft; 205 var offsetTop = event.clientY - dragState.startTop; 206 207 var left = dragState.resizerStartLeft + offsetLeft; 208 var top = dragState.resizerStartTop + offsetTop; 209 210 if (left < containment.left) { 211 left = containment.left; 212 } 213 214 if (top < containment.top) { 215 top = containment.top; 216 } 217 218 if (left + dragState.resizerStartWidth > containment.right) { 219 left = containment.right - dragState.resizerStartWidth; 220 } 221 222 if (top + dragState.resizerStartHeight > containment.bottom) { 223 top = containment.bottom - dragState.resizerStartHeight; 224 } 225 226 dom.style.left = left + 'px'; 227 dom.style.top = top + 'px'; 228 229 self.doOnStateChange(); 230 }, 231 end: function() { 232 dragState = {}; 233 if (self.doOnDragEnd) { 234 self.doOnDragEnd(); 235 } 236 } 237 }); 238 }; 239 240 Resizer.prototype.bindResizeEvent = function(dom) { 241 var self = this; 242 var resizeState = {}; 243 var aspectRatio = self.aspectRatio; 244 245 if (typeof aspectRatio !== 'number') { 246 aspectRatio = undefined; 247 } 248 249 var makeResizable = function(bar) { 250 var type = bar.className.split(' ')[0]; 251 var transformMap = configDirection[type.substr(4)]; 252 253 var containment; 254 255 draggable(bar, { 256 start: function(event) { 257 var parentNode = dom.parentNode; 258 containment = { 259 left: 0, 260 top: 0, 261 width: parentNode.clientWidth, 262 height: parentNode.clientHeight, 263 right: parentNode.clientWidth, 264 bottom: parentNode.clientHeight 265 }; 266 267 resizeState.startWidth = dom.clientWidth; 268 resizeState.startHeight = dom.clientHeight; 269 resizeState.startLeft = event.clientX; 270 resizeState.startTop = event.clientY; 271 272 var position = getPosition(dom); 273 resizeState.resizerStartLeft = position.left; 274 resizeState.resizerStartTop = position.top; 275 }, 276 drag: function(event) { 277 var widthRatio = transformMap.width; 278 var heightRatio = transformMap.height; 279 280 var offsetLeft = event.clientX - resizeState.startLeft; 281 var offsetTop = event.clientY - resizeState.startTop; 282 283 var width, 284 height, 285 minWidth = 50, 286 maxWidth = 10000, 287 minHeight = 50, 288 maxHeight = 10000; 289 290 if (widthRatio !== undefined) { 291 width = resizeState.startWidth + widthRatio * offsetLeft; 292 if (width < minWidth) { 293 width = minWidth; 294 } 295 296 if (maxWidth && width > maxWidth) { 297 width = maxWidth; 298 } 299 } 300 301 if (heightRatio !== undefined) { 302 height = resizeState.startHeight + heightRatio * offsetTop; 303 if (height < minHeight) { 304 height = minHeight; 305 } 306 307 if (maxHeight && height > maxHeight) { 308 height = maxHeight; 309 } 310 } 311 312 if (aspectRatio !== undefined) { 313 if (type === 'ord-n' || type === 'ord-s') { 314 width = height * aspectRatio; 315 } else if (type === 'ord-w' || type === 'ord-e') { 316 height = width / aspectRatio; 317 } else { 318 if (width / height < aspectRatio) { 319 height = width / aspectRatio; 320 } else { 321 width = height * aspectRatio; 322 } 323 } 324 } 325 326 var position = { 327 left: resizeState.resizerStartLeft, 328 top: resizeState.resizerStartTop 329 }; 330 331 if (transformMap.left !== undefined) { 332 position.left = 333 resizeState.resizerStartLeft + 334 (width - resizeState.startWidth) * widthRatio; 335 } 336 337 if (transformMap.top !== undefined) { 338 position.top = 339 resizeState.resizerStartTop + 340 (height - resizeState.startHeight) * heightRatio; 341 } 342 343 //=== containment start 344 345 if (width + position.left > containment.right) { 346 width = containment.right - position.left; 347 } 348 349 if (position.left < containment.left) { 350 width -= containment.left - position.left; 351 position.left = containment.left; 352 } 353 354 if (height + position.top > containment.bottom) { 355 height = containment.bottom - position.top; 356 } 357 358 if (position.top < containment.top) { 359 height -= containment.top - position.top; 360 position.top = containment.top; 361 } 362 363 //=== containment end 364 365 if (aspectRatio !== undefined) { 366 if (width / height < aspectRatio) { 367 height = width / aspectRatio; 368 } else { 369 width = height * aspectRatio; 370 } 371 } 372 373 if (transformMap.left !== undefined) { 374 position.left = 375 resizeState.resizerStartLeft + 376 (width - resizeState.startWidth) * widthRatio; 377 } 378 379 if (transformMap.top !== undefined) { 380 position.top = 381 resizeState.resizerStartTop + 382 (height - resizeState.startHeight) * heightRatio; 383 } 384 385 dom.style.width = width + 'px'; 386 dom.style.height = height + 'px'; 387 388 if (position.left !== undefined) { 389 dom.style.left = position.left + 'px'; 390 } 391 392 if (position.top !== undefined) { 393 dom.style.top = position.top + 'px'; 394 } 395 396 self.doOnStateChange(); 397 }, 398 end: function() { 399 if (self.doOnDragEnd) { 400 self.doOnDragEnd(); 401 } 402 } 403 }); 404 }; 405 406 var bars = dom.querySelectorAll('.resize-bar'); 407 var handles = dom.querySelectorAll('.resize-handle'); 408 409 var i, j; 410 411 for (i = 0, j = bars.length; i < j; i++) { 412 makeResizable(bars[i]); 413 } 414 415 for (i = 0, j = handles.length; i < j; i++) { 416 makeResizable(handles[i]); 417 } 418 }; 419 420 Resizer.prototype.render = function(container) { 421 var self = this; 422 423 var dom = createDOM({ 424 tag: 'div', 425 className: 'resizer', 426 content: [ 427 { 428 tag: 'div', 429 className: 'ord-n resize-bar' 430 }, 431 { 432 tag: 'div', 433 className: 'ord-s resize-bar' 434 }, 435 { 436 tag: 'div', 437 className: 'ord-w resize-bar' 438 }, 439 { 440 tag: 'div', 441 className: 'ord-e resize-bar' 442 }, 443 { 444 tag: 'div', 445 className: 'ord-nw resize-handle' 446 }, 447 { 448 tag: 'div', 449 className: 'ord-n resize-handle' 450 }, 451 { 452 tag: 'div', 453 className: 'ord-ne resize-handle' 454 }, 455 { 456 tag: 'div', 457 className: 'ord-w resize-handle' 458 }, 459 { 460 tag: 'div', 461 className: 'ord-e resize-handle' 462 }, 463 { 464 tag: 'div', 465 className: 'ord-sw resize-handle' 466 }, 467 { 468 tag: 'div', 469 className: 'ord-s resize-handle' 470 }, 471 { 472 tag: 'div', 473 className: 'ord-se resize-handle' 474 } 475 ] 476 }); 477 478 self.dom = dom; 479 480 self.bindResizeEvent(dom); 481 self.makeDraggable(dom); 482 483 if (container) { 484 container.appendChild(dom); 485 } 486 487 return dom; 488 }; 489 490 export default Resizer;
结束!!! 有能用上的麻烦点个推荐
<!-- @format -->