diff --git a/CHANGELOG.zh_CN.md b/CHANGELOG.zh_CN.md index 425220c9..f27a7355 100644 --- a/CHANGELOG.zh_CN.md +++ b/CHANGELOG.zh_CN.md @@ -9,6 +9,7 @@ - **CropperImage** `Cropper` 头像裁剪新增圆形裁剪功能 - **CropperAvatar** 新增头像上传组件 - **Drawer** `useDrawer`新增`closeDrawer`函数 +- **Preview** 新增`createImgPreview`图片预览函数 ### 🐛 Bug Fixes diff --git a/src/components/Form/src/components/ApiSelect.vue b/src/components/Form/src/components/ApiSelect.vue index 389d9f46..9fc5160d 100644 --- a/src/components/Form/src/components/ApiSelect.vue +++ b/src/components/Form/src/components/ApiSelect.vue @@ -26,7 +26,6 @@ import { useRuleFormItem } from '/@/hooks/component/useFormItem'; import { useAttrs } from '/@/hooks/core/useAttrs'; import { get, omit } from 'lodash-es'; - import { LoadingOutlined } from '@ant-design/icons-vue'; import { useI18n } from '/@/hooks/web/useI18n'; import { propTypes } from '/@/utils/propTypes'; diff --git a/src/components/Form/src/components/FormAction.vue b/src/components/Form/src/components/FormAction.vue index edea5eac..ba6ff251 100644 --- a/src/components/Form/src/components/FormAction.vue +++ b/src/components/Form/src/components/FormAction.vue @@ -40,13 +40,11 @@ + diff --git a/src/components/Preview/src/functional.ts b/src/components/Preview/src/functional.ts index 0f9eba0a..1f4ba671 100644 --- a/src/components/Preview/src/functional.ts +++ b/src/components/Preview/src/functional.ts @@ -1,11 +1,9 @@ -import ImgPreview from './index'; +import type { Options, Props } from './typing'; +import ImgPreview from './Functional.vue'; import { isClient } from '/@/utils/is'; - -import type { Options, Props } from './types'; - import { createVNode, render } from 'vue'; -let instance: any = null; +let instance: ReturnType | null = null; export function createImgPreview(options: Options) { if (!isClient) return; const { imageList, show = true, index = 0 } = options; diff --git a/src/components/Preview/src/index.less b/src/components/Preview/src/index.less deleted file mode 100644 index 0732a24d..00000000 --- a/src/components/Preview/src/index.less +++ /dev/null @@ -1,118 +0,0 @@ -.img-preview { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: @preview-comp-z-index; - background: rgba(0, 0, 0, 0.5); - user-select: none; - - &-content { - display: flex; - width: 100%; - height: 100%; - color: @white; - justify-content: center; - align-items: center; - } - - &-image { - cursor: pointer; - transition: transform 0.3s; - } - - &__close { - position: absolute; - top: -40px; - right: -40px; - width: 80px; - height: 80px; - overflow: hidden; - color: @white; - cursor: pointer; - background-color: rgba(0, 0, 0, 0.5); - border-radius: 50%; - transition: all 0.2s; - - &-icon { - position: absolute; - top: 46px; - left: 16px; - font-size: 16px; - } - - &:hover { - background-color: rgba(0, 0, 0, 0.8); - } - } - - &__index { - position: absolute; - bottom: 5%; - left: 50%; - padding: 0 22px; - font-size: 16px; - background: rgba(109, 109, 109, 0.6); - border-radius: 15px; - transform: translateX(-50%); - } - - &__controller { - position: absolute; - bottom: 10%; - left: 50%; - display: flex; - width: 260px; - height: 44px; - padding: 0 22px; - margin-left: -139px; - background: rgba(109, 109, 109, 0.6); - border-radius: 22px; - justify-content: center; - - &-item { - display: flex; - height: 100%; - padding: 0 9px; - font-size: 24px; - cursor: pointer; - transition: all 0.2s; - - &:hover { - transform: scale(1.2); - } - - img { - width: 1em; - } - } - } - - &__arrow { - position: absolute; - top: 50%; - display: flex; - align-items: center; - justify-content: center; - width: 50px; - height: 50px; - font-size: 28px; - cursor: pointer; - background-color: rgba(0, 0, 0, 0.5); - border-radius: 50%; - transition: all 0.2s; - - &:hover { - background-color: rgba(0, 0, 0, 0.8); - } - - &.left { - left: 50px; - } - - &.right { - right: 50px; - } - } -} diff --git a/src/components/Preview/src/index.tsx b/src/components/Preview/src/index.tsx deleted file mode 100644 index c674aa5c..00000000 --- a/src/components/Preview/src/index.tsx +++ /dev/null @@ -1,305 +0,0 @@ -import './index.less'; - -import { defineComponent, ref, unref, computed, reactive, watchEffect } from 'vue'; - -// @ts-ignore -import { basicProps } from './props'; -// @ts-ignore -import { Props } from './types'; - -import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; -// import { Spin } from 'ant-design-vue'; - -import resumeSvg from '/@/assets/svg/preview/resume.svg'; -import rotateSvg from '/@/assets/svg/preview/p-rotate.svg'; -import scaleSvg from '/@/assets/svg/preview/scale.svg'; -import unScaleSvg from '/@/assets/svg/preview/unscale.svg'; -import unRotateSvg from '/@/assets/svg/preview/unrotate.svg'; -enum StatueEnum { - LOADING, - DONE, - FAIL, -} -interface ImgState { - currentUrl: string; - imgScale: number; - imgRotate: number; - imgTop: number; - imgLeft: number; - currentIndex: number; - status: StatueEnum; - moveX: number; - moveY: number; - show: boolean; -} - -const prefixCls = 'img-preview'; -export default defineComponent({ - name: 'ImagePreview', - props: basicProps, - setup(props: Props) { - const imgState = reactive({ - currentUrl: '', - imgScale: 1, - imgRotate: 0, - imgTop: 0, - imgLeft: 0, - status: StatueEnum.LOADING, - currentIndex: 0, - moveX: 0, - moveY: 0, - show: props.show, - }); - - const wrapElRef = ref(null); - const imgElRef = ref(null); - - // 初始化 - function init() { - initMouseWheel(); - const { index, imageList } = props; - - if (!imageList || !imageList.length) { - throw new Error('imageList is undefined'); - } - imgState.currentIndex = index; - handleIChangeImage(imageList[index]); - } - - // 重置 - function initState() { - imgState.imgScale = 1; - imgState.imgRotate = 0; - imgState.imgTop = 0; - imgState.imgLeft = 0; - } - - // 初始化鼠标滚轮事件 - function initMouseWheel() { - const wrapEl = unref(wrapElRef); - if (!wrapEl) { - return; - } - (wrapEl as any).onmousewheel = scrollFunc; - // 火狐浏览器没有onmousewheel事件,用DOMMouseScroll代替 - document.body.addEventListener('DOMMouseScroll', scrollFunc); - // 禁止火狐浏览器下拖拽图片的默认事件 - document.ondragstart = function () { - return false; - }; - } - - // 监听鼠标滚轮 - function scrollFunc(e: any) { - e = e || window.event; - e.delta = e.wheelDelta || -e.detail; - - e.preventDefault(); - if (e.delta > 0) { - // 滑轮向上滚动 - scaleFunc(0.015); - } - if (e.delta < 0) { - // 滑轮向下滚动 - scaleFunc(-0.015); - } - } - // 缩放函数 - function scaleFunc(num: number) { - if (imgState.imgScale <= 0.2 && num < 0) return; - imgState.imgScale += num; - } - - // 旋转图片 - function rotateFunc(deg: number) { - imgState.imgRotate += deg; - } - - // 鼠标事件 - function handleMouseUp() { - const imgEl = unref(imgElRef); - if (!imgEl) return; - imgEl.onmousemove = null; - } - - // 更换图片 - function handleIChangeImage(url: string) { - imgState.status = StatueEnum.LOADING; - const img = new Image(); - img.src = url; - img.onload = () => { - imgState.currentUrl = url; - imgState.status = StatueEnum.DONE; - }; - img.onerror = () => { - imgState.status = StatueEnum.FAIL; - }; - } - - // 关闭 - function handleClose(e: MouseEvent) { - e && e.stopPropagation(); - imgState.show = false; - // 移除火狐浏览器下的鼠标滚动事件 - document.body.removeEventListener('DOMMouseScroll', scrollFunc); - // 恢复火狐及Safari浏览器下的图片拖拽 - document.ondragstart = null; - } - - // 图片复原 - function resume() { - initState(); - } - - // 上一页下一页 - function handleChange(direction: 'left' | 'right') { - const { currentIndex } = imgState; - const { imageList } = props; - if (direction === 'left') { - imgState.currentIndex--; - if (currentIndex <= 0) { - imgState.currentIndex = imageList.length - 1; - } - } - if (direction === 'right') { - imgState.currentIndex++; - if (currentIndex >= imageList.length - 1) { - imgState.currentIndex = 0; - } - } - handleIChangeImage(imageList[imgState.currentIndex]); - } - - function handleAddMoveListener(e: MouseEvent) { - e = e || window.event; - imgState.moveX = e.clientX; - imgState.moveY = e.clientY; - const imgEl = unref(imgElRef); - if (imgEl) { - imgEl.onmousemove = moveFunc; - } - } - - function moveFunc(e: MouseEvent) { - e = e || window.event; - e.preventDefault(); - const movementX = e.clientX - imgState.moveX; - const movementY = e.clientY - imgState.moveY; - imgState.imgLeft += movementX; - imgState.imgTop += movementY; - imgState.moveX = e.clientX; - imgState.moveY = e.clientY; - } - - // 获取图片样式 - const getImageStyle = computed(() => { - const { imgScale, imgRotate, imgTop, imgLeft } = imgState; - return { - transform: `scale(${imgScale}) rotate(${imgRotate}deg)`, - marginTop: `${imgTop}px`, - marginLeft: `${imgLeft}px`, - }; - }); - - const getIsMultipleImage = computed(() => { - const { imageList } = props; - return imageList.length > 1; - }); - - watchEffect(() => { - if (props.show) { - init(); - } - if (props.imageList) { - initState(); - } - }); - - const renderClose = () => { - return ( -
- -
- ); - }; - - const renderIndex = () => { - if (!unref(getIsMultipleImage)) { - return null; - } - const { currentIndex } = imgState; - const { imageList } = props; - return ( -
- {currentIndex + 1} / {imageList.length} -
- ); - }; - - const renderController = () => { - return ( -
-
scaleFunc(-0.15)}> - -
-
scaleFunc(0.15)}> - -
-
- -
-
rotateFunc(-90)}> - -
-
rotateFunc(90)}> - -
-
- ); - }; - - const renderArrow = (direction: 'left' | 'right') => { - if (!unref(getIsMultipleImage)) { - return null; - } - return ( -
handleChange(direction)}> - {direction === 'left' ? : } -
- ); - }; - - return () => { - return ( - imgState.show && ( -
-
- {/*}*/} - {/* spinning={true}*/} - {/* class={[*/} - {/* `${prefixCls}-image`,*/} - {/* {*/} - {/* hidden: imgState.status !== StatueEnum.LOADING,*/} - {/* },*/} - {/* ]}*/} - {/*/>*/} - - {renderClose()} - {renderIndex()} - {renderController()} - {renderArrow('left')} - {renderArrow('right')} -
-
- ) - ); - }; - }, -}); diff --git a/src/components/Preview/src/props.ts b/src/components/Preview/src/props.ts deleted file mode 100644 index c6d7c8af..00000000 --- a/src/components/Preview/src/props.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { PropType } from 'vue'; -export const basicProps = { - show: { - type: Boolean as PropType, - default: false, - }, - imageList: { - type: [Array] as PropType, - default: null, - }, - index: { - type: Number as PropType, - default: 0, - }, -}; diff --git a/src/components/Preview/src/types.ts b/src/components/Preview/src/typing.ts similarity index 100% rename from src/components/Preview/src/types.ts rename to src/components/Preview/src/typing.ts diff --git a/src/views/demo/feat/img-preview/index.vue b/src/views/demo/feat/img-preview/index.vue index cadae76a..79a81c32 100644 --- a/src/views/demo/feat/img-preview/index.vue +++ b/src/views/demo/feat/img-preview/index.vue @@ -1,7 +1,7 @@