feat: 使用tailwindcss 重构样式
parent
66d8690238
commit
61c086e02a
|
|
@ -28,13 +28,10 @@ const props = withDefaults(defineProps<CropperAvatarProps>(), {
|
||||||
const emit = defineEmits(['update:value', 'change']);
|
const emit = defineEmits(['update:value', 'change']);
|
||||||
|
|
||||||
const sourceValue = ref(props.value || '');
|
const sourceValue = ref(props.value || '');
|
||||||
const prefixCls = 'cropper-avatar';
|
|
||||||
const [CropperModal, modalApi] = useVbenModal({
|
const [CropperModal, modalApi] = useVbenModal({
|
||||||
connectedComponent: cropperModal,
|
connectedComponent: cropperModal,
|
||||||
});
|
});
|
||||||
|
|
||||||
const getClass = computed(() => [prefixCls]);
|
|
||||||
|
|
||||||
const getWidth = computed(() => `${`${props.width}`.replace(/px/, '')}px`);
|
const getWidth = computed(() => `${`${props.width}`.replace(/px/, '')}px`);
|
||||||
|
|
||||||
const getIconWidth = computed(
|
const getIconWidth = computed(
|
||||||
|
|
@ -74,29 +71,42 @@ defineExpose({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="getClass" :style="getStyle">
|
<!-- 头像容器 -->
|
||||||
|
<div class="inline-block text-center" :style="getStyle">
|
||||||
|
<!-- 图片包装器 -->
|
||||||
<div
|
<div
|
||||||
:class="`${prefixCls}-image-wrapper`"
|
class="group relative cursor-pointer overflow-hidden rounded-full border border-gray-200 bg-white"
|
||||||
:style="getImageWrapperStyle"
|
:style="getImageWrapperStyle"
|
||||||
@click="openModal"
|
@click="openModal"
|
||||||
>
|
>
|
||||||
<div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle">
|
<!-- 遮罩层 -->
|
||||||
|
<div
|
||||||
|
class="duration-400 absolute inset-0 flex cursor-pointer items-center justify-center rounded-full bg-black bg-opacity-40 opacity-0 transition-opacity group-hover:opacity-100"
|
||||||
|
:style="getImageWrapperStyle"
|
||||||
|
>
|
||||||
<IconifyIcon
|
<IconifyIcon
|
||||||
icon="lucide:cloud-upload"
|
icon="lucide:cloud-upload"
|
||||||
class="text-gray-400"
|
class="m-auto text-gray-400"
|
||||||
:style="{
|
:style="{
|
||||||
...getImageWrapperStyle,
|
...getImageWrapperStyle,
|
||||||
width: `${getIconWidth}`,
|
width: getIconWidth,
|
||||||
height: `${getIconWidth}`,
|
height: getIconWidth,
|
||||||
lineHeight: `${getIconWidth}`,
|
lineHeight: getIconWidth,
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<img v-if="sourceValue" :src="sourceValue" alt="avatar" />
|
<!-- 头像图片 -->
|
||||||
|
<img
|
||||||
|
v-if="sourceValue"
|
||||||
|
:src="sourceValue"
|
||||||
|
alt="avatar"
|
||||||
|
class="h-full w-full object-cover"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 上传按钮 -->
|
||||||
<Button
|
<Button
|
||||||
v-if="showBtn"
|
v-if="showBtn"
|
||||||
:class="`${prefixCls}-upload-btn`"
|
class="mx-auto mt-2"
|
||||||
@click="openModal"
|
@click="openModal"
|
||||||
v-bind="btnProps"
|
v-bind="btnProps"
|
||||||
>
|
>
|
||||||
|
|
@ -111,49 +121,3 @@ defineExpose({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.cropper-avatar {
|
|
||||||
display: inline-block;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&-image-wrapper {
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-image-mask {
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: inherit;
|
|
||||||
height: inherit;
|
|
||||||
cursor: pointer;
|
|
||||||
background: rgb(0 0 0 / 40%);
|
|
||||||
border: inherit;
|
|
||||||
border-radius: inherit;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.4s;
|
|
||||||
|
|
||||||
::v-deep(svg) {
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-image-mask:hover {
|
|
||||||
opacity: 40;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-upload-btn {
|
|
||||||
margin: 10px auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ const cropper = ref<CropperType>();
|
||||||
let scaleX = 1;
|
let scaleX = 1;
|
||||||
let scaleY = 1;
|
let scaleY = 1;
|
||||||
|
|
||||||
const prefixCls = 'cropper-am';
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
onConfirm: handleOk,
|
onConfirm: handleOk,
|
||||||
onOpenChange(isOpen) {
|
onOpenChange(isOpen) {
|
||||||
|
|
@ -129,9 +128,13 @@ async function handleOk() {
|
||||||
:title="$t('ui.cropper.modalTitle')"
|
:title="$t('ui.cropper.modalTitle')"
|
||||||
class="w-2/3"
|
class="w-2/3"
|
||||||
>
|
>
|
||||||
<div :class="prefixCls">
|
<div class="flex h-96">
|
||||||
<div :class="`${prefixCls}-left`" class="w-full">
|
<!-- 左侧区域 -->
|
||||||
<div :class="`${prefixCls}-cropper`">
|
<div class="h-full w-3/5">
|
||||||
|
<!-- 裁剪器容器 -->
|
||||||
|
<div
|
||||||
|
class="relative h-[300px] bg-gradient-to-b from-neutral-50 to-neutral-200"
|
||||||
|
>
|
||||||
<CropperImage
|
<CropperImage
|
||||||
v-if="src"
|
v-if="src"
|
||||||
:circled="circled"
|
:circled="circled"
|
||||||
|
|
@ -142,7 +145,8 @@ async function handleOk() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :class="`${prefixCls}-toolbar`">
|
<!-- 工具栏 -->
|
||||||
|
<div class="mt-4 flex items-center justify-between">
|
||||||
<Upload
|
<Upload
|
||||||
:before-upload="handleBeforeUpload"
|
:before-upload="handleBeforeUpload"
|
||||||
:file-list="[]"
|
:file-list="[]"
|
||||||
|
|
@ -266,16 +270,26 @@ async function handleOk() {
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :class="`${prefixCls}-right`">
|
|
||||||
<div :class="`${prefixCls}-preview`">
|
<!-- 右侧区域 -->
|
||||||
|
<div class="h-full w-2/5">
|
||||||
|
<!-- 预览区域 -->
|
||||||
|
<div
|
||||||
|
class="mx-auto h-56 w-56 overflow-hidden rounded-full border border-gray-200"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
v-if="previewSource"
|
v-if="previewSource"
|
||||||
:alt="$t('ui.cropper.preview')"
|
:alt="$t('ui.cropper.preview')"
|
||||||
:src="previewSource"
|
:src="previewSource"
|
||||||
|
class="h-full w-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 头像组合预览 -->
|
||||||
<template v-if="previewSource">
|
<template v-if="previewSource">
|
||||||
<div :class="`${prefixCls}-group`">
|
<div
|
||||||
|
class="mt-2 flex items-center justify-around border-t border-gray-200 pt-2"
|
||||||
|
>
|
||||||
<Avatar :src="previewSource" size="large" />
|
<Avatar :src="previewSource" size="large" />
|
||||||
<Avatar :size="48" :src="previewSource" />
|
<Avatar :size="48" :src="previewSource" />
|
||||||
<Avatar :size="64" :src="previewSource" />
|
<Avatar :size="64" :src="previewSource" />
|
||||||
|
|
@ -286,76 +300,3 @@ async function handleOk() {
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.cropper-am {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&-left,
|
|
||||||
&-right {
|
|
||||||
height: 340px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-left {
|
|
||||||
width: 55%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-right {
|
|
||||||
width: 45%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-cropper {
|
|
||||||
height: 300px;
|
|
||||||
background: #eee;
|
|
||||||
background-image:
|
|
||||||
linear-gradient(
|
|
||||||
45deg,
|
|
||||||
rgb(0 0 0 / 25%) 25%,
|
|
||||||
transparent 0,
|
|
||||||
transparent 75%,
|
|
||||||
rgb(0 0 0 / 25%) 0
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
45deg,
|
|
||||||
rgb(0 0 0 / 25%) 25%,
|
|
||||||
transparent 0,
|
|
||||||
transparent 75%,
|
|
||||||
rgb(0 0 0 / 25%) 0
|
|
||||||
);
|
|
||||||
background-position:
|
|
||||||
0 0,
|
|
||||||
12px 12px;
|
|
||||||
background-size: 24px 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-toolbar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-preview {
|
|
||||||
width: 220px;
|
|
||||||
height: 220px;
|
|
||||||
margin: 0 auto;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-group {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-around;
|
|
||||||
padding-top: 8px;
|
|
||||||
margin-top: 8px;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ const imgElRef = ref<ElRef<HTMLImageElement>>();
|
||||||
const cropper = ref<Cropper | null>();
|
const cropper = ref<Cropper | null>();
|
||||||
const isReady = ref(false);
|
const isReady = ref(false);
|
||||||
|
|
||||||
const prefixCls = 'cropper-image';
|
|
||||||
const debounceRealTimeCropped = useDebounceFn(realTimeCropped, 80);
|
const debounceRealTimeCropped = useDebounceFn(realTimeCropped, 80);
|
||||||
|
|
||||||
const getImageStyle = computed((): CSSProperties => {
|
const getImageStyle = computed((): CSSProperties => {
|
||||||
|
|
@ -46,10 +45,9 @@ const getImageStyle = computed((): CSSProperties => {
|
||||||
|
|
||||||
const getClass = computed(() => {
|
const getClass = computed(() => {
|
||||||
return [
|
return [
|
||||||
prefixCls,
|
|
||||||
attrs.class,
|
attrs.class,
|
||||||
{
|
{
|
||||||
[`${prefixCls}--circled`]: props.circled,
|
'cropper-image--circled': props.circled,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
@ -115,10 +113,9 @@ function cropped() {
|
||||||
imgInfo,
|
imgInfo,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
fileReader.addEventListener('error', () => {
|
||||||
fileReader.onerror = () => {
|
|
||||||
emit('cropendError');
|
emit('cropendError');
|
||||||
};
|
});
|
||||||
}, 'image/png');
|
}, 'image/png');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,6 +154,7 @@ function getRoundedCanvas() {
|
||||||
:crossorigin="crossorigin"
|
:crossorigin="crossorigin"
|
||||||
:src="src"
|
:src="src"
|
||||||
:style="getImageStyle"
|
:style="getImageStyle"
|
||||||
|
class="h-auto max-w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue