提交 2e11ea67 作者: Vben

wip: add cropper iamge component

上级 39d629a0
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
"ant-design-vue": "^2.1.2", "ant-design-vue": "^2.1.2",
"apexcharts": "^3.26.0", "apexcharts": "^3.26.0",
"axios": "^0.21.1", "axios": "^0.21.1",
"cropperjs": "^1.5.11",
"crypto-js": "^4.0.0", "crypto-js": "^4.0.0",
"echarts": "^5.0.2", "echarts": "^5.0.2",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
...@@ -91,7 +92,7 @@ ...@@ -91,7 +92,7 @@
"esno": "^0.5.0", "esno": "^0.5.0",
"fs-extra": "^9.1.0", "fs-extra": "^9.1.0",
"http-server": "^0.12.3", "http-server": "^0.12.3",
"husky": "^5.2.0", "husky": "^6.0.0",
"inquirer": "^8.0.0", "inquirer": "^8.0.0",
"is-ci": "^3.0.0", "is-ci": "^3.0.0",
"less": "^4.1.1", "less": "^4.1.1",
...@@ -117,7 +118,7 @@ ...@@ -117,7 +118,7 @@
"vite-plugin-style-import": "^0.9.1", "vite-plugin-style-import": "^0.9.1",
"vite-plugin-svg-icons": "^0.4.0", "vite-plugin-svg-icons": "^0.4.0",
"vite-plugin-theme": "^0.5.0", "vite-plugin-theme": "^0.5.0",
"vite-plugin-windicss": "0.10.4", "vite-plugin-windicss": "0.11.0",
"vue-eslint-parser": "^7.6.0" "vue-eslint-parser": "^7.6.0"
}, },
"resolutions": { "resolutions": {
......
import type Cropper from 'cropperjs';
export type { Cropper };
export { default as CropperImage } from './src/index.vue';
<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>
<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 @@ ...@@ -5,14 +5,20 @@
</span> </span>
<template #overlay> <template #overlay>
<a-menu :selectedKeys="selectedKeys"> <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 <a-menu-item
v-bind="getAttr(item.event)" v-bind="getAttr(item.event)"
@click="handleClickMenu(item)" @click="handleClickMenu(item)"
:disabled="item.disabled" :disabled="item.disabled"
> >
<Popconfirm v-if="popconfirm" v-bind="item">
<Icon :icon="item.icon" v-if="item.icon" /> <Icon :icon="item.icon" v-if="item.icon" />
<span class="ml-1">{{ item.text }}</span> <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-item>
<a-menu-divider v-if="item.divider" :key="`d-${item.event}`" /> <a-menu-divider v-if="item.divider" :key="`d-${item.event}`" />
</template> </template>
...@@ -25,9 +31,9 @@ ...@@ -25,9 +31,9 @@
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import type { DropMenu } from './types'; import type { DropMenu } from './types';
import { defineComponent, computed, unref } from 'vue'; import { defineComponent } from 'vue';
import { Dropdown, Menu } from 'ant-design-vue'; import { Dropdown, Menu, Popconfirm } from 'ant-design-vue';
import Icon from '/@/components/Icon/index'; import { Icon } from '/@/components/Icon';
export default defineComponent({ export default defineComponent({
name: 'BasicDropdown', name: 'BasicDropdown',
...@@ -37,8 +43,10 @@ ...@@ -37,8 +43,10 @@
[Menu.Item.name]: Menu.Item, [Menu.Item.name]: Menu.Item,
[Menu.Divider.name]: Menu.Divider, [Menu.Divider.name]: Menu.Divider,
Icon, Icon,
Popconfirm,
}, },
props: { props: {
popconfirm: Boolean,
/** /**
* the trigger mode which executes the drop-down action * the trigger mode which executes the drop-down action
* @default ['hover'] * @default ['hover']
...@@ -61,19 +69,15 @@ ...@@ -61,19 +69,15 @@
}, },
emits: ['menuEvent'], emits: ['menuEvent'],
setup(props, { emit }) { setup(props, { emit }) {
const getMenuList = computed(() => props.dropMenuList);
function handleClickMenu(item: DropMenu) { function handleClickMenu(item: DropMenu) {
const { event } = item; const { event } = item;
const menu = unref(getMenuList).find((item) => `${item.event}` === `${event}`); const menu = props.dropMenuList.find((item) => `${item.event}` === `${event}`);
emit('menuEvent', menu); emit('menuEvent', menu);
item.onClick?.(); item.onClick?.();
} }
return { return {
handleClickMenu, handleClickMenu,
getMenuList, getAttr: (key: string | number) => ({ key }),
getAttr: (key: string) => ({ key }),
}; };
}, },
}); });
......
...@@ -10,7 +10,12 @@ ...@@ -10,7 +10,12 @@
v-if="divider && index < getActions.length - (dropDownActions ? 0 : 1)" v-if="divider && index < getActions.length - (dropDownActions ? 0 : 1)"
/> />
</template> </template>
<Dropdown :trigger="['hover']" :dropMenuList="getDropList" v-if="dropDownActions"> <Dropdown
:trigger="['hover']"
:dropMenuList="getDropdownList"
popconfirm
v-if="dropDownActions"
>
<slot name="more"></slot> <slot name="more"></slot>
<a-button type="link" size="small" v-if="!$slots.more"> <a-button type="link" size="small" v-if="!$slots.more">
<MoreOutlined class="icon-more" /> <MoreOutlined class="icon-more" />
...@@ -71,11 +76,12 @@ ...@@ -71,11 +76,12 @@
}); });
}); });
const getDropList = computed(() => { const getDropdownList = computed(() => {
return (toRaw(props.dropDownActions) || []).map((action, index) => { return (toRaw(props.dropDownActions) || []).map((action, index) => {
const { label } = action; const { label, popConfirm } = action;
return { return {
...action, ...action,
...popConfirm,
text: label, text: label,
divider: index < props.dropDownActions.length - 1 ? props.divider : false, divider: index < props.dropDownActions.length - 1 ? props.divider : false,
}; };
...@@ -88,7 +94,7 @@ ...@@ -88,7 +94,7 @@
return actionColumn?.align ?? 'left'; return actionColumn?.align ?? 'left';
}); });
return { prefixCls, getActions, getDropList, getAlign }; return { prefixCls, getActions, getDropdownList, getAlign };
}, },
}); });
</script> </script>
......
...@@ -33,5 +33,6 @@ export default { ...@@ -33,5 +33,6 @@ export default {
loading: 'Loading', loading: 'Loading',
time: 'Time', time: 'Relative Time',
cropperImage: 'Cropper Image',
}; };
...@@ -32,5 +32,6 @@ export default { ...@@ -32,5 +32,6 @@ export default {
loading: 'Loading', loading: 'Loading',
time: '时间组件', time: '相对时间',
cropperImage: '图片裁剪',
}; };
...@@ -6,7 +6,9 @@ const menu: MenuModule = { ...@@ -6,7 +6,9 @@ const menu: MenuModule = {
menu: { menu: {
name: t('routes.demo.comp.comp'), name: t('routes.demo.comp.comp'),
path: '/comp', path: '/comp',
tag: {
dot: true,
},
children: [ children: [
{ {
path: 'basic', path: 'basic',
...@@ -115,6 +117,13 @@ const menu: MenuModule = { ...@@ -115,6 +117,13 @@ const menu: MenuModule = {
], ],
}, },
{ {
path: 'cropper',
name: t('routes.demo.comp.cropperImage'),
tag: {
content: 'new',
},
},
{
path: 'countTo', path: 'countTo',
name: t('routes.demo.comp.countTo'), name: t('routes.demo.comp.countTo'),
}, },
......
...@@ -233,6 +233,14 @@ const comp: AppRouteModule = { ...@@ -233,6 +233,14 @@ const comp: AppRouteModule = {
}, },
}, },
{ {
path: 'cropper',
name: 'CropperDemo',
component: () => import('/@/views/demo/comp/cropper/index.vue'),
meta: {
title: t('routes.demo.comp.cropperImage'),
},
},
{
path: 'timestamp', path: 'timestamp',
name: 'TimeDemo', name: 'TimeDemo',
component: () => import('/@/views/demo/comp/time/index.vue'), component: () => import('/@/views/demo/comp/time/index.vue'),
......
<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 @@ ...@@ -9,7 +9,7 @@
<div class="mb-2"> 头像 </div> <div class="mb-2"> 头像 </div>
<img width="140" :src="headerImg" /> <img width="140" :src="headerImg" />
<Upload :showUploadList="false"> <Upload :showUploadList="false">
<Button type="ghost" class="ml-5"> <Icon icon="feather:upload" />更换头像 </Button> <Button class="ml-5"> <Icon icon="feather:upload" />更换头像 </Button>
</Upload> </Upload>
</div> </div>
</a-col> </a-col>
......
...@@ -13,7 +13,10 @@ ...@@ -13,7 +13,10 @@
:dropDownActions="[ :dropDownActions="[
{ {
label: '启用', label: '启用',
onClick: handleOpen.bind(null, record), popConfirm: {
title: '是否启用?',
confirm: handleOpen.bind(null, record),
},
}, },
]" ]"
/> />
......
...@@ -85,7 +85,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { ...@@ -85,7 +85,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
'ant-design-vue/es/locale/en_US', 'ant-design-vue/es/locale/en_US',
'moment/dist/locale/eu', 'moment/dist/locale/eu',
], ],
exclude: ['vue-demi'], exclude: ['vue-demi', 'consolidate'],
}, },
}; };
}; };
...@@ -2077,10 +2077,10 @@ ...@@ -2077,10 +2077,10 @@
dependencies: dependencies:
vue-demi latest vue-demi latest
"@windicss/plugin-utils@0.10.4": "@windicss/plugin-utils@0.11.0":
version "0.10.4" version "0.11.0"
resolved "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-0.10.4.tgz#ed9163b09e030f7358cc4742b1f2b6c92d084d5d" resolved "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-0.11.0.tgz#142fe810c9cf71719074518295f65bd01f0ac895"
integrity sha512-jQu69qzA56Lv18OK8U4mUTDV17st4EdPawQuaRG2VNK+ZEQWYsMNnqGxhzDTl/NhWTGCcTb3D6mlFPNo0QDOFg== integrity sha512-NIBJ7/wG8Ty///Qxktefud4OI18XjZkycW6DSkWV7++aYyZOdDgGRn493pU+5QZuXeXU8iNR6NKZDtFNcYD7kQ==
dependencies: dependencies:
debug "^4.3.2" debug "^4.3.2"
fast-glob "^3.2.5" fast-glob "^3.2.5"
...@@ -2088,7 +2088,7 @@ ...@@ -2088,7 +2088,7 @@
micromatch "^4.0.2" micromatch "^4.0.2"
pirates "^4.0.1" pirates "^4.0.1"
sucrase "^3.17.1" sucrase "^3.17.1"
windicss "^2.5.7" windicss "^2.5.8"
"@zxcvbn-ts/core@^0.3.0": "@zxcvbn-ts/core@^0.3.0":
version "0.3.0" version "0.3.0"
...@@ -3582,6 +3582,11 @@ create-require@^1.1.0: ...@@ -3582,6 +3582,11 @@ create-require@^1.1.0:
resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cropperjs@^1.5.11:
version "1.5.11"
resolved "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.11.tgz#502ae6d8ca098b124de6813601cca70015879fc0"
integrity sha512-SJUeBBhtNBnnn+UrLKluhFRIXLJn7XFPv8QN1j49X5t+BIMwkgvDev541f96bmu8Xe0TgCx3gON22KmY/VddaA==
cross-env@^7.0.3: cross-env@^7.0.3:
version "7.0.3" version "7.0.3"
resolved "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" resolved "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
...@@ -5698,10 +5703,10 @@ husky@^5.1.3: ...@@ -5698,10 +5703,10 @@ husky@^5.1.3:
resolved "https://registry.npmjs.org/husky/-/husky-5.1.3.tgz#1a0645a4fe3ffc006c4d0d8bd0bcb4c98787cc9d" resolved "https://registry.npmjs.org/husky/-/husky-5.1.3.tgz#1a0645a4fe3ffc006c4d0d8bd0bcb4c98787cc9d"
integrity sha512-fbNJ+Gz5wx2LIBtMweJNY1D7Uc8p1XERi5KNRMccwfQA+rXlxWNSdUxswo0gT8XqxywTIw7Ywm/F4v/O35RdMg== integrity sha512-fbNJ+Gz5wx2LIBtMweJNY1D7Uc8p1XERi5KNRMccwfQA+rXlxWNSdUxswo0gT8XqxywTIw7Ywm/F4v/O35RdMg==
husky@^5.2.0: husky@^6.0.0:
version "5.2.0" version "6.0.0"
resolved "https://registry.npmjs.org/husky/-/husky-5.2.0.tgz#fc5e1c2300d34855d47de4753607d00943fc0802" resolved "https://registry.npmjs.org/husky/-/husky-6.0.0.tgz#810f11869adf51604c32ea577edbc377d7f9319e"
integrity sha512-AM8T/auHXRBxlrfPVLKP6jt49GCM2Zz47m8G3FOMsLmTv8Dj/fKVWE0Rh2d4Qrvmy131xEsdQnb3OXRib67PGg== integrity sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ==
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
version "0.4.24" version "0.4.24"
...@@ -11005,15 +11010,15 @@ vite-plugin-theme@^0.5.0: ...@@ -11005,15 +11010,15 @@ vite-plugin-theme@^0.5.0:
tinycolor2 "^1.4.2" tinycolor2 "^1.4.2"
ts-jest "^26.5.3" ts-jest "^26.5.3"
vite-plugin-windicss@0.10.4: vite-plugin-windicss@0.11.0:
version "0.10.4" version "0.11.0"
resolved "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-0.10.4.tgz#e93577111ea0a55befbe4e2aa2e596f55f6b74b2" resolved "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-0.11.0.tgz#784b3b7c72d29e85d52dad705a846f8e2366a2fc"
integrity sha512-P7alH2dGGw3OTgjs9yZG2w0i+o1HKD8PChwhm2ftP+lLCe1xDL3LReheuRil9p2xPYzrVouER2YTbIdLUEThrQ== integrity sha512-DSXomaGHXAXIF9zPk7T9KG8m1bMNNiIHpeoClX44qxNHT7ryI6iDI8gkWdNfYVjxjcoXSTxKqKa11MA+C7mmAA==
dependencies: dependencies:
"@windicss/plugin-utils" "0.10.4" "@windicss/plugin-utils" "0.11.0"
chalk "^4.1.0" chalk "^4.1.0"
debug "^4.3.2" debug "^4.3.2"
windicss "^2.5.7" windicss "^2.5.8"
vite@2.1.3: vite@2.1.3:
version "2.1.3" version "2.1.3"
...@@ -11187,10 +11192,10 @@ which@^2.0.1, which@^2.0.2: ...@@ -11187,10 +11192,10 @@ which@^2.0.1, which@^2.0.2:
dependencies: dependencies:
isexe "^2.0.0" isexe "^2.0.0"
windicss@^2.5.7: windicss@^2.5.8:
version "2.5.7" version "2.5.8"
resolved "https://registry.npmjs.org/windicss/-/windicss-2.5.7.tgz#aea36568cfb412e1c673468496e920f21ef06086" resolved "https://registry.npmjs.org/windicss/-/windicss-2.5.8.tgz#254980044de3031276062b90cfce53c13ee489bf"
integrity sha512-gsWZkotmw9Hr7yZy2nJAp46pmgMO1wXFFa3rfLWm57KDM31U/AucksQnwZi7zxsKM9c6O/z/61/Uvv4J096zKA== integrity sha512-zHkozdIqv1YTIGHBOHeFGsuZVTN5yAMz6FW5Bp8im9JZxSRZLOLKdJB0K75SL13iLHKXHrC1ukwJjjL8CohrUw==
wmf@~1.0.1: wmf@~1.0.1:
version "1.0.2" version "1.0.2"
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论