Commit 305630e3fd886b3f690f890a934a8a6ba224fba1
1 parent
3f6920f7
feat(preview): added createImgPreview picture preview function
Showing
18 changed files
with
428 additions
and
442 deletions
CHANGELOG.zh_CN.md
@@ -9,6 +9,7 @@ | @@ -9,6 +9,7 @@ | ||
9 | - **CropperImage** `Cropper` 头像裁剪新增圆形裁剪功能 | 9 | - **CropperImage** `Cropper` 头像裁剪新增圆形裁剪功能 |
10 | - **CropperAvatar** 新增头像上传组件 | 10 | - **CropperAvatar** 新增头像上传组件 |
11 | - **Drawer** `useDrawer`新增`closeDrawer`函数 | 11 | - **Drawer** `useDrawer`新增`closeDrawer`函数 |
12 | +- **Preview** 新增`createImgPreview`图片预览函数 | ||
12 | 13 | ||
13 | ### 🐛 Bug Fixes | 14 | ### 🐛 Bug Fixes |
14 | 15 |
src/components/Form/src/components/ApiSelect.vue
@@ -26,7 +26,6 @@ | @@ -26,7 +26,6 @@ | ||
26 | import { useRuleFormItem } from '/@/hooks/component/useFormItem'; | 26 | import { useRuleFormItem } from '/@/hooks/component/useFormItem'; |
27 | import { useAttrs } from '/@/hooks/core/useAttrs'; | 27 | import { useAttrs } from '/@/hooks/core/useAttrs'; |
28 | import { get, omit } from 'lodash-es'; | 28 | import { get, omit } from 'lodash-es'; |
29 | - | ||
30 | import { LoadingOutlined } from '@ant-design/icons-vue'; | 29 | import { LoadingOutlined } from '@ant-design/icons-vue'; |
31 | import { useI18n } from '/@/hooks/web/useI18n'; | 30 | import { useI18n } from '/@/hooks/web/useI18n'; |
32 | import { propTypes } from '/@/utils/propTypes'; | 31 | import { propTypes } from '/@/utils/propTypes'; |
src/components/Form/src/components/FormAction.vue
@@ -40,13 +40,11 @@ | @@ -40,13 +40,11 @@ | ||
40 | <script lang="ts"> | 40 | <script lang="ts"> |
41 | import type { ColEx } from '../types/index'; | 41 | import type { ColEx } from '../types/index'; |
42 | import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes'; | 42 | import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes'; |
43 | - | ||
44 | import { defineComponent, computed, PropType } from 'vue'; | 43 | import { defineComponent, computed, PropType } from 'vue'; |
45 | import { Form, Col } from 'ant-design-vue'; | 44 | import { Form, Col } from 'ant-design-vue'; |
46 | import { Button } from '/@/components/Button'; | 45 | import { Button } from '/@/components/Button'; |
47 | import { BasicArrow } from '/@/components/Basic/index'; | 46 | import { BasicArrow } from '/@/components/Basic/index'; |
48 | import { useFormContext } from '../hooks/useFormContext'; | 47 | import { useFormContext } from '../hooks/useFormContext'; |
49 | - | ||
50 | import { useI18n } from '/@/hooks/web/useI18n'; | 48 | import { useI18n } from '/@/hooks/web/useI18n'; |
51 | import { propTypes } from '/@/utils/propTypes'; | 49 | import { propTypes } from '/@/utils/propTypes'; |
52 | 50 |
src/components/Form/src/components/FormItem.vue
@@ -4,17 +4,14 @@ | @@ -4,17 +4,14 @@ | ||
4 | import type { FormSchema } from '../types/form'; | 4 | import type { FormSchema } from '../types/form'; |
5 | import type { ValidationRule } from 'ant-design-vue/lib/form/Form'; | 5 | import type { ValidationRule } from 'ant-design-vue/lib/form/Form'; |
6 | import type { TableActionType } from '/@/components/Table'; | 6 | import type { TableActionType } from '/@/components/Table'; |
7 | - | ||
8 | import { defineComponent, computed, unref, toRefs } from 'vue'; | 7 | import { defineComponent, computed, unref, toRefs } from 'vue'; |
9 | import { Form, Col } from 'ant-design-vue'; | 8 | import { Form, Col } from 'ant-design-vue'; |
10 | import { componentMap } from '../componentMap'; | 9 | import { componentMap } from '../componentMap'; |
11 | import { BasicHelp } from '/@/components/Basic'; | 10 | import { BasicHelp } from '/@/components/Basic'; |
12 | - | ||
13 | import { isBoolean, isFunction, isNull } from '/@/utils/is'; | 11 | import { isBoolean, isFunction, isNull } from '/@/utils/is'; |
14 | import { getSlot } from '/@/utils/helper/tsxHelper'; | 12 | import { getSlot } from '/@/utils/helper/tsxHelper'; |
15 | import { createPlaceholderMessage, setComponentRuleType } from '../helper'; | 13 | import { createPlaceholderMessage, setComponentRuleType } from '../helper'; |
16 | import { upperFirst, cloneDeep } from 'lodash-es'; | 14 | import { upperFirst, cloneDeep } from 'lodash-es'; |
17 | - | ||
18 | import { useItemLabelWidth } from '../hooks/useLabelWidth'; | 15 | import { useItemLabelWidth } from '../hooks/useLabelWidth'; |
19 | import { useI18n } from '/@/hooks/web/useI18n'; | 16 | import { useI18n } from '/@/hooks/web/useI18n'; |
20 | 17 | ||
@@ -91,7 +88,6 @@ | @@ -91,7 +88,6 @@ | ||
91 | if (isBoolean(dynamicDisabled)) { | 88 | if (isBoolean(dynamicDisabled)) { |
92 | disabled = dynamicDisabled; | 89 | disabled = dynamicDisabled; |
93 | } | 90 | } |
94 | - | ||
95 | if (isFunction(dynamicDisabled)) { | 91 | if (isFunction(dynamicDisabled)) { |
96 | disabled = dynamicDisabled(unref(getValues)); | 92 | disabled = dynamicDisabled(unref(getValues)); |
97 | } | 93 | } |
@@ -276,7 +272,6 @@ | @@ -276,7 +272,6 @@ | ||
276 | : { | 272 | : { |
277 | default: () => renderComponentContent, | 273 | default: () => renderComponentContent, |
278 | }; | 274 | }; |
279 | - | ||
280 | return <Comp {...compAttr}>{compSlot}</Comp>; | 275 | return <Comp {...compAttr}>{compSlot}</Comp>; |
281 | } | 276 | } |
282 | 277 | ||
@@ -317,7 +312,6 @@ | @@ -317,7 +312,6 @@ | ||
317 | }; | 312 | }; |
318 | 313 | ||
319 | const showSuffix = !!suffix; | 314 | const showSuffix = !!suffix; |
320 | - | ||
321 | const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix; | 315 | const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix; |
322 | 316 | ||
323 | return ( | 317 | return ( |
@@ -338,16 +332,18 @@ | @@ -338,16 +332,18 @@ | ||
338 | </Form.Item> | 332 | </Form.Item> |
339 | ); | 333 | ); |
340 | } | 334 | } |
335 | + | ||
341 | return () => { | 336 | return () => { |
342 | const { colProps = {}, colSlot, renderColContent, component } = props.schema; | 337 | const { colProps = {}, colSlot, renderColContent, component } = props.schema; |
343 | - if (!componentMap.has(component)) return null; | 338 | + if (!componentMap.has(component)) { |
339 | + return null; | ||
340 | + } | ||
344 | 341 | ||
345 | const { baseColProps = {} } = props.formProps; | 342 | const { baseColProps = {} } = props.formProps; |
346 | - | ||
347 | const realColProps = { ...baseColProps, ...colProps }; | 343 | const realColProps = { ...baseColProps, ...colProps }; |
348 | const { isIfShow, isShow } = getShow(); | 344 | const { isIfShow, isShow } = getShow(); |
349 | - | ||
350 | const values = unref(getValues); | 345 | const values = unref(getValues); |
346 | + | ||
351 | const getContent = () => { | 347 | const getContent = () => { |
352 | return colSlot | 348 | return colSlot |
353 | ? getSlot(slots, colSlot, values) | 349 | ? getSlot(slots, colSlot, values) |
src/components/Form/src/components/RadioButtonGroup.vue
1 | <!-- | 1 | <!-- |
2 | * @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component | 2 | * @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component |
3 | --> | 3 | --> |
4 | - | ||
5 | <template> | 4 | <template> |
6 | <RadioGroup v-bind="attrs" v-model:value="state" button-style="solid"> | 5 | <RadioGroup v-bind="attrs" v-model:value="state" button-style="solid"> |
7 | <template v-for="item in getOptions" :key="`${item.value}`"> | 6 | <template v-for="item in getOptions" :key="`${item.value}`"> |
@@ -17,6 +16,7 @@ | @@ -17,6 +16,7 @@ | ||
17 | import { isString } from '/@/utils/is'; | 16 | import { isString } from '/@/utils/is'; |
18 | import { useRuleFormItem } from '/@/hooks/component/useFormItem'; | 17 | import { useRuleFormItem } from '/@/hooks/component/useFormItem'; |
19 | import { useAttrs } from '/@/hooks/core/useAttrs'; | 18 | import { useAttrs } from '/@/hooks/core/useAttrs'; |
19 | + | ||
20 | type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean }; | 20 | type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean }; |
21 | type RadioItem = string | OptionsItem; | 21 | type RadioItem = string | OptionsItem; |
22 | 22 | ||
@@ -39,6 +39,7 @@ | @@ -39,6 +39,7 @@ | ||
39 | const attrs = useAttrs(); | 39 | const attrs = useAttrs(); |
40 | // Embedded in the form, just use the hook binding to perform form verification | 40 | // Embedded in the form, just use the hook binding to perform form verification |
41 | const [state] = useRuleFormItem(props); | 41 | const [state] = useRuleFormItem(props); |
42 | + | ||
42 | // Processing options value | 43 | // Processing options value |
43 | const getOptions = computed((): OptionsItem[] => { | 44 | const getOptions = computed((): OptionsItem[] => { |
44 | const { options } = props; | 45 | const { options } = props; |
src/components/Form/src/hooks/useAdvanced.ts
@@ -2,10 +2,8 @@ import type { ColEx } from '../types'; | @@ -2,10 +2,8 @@ import type { ColEx } from '../types'; | ||
2 | import type { AdvanceState } from '../types/hooks'; | 2 | import type { AdvanceState } from '../types/hooks'; |
3 | import type { ComputedRef, Ref } from 'vue'; | 3 | import type { ComputedRef, Ref } from 'vue'; |
4 | import type { FormProps, FormSchema } from '../types/form'; | 4 | import type { FormProps, FormSchema } from '../types/form'; |
5 | - | ||
6 | import { computed, unref, watch } from 'vue'; | 5 | import { computed, unref, watch } from 'vue'; |
7 | import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is'; | 6 | import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is'; |
8 | - | ||
9 | import { useBreakpoint } from '/@/hooks/event/useBreakpoint'; | 7 | import { useBreakpoint } from '/@/hooks/event/useBreakpoint'; |
10 | import { useDebounceFn } from '@vueuse/core'; | 8 | import { useDebounceFn } from '@vueuse/core'; |
11 | 9 |
src/components/Form/src/hooks/useAutoFocus.ts
@@ -16,16 +16,22 @@ export async function useAutoFocus({ | @@ -16,16 +16,22 @@ export async function useAutoFocus({ | ||
16 | isInitedDefault, | 16 | isInitedDefault, |
17 | }: UseAutoFocusContext) { | 17 | }: UseAutoFocusContext) { |
18 | watchEffect(async () => { | 18 | watchEffect(async () => { |
19 | - if (unref(isInitedDefault) || !unref(getProps).autoFocusFirstItem) return; | 19 | + if (unref(isInitedDefault) || !unref(getProps).autoFocusFirstItem) { |
20 | + return; | ||
21 | + } | ||
20 | await nextTick(); | 22 | await nextTick(); |
21 | const schemas = unref(getSchema); | 23 | const schemas = unref(getSchema); |
22 | const formEl = unref(formElRef); | 24 | const formEl = unref(formElRef); |
23 | const el = (formEl as any)?.$el as HTMLElement; | 25 | const el = (formEl as any)?.$el as HTMLElement; |
24 | - if (!formEl || !el || !schemas || schemas.length === 0) return; | 26 | + if (!formEl || !el || !schemas || schemas.length === 0) { |
27 | + return; | ||
28 | + } | ||
25 | 29 | ||
26 | const firstItem = schemas[0]; | 30 | const firstItem = schemas[0]; |
27 | // Only open when the first form item is input type | 31 | // Only open when the first form item is input type |
28 | - if (!firstItem.component.includes('Input')) return; | 32 | + if (!firstItem.component.includes('Input')) { |
33 | + return; | ||
34 | + } | ||
29 | 35 | ||
30 | const inputEl = el.querySelector('.ant-row:first-child input') as Nullable<HTMLInputElement>; | 36 | const inputEl = el.querySelector('.ant-row:first-child input') as Nullable<HTMLInputElement>; |
31 | if (!inputEl) return; | 37 | if (!inputEl) return; |
src/components/Form/src/hooks/useForm.ts
1 | +import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form'; | ||
2 | +import type { NamePath } from 'ant-design-vue/lib/form/interface'; | ||
3 | +import type { DynamicProps } from '/#/utils'; | ||
1 | import { ref, onUnmounted, unref, nextTick, watch } from 'vue'; | 4 | import { ref, onUnmounted, unref, nextTick, watch } from 'vue'; |
2 | - | ||
3 | import { isProdMode } from '/@/utils/env'; | 5 | import { isProdMode } from '/@/utils/env'; |
4 | import { error } from '/@/utils/log'; | 6 | import { error } from '/@/utils/log'; |
5 | import { getDynamicProps } from '/@/utils'; | 7 | import { getDynamicProps } from '/@/utils'; |
6 | 8 | ||
7 | -import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form'; | ||
8 | -import type { NamePath } from 'ant-design-vue/lib/form/interface'; | ||
9 | -import type { DynamicProps } from '/#/utils'; | ||
10 | - | ||
11 | export declare type ValidateFields = (nameList?: NamePath[]) => Promise<Recordable>; | 9 | export declare type ValidateFields = (nameList?: NamePath[]) => Promise<Recordable>; |
12 | 10 | ||
13 | type Props = Partial<DynamicProps<FormProps>>; | 11 | type Props = Partial<DynamicProps<FormProps>>; |
src/components/Form/src/hooks/useFormEvents.ts
1 | import type { ComputedRef, Ref } from 'vue'; | 1 | import type { ComputedRef, Ref } from 'vue'; |
2 | import type { FormProps, FormSchema, FormActionType } from '../types/form'; | 2 | import type { FormProps, FormSchema, FormActionType } from '../types/form'; |
3 | import type { NamePath } from 'ant-design-vue/lib/form/interface'; | 3 | import type { NamePath } from 'ant-design-vue/lib/form/interface'; |
4 | - | ||
5 | import { unref, toRaw } from 'vue'; | 4 | import { unref, toRaw } from 'vue'; |
6 | - | ||
7 | import { isArray, isFunction, isObject, isString } from '/@/utils/is'; | 5 | import { isArray, isFunction, isObject, isString } from '/@/utils/is'; |
8 | import { deepMerge } from '/@/utils'; | 6 | import { deepMerge } from '/@/utils'; |
9 | import { dateItemType, handleInputNumberValue } from '../helper'; | 7 | import { dateItemType, handleInputNumberValue } from '../helper'; |
src/components/Form/src/hooks/useFormValues.ts
1 | import { isArray, isFunction, isObject, isString, isNullOrUnDef } from '/@/utils/is'; | 1 | import { isArray, isFunction, isObject, isString, isNullOrUnDef } from '/@/utils/is'; |
2 | import { dateUtil } from '/@/utils/dateUtil'; | 2 | import { dateUtil } from '/@/utils/dateUtil'; |
3 | - | ||
4 | import { unref } from 'vue'; | 3 | import { unref } from 'vue'; |
5 | import type { Ref, ComputedRef } from 'vue'; | 4 | import type { Ref, ComputedRef } from 'vue'; |
6 | import type { FormProps, FormSchema } from '../types/form'; | 5 | import type { FormProps, FormSchema } from '../types/form'; |
7 | - | ||
8 | import { set } from 'lodash-es'; | 6 | import { set } from 'lodash-es'; |
9 | 7 | ||
10 | interface UseFormValuesContext { | 8 | interface UseFormValuesContext { |
src/components/Form/src/types/form.ts
1 | import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface'; | 1 | import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface'; |
2 | import type { VNode } from 'vue'; | 2 | import type { VNode } from 'vue'; |
3 | import type { ButtonProps as AntdButtonProps } from 'ant-design-vue/es/button/buttonTypes'; | 3 | import type { ButtonProps as AntdButtonProps } from 'ant-design-vue/es/button/buttonTypes'; |
4 | - | ||
5 | import type { FormItem } from './formItem'; | 4 | import type { FormItem } from './formItem'; |
6 | import type { ColEx, ComponentType } from './index'; | 5 | import type { ColEx, ComponentType } from './index'; |
7 | import type { TableActionType } from '/@/components/Table/src/types/table'; | 6 | import type { TableActionType } from '/@/components/Table/src/types/table'; |
src/components/Form/src/types/index.ts
@@ -90,9 +90,7 @@ export type ComponentType = | @@ -90,9 +90,7 @@ export type ComponentType = | ||
90 | | 'InputCountDown' | 90 | | 'InputCountDown' |
91 | | 'Select' | 91 | | 'Select' |
92 | | 'ApiSelect' | 92 | | 'ApiSelect' |
93 | - | 'SelectOptGroup' | ||
94 | | 'TreeSelect' | 93 | | 'TreeSelect' |
95 | - | 'Transfer' | ||
96 | | 'RadioButtonGroup' | 94 | | 'RadioButtonGroup' |
97 | | 'RadioGroup' | 95 | | 'RadioGroup' |
98 | | 'Checkbox' | 96 | | 'Checkbox' |
src/components/Preview/src/index.tsx renamed to src/components/Preview/src/Functional.vue
1 | -import './index.less'; | ||
2 | - | ||
3 | -import { defineComponent, ref, unref, computed, reactive, watchEffect } from 'vue'; | ||
4 | - | ||
5 | -// @ts-ignore | ||
6 | -import { basicProps } from './props'; | ||
7 | -// @ts-ignore | ||
8 | -import { Props } from './types'; | ||
9 | - | ||
10 | -import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; | ||
11 | -// import { Spin } from 'ant-design-vue'; | ||
12 | - | ||
13 | -import resumeSvg from '/@/assets/svg/preview/resume.svg'; | ||
14 | -import rotateSvg from '/@/assets/svg/preview/p-rotate.svg'; | ||
15 | -import scaleSvg from '/@/assets/svg/preview/scale.svg'; | ||
16 | -import unScaleSvg from '/@/assets/svg/preview/unscale.svg'; | ||
17 | -import unRotateSvg from '/@/assets/svg/preview/unrotate.svg'; | ||
18 | -enum StatueEnum { | ||
19 | - LOADING, | ||
20 | - DONE, | ||
21 | - FAIL, | ||
22 | -} | ||
23 | -interface ImgState { | ||
24 | - currentUrl: string; | ||
25 | - imgScale: number; | ||
26 | - imgRotate: number; | ||
27 | - imgTop: number; | ||
28 | - imgLeft: number; | ||
29 | - currentIndex: number; | ||
30 | - status: StatueEnum; | ||
31 | - moveX: number; | ||
32 | - moveY: number; | ||
33 | - show: boolean; | ||
34 | -} | ||
35 | - | ||
36 | -const prefixCls = 'img-preview'; | ||
37 | -export default defineComponent({ | ||
38 | - name: 'ImagePreview', | ||
39 | - props: basicProps, | ||
40 | - setup(props: Props) { | ||
41 | - const imgState = reactive<ImgState>({ | ||
42 | - currentUrl: '', | ||
43 | - imgScale: 1, | ||
44 | - imgRotate: 0, | ||
45 | - imgTop: 0, | ||
46 | - imgLeft: 0, | ||
47 | - status: StatueEnum.LOADING, | ||
48 | - currentIndex: 0, | ||
49 | - moveX: 0, | ||
50 | - moveY: 0, | ||
51 | - show: props.show, | ||
52 | - }); | ||
53 | - | ||
54 | - const wrapElRef = ref<HTMLDivElement | null>(null); | ||
55 | - const imgElRef = ref<HTMLImageElement | null>(null); | ||
56 | - | ||
57 | - // 初始化 | ||
58 | - function init() { | ||
59 | - initMouseWheel(); | ||
60 | - const { index, imageList } = props; | ||
61 | - | ||
62 | - if (!imageList || !imageList.length) { | ||
63 | - throw new Error('imageList is undefined'); | 1 | +<script lang="tsx"> |
2 | + import { defineComponent, ref, unref, computed, reactive, watchEffect } from 'vue'; | ||
3 | + import { Props } from './typing'; | ||
4 | + import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; | ||
5 | + import resumeSvg from '/@/assets/svg/preview/resume.svg'; | ||
6 | + import rotateSvg from '/@/assets/svg/preview/p-rotate.svg'; | ||
7 | + import scaleSvg from '/@/assets/svg/preview/scale.svg'; | ||
8 | + import unScaleSvg from '/@/assets/svg/preview/unscale.svg'; | ||
9 | + import unRotateSvg from '/@/assets/svg/preview/unrotate.svg'; | ||
10 | + | ||
11 | + enum StatueEnum { | ||
12 | + LOADING, | ||
13 | + DONE, | ||
14 | + FAIL, | ||
15 | + } | ||
16 | + interface ImgState { | ||
17 | + currentUrl: string; | ||
18 | + imgScale: number; | ||
19 | + imgRotate: number; | ||
20 | + imgTop: number; | ||
21 | + imgLeft: number; | ||
22 | + currentIndex: number; | ||
23 | + status: StatueEnum; | ||
24 | + moveX: number; | ||
25 | + moveY: number; | ||
26 | + show: boolean; | ||
27 | + } | ||
28 | + const props = { | ||
29 | + show: { | ||
30 | + type: Boolean as PropType<boolean>, | ||
31 | + default: false, | ||
32 | + }, | ||
33 | + imageList: { | ||
34 | + type: [Array] as PropType<string[]>, | ||
35 | + default: null, | ||
36 | + }, | ||
37 | + index: { | ||
38 | + type: Number as PropType<number>, | ||
39 | + default: 0, | ||
40 | + }, | ||
41 | + }; | ||
42 | + | ||
43 | + const prefixCls = 'img-preview'; | ||
44 | + export default defineComponent({ | ||
45 | + name: 'ImagePreview', | ||
46 | + props, | ||
47 | + setup(props: Props) { | ||
48 | + const imgState = reactive<ImgState>({ | ||
49 | + currentUrl: '', | ||
50 | + imgScale: 1, | ||
51 | + imgRotate: 0, | ||
52 | + imgTop: 0, | ||
53 | + imgLeft: 0, | ||
54 | + status: StatueEnum.LOADING, | ||
55 | + currentIndex: 0, | ||
56 | + moveX: 0, | ||
57 | + moveY: 0, | ||
58 | + show: props.show, | ||
59 | + }); | ||
60 | + | ||
61 | + const wrapElRef = ref<HTMLDivElement | null>(null); | ||
62 | + const imgElRef = ref<HTMLImageElement | null>(null); | ||
63 | + | ||
64 | + // 初始化 | ||
65 | + function init() { | ||
66 | + initMouseWheel(); | ||
67 | + const { index, imageList } = props; | ||
68 | + | ||
69 | + if (!imageList || !imageList.length) { | ||
70 | + throw new Error('imageList is undefined'); | ||
71 | + } | ||
72 | + imgState.currentIndex = index; | ||
73 | + handleIChangeImage(imageList[index]); | ||
64 | } | 74 | } |
65 | - imgState.currentIndex = index; | ||
66 | - handleIChangeImage(imageList[index]); | ||
67 | - } | ||
68 | 75 | ||
69 | - // 重置 | ||
70 | - function initState() { | ||
71 | - imgState.imgScale = 1; | ||
72 | - imgState.imgRotate = 0; | ||
73 | - imgState.imgTop = 0; | ||
74 | - imgState.imgLeft = 0; | ||
75 | - } | 76 | + // 重置 |
77 | + function initState() { | ||
78 | + imgState.imgScale = 1; | ||
79 | + imgState.imgRotate = 0; | ||
80 | + imgState.imgTop = 0; | ||
81 | + imgState.imgLeft = 0; | ||
82 | + } | ||
76 | 83 | ||
77 | - // 初始化鼠标滚轮事件 | ||
78 | - function initMouseWheel() { | ||
79 | - const wrapEl = unref(wrapElRef); | ||
80 | - if (!wrapEl) { | ||
81 | - return; | 84 | + // 初始化鼠标滚轮事件 |
85 | + function initMouseWheel() { | ||
86 | + const wrapEl = unref(wrapElRef); | ||
87 | + if (!wrapEl) { | ||
88 | + return; | ||
89 | + } | ||
90 | + (wrapEl as any).onmousewheel = scrollFunc; | ||
91 | + // 火狐浏览器没有onmousewheel事件,用DOMMouseScroll代替 | ||
92 | + document.body.addEventListener('DOMMouseScroll', scrollFunc); | ||
93 | + // 禁止火狐浏览器下拖拽图片的默认事件 | ||
94 | + document.ondragstart = function () { | ||
95 | + return false; | ||
96 | + }; | ||
82 | } | 97 | } |
83 | - (wrapEl as any).onmousewheel = scrollFunc; | ||
84 | - // 火狐浏览器没有onmousewheel事件,用DOMMouseScroll代替 | ||
85 | - document.body.addEventListener('DOMMouseScroll', scrollFunc); | ||
86 | - // 禁止火狐浏览器下拖拽图片的默认事件 | ||
87 | - document.ondragstart = function () { | ||
88 | - return false; | ||
89 | - }; | ||
90 | - } | ||
91 | 98 | ||
92 | - // 监听鼠标滚轮 | ||
93 | - function scrollFunc(e: any) { | ||
94 | - e = e || window.event; | ||
95 | - e.delta = e.wheelDelta || -e.detail; | 99 | + // 监听鼠标滚轮 |
100 | + function scrollFunc(e: any) { | ||
101 | + e = e || window.event; | ||
102 | + e.delta = e.wheelDelta || -e.detail; | ||
96 | 103 | ||
97 | - e.preventDefault(); | ||
98 | - if (e.delta > 0) { | ||
99 | - // 滑轮向上滚动 | ||
100 | - scaleFunc(0.015); | 104 | + e.preventDefault(); |
105 | + if (e.delta > 0) { | ||
106 | + // 滑轮向上滚动 | ||
107 | + scaleFunc(0.015); | ||
108 | + } | ||
109 | + if (e.delta < 0) { | ||
110 | + // 滑轮向下滚动 | ||
111 | + scaleFunc(-0.015); | ||
112 | + } | ||
101 | } | 113 | } |
102 | - if (e.delta < 0) { | ||
103 | - // 滑轮向下滚动 | ||
104 | - scaleFunc(-0.015); | 114 | + // 缩放函数 |
115 | + function scaleFunc(num: number) { | ||
116 | + if (imgState.imgScale <= 0.2 && num < 0) return; | ||
117 | + imgState.imgScale += num; | ||
105 | } | 118 | } |
106 | - } | ||
107 | - // 缩放函数 | ||
108 | - function scaleFunc(num: number) { | ||
109 | - if (imgState.imgScale <= 0.2 && num < 0) return; | ||
110 | - imgState.imgScale += num; | ||
111 | - } | ||
112 | 119 | ||
113 | - // 旋转图片 | ||
114 | - function rotateFunc(deg: number) { | ||
115 | - imgState.imgRotate += deg; | ||
116 | - } | 120 | + // 旋转图片 |
121 | + function rotateFunc(deg: number) { | ||
122 | + imgState.imgRotate += deg; | ||
123 | + } | ||
117 | 124 | ||
118 | - // 鼠标事件 | ||
119 | - function handleMouseUp() { | ||
120 | - const imgEl = unref(imgElRef); | ||
121 | - if (!imgEl) return; | ||
122 | - imgEl.onmousemove = null; | ||
123 | - } | 125 | + // 鼠标事件 |
126 | + function handleMouseUp() { | ||
127 | + const imgEl = unref(imgElRef); | ||
128 | + if (!imgEl) return; | ||
129 | + imgEl.onmousemove = null; | ||
130 | + } | ||
124 | 131 | ||
125 | - // 更换图片 | ||
126 | - function handleIChangeImage(url: string) { | ||
127 | - imgState.status = StatueEnum.LOADING; | ||
128 | - const img = new Image(); | ||
129 | - img.src = url; | ||
130 | - img.onload = () => { | ||
131 | - imgState.currentUrl = url; | ||
132 | - imgState.status = StatueEnum.DONE; | ||
133 | - }; | ||
134 | - img.onerror = () => { | ||
135 | - imgState.status = StatueEnum.FAIL; | ||
136 | - }; | ||
137 | - } | 132 | + // 更换图片 |
133 | + function handleIChangeImage(url: string) { | ||
134 | + imgState.status = StatueEnum.LOADING; | ||
135 | + const img = new Image(); | ||
136 | + img.src = url; | ||
137 | + img.onload = () => { | ||
138 | + imgState.currentUrl = url; | ||
139 | + imgState.status = StatueEnum.DONE; | ||
140 | + }; | ||
141 | + img.onerror = () => { | ||
142 | + imgState.status = StatueEnum.FAIL; | ||
143 | + }; | ||
144 | + } | ||
138 | 145 | ||
139 | - // 关闭 | ||
140 | - function handleClose(e: MouseEvent) { | ||
141 | - e && e.stopPropagation(); | ||
142 | - imgState.show = false; | ||
143 | - // 移除火狐浏览器下的鼠标滚动事件 | ||
144 | - document.body.removeEventListener('DOMMouseScroll', scrollFunc); | ||
145 | - // 恢复火狐及Safari浏览器下的图片拖拽 | ||
146 | - document.ondragstart = null; | ||
147 | - } | 146 | + // 关闭 |
147 | + function handleClose(e: MouseEvent) { | ||
148 | + e && e.stopPropagation(); | ||
149 | + imgState.show = false; | ||
150 | + // 移除火狐浏览器下的鼠标滚动事件 | ||
151 | + document.body.removeEventListener('DOMMouseScroll', scrollFunc); | ||
152 | + // 恢复火狐及Safari浏览器下的图片拖拽 | ||
153 | + document.ondragstart = null; | ||
154 | + } | ||
148 | 155 | ||
149 | - // 图片复原 | ||
150 | - function resume() { | ||
151 | - initState(); | ||
152 | - } | 156 | + // 图片复原 |
157 | + function resume() { | ||
158 | + initState(); | ||
159 | + } | ||
153 | 160 | ||
154 | - // 上一页下一页 | ||
155 | - function handleChange(direction: 'left' | 'right') { | ||
156 | - const { currentIndex } = imgState; | ||
157 | - const { imageList } = props; | ||
158 | - if (direction === 'left') { | ||
159 | - imgState.currentIndex--; | ||
160 | - if (currentIndex <= 0) { | ||
161 | - imgState.currentIndex = imageList.length - 1; | 161 | + // 上一页下一页 |
162 | + function handleChange(direction: 'left' | 'right') { | ||
163 | + const { currentIndex } = imgState; | ||
164 | + const { imageList } = props; | ||
165 | + if (direction === 'left') { | ||
166 | + imgState.currentIndex--; | ||
167 | + if (currentIndex <= 0) { | ||
168 | + imgState.currentIndex = imageList.length - 1; | ||
169 | + } | ||
170 | + } | ||
171 | + if (direction === 'right') { | ||
172 | + imgState.currentIndex++; | ||
173 | + if (currentIndex >= imageList.length - 1) { | ||
174 | + imgState.currentIndex = 0; | ||
175 | + } | ||
162 | } | 176 | } |
177 | + handleIChangeImage(imageList[imgState.currentIndex]); | ||
163 | } | 178 | } |
164 | - if (direction === 'right') { | ||
165 | - imgState.currentIndex++; | ||
166 | - if (currentIndex >= imageList.length - 1) { | ||
167 | - imgState.currentIndex = 0; | 179 | + |
180 | + function handleAddMoveListener(e: MouseEvent) { | ||
181 | + e = e || window.event; | ||
182 | + imgState.moveX = e.clientX; | ||
183 | + imgState.moveY = e.clientY; | ||
184 | + const imgEl = unref(imgElRef); | ||
185 | + if (imgEl) { | ||
186 | + imgEl.onmousemove = moveFunc; | ||
168 | } | 187 | } |
169 | } | 188 | } |
170 | - handleIChangeImage(imageList[imgState.currentIndex]); | 189 | + |
190 | + function moveFunc(e: MouseEvent) { | ||
191 | + e = e || window.event; | ||
192 | + e.preventDefault(); | ||
193 | + const movementX = e.clientX - imgState.moveX; | ||
194 | + const movementY = e.clientY - imgState.moveY; | ||
195 | + imgState.imgLeft += movementX; | ||
196 | + imgState.imgTop += movementY; | ||
197 | + imgState.moveX = e.clientX; | ||
198 | + imgState.moveY = e.clientY; | ||
199 | + } | ||
200 | + | ||
201 | + // 获取图片样式 | ||
202 | + const getImageStyle = computed(() => { | ||
203 | + const { imgScale, imgRotate, imgTop, imgLeft } = imgState; | ||
204 | + return { | ||
205 | + transform: `scale(${imgScale}) rotate(${imgRotate}deg)`, | ||
206 | + marginTop: `${imgTop}px`, | ||
207 | + marginLeft: `${imgLeft}px`, | ||
208 | + }; | ||
209 | + }); | ||
210 | + | ||
211 | + const getIsMultipleImage = computed(() => { | ||
212 | + const { imageList } = props; | ||
213 | + return imageList.length > 1; | ||
214 | + }); | ||
215 | + | ||
216 | + watchEffect(() => { | ||
217 | + if (props.show) { | ||
218 | + init(); | ||
219 | + } | ||
220 | + if (props.imageList) { | ||
221 | + initState(); | ||
222 | + } | ||
223 | + }); | ||
224 | + | ||
225 | + const renderClose = () => { | ||
226 | + return ( | ||
227 | + <div class={`${prefixCls}__close`} onClick={handleClose}> | ||
228 | + <CloseOutlined class={`${prefixCls}__close-icon`} /> | ||
229 | + </div> | ||
230 | + ); | ||
231 | + }; | ||
232 | + | ||
233 | + const renderIndex = () => { | ||
234 | + if (!unref(getIsMultipleImage)) { | ||
235 | + return null; | ||
236 | + } | ||
237 | + const { currentIndex } = imgState; | ||
238 | + const { imageList } = props; | ||
239 | + return ( | ||
240 | + <div class={`${prefixCls}__index`}> | ||
241 | + {currentIndex + 1} / {imageList.length} | ||
242 | + </div> | ||
243 | + ); | ||
244 | + }; | ||
245 | + | ||
246 | + const renderController = () => { | ||
247 | + return ( | ||
248 | + <div class={`${prefixCls}__controller`}> | ||
249 | + <div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(-0.15)}> | ||
250 | + <img src={unScaleSvg} /> | ||
251 | + </div> | ||
252 | + <div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(0.15)}> | ||
253 | + <img src={scaleSvg} /> | ||
254 | + </div> | ||
255 | + <div class={`${prefixCls}__controller-item`} onClick={resume}> | ||
256 | + <img src={resumeSvg} /> | ||
257 | + </div> | ||
258 | + <div class={`${prefixCls}__controller-item`} onClick={() => rotateFunc(-90)}> | ||
259 | + <img src={unRotateSvg} /> | ||
260 | + </div> | ||
261 | + <div class={`${prefixCls}__controller-item`} onClick={() => rotateFunc(90)}> | ||
262 | + <img src={rotateSvg} /> | ||
263 | + </div> | ||
264 | + </div> | ||
265 | + ); | ||
266 | + }; | ||
267 | + | ||
268 | + const renderArrow = (direction: 'left' | 'right') => { | ||
269 | + if (!unref(getIsMultipleImage)) { | ||
270 | + return null; | ||
271 | + } | ||
272 | + return ( | ||
273 | + <div class={[`${prefixCls}__arrow`, direction]} onClick={() => handleChange(direction)}> | ||
274 | + {direction === 'left' ? <LeftOutlined /> : <RightOutlined />} | ||
275 | + </div> | ||
276 | + ); | ||
277 | + }; | ||
278 | + | ||
279 | + return () => { | ||
280 | + return ( | ||
281 | + imgState.show && ( | ||
282 | + <div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp}> | ||
283 | + <div class={`${prefixCls}-content`}> | ||
284 | + {/*<Spin*/} | ||
285 | + {/* indicator={<LoadingOutlined style="font-size: 24px" spin />}*/} | ||
286 | + {/* spinning={true}*/} | ||
287 | + {/* class={[*/} | ||
288 | + {/* `${prefixCls}-image`,*/} | ||
289 | + {/* {*/} | ||
290 | + {/* hidden: imgState.status !== StatueEnum.LOADING,*/} | ||
291 | + {/* },*/} | ||
292 | + {/* ]}*/} | ||
293 | + {/*/>*/} | ||
294 | + <img | ||
295 | + style={unref(getImageStyle)} | ||
296 | + class={[ | ||
297 | + `${prefixCls}-image`, | ||
298 | + imgState.status === StatueEnum.DONE ? '' : 'hidden', | ||
299 | + ]} | ||
300 | + ref={imgElRef} | ||
301 | + src={imgState.currentUrl} | ||
302 | + onMousedown={handleAddMoveListener} | ||
303 | + /> | ||
304 | + {renderClose()} | ||
305 | + {renderIndex()} | ||
306 | + {renderController()} | ||
307 | + {renderArrow('left')} | ||
308 | + {renderArrow('right')} | ||
309 | + </div> | ||
310 | + </div> | ||
311 | + ) | ||
312 | + ); | ||
313 | + }; | ||
314 | + }, | ||
315 | + }); | ||
316 | +</script> | ||
317 | +<style lang="less"> | ||
318 | + .img-preview { | ||
319 | + position: fixed; | ||
320 | + top: 0; | ||
321 | + right: 0; | ||
322 | + bottom: 0; | ||
323 | + left: 0; | ||
324 | + z-index: @preview-comp-z-index; | ||
325 | + background: rgba(0, 0, 0, 0.5); | ||
326 | + user-select: none; | ||
327 | + | ||
328 | + &-content { | ||
329 | + display: flex; | ||
330 | + width: 100%; | ||
331 | + height: 100%; | ||
332 | + color: @white; | ||
333 | + justify-content: center; | ||
334 | + align-items: center; | ||
335 | + } | ||
336 | + | ||
337 | + &-image { | ||
338 | + cursor: pointer; | ||
339 | + transition: transform 0.3s; | ||
171 | } | 340 | } |
172 | 341 | ||
173 | - function handleAddMoveListener(e: MouseEvent) { | ||
174 | - e = e || window.event; | ||
175 | - imgState.moveX = e.clientX; | ||
176 | - imgState.moveY = e.clientY; | ||
177 | - const imgEl = unref(imgElRef); | ||
178 | - if (imgEl) { | ||
179 | - imgEl.onmousemove = moveFunc; | 342 | + &__close { |
343 | + position: absolute; | ||
344 | + top: -40px; | ||
345 | + right: -40px; | ||
346 | + width: 80px; | ||
347 | + height: 80px; | ||
348 | + overflow: hidden; | ||
349 | + color: @white; | ||
350 | + cursor: pointer; | ||
351 | + background-color: rgba(0, 0, 0, 0.5); | ||
352 | + border-radius: 50%; | ||
353 | + transition: all 0.2s; | ||
354 | + | ||
355 | + &-icon { | ||
356 | + position: absolute; | ||
357 | + top: 46px; | ||
358 | + left: 16px; | ||
359 | + font-size: 16px; | ||
360 | + } | ||
361 | + | ||
362 | + &:hover { | ||
363 | + background-color: rgba(0, 0, 0, 0.8); | ||
180 | } | 364 | } |
181 | } | 365 | } |
182 | 366 | ||
183 | - function moveFunc(e: MouseEvent) { | ||
184 | - e = e || window.event; | ||
185 | - e.preventDefault(); | ||
186 | - const movementX = e.clientX - imgState.moveX; | ||
187 | - const movementY = e.clientY - imgState.moveY; | ||
188 | - imgState.imgLeft += movementX; | ||
189 | - imgState.imgTop += movementY; | ||
190 | - imgState.moveX = e.clientX; | ||
191 | - imgState.moveY = e.clientY; | 367 | + &__index { |
368 | + position: absolute; | ||
369 | + bottom: 5%; | ||
370 | + left: 50%; | ||
371 | + padding: 0 22px; | ||
372 | + font-size: 16px; | ||
373 | + background: rgba(109, 109, 109, 0.6); | ||
374 | + border-radius: 15px; | ||
375 | + transform: translateX(-50%); | ||
192 | } | 376 | } |
193 | 377 | ||
194 | - // 获取图片样式 | ||
195 | - const getImageStyle = computed(() => { | ||
196 | - const { imgScale, imgRotate, imgTop, imgLeft } = imgState; | ||
197 | - return { | ||
198 | - transform: `scale(${imgScale}) rotate(${imgRotate}deg)`, | ||
199 | - marginTop: `${imgTop}px`, | ||
200 | - marginLeft: `${imgLeft}px`, | ||
201 | - }; | ||
202 | - }); | 378 | + &__controller { |
379 | + position: absolute; | ||
380 | + bottom: 10%; | ||
381 | + left: 50%; | ||
382 | + display: flex; | ||
383 | + width: 260px; | ||
384 | + height: 44px; | ||
385 | + padding: 0 22px; | ||
386 | + margin-left: -139px; | ||
387 | + background: rgba(109, 109, 109, 0.6); | ||
388 | + border-radius: 22px; | ||
389 | + justify-content: center; | ||
390 | + | ||
391 | + &-item { | ||
392 | + display: flex; | ||
393 | + height: 100%; | ||
394 | + padding: 0 9px; | ||
395 | + font-size: 24px; | ||
396 | + cursor: pointer; | ||
397 | + transition: all 0.2s; | ||
203 | 398 | ||
204 | - const getIsMultipleImage = computed(() => { | ||
205 | - const { imageList } = props; | ||
206 | - return imageList.length > 1; | ||
207 | - }); | 399 | + &:hover { |
400 | + transform: scale(1.2); | ||
401 | + } | ||
208 | 402 | ||
209 | - watchEffect(() => { | ||
210 | - if (props.show) { | ||
211 | - init(); | 403 | + img { |
404 | + width: 1em; | ||
405 | + } | ||
212 | } | 406 | } |
213 | - if (props.imageList) { | ||
214 | - initState(); | 407 | + } |
408 | + | ||
409 | + &__arrow { | ||
410 | + position: absolute; | ||
411 | + top: 50%; | ||
412 | + display: flex; | ||
413 | + align-items: center; | ||
414 | + justify-content: center; | ||
415 | + width: 50px; | ||
416 | + height: 50px; | ||
417 | + font-size: 28px; | ||
418 | + cursor: pointer; | ||
419 | + background-color: rgba(0, 0, 0, 0.5); | ||
420 | + border-radius: 50%; | ||
421 | + transition: all 0.2s; | ||
422 | + | ||
423 | + &:hover { | ||
424 | + background-color: rgba(0, 0, 0, 0.8); | ||
215 | } | 425 | } |
216 | - }); | ||
217 | - | ||
218 | - const renderClose = () => { | ||
219 | - return ( | ||
220 | - <div class={`${prefixCls}__close`} onClick={handleClose}> | ||
221 | - <CloseOutlined class={`${prefixCls}__close-icon`} /> | ||
222 | - </div> | ||
223 | - ); | ||
224 | - }; | ||
225 | - | ||
226 | - const renderIndex = () => { | ||
227 | - if (!unref(getIsMultipleImage)) { | ||
228 | - return null; | 426 | + |
427 | + &.left { | ||
428 | + left: 50px; | ||
229 | } | 429 | } |
230 | - const { currentIndex } = imgState; | ||
231 | - const { imageList } = props; | ||
232 | - return ( | ||
233 | - <div class={`${prefixCls}__index`}> | ||
234 | - {currentIndex + 1} / {imageList.length} | ||
235 | - </div> | ||
236 | - ); | ||
237 | - }; | ||
238 | - | ||
239 | - const renderController = () => { | ||
240 | - return ( | ||
241 | - <div class={`${prefixCls}__controller`}> | ||
242 | - <div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(-0.15)}> | ||
243 | - <img src={unScaleSvg} /> | ||
244 | - </div> | ||
245 | - <div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(0.15)}> | ||
246 | - <img src={scaleSvg} /> | ||
247 | - </div> | ||
248 | - <div class={`${prefixCls}__controller-item`} onClick={resume}> | ||
249 | - <img src={resumeSvg} /> | ||
250 | - </div> | ||
251 | - <div class={`${prefixCls}__controller-item`} onClick={() => rotateFunc(-90)}> | ||
252 | - <img src={unRotateSvg} /> | ||
253 | - </div> | ||
254 | - <div class={`${prefixCls}__controller-item`} onClick={() => rotateFunc(90)}> | ||
255 | - <img src={rotateSvg} /> | ||
256 | - </div> | ||
257 | - </div> | ||
258 | - ); | ||
259 | - }; | ||
260 | 430 | ||
261 | - const renderArrow = (direction: 'left' | 'right') => { | ||
262 | - if (!unref(getIsMultipleImage)) { | ||
263 | - return null; | 431 | + &.right { |
432 | + right: 50px; | ||
264 | } | 433 | } |
265 | - return ( | ||
266 | - <div class={[`${prefixCls}__arrow`, direction]} onClick={() => handleChange(direction)}> | ||
267 | - {direction === 'left' ? <LeftOutlined /> : <RightOutlined />} | ||
268 | - </div> | ||
269 | - ); | ||
270 | - }; | ||
271 | - | ||
272 | - return () => { | ||
273 | - return ( | ||
274 | - imgState.show && ( | ||
275 | - <div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp}> | ||
276 | - <div class={`${prefixCls}-content`}> | ||
277 | - {/*<Spin*/} | ||
278 | - {/* indicator={<LoadingOutlined style="font-size: 24px" spin />}*/} | ||
279 | - {/* spinning={true}*/} | ||
280 | - {/* class={[*/} | ||
281 | - {/* `${prefixCls}-image`,*/} | ||
282 | - {/* {*/} | ||
283 | - {/* hidden: imgState.status !== StatueEnum.LOADING,*/} | ||
284 | - {/* },*/} | ||
285 | - {/* ]}*/} | ||
286 | - {/*/>*/} | ||
287 | - <img | ||
288 | - style={unref(getImageStyle)} | ||
289 | - class={[`${prefixCls}-image`, imgState.status === StatueEnum.DONE ? '' : 'hidden']} | ||
290 | - ref={imgElRef} | ||
291 | - src={imgState.currentUrl} | ||
292 | - onMousedown={handleAddMoveListener} | ||
293 | - /> | ||
294 | - {renderClose()} | ||
295 | - {renderIndex()} | ||
296 | - {renderController()} | ||
297 | - {renderArrow('left')} | ||
298 | - {renderArrow('right')} | ||
299 | - </div> | ||
300 | - </div> | ||
301 | - ) | ||
302 | - ); | ||
303 | - }; | ||
304 | - }, | ||
305 | -}); | 434 | + } |
435 | + } | ||
436 | +</style> |
src/components/Preview/src/functional.ts
1 | -import ImgPreview from './index'; | 1 | +import type { Options, Props } from './typing'; |
2 | +import ImgPreview from './Functional.vue'; | ||
2 | import { isClient } from '/@/utils/is'; | 3 | import { isClient } from '/@/utils/is'; |
3 | - | ||
4 | -import type { Options, Props } from './types'; | ||
5 | - | ||
6 | import { createVNode, render } from 'vue'; | 4 | import { createVNode, render } from 'vue'; |
7 | 5 | ||
8 | -let instance: any = null; | 6 | +let instance: ReturnType<typeof createVNode> | null = null; |
9 | export function createImgPreview(options: Options) { | 7 | export function createImgPreview(options: Options) { |
10 | if (!isClient) return; | 8 | if (!isClient) return; |
11 | const { imageList, show = true, index = 0 } = options; | 9 | const { imageList, show = true, index = 0 } = options; |
src/components/Preview/src/index.less deleted
100644 → 0
1 | -.img-preview { | ||
2 | - position: fixed; | ||
3 | - top: 0; | ||
4 | - right: 0; | ||
5 | - bottom: 0; | ||
6 | - left: 0; | ||
7 | - z-index: @preview-comp-z-index; | ||
8 | - background: rgba(0, 0, 0, 0.5); | ||
9 | - user-select: none; | ||
10 | - | ||
11 | - &-content { | ||
12 | - display: flex; | ||
13 | - width: 100%; | ||
14 | - height: 100%; | ||
15 | - color: @white; | ||
16 | - justify-content: center; | ||
17 | - align-items: center; | ||
18 | - } | ||
19 | - | ||
20 | - &-image { | ||
21 | - cursor: pointer; | ||
22 | - transition: transform 0.3s; | ||
23 | - } | ||
24 | - | ||
25 | - &__close { | ||
26 | - position: absolute; | ||
27 | - top: -40px; | ||
28 | - right: -40px; | ||
29 | - width: 80px; | ||
30 | - height: 80px; | ||
31 | - overflow: hidden; | ||
32 | - color: @white; | ||
33 | - cursor: pointer; | ||
34 | - background-color: rgba(0, 0, 0, 0.5); | ||
35 | - border-radius: 50%; | ||
36 | - transition: all 0.2s; | ||
37 | - | ||
38 | - &-icon { | ||
39 | - position: absolute; | ||
40 | - top: 46px; | ||
41 | - left: 16px; | ||
42 | - font-size: 16px; | ||
43 | - } | ||
44 | - | ||
45 | - &:hover { | ||
46 | - background-color: rgba(0, 0, 0, 0.8); | ||
47 | - } | ||
48 | - } | ||
49 | - | ||
50 | - &__index { | ||
51 | - position: absolute; | ||
52 | - bottom: 5%; | ||
53 | - left: 50%; | ||
54 | - padding: 0 22px; | ||
55 | - font-size: 16px; | ||
56 | - background: rgba(109, 109, 109, 0.6); | ||
57 | - border-radius: 15px; | ||
58 | - transform: translateX(-50%); | ||
59 | - } | ||
60 | - | ||
61 | - &__controller { | ||
62 | - position: absolute; | ||
63 | - bottom: 10%; | ||
64 | - left: 50%; | ||
65 | - display: flex; | ||
66 | - width: 260px; | ||
67 | - height: 44px; | ||
68 | - padding: 0 22px; | ||
69 | - margin-left: -139px; | ||
70 | - background: rgba(109, 109, 109, 0.6); | ||
71 | - border-radius: 22px; | ||
72 | - justify-content: center; | ||
73 | - | ||
74 | - &-item { | ||
75 | - display: flex; | ||
76 | - height: 100%; | ||
77 | - padding: 0 9px; | ||
78 | - font-size: 24px; | ||
79 | - cursor: pointer; | ||
80 | - transition: all 0.2s; | ||
81 | - | ||
82 | - &:hover { | ||
83 | - transform: scale(1.2); | ||
84 | - } | ||
85 | - | ||
86 | - img { | ||
87 | - width: 1em; | ||
88 | - } | ||
89 | - } | ||
90 | - } | ||
91 | - | ||
92 | - &__arrow { | ||
93 | - position: absolute; | ||
94 | - top: 50%; | ||
95 | - display: flex; | ||
96 | - align-items: center; | ||
97 | - justify-content: center; | ||
98 | - width: 50px; | ||
99 | - height: 50px; | ||
100 | - font-size: 28px; | ||
101 | - cursor: pointer; | ||
102 | - background-color: rgba(0, 0, 0, 0.5); | ||
103 | - border-radius: 50%; | ||
104 | - transition: all 0.2s; | ||
105 | - | ||
106 | - &:hover { | ||
107 | - background-color: rgba(0, 0, 0, 0.8); | ||
108 | - } | ||
109 | - | ||
110 | - &.left { | ||
111 | - left: 50px; | ||
112 | - } | ||
113 | - | ||
114 | - &.right { | ||
115 | - right: 50px; | ||
116 | - } | ||
117 | - } | ||
118 | -} |
src/components/Preview/src/props.ts deleted
100644 → 0
1 | -import { PropType } from 'vue'; | ||
2 | -export const basicProps = { | ||
3 | - show: { | ||
4 | - type: Boolean as PropType<boolean>, | ||
5 | - default: false, | ||
6 | - }, | ||
7 | - imageList: { | ||
8 | - type: [Array] as PropType<string[]>, | ||
9 | - default: null, | ||
10 | - }, | ||
11 | - index: { | ||
12 | - type: Number as PropType<number>, | ||
13 | - default: 0, | ||
14 | - }, | ||
15 | -}; |
src/components/Preview/src/types.ts renamed to src/components/Preview/src/typing.ts
src/views/demo/feat/img-preview/index.vue
1 | <template> | 1 | <template> |
2 | <PageWrapper title="图片预览示例"> | 2 | <PageWrapper title="图片预览示例"> |
3 | - <p @click="openImg">打开图片</p> | ||
4 | <ImagePreview :imageList="imgList" /> | 3 | <ImagePreview :imageList="imgList" /> |
4 | + <a-button @click="openImg" type="primary">无预览图</a-button> | ||
5 | </PageWrapper> | 5 | </PageWrapper> |
6 | </template> | 6 | </template> |
7 | <script lang="ts"> | 7 | <script lang="ts"> |