feat:【ele】cropper、form-create\rules todo 优化

This commit is contained in:
puhui999
2025-12-28 15:44:21 +08:00
parent 63a8d562ce
commit c023ebbdb9
5 changed files with 58 additions and 151 deletions

View File

@@ -18,7 +18,7 @@ const props = withDefaults(defineProps<CropperAvatarProps>(), {
width: 200,
value: '',
showBtn: true,
btnProps: () => ({}),
btnProps: () => ({}) as any,
btnText: '',
uploadApi: () => Promise.resolve(),
size: 5,
@@ -27,14 +27,10 @@ const props = withDefaults(defineProps<CropperAvatarProps>(), {
const emit = defineEmits(['update:value', 'change']);
const sourceValue = ref(props.value || '');
// TODO @puhui999这个有办法去掉么
const prefixCls = 'cropper-avatar';
const [CropperModal, modalApi] = useVbenModal({
connectedComponent: cropperModal,
});
const getClass = computed(() => [prefixCls]);
const getWidth = computed(() => `${`${props.width}`.replace(/px/, '')}px`);
const getIconWidth = computed(
@@ -74,34 +70,41 @@ defineExpose({
</script>
<template>
<!-- TODO @puhui999html 部分看看有没办法和 web-antd/src/components/cropper/cropper-avatar.vue 风格更接近 -->
<!-- 头像容器 -->
<div :class="getClass" :style="getStyle">
<div class="inline-block text-center" :style="getStyle">
<!-- 图片包装器 -->
<div
:class="`${prefixCls}-image-wrapper`"
class="group relative cursor-pointer overflow-hidden rounded-full border border-gray-200 bg-white"
:style="getImageWrapperStyle"
@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"
>
<span
:style="{
...getImageWrapperStyle,
width: `${getIconWidth}`,
height: `${getIconWidth}`,
lineHeight: `${getIconWidth}`,
width: getIconWidth,
height: getIconWidth,
lineHeight: getIconWidth,
}"
class="icon-[ant-design--cloud-upload-outlined] text-[#d6d6d6]"
class="icon-[ant-design--cloud-upload-outlined] text-gray-400"
></span>
</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>
<!-- 上传按钮 -->
<ElButton
v-if="showBtn"
:class="`${prefixCls}-upload-btn`"
class="mx-auto mt-2"
@click="openModal"
v-bind="btnProps"
>
@@ -116,50 +119,3 @@ defineExpose({
/>
</div>
</template>
<style lang="scss" scoped>
/* TODO @puhui999要类似 web-antd/src/components/cropper/cropper-avatar.vue 减少 scss通过 tindwind 么? */
.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>

View File

@@ -36,7 +36,6 @@ const cropper = ref<CropperType>();
let scaleX = 1;
let scaleY = 1;
const prefixCls = 'cropper-am';
const [Modal, modalApi] = useVbenModal({
onConfirm: handleOk,
onOpenChange(isOpen) {
@@ -120,11 +119,34 @@ async function handleOk() {
:title="$t('ui.cropper.modalTitle')"
class="w-[800px]"
>
<div :class="prefixCls">
<div class="flex">
<!-- 左侧区域 -->
<div :class="`${prefixCls}-left`" class="w-full">
<div class="h-[340px] w-[55%]">
<!-- 裁剪器容器 -->
<div :class="`${prefixCls}-cropper`">
<div
class="h-[300px] bg-[#eee]"
style="
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;
"
>
<CropperImage
v-if="src"
:circled="circled"
@@ -136,7 +158,7 @@ async function handleOk() {
</div>
<!-- 工具栏 -->
<div :class="`${prefixCls}-toolbar`">
<div class="mt-2.5 flex items-center justify-between">
<ElUpload
:before-upload="handleBeforeUpload"
:file-list="[]"
@@ -281,18 +303,23 @@ async function handleOk() {
</div>
<!-- 右侧区域 -->
<div :class="`${prefixCls}-right`">
<div class="h-[340px] w-[45%]">
<!-- 预览区域 -->
<div :class="`${prefixCls}-preview`">
<div
class="mx-auto h-[220px] w-[220px] overflow-hidden rounded-full border border-gray-200"
>
<img
v-if="previewSource"
:alt="$t('ui.cropper.preview')"
:src="previewSource"
class="h-full w-full"
/>
</div>
<!-- 头像组合预览 -->
<template v-if="previewSource">
<div :class="`${prefixCls}-group`">
<div
class="mt-2 flex items-center justify-around border-t border-gray-200 pt-2"
>
<ElAvatar :src="previewSource" size="large" />
<ElAvatar :size="48" :src="previewSource" />
<ElAvatar :size="64" :src="previewSource" />
@@ -303,77 +330,3 @@ async function handleOk() {
</div>
</Modal>
</template>
<style lang="scss">
/* TODO @puhui999要类似 web-antd/src/components/cropper/cropper-avatar.vue 减少 scss通过 tindwind 么? */
.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>

View File

@@ -33,8 +33,6 @@ const imgElRef = ref<ElRef<HTMLImageElement>>();
const cropper = ref<Cropper | null>();
const isReady = ref(false);
// TODO @puhui999这个有办法去掉么
const prefixCls = 'cropper-image';
const debounceRealTimeCropped = useDebounceFn(realTimeCropped, 80);
const getImageStyle = computed((): CSSProperties => {
@@ -47,10 +45,9 @@ const getImageStyle = computed((): CSSProperties => {
const getClass = computed(() => {
return [
prefixCls,
attrs.class,
{
[`${prefixCls}--circled`]: props.circled,
'cropper-image--circled': props.circled,
},
];
});
@@ -158,6 +155,7 @@ function getRoundedCanvas() {
:crossorigin="crossorigin"
:src="src"
:style="getImageStyle"
class="h-auto max-w-full"
/>
</div>
</template>

View File

@@ -121,7 +121,7 @@ const apiSelectRule = [
field: 'data',
title: '请求参数 JSON 格式',
props: {
autosize: true, // TODO @puhui999这里时 autoSize 还是 autosize 哈?和 antd 不同
autosize: true,
type: 'textarea',
placeholder: '{"type": 1}',
},
@@ -155,7 +155,7 @@ const apiSelectRule = [
info: `data 为接口返回值,需要写一个匿名函数解析返回值为选择器 options 列表
(data: any)=>{ label: string; value: any }[]`,
props: {
autosize: true, // TODO @puhui999这里时 autoSize 还是 autosize 哈?和 antd 不同
autosize: true,
rows: { minRows: 2, maxRows: 6 },
type: 'textarea',
placeholder: `

View File

@@ -39,7 +39,7 @@ export function useDictSelectRule() {
title: label,
info: '',
$required: false,
// TODO @puhui999vben 版本里,这里有个 modelField: 'value', 需要添加么?
modelField: 'model-value',
};
},
props(_: any, { t }: any) {