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 -->
posted @ 2020-07-02 16:13  ____chen  阅读(3986)  评论(1编辑  收藏  举报