mirror of
https://gitee.com/bootx/dax-pay-ui.git
synced 2025-09-29 06:53:01 +00:00
wip: add cropper iamge component
This commit is contained in:
4
src/components/Cropper/index.ts
Normal file
4
src/components/Cropper/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import type Cropper from 'cropperjs';
|
||||
|
||||
export type { Cropper };
|
||||
export { default as CropperImage } from './src/index.vue';
|
15
src/components/Cropper/src/AvatarCropper.vue
Normal file
15
src/components/Cropper/src/AvatarCropper.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div :class="$attrs.class" :style="$attrs.style"> </div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
// TODO
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AvatarCropper',
|
||||
props: {},
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
</script>
|
100
src/components/Cropper/src/index.vue
Normal file
100
src/components/Cropper/src/index.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div :class="$attrs.class" :style="$attrs.style">
|
||||
<img ref="imgElRef" :src="src" :alt="alt" :crossorigin="crossorigin" :style="getImageStyle" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
import { defineComponent, onMounted, ref, unref, computed } from 'vue';
|
||||
|
||||
import Cropper from 'cropperjs';
|
||||
import 'cropperjs/dist/cropper.css';
|
||||
|
||||
type Options = Cropper.Options;
|
||||
|
||||
const defaultOptions: Cropper.Options = {
|
||||
aspectRatio: 16 / 9,
|
||||
zoomable: true,
|
||||
zoomOnTouch: true,
|
||||
zoomOnWheel: true,
|
||||
cropBoxMovable: true,
|
||||
cropBoxResizable: true,
|
||||
toggleDragModeOnDblclick: true,
|
||||
autoCrop: true,
|
||||
background: true,
|
||||
highlight: true,
|
||||
center: true,
|
||||
responsive: true,
|
||||
restore: true,
|
||||
checkCrossOrigin: true,
|
||||
checkOrientation: true,
|
||||
scalable: true,
|
||||
modal: true,
|
||||
guides: true,
|
||||
movable: true,
|
||||
rotatable: true,
|
||||
};
|
||||
export default defineComponent({
|
||||
name: 'CropperImage',
|
||||
props: {
|
||||
src: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
alt: {
|
||||
type: String,
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '500px',
|
||||
},
|
||||
crossorigin: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
imageStyle: {
|
||||
type: Object as PropType<CSSProperties>,
|
||||
default: {},
|
||||
},
|
||||
options: {
|
||||
type: Object as PropType<Options>,
|
||||
default: {},
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const imgElRef = ref<ElRef<HTMLImageElement>>(null);
|
||||
const cropper = ref<Nullable<Cropper>>(null);
|
||||
|
||||
const isReady = ref(false);
|
||||
|
||||
const getImageStyle = computed(
|
||||
(): CSSProperties => {
|
||||
return {
|
||||
height: props.height,
|
||||
maxWidth: '100%',
|
||||
...props.imageStyle,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
async function init() {
|
||||
const imgEl = unref(imgElRef);
|
||||
if (!imgEl) {
|
||||
return;
|
||||
}
|
||||
cropper.value = new Cropper(imgEl, {
|
||||
...defaultOptions,
|
||||
ready: () => {
|
||||
isReady.value = true;
|
||||
},
|
||||
...props.options,
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(init);
|
||||
|
||||
return { imgElRef, getImageStyle, isReady };
|
||||
},
|
||||
});
|
||||
</script>
|
@@ -5,14 +5,20 @@
|
||||
</span>
|
||||
<template #overlay>
|
||||
<a-menu :selectedKeys="selectedKeys">
|
||||
<template v-for="item in getMenuList" :key="`${item.event}`">
|
||||
<template v-for="item in dropMenuList" :key="`${item.event}`">
|
||||
<a-menu-item
|
||||
v-bind="getAttr(item.event)"
|
||||
@click="handleClickMenu(item)"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<Icon :icon="item.icon" v-if="item.icon" />
|
||||
<span class="ml-1">{{ item.text }}</span>
|
||||
<Popconfirm v-if="popconfirm" v-bind="item">
|
||||
<Icon :icon="item.icon" v-if="item.icon" />
|
||||
<span class="ml-1">{{ item.text }}</span>
|
||||
</Popconfirm>
|
||||
<template v-else>
|
||||
<Icon :icon="item.icon" v-if="item.icon" />
|
||||
<span class="ml-1">{{ item.text }}</span>
|
||||
</template>
|
||||
</a-menu-item>
|
||||
<a-menu-divider v-if="item.divider" :key="`d-${item.event}`" />
|
||||
</template>
|
||||
@@ -25,9 +31,9 @@
|
||||
import type { PropType } from 'vue';
|
||||
import type { DropMenu } from './types';
|
||||
|
||||
import { defineComponent, computed, unref } from 'vue';
|
||||
import { Dropdown, Menu } from 'ant-design-vue';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import { defineComponent } from 'vue';
|
||||
import { Dropdown, Menu, Popconfirm } from 'ant-design-vue';
|
||||
import { Icon } from '/@/components/Icon';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicDropdown',
|
||||
@@ -37,8 +43,10 @@
|
||||
[Menu.Item.name]: Menu.Item,
|
||||
[Menu.Divider.name]: Menu.Divider,
|
||||
Icon,
|
||||
Popconfirm,
|
||||
},
|
||||
props: {
|
||||
popconfirm: Boolean,
|
||||
/**
|
||||
* the trigger mode which executes the drop-down action
|
||||
* @default ['hover']
|
||||
@@ -61,19 +69,15 @@
|
||||
},
|
||||
emits: ['menuEvent'],
|
||||
setup(props, { emit }) {
|
||||
const getMenuList = computed(() => props.dropMenuList);
|
||||
|
||||
function handleClickMenu(item: DropMenu) {
|
||||
const { event } = item;
|
||||
const menu = unref(getMenuList).find((item) => `${item.event}` === `${event}`);
|
||||
const menu = props.dropMenuList.find((item) => `${item.event}` === `${event}`);
|
||||
emit('menuEvent', menu);
|
||||
item.onClick?.();
|
||||
}
|
||||
|
||||
return {
|
||||
handleClickMenu,
|
||||
getMenuList,
|
||||
getAttr: (key: string) => ({ key }),
|
||||
getAttr: (key: string | number) => ({ key }),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@@ -10,7 +10,12 @@
|
||||
v-if="divider && index < getActions.length - (dropDownActions ? 0 : 1)"
|
||||
/>
|
||||
</template>
|
||||
<Dropdown :trigger="['hover']" :dropMenuList="getDropList" v-if="dropDownActions">
|
||||
<Dropdown
|
||||
:trigger="['hover']"
|
||||
:dropMenuList="getDropdownList"
|
||||
popconfirm
|
||||
v-if="dropDownActions"
|
||||
>
|
||||
<slot name="more"></slot>
|
||||
<a-button type="link" size="small" v-if="!$slots.more">
|
||||
<MoreOutlined class="icon-more" />
|
||||
@@ -71,11 +76,12 @@
|
||||
});
|
||||
});
|
||||
|
||||
const getDropList = computed(() => {
|
||||
const getDropdownList = computed(() => {
|
||||
return (toRaw(props.dropDownActions) || []).map((action, index) => {
|
||||
const { label } = action;
|
||||
const { label, popConfirm } = action;
|
||||
return {
|
||||
...action,
|
||||
...popConfirm,
|
||||
text: label,
|
||||
divider: index < props.dropDownActions.length - 1 ? props.divider : false,
|
||||
};
|
||||
@@ -88,7 +94,7 @@
|
||||
return actionColumn?.align ?? 'left';
|
||||
});
|
||||
|
||||
return { prefixCls, getActions, getDropList, getAlign };
|
||||
return { prefixCls, getActions, getDropdownList, getAlign };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@@ -33,5 +33,6 @@ export default {
|
||||
|
||||
loading: 'Loading',
|
||||
|
||||
time: 'Time',
|
||||
time: 'Relative Time',
|
||||
cropperImage: 'Cropper Image',
|
||||
};
|
||||
|
@@ -32,5 +32,6 @@ export default {
|
||||
|
||||
loading: 'Loading',
|
||||
|
||||
time: '时间组件',
|
||||
time: '相对时间',
|
||||
cropperImage: '图片裁剪',
|
||||
};
|
||||
|
@@ -6,7 +6,9 @@ const menu: MenuModule = {
|
||||
menu: {
|
||||
name: t('routes.demo.comp.comp'),
|
||||
path: '/comp',
|
||||
|
||||
tag: {
|
||||
dot: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'basic',
|
||||
@@ -114,6 +116,13 @@ const menu: MenuModule = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'cropper',
|
||||
name: t('routes.demo.comp.cropperImage'),
|
||||
tag: {
|
||||
content: 'new',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'countTo',
|
||||
name: t('routes.demo.comp.countTo'),
|
||||
|
@@ -232,6 +232,14 @@ const comp: AppRouteModule = {
|
||||
title: t('routes.demo.comp.transition'),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'cropper',
|
||||
name: 'CropperDemo',
|
||||
component: () => import('/@/views/demo/comp/cropper/index.vue'),
|
||||
meta: {
|
||||
title: t('routes.demo.comp.cropperImage'),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'timestamp',
|
||||
name: 'TimeDemo',
|
||||
|
22
src/views/demo/comp/cropper/index.vue
Normal file
22
src/views/demo/comp/cropper/index.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<PageWrapper title="图片裁剪示例" contentBackground>
|
||||
<CropperImage src="https://fengyuanchen.github.io/cropperjs/images/picture.jpg"></CropperImage>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
|
||||
import { CropperImage } from '/@/components/Cropper';
|
||||
|
||||
import img from '/@/assets/images/header.jpg';
|
||||
export default defineComponent({
|
||||
components: {
|
||||
PageWrapper,
|
||||
CropperImage,
|
||||
},
|
||||
setup() {
|
||||
return { img };
|
||||
},
|
||||
});
|
||||
</script>
|
@@ -9,7 +9,7 @@
|
||||
<div class="mb-2"> 头像 </div>
|
||||
<img width="140" :src="headerImg" />
|
||||
<Upload :showUploadList="false">
|
||||
<Button type="ghost" class="ml-5"> <Icon icon="feather:upload" />更换头像 </Button>
|
||||
<Button class="ml-5"> <Icon icon="feather:upload" />更换头像 </Button>
|
||||
</Upload>
|
||||
</div>
|
||||
</a-col>
|
||||
|
@@ -13,7 +13,10 @@
|
||||
:dropDownActions="[
|
||||
{
|
||||
label: '启用',
|
||||
onClick: handleOpen.bind(null, record),
|
||||
popConfirm: {
|
||||
title: '是否启用?',
|
||||
confirm: handleOpen.bind(null, record),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
|
Reference in New Issue
Block a user