Merge branch 'develop' of https://gitee.com/layui-vue/layui-vue into develop
This commit is contained in:
		
						commit
						5cf3eb5a00
					
				@ -311,12 +311,12 @@ export default {
 | 
				
			|||||||
| zIndex          | 自定义层级    | --                         |
 | 
					| zIndex          | 自定义层级    | --                         |
 | 
				
			||||||
| type            | 类型          | `1: component` `2: iframe` |
 | 
					| type            | 类型          | `1: component` `2: iframe` |
 | 
				
			||||||
| closeBtn        | 显示关闭      | true                       |
 | 
					| closeBtn        | 显示关闭      | true                       |
 | 
				
			||||||
| btn             | 按钮          |                            |
 | 
					| btn             | 按钮 格式:{text:"",callback:function(){}} | --    |
 | 
				
			||||||
| btnAlign        | 按钮布局      | `l` `r` `c`                |
 | 
					| btnAlign        | 按钮布局      | `l` `r` `c`                |
 | 
				
			||||||
| anim            | 入场动画      | `0` `-` `6`                |
 | 
					| anim            | 入场动画      | `0` `-` `6`                |
 | 
				
			||||||
| isOutAnim       | 关闭动画      | `true` `false`             |
 | 
					| isOutAnim       | 关闭动画      | `true` `false`             |
 | 
				
			||||||
| success            | 显示回调   | --                |
 | 
					| success            | 显示回调   | --                |
 | 
				
			||||||
| end       | 关闭回调      | --             |
 | 
					| close       | 关闭回调      | --             |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
:::
 | 
					:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -106,13 +106,13 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
:::
 | 
					:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
::: title 自定义预览
 | 
					::: title 自定义预览/上传禁用
 | 
				
			||||||
:::
 | 
					:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
::: demo 使用 `lay-upload` 标签, 使用 `#preview` 自定义预览的UI交互
 | 
					::: demo 使用 `lay-upload` 标签, 使用 `#preview` 自定义预览的UI交互,使用  `disabled` 添加上传禁用
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <lay-upload @done="getUploadFile2">
 | 
					  <lay-upload @done="getUploadFile2" :disabled="true">
 | 
				
			||||||
    <template #preview>
 | 
					    <template #preview>
 | 
				
			||||||
      <div class="easy-wrap">
 | 
					      <div class="easy-wrap">
 | 
				
			||||||
        <img src="https://chixian.oss-cn-hangzhou.aliyuncs.com/20211023003617_0706a.jpg" style="width:62.9px;height:63.2px"/>
 | 
					        <img src="https://chixian.oss-cn-hangzhou.aliyuncs.com/20211023003617_0706a.jpg" style="width:62.9px;height:63.2px"/>
 | 
				
			||||||
@ -141,6 +141,49 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
:::
 | 
					:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					::: title 提供默认剪裁功能
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					::: demo 使用 `lay-upload` 标签, 添加 `cut` 开启 选择文件后剪裁功能
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <lay-upload @cutdone="getCutDone" @cutcancel="getCutCancel" :cut="true" :multiple="false" @done="getFileDone">
 | 
				
			||||||
 | 
					    <template #preview>
 | 
				
			||||||
 | 
					      <div class="easy-wrap" v-if="cutUrl">
 | 
				
			||||||
 | 
					        <img :src="cutUrl"/>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </template>
 | 
				
			||||||
 | 
					  </lay-upload>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					import { ref } from 'vue'
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  setup() {
 | 
				
			||||||
 | 
					    const cutUrl = ref("");
 | 
				
			||||||
 | 
					    const getCutDone=(res)=>{
 | 
				
			||||||
 | 
					      console.log("getCutDone",res);
 | 
				
			||||||
 | 
					      cutUrl.value = res.msg;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    const getCutCancel=(res)=>{
 | 
				
			||||||
 | 
					      console.log("getCutCancel",res);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    const getFileDone=(res)=>{
 | 
				
			||||||
 | 
					      console.log("getFileDone",res);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      getCutDone,
 | 
				
			||||||
 | 
					      getCutCancel,
 | 
				
			||||||
 | 
					      getFileDone,
 | 
				
			||||||
 | 
					      cutUrl
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
::: title Upload 属性
 | 
					::: title Upload 属性
 | 
				
			||||||
:::
 | 
					:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -157,6 +200,9 @@ export default {
 | 
				
			|||||||
| multiple | 是否允许多文件上传。设置 true即可开启。不支持ie8/9 | boolean  | false  | -- |
 | 
					| multiple | 是否允许多文件上传。设置 true即可开启。不支持ie8/9 | boolean  | false  | -- |
 | 
				
			||||||
| number | 设置同时可上传的文件数量,一般配合 multiple 参数出现。 | number  | `0(不限制)`  | -- |
 | 
					| number | 设置同时可上传的文件数量,一般配合 multiple 参数出现。 | number  | `0(不限制)`  | -- |
 | 
				
			||||||
| drag | 是否接受拖拽的文件上传,设置 false 可禁用。不支持ie8/9 | boolean  | true  | -- |
 | 
					| drag | 是否接受拖拽的文件上传,设置 false 可禁用。不支持ie8/9 | boolean  | true  | -- |
 | 
				
			||||||
 | 
					| disabled | 设置文件禁用 | boolean  | false  | -- |
 | 
				
			||||||
 | 
					| cut | 是否开启选择图片后检测,设置true可开启 | boolean  | false  | -- |
 | 
				
			||||||
 | 
					| cutOptions | 开启剪裁的模态弹窗与剪裁框的配置 | object  | { layerOption,copperOption }  | -- |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
:::
 | 
					:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -182,7 +228,8 @@ export default {
 | 
				
			|||||||
| before | 上传事务开启前的回调 | --   |
 | 
					| before | 上传事务开启前的回调 | --   |
 | 
				
			||||||
| done | 上传事务结束的回调 | --   |
 | 
					| done | 上传事务结束的回调 | --   |
 | 
				
			||||||
| error | 上传事务中出现错误的回调 | --   |
 | 
					| error | 上传事务中出现错误的回调 | --   |
 | 
				
			||||||
 | 
					| cutdown | 剪裁完成 | --   |
 | 
				
			||||||
 | 
					| cutclose | 剪裁取消 | --   |
 | 
				
			||||||
:::
 | 
					:::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -43,6 +43,7 @@
 | 
				
			|||||||
    "@layui/layer-vue": "^1.3.11",
 | 
					    "@layui/layer-vue": "^1.3.11",
 | 
				
			||||||
    "@vueuse/core": "^7.6.2",
 | 
					    "@vueuse/core": "^7.6.2",
 | 
				
			||||||
    "async-validator": "^4.0.7",
 | 
					    "async-validator": "^4.0.7",
 | 
				
			||||||
 | 
					    "cropperjs": "^1.5.12",
 | 
				
			||||||
    "darkreader": "^4.9.46",
 | 
					    "darkreader": "^4.9.46",
 | 
				
			||||||
    "evtd": "^0.2.3",
 | 
					    "evtd": "^0.2.3",
 | 
				
			||||||
    "moment": "^2.29.1",
 | 
					    "moment": "^2.29.1",
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ export default {
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import "./index.less";
 | 
					import "./index.less";
 | 
				
			||||||
import { withDefaults } from "vue";
 | 
					import { withDefaults } from "vue";
 | 
				
			||||||
import { String } from "src/types";
 | 
					import { String } from "../../types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface LayEmptyProps {
 | 
					export interface LayEmptyProps {
 | 
				
			||||||
  description?: String;
 | 
					  description?: String;
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ export default {
 | 
				
			|||||||
import "./index.less";
 | 
					import "./index.less";
 | 
				
			||||||
import { useSlots } from "vue";
 | 
					import { useSlots } from "vue";
 | 
				
			||||||
import { useI18n } from "vue-i18n";
 | 
					import { useI18n } from "vue-i18n";
 | 
				
			||||||
import { Boolean, String } from "src/types";
 | 
					import { Boolean, String } from "../../types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { t } = useI18n();
 | 
					const { t } = useI18n();
 | 
				
			||||||
const slots = useSlots();
 | 
					const slots = useSlots();
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										9
									
								
								src/component/upload/cropper.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/component/upload/cropper.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/*!
 | 
				
			||||||
 | 
					 * Cropper.js v1.5.12
 | 
				
			||||||
 | 
					 * https://fengyuanchen.github.io/cropperjs
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright 2015-present Chen Fengyuan
 | 
				
			||||||
 | 
					 * Released under the MIT license
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Date: 2021-06-12T08:00:11.623Z
 | 
				
			||||||
 | 
					 */.cropper-container{direction:ltr;font-size:0;line-height:0;position:relative;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cropper-container img{image-orientation:0deg;display:block;height:100%;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:rgba(51,153,255,.75);overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media (min-width:768px){.cropper-point.point-se{height:15px;width:15px}}@media (min-width:992px){.cropper-point.point-se{height:10px;width:10px}}@media (min-width:1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC")}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}
 | 
				
			||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
@import (reference) "../../theme/variable.less";
 | 
					@import (reference) "../../theme/variable.less";
 | 
				
			||||||
 | 
					@import "./cropper.min.css";
 | 
				
			||||||
.layui-upload-file {
 | 
					.layui-upload-file {
 | 
				
			||||||
  // display: none !important;
 | 
					  // display: none !important;
 | 
				
			||||||
  opacity: 0.01;
 | 
					  opacity: 0.01;
 | 
				
			||||||
@ -85,3 +85,17 @@
 | 
				
			|||||||
.layui-btn-container .layui-upload-choose {
 | 
					.layui-btn-container .layui-upload-choose {
 | 
				
			||||||
  padding-left: 0;
 | 
					  padding-left: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.layui-upload-drag-disable{
 | 
				
			||||||
 | 
					  opacity:0.8;
 | 
				
			||||||
 | 
					  z-index:1;
 | 
				
			||||||
 | 
					  cursor: not-allowed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.copper-container{
 | 
				
			||||||
 | 
					  // width:1000px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					._lay_upload_img{
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  max-width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -7,11 +7,49 @@ export default {
 | 
				
			|||||||
import "./index.less";
 | 
					import "./index.less";
 | 
				
			||||||
import { Recordable } from "../../types";
 | 
					import { Recordable } from "../../types";
 | 
				
			||||||
import { layer } from "@layui/layer-vue";
 | 
					import { layer } from "@layui/layer-vue";
 | 
				
			||||||
import { ref, useSlots, withDefaults } from "vue";
 | 
					import {
 | 
				
			||||||
 | 
					  computed,
 | 
				
			||||||
 | 
					  ComputedRef,
 | 
				
			||||||
 | 
					  getCurrentInstance,
 | 
				
			||||||
 | 
					  nextTick,
 | 
				
			||||||
 | 
					  ref,
 | 
				
			||||||
 | 
					  toRaw,
 | 
				
			||||||
 | 
					  useSlots,
 | 
				
			||||||
 | 
					  withDefaults,
 | 
				
			||||||
 | 
					} from "vue";
 | 
				
			||||||
import { templateRef } from "@vueuse/core";
 | 
					import { templateRef } from "@vueuse/core";
 | 
				
			||||||
 | 
					import { LayLayer } from "@layui/layer-vue";
 | 
				
			||||||
 | 
					import Cropper from "cropperjs";
 | 
				
			||||||
// 组件的参数字段类型
 | 
					// 组件的参数字段类型
 | 
				
			||||||
//https://www.layuiweb.com/doc/modules/upload.html#options
 | 
					//https://www.layuiweb.com/doc/modules/upload.html#options
 | 
				
			||||||
 | 
					export interface LayerButton {
 | 
				
			||||||
 | 
					  text: string;
 | 
				
			||||||
 | 
					  callback: Function;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export interface LayerModal {
 | 
				
			||||||
 | 
					  title?: string;
 | 
				
			||||||
 | 
					  resize?: boolean;
 | 
				
			||||||
 | 
					  move?: boolean;
 | 
				
			||||||
 | 
					  maxmin?: boolean;
 | 
				
			||||||
 | 
					  offset?: string[];
 | 
				
			||||||
 | 
					  content?: string;
 | 
				
			||||||
 | 
					  shade?: boolean;
 | 
				
			||||||
 | 
					  shadeClose?: boolean;
 | 
				
			||||||
 | 
					  shadeOpacity?: number;
 | 
				
			||||||
 | 
					  zIndex?: number;
 | 
				
			||||||
 | 
					  type?: "component" | "iframe";
 | 
				
			||||||
 | 
					  closeBtn?: boolean;
 | 
				
			||||||
 | 
					  area: string[];
 | 
				
			||||||
 | 
					  btn?: LayerButton[];
 | 
				
			||||||
 | 
					  btnAlign?: "l" | "r" | "c";
 | 
				
			||||||
 | 
					  anim?: boolean;
 | 
				
			||||||
 | 
					  isOutAnim?: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export interface cutOptions {
 | 
				
			||||||
 | 
					  layerOption: LayerModal;
 | 
				
			||||||
 | 
					  copperOption?: typeof Cropper;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface LayUploadProps {
 | 
					export interface LayUploadProps {
 | 
				
			||||||
  url?: string;
 | 
					  url?: string;
 | 
				
			||||||
  data?: any;
 | 
					  data?: any;
 | 
				
			||||||
@ -22,8 +60,50 @@ export interface LayUploadProps {
 | 
				
			|||||||
  multiple?: boolean;
 | 
					  multiple?: boolean;
 | 
				
			||||||
  number?: number;
 | 
					  number?: number;
 | 
				
			||||||
  drag?: boolean;
 | 
					  drag?: boolean;
 | 
				
			||||||
 | 
					  disabled?: boolean;
 | 
				
			||||||
 | 
					  cut?: boolean;
 | 
				
			||||||
 | 
					  cutOptions: cutOptions;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getCutDownResult = () => {
 | 
				
			||||||
 | 
					  if (_cropper) {
 | 
				
			||||||
 | 
					    const canvas = _cropper.getCroppedCanvas();
 | 
				
			||||||
 | 
					    let imgData = canvas.toDataURL('"image/png"');
 | 
				
			||||||
 | 
					    let currentTimeStamp = new Date().valueOf();
 | 
				
			||||||
 | 
					    emit("cutdone", Object.assign({ currentTimeStamp, msg: imgData }));
 | 
				
			||||||
 | 
					    let newFile = dataURLtoFile(imgData);
 | 
				
			||||||
 | 
					    commonUploadTransaction([newFile]);
 | 
				
			||||||
 | 
					    nextTick(() => clearAllCutEffect());
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    errorF(cutInitErrorMsg);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					const closeCutDownModal = () => {
 | 
				
			||||||
 | 
					  let currentTimeStamp = new Date().valueOf();
 | 
				
			||||||
 | 
					  emit("cutcancel", Object.assign({ currentTimeStamp }));
 | 
				
			||||||
 | 
					  nextTick(() => clearAllCutEffect());
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					const clearAllCutEffect = () => {
 | 
				
			||||||
 | 
					  activeUploadFiles.value = [];
 | 
				
			||||||
 | 
					  activeUploadFilesImgs.value = [];
 | 
				
			||||||
 | 
					  innerCutVisible.value = false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let defaultCutLayerOption: LayerModal = {
 | 
				
			||||||
 | 
					  title: "标题",
 | 
				
			||||||
 | 
					  move: true,
 | 
				
			||||||
 | 
					  maxmin: false,
 | 
				
			||||||
 | 
					  offset: [],
 | 
				
			||||||
 | 
					  btn: [
 | 
				
			||||||
 | 
					    { text: "导出", callback: getCutDownResult },
 | 
				
			||||||
 | 
					    { text: "取消", callback: closeCutDownModal },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  area: ["640px", "640px"],
 | 
				
			||||||
 | 
					  content: "11",
 | 
				
			||||||
 | 
					  shade: true,
 | 
				
			||||||
 | 
					  shadeClose: true,
 | 
				
			||||||
 | 
					  type: "component",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
const props = withDefaults(defineProps<LayUploadProps>(), {
 | 
					const props = withDefaults(defineProps<LayUploadProps>(), {
 | 
				
			||||||
  acceptMime: "images",
 | 
					  acceptMime: "images",
 | 
				
			||||||
  field: "file",
 | 
					  field: "file",
 | 
				
			||||||
@ -31,22 +111,48 @@ const props = withDefaults(defineProps<LayUploadProps>(), {
 | 
				
			|||||||
  multiple: false,
 | 
					  multiple: false,
 | 
				
			||||||
  number: 0,
 | 
					  number: 0,
 | 
				
			||||||
  drag: false,
 | 
					  drag: false,
 | 
				
			||||||
 | 
					  disabled: false,
 | 
				
			||||||
 | 
					  cut: false,
 | 
				
			||||||
 | 
					  cutOptions: void 0,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const slot = useSlots();
 | 
					const slot = useSlots();
 | 
				
			||||||
const slots = slot.default && slot.default();
 | 
					const slots = slot.default && slot.default();
 | 
				
			||||||
const emit = defineEmits(["choose", "before", "done", "error"]);
 | 
					const context = getCurrentInstance();
 | 
				
			||||||
 | 
					const emit = defineEmits([
 | 
				
			||||||
 | 
					  "choose",
 | 
				
			||||||
 | 
					  "before",
 | 
				
			||||||
 | 
					  "done",
 | 
				
			||||||
 | 
					  "error",
 | 
				
			||||||
 | 
					  "cutdone",
 | 
				
			||||||
 | 
					  "cutcancel",
 | 
				
			||||||
 | 
					]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 内部变量
 | 
					// 内部变量
 | 
				
			||||||
const isDragEnter = ref(false);
 | 
					const isDragEnter = ref(false);
 | 
				
			||||||
 | 
					// 待处理的上传文件
 | 
				
			||||||
 | 
					const activeUploadFiles = ref<any[]>([]);
 | 
				
			||||||
 | 
					// 待处理的上传图片
 | 
				
			||||||
 | 
					const activeUploadFilesImgs = ref<any[]>([]);
 | 
				
			||||||
const orgFileInput = templateRef<HTMLElement>("orgFileInput");
 | 
					const orgFileInput = templateRef<HTMLElement>("orgFileInput");
 | 
				
			||||||
 | 
					let _cropper: any = null;
 | 
				
			||||||
 | 
					let computedCutLayerOption: ComputedRef<LayerModal>;
 | 
				
			||||||
 | 
					if (props.cutOptions && props.cutOptions.layerOption) {
 | 
				
			||||||
 | 
					  computedCutLayerOption = computed(() =>
 | 
				
			||||||
 | 
					    Object.assign(defaultCutLayerOption, props.cutOptions.layerOption)
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
					  computedCutLayerOption = computed(() => defaultCutLayerOption);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 统一异常提示的常量
 | 
					// 统一异常提示的常量
 | 
				
			||||||
const defaultErrorMsg = "上传失败";
 | 
					const defaultErrorMsg = "上传失败";
 | 
				
			||||||
const urlErrorMsg = "上传地址格式不合法";
 | 
					const urlErrorMsg = "上传地址格式不合法";
 | 
				
			||||||
const numberErrorMsg = "文件上传超过规定的个数";
 | 
					const numberErrorMsg = "文件上传超过规定的个数";
 | 
				
			||||||
const sizeErrorMsg = "文件大小超过限制";
 | 
					const sizeErrorMsg = "文件大小超过限制";
 | 
				
			||||||
const uploadRemoteErrorMsg = "请求上传接口出现异常";
 | 
					const uploadRemoteErrorMsg = "请求上传接口出现异常";
 | 
				
			||||||
 | 
					const cutInitErrorMsg = "剪裁插件初始化失败";
 | 
				
			||||||
 | 
					// 统一成功提示
 | 
				
			||||||
const uploadSuccess = "上传成功";
 | 
					const uploadSuccess = "上传成功";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//内部方法 -> start
 | 
					//内部方法 -> start
 | 
				
			||||||
@ -56,6 +162,8 @@ interface localUploadTransaction {
 | 
				
			|||||||
  files: File[] | Blob[];
 | 
					  files: File[] | Blob[];
 | 
				
			||||||
  [propMame: string]: any;
 | 
					  [propMame: string]: any;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const innerCutVisible = ref<boolean>(false);
 | 
				
			||||||
const localUploadTransaction = (option: localUploadTransaction) => {
 | 
					const localUploadTransaction = (option: localUploadTransaction) => {
 | 
				
			||||||
  const { url, files } = option;
 | 
					  const { url, files } = option;
 | 
				
			||||||
  let formData = new FormData();
 | 
					  let formData = new FormData();
 | 
				
			||||||
@ -88,6 +196,20 @@ interface localUploadOption {
 | 
				
			|||||||
  url: string;
 | 
					  url: string;
 | 
				
			||||||
  [propMame: string]: any;
 | 
					  [propMame: string]: any;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					const dataURLtoFile = (dataurl: string) => {
 | 
				
			||||||
 | 
					  let arr: any[] = dataurl.split(",");
 | 
				
			||||||
 | 
					  let mime: string = "";
 | 
				
			||||||
 | 
					  if (arr.length > 0) {
 | 
				
			||||||
 | 
					    mime = arr[0].match(/:(.*?);/)[1];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let bstr = atob(arr[1]);
 | 
				
			||||||
 | 
					  let n = bstr.length;
 | 
				
			||||||
 | 
					  let u8arr = new Uint8Array(n);
 | 
				
			||||||
 | 
					  while (n--) {
 | 
				
			||||||
 | 
					    u8arr[n] = bstr.charCodeAt(n);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return new Blob([u8arr], { type: mime });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const errorF = (errorText: string) => {
 | 
					const errorF = (errorText: string) => {
 | 
				
			||||||
  let currentTimeStamp = new Date().valueOf();
 | 
					  let currentTimeStamp = new Date().valueOf();
 | 
				
			||||||
@ -139,7 +261,13 @@ const localUpload = (option: localUploadOption, callback: Function) => {
 | 
				
			|||||||
    cb();
 | 
					    cb();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					const filetoDataURL = (file: File, fn: Function) => {
 | 
				
			||||||
 | 
					  const reader = new FileReader();
 | 
				
			||||||
 | 
					  reader.onloadend = function (e: any) {
 | 
				
			||||||
 | 
					    fn(e.target.result);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  reader.readAsDataURL(file);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
const getUploadChange = (e: any) => {
 | 
					const getUploadChange = (e: any) => {
 | 
				
			||||||
  const files = e.target.files;
 | 
					  const files = e.target.files;
 | 
				
			||||||
  const _files = [...files];
 | 
					  const _files = [...files];
 | 
				
			||||||
@ -165,17 +293,43 @@ const getUploadChange = (e: any) => {
 | 
				
			|||||||
          _sizeErrorFile.name
 | 
					          _sizeErrorFile.name
 | 
				
			||||||
        } ${sizeErrorMsg},文件最大不可超过${props.size * 1000}kb`;
 | 
					        } ${sizeErrorMsg},文件最大不可超过${props.size * 1000}kb`;
 | 
				
			||||||
        errorF(errorMsg);
 | 
					        errorF(errorMsg);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  for (let item of _files) {
 | 
				
			||||||
 | 
					    activeUploadFiles.value.push(item);
 | 
				
			||||||
 | 
					    filetoDataURL(item, function (res: any) {
 | 
				
			||||||
 | 
					      activeUploadFilesImgs.value.push(res);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let arm1 = props.cut && props.acceptMime == "images" && !props.multiple;
 | 
				
			||||||
 | 
					  let arm2 = props.cut && props.acceptMime == "images" && props.multiple;
 | 
				
			||||||
 | 
					  if (arm1) {
 | 
				
			||||||
 | 
					    innerCutVisible.value = true;
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					      let _imgs = document.getElementsByClassName("_lay_upload_img");
 | 
				
			||||||
 | 
					      let _img = _imgs[0];
 | 
				
			||||||
 | 
					      _cropper = new Cropper(_img, {
 | 
				
			||||||
 | 
					        aspectRatio: 16 / 9,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }, 400);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    if (arm2) {
 | 
				
			||||||
 | 
					      console.warn(
 | 
				
			||||||
 | 
					        "layui-vue:当前版本暂不支持单次多文件剪裁,尝试设置 multiple 为false,通过@done获取返回文件对象"
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    commonUploadTransaction(_files);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					const commonUploadTransaction = (_files: any[]) => {
 | 
				
			||||||
  if (props.url) {
 | 
					  if (props.url) {
 | 
				
			||||||
    // 表单提交
 | 
					 | 
				
			||||||
    localUploadTransaction({
 | 
					    localUploadTransaction({
 | 
				
			||||||
      url: props.url,
 | 
					      url: props.url,
 | 
				
			||||||
      files: _files,
 | 
					      files: _files,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    // 抛出上传文件信息
 | 
					 | 
				
			||||||
    emit("done", _files);
 | 
					    emit("done", _files);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -188,21 +342,9 @@ const chooseFile = () => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
const clickOrgInput = () => {
 | 
					const clickOrgInput = () => {
 | 
				
			||||||
  let currentTimeStamp = new Date().valueOf();
 | 
					  let currentTimeStamp = new Date().valueOf();
 | 
				
			||||||
  //console.log(currentTimeStamp);
 | 
					 | 
				
			||||||
  emit("choose", currentTimeStamp);
 | 
					  emit("choose", currentTimeStamp);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
const uploadDragOver = (e: any) => {};
 | 
					const cutTransaction = () => {};
 | 
				
			||||||
const uploadDragDrop = (e: any) => {
 | 
					 | 
				
			||||||
  isDragEnter.value = false;
 | 
					 | 
				
			||||||
  console.log(e);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
const uploadDragStop = (e: any) => {};
 | 
					 | 
				
			||||||
const uploadDragEnter = (e: any) => {
 | 
					 | 
				
			||||||
  isDragEnter.value = true;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
const uploadDragLeave = (e: any) => {
 | 
					 | 
				
			||||||
  isDragEnter.value = false;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
//内部方法 -> end
 | 
					//内部方法 -> end
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
@ -216,11 +358,12 @@ const uploadDragLeave = (e: any) => {
 | 
				
			|||||||
      :name="field"
 | 
					      :name="field"
 | 
				
			||||||
      @change="getUploadChange"
 | 
					      @change="getUploadChange"
 | 
				
			||||||
      :field="field"
 | 
					      :field="field"
 | 
				
			||||||
 | 
					      :disabled="disabled"
 | 
				
			||||||
      ref="orgFileInput"
 | 
					      ref="orgFileInput"
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
    <div v-if="!drag">
 | 
					    <div v-if="!drag">
 | 
				
			||||||
      <div class="layui-upload-btn-box">
 | 
					      <div class="layui-upload-btn-box">
 | 
				
			||||||
        <lay-button type="primary" @click.stop="chooseFile"
 | 
					        <lay-button type="primary" @click.stop="chooseFile" :disabled="disabled"
 | 
				
			||||||
          >上传图片</lay-button
 | 
					          >上传图片</lay-button
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
@ -228,11 +371,13 @@ const uploadDragLeave = (e: any) => {
 | 
				
			|||||||
    <div
 | 
					    <div
 | 
				
			||||||
      v-else
 | 
					      v-else
 | 
				
			||||||
      class="layui-upload-drag"
 | 
					      class="layui-upload-drag"
 | 
				
			||||||
      :class="isDragEnter ? 'layui-upload-drag-draging' : ''"
 | 
					      :class="
 | 
				
			||||||
      @dragleave.stop="uploadDragLeave"
 | 
					        disabled
 | 
				
			||||||
      @dragenter.stop="uploadDragEnter"
 | 
					          ? 'layui-upload-drag-disable'
 | 
				
			||||||
      @dragover.stop="uploadDragOver"
 | 
					          : isDragEnter
 | 
				
			||||||
      @drop="uploadDragDrop"
 | 
					          ? 'layui-upload-drag-draging'
 | 
				
			||||||
 | 
					          : ''
 | 
				
			||||||
 | 
					      "
 | 
				
			||||||
      @click.stop="chooseFile"
 | 
					      @click.stop="chooseFile"
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <i class="layui-icon"></i>
 | 
					      <i class="layui-icon"></i>
 | 
				
			||||||
@ -242,6 +387,34 @@ const uploadDragLeave = (e: any) => {
 | 
				
			|||||||
        <img src="" alt="上传成功后渲染" style="max-width: 196px" />
 | 
					        <img src="" alt="上传成功后渲染" style="max-width: 196px" />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    <lay-layer
 | 
				
			||||||
 | 
					      :title="computedCutLayerOption.title"
 | 
				
			||||||
 | 
					      :move="computedCutLayerOption.move"
 | 
				
			||||||
 | 
					      :resize="computedCutLayerOption.resize"
 | 
				
			||||||
 | 
					      :shade="computedCutLayerOption.shade"
 | 
				
			||||||
 | 
					      :shadeClose="computedCutLayerOption.shadeClose"
 | 
				
			||||||
 | 
					      :shadeOpacity="computedCutLayerOption.shadeOpacity"
 | 
				
			||||||
 | 
					      :zIndex="computedCutLayerOption.zIndex"
 | 
				
			||||||
 | 
					      :btnAlign="computedCutLayerOption.btnAlign"
 | 
				
			||||||
 | 
					      :area="computedCutLayerOption.area"
 | 
				
			||||||
 | 
					      :anim="computedCutLayerOption.anim"
 | 
				
			||||||
 | 
					      :isOutAnim="computedCutLayerOption.isOutAnim"
 | 
				
			||||||
 | 
					      :btn="computedCutLayerOption.btn"
 | 
				
			||||||
 | 
					      v-model="innerCutVisible"
 | 
				
			||||||
 | 
					      @close="clearAllCutEffect"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <div
 | 
				
			||||||
 | 
					        class="copper-container"
 | 
				
			||||||
 | 
					        v-for="(base64str, index) in activeUploadFilesImgs"
 | 
				
			||||||
 | 
					        :key="`file${index}`"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <img
 | 
				
			||||||
 | 
					          :src="base64str"
 | 
				
			||||||
 | 
					          :id="`_lay_upload_img${index}`"
 | 
				
			||||||
 | 
					          class="_lay_upload_img"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </lay-layer>
 | 
				
			||||||
    <div class="layui-upload-list">
 | 
					    <div class="layui-upload-list">
 | 
				
			||||||
      <slot name="preview"></slot>
 | 
					      <slot name="preview"></slot>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user