mirror of
https://github.com/vbenjs/gf-vben-admin.git
synced 2025-02-02 19:08:40 +08:00
feat(avatar-cropper): more props added
为头像剪裁组件添加value属性、onChange事件以及用于自定义上传按钮的属性;更新个人设置页的头像设置
This commit is contained in:
parent
6cbac4b7ec
commit
b96ea0753b
@ -7,6 +7,11 @@
|
|||||||
- 增加`ignoreRoute`用于在`ROUTE_MAPPING`或`BACK`权限模式下仅生成菜单
|
- 增加`ignoreRoute`用于在`ROUTE_MAPPING`或`BACK`权限模式下仅生成菜单
|
||||||
- 增加`hidePathForChildren`配置,标识为子项目生成菜单时忽略本级`path`
|
- 增加`hidePathForChildren`配置,标识为子项目生成菜单时忽略本级`path`
|
||||||
- **TableAction** 新增`tooltip`配置,可以为按钮增加 tooltip 提示
|
- **TableAction** 新增`tooltip`配置,可以为按钮增加 tooltip 提示
|
||||||
|
- **CropperAvatar**
|
||||||
|
- 新增`value`用于设置当前头像
|
||||||
|
- 新增`onChange`用于接受头像剪裁并上传成功事件
|
||||||
|
- 新增`btnText`、`btnProps` 用于自定义上传按钮文案和属性
|
||||||
|
- 为剪裁`Modal`内的操作按钮添加工具提示
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
@ -1,33 +1,59 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="getClass" :style="getStyle">
|
<div :class="getClass" :style="getStyle">
|
||||||
<div :class="`${prefixCls}-image-wrapper`" :style="getImageWrapperStyle" @click="openModal">
|
<div :class="`${prefixCls}-image-wrapper`" :style="getImageWrapperStyle" @click="openModal">
|
||||||
|
<div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle">
|
||||||
|
<Icon
|
||||||
|
icon="ant-design:cloud-upload-outlined"
|
||||||
|
:size="getIconWidth"
|
||||||
|
:style="getImageWrapperStyle"
|
||||||
|
color="#d6d6d6"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<img :src="sourceValue" v-if="sourceValue" alt="avatar" />
|
<img :src="sourceValue" v-if="sourceValue" alt="avatar" />
|
||||||
</div>
|
</div>
|
||||||
<a-button :class="`${prefixCls}-upload-btn`" @click="openModal">
|
<a-button
|
||||||
{{ t('component.cropper.selectImage') }}
|
:class="`${prefixCls}-upload-btn`"
|
||||||
|
@click="openModal"
|
||||||
|
v-if="showBtn"
|
||||||
|
v-bind="btnProps"
|
||||||
|
>
|
||||||
|
{{ btnText ? btnText : t('component.cropper.selectImage') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<CopperModal @register="register" @uploadSuccess="handleUploadSuccess" :uploadApi="uploadApi" />
|
|
||||||
|
<CopperModal
|
||||||
|
@register="register"
|
||||||
|
@uploadSuccess="handleUploadSuccess"
|
||||||
|
:uploadApi="uploadApi"
|
||||||
|
:src="sourceValue"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, CSSProperties, unref, ref } from 'vue';
|
import { defineComponent, computed, CSSProperties, unref, ref, watchEffect, watch } from 'vue';
|
||||||
import CopperModal from './CopperModal.vue';
|
import CopperModal from './CopperModal.vue';
|
||||||
import { useDesign } from '/@/hooks/web/useDesign';
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
import { useModal } from '/@/components/Modal';
|
import { useModal } from '/@/components/Modal';
|
||||||
import { useMessage } from '/@/hooks/web/useMessage';
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
import { useI18n } from '/@/hooks/web/useI18n';
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
|
import type { ButtonProps } from '/@/components/Button';
|
||||||
|
import Icon from '/@/components/Icon';
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
width: { type: [String, Number], default: '200px' },
|
width: { type: [String, Number], default: '200px' },
|
||||||
|
value: { type: String },
|
||||||
|
showBtn: { type: Boolean, default: true },
|
||||||
|
btnProps: { type: Object as ButtonProps },
|
||||||
|
btnText: { type: String, default: '' },
|
||||||
uploadApi: { type: Function as PropType<({ file: Blob, name: string }) => Promise<void>> },
|
uploadApi: { type: Function as PropType<({ file: Blob, name: string }) => Promise<void>> },
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'CropperAvatar',
|
name: 'CropperAvatar',
|
||||||
components: { CopperModal },
|
components: { CopperModal, Icon },
|
||||||
props,
|
props,
|
||||||
setup(props) {
|
emits: ['update:value', 'change'],
|
||||||
const sourceValue = ref('');
|
setup(props, { emit }) {
|
||||||
|
const sourceValue = ref(props.value || '');
|
||||||
const { prefixCls } = useDesign('cropper-avatar');
|
const { prefixCls } = useDesign('cropper-avatar');
|
||||||
const [register, { openModal }] = useModal();
|
const [register, { openModal }] = useModal();
|
||||||
const { createMessage } = useMessage();
|
const { createMessage } = useMessage();
|
||||||
@ -37,14 +63,28 @@
|
|||||||
|
|
||||||
const getWidth = computed(() => `${props.width}`.replace(/px/, '') + 'px');
|
const getWidth = computed(() => `${props.width}`.replace(/px/, '') + 'px');
|
||||||
|
|
||||||
|
const getIconWidth = computed(() => parseInt(`${props.width}`.replace(/px/, '')) / 2 + 'px');
|
||||||
|
|
||||||
const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) }));
|
const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) }));
|
||||||
|
|
||||||
const getImageWrapperStyle = computed(
|
const getImageWrapperStyle = computed(
|
||||||
(): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) })
|
(): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
sourceValue.value = props.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => sourceValue.value,
|
||||||
|
(v: string) => {
|
||||||
|
emit('update:value', v);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
function handleUploadSuccess({ source }) {
|
function handleUploadSuccess({ source }) {
|
||||||
sourceValue.value = source;
|
sourceValue.value = source;
|
||||||
|
emit('change', source);
|
||||||
createMessage.success(t('component.cropper.uploadSuccess'));
|
createMessage.success(t('component.cropper.uploadSuccess'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,6 +93,7 @@
|
|||||||
prefixCls,
|
prefixCls,
|
||||||
register,
|
register,
|
||||||
openModal,
|
openModal,
|
||||||
|
getIconWidth,
|
||||||
sourceValue,
|
sourceValue,
|
||||||
getClass,
|
getClass,
|
||||||
getImageWrapperStyle,
|
getImageWrapperStyle,
|
||||||
@ -82,6 +123,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-image-mask {
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
border-radius: inherit;
|
||||||
|
border: inherit;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-transition: opacity 0.4s;
|
||||||
|
transition: opacity 0.4s;
|
||||||
|
|
||||||
|
::v-deep(svg) {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-image-mask:hover {
|
||||||
|
opacity: 40;
|
||||||
|
}
|
||||||
|
|
||||||
&-upload-btn {
|
&-upload-btn {
|
||||||
margin: 10px auto;
|
margin: 10px auto;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<PageWrapper title="图片裁剪示例" content="需要开启测试接口服务才能进行上传测试!">
|
<PageWrapper title="图片裁剪示例" content="需要开启测试接口服务才能进行上传测试!">
|
||||||
<CollapseContainer title="头像裁剪">
|
<CollapseContainer title="头像裁剪">
|
||||||
<CropperAvatar :uploadApi="uploadApi" />
|
<CropperAvatar :uploadApi="uploadApi" :value="avatar" />
|
||||||
</CollapseContainer>
|
</CollapseContainer>
|
||||||
|
|
||||||
<CollapseContainer title="矩形裁剪" class="my-4">
|
<CollapseContainer title="矩形裁剪" class="my-4">
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<div class="cropper-container mr-10">
|
<div class="cropper-container mr-10">
|
||||||
<CropperImage ref="refCropper" :src="img" @cropend="handleCropend" style="width: 40vw" />
|
<CropperImage ref="refCropper" :src="img" @cropend="handleCropend" style="width: 40vw" />
|
||||||
</div>
|
</div>
|
||||||
<img :src="cropperImg" class="croppered" v-if="cropperImg" />
|
<img :src="cropperImg" class="croppered" v-if="cropperImg" alt="" />
|
||||||
</div>
|
</div>
|
||||||
<p v-if="cropperImg">裁剪后图片信息:{{ info }}</p>
|
<p v-if="cropperImg">裁剪后图片信息:{{ info }}</p>
|
||||||
</CollapseContainer>
|
</CollapseContainer>
|
||||||
@ -34,10 +34,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref } from 'vue';
|
import { defineComponent, ref } from 'vue';
|
||||||
import { PageWrapper } from '/@/components/Page';
|
import { PageWrapper } from '/@/components/Page';
|
||||||
import { CollapseContainer } from '/@/components/Container/index';
|
import { CollapseContainer } from '/@/components/Container';
|
||||||
import { CropperImage, CropperAvatar } from '/@/components/Cropper';
|
import { CropperImage, CropperAvatar } from '/@/components/Cropper';
|
||||||
import { uploadApi } from '/@/api/sys/upload';
|
import { uploadApi } from '/@/api/sys/upload';
|
||||||
import img from '/@/assets/images/header.jpg';
|
import img from '/@/assets/images/header.jpg';
|
||||||
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@ -51,7 +52,8 @@
|
|||||||
const cropperImg = ref('');
|
const cropperImg = ref('');
|
||||||
const circleInfo = ref('');
|
const circleInfo = ref('');
|
||||||
const circleImg = ref('');
|
const circleImg = ref('');
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const avatar = ref(userStore.getUserInfo?.avatar || '');
|
||||||
function handleCropend({ imgBase64, imgInfo }) {
|
function handleCropend({ imgBase64, imgInfo }) {
|
||||||
info.value = imgInfo;
|
info.value = imgInfo;
|
||||||
cropperImg.value = imgBase64;
|
cropperImg.value = imgBase64;
|
||||||
@ -70,6 +72,7 @@
|
|||||||
circleImg,
|
circleImg,
|
||||||
handleCropend,
|
handleCropend,
|
||||||
handleCircleCropend,
|
handleCircleCropend,
|
||||||
|
avatar,
|
||||||
uploadApi,
|
uploadApi,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -7,10 +7,14 @@
|
|||||||
<a-col :span="10">
|
<a-col :span="10">
|
||||||
<div class="change-avatar">
|
<div class="change-avatar">
|
||||||
<div class="mb-2"> 头像 </div>
|
<div class="mb-2"> 头像 </div>
|
||||||
<img width="140" :src="avatar" />
|
<CropperAvatar
|
||||||
<Upload :showUploadList="false">
|
:uploadApi="uploadApi"
|
||||||
<Button class="ml-5"> <Icon icon="feather:upload" />更换头像 </Button>
|
:value="avatar"
|
||||||
</Upload>
|
btnText="更换头像"
|
||||||
|
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
|
||||||
|
@change="updateAvatar"
|
||||||
|
width="150"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@ -18,11 +22,11 @@
|
|||||||
</CollapseContainer>
|
</CollapseContainer>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Button, Upload, Row, Col } from 'ant-design-vue';
|
import { Button, Row, Col } from 'ant-design-vue';
|
||||||
import { computed, defineComponent, onMounted } from 'vue';
|
import { computed, defineComponent, onMounted } from 'vue';
|
||||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||||
import { CollapseContainer } from '/@/components/Container/index';
|
import { CollapseContainer } from '/@/components/Container';
|
||||||
import Icon from '/@/components/Icon/index';
|
import { CropperAvatar } from '/@/components/Cropper';
|
||||||
|
|
||||||
import { useMessage } from '/@/hooks/web/useMessage';
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
|
||||||
@ -30,16 +34,16 @@
|
|||||||
import { accountInfoApi } from '/@/api/demo/account';
|
import { accountInfoApi } from '/@/api/demo/account';
|
||||||
import { baseSetschemas } from './data';
|
import { baseSetschemas } from './data';
|
||||||
import { useUserStore } from '/@/store/modules/user';
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
import { uploadApi } from '/@/api/sys/upload';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
BasicForm,
|
BasicForm,
|
||||||
CollapseContainer,
|
CollapseContainer,
|
||||||
Button,
|
Button,
|
||||||
Upload,
|
ARow: Row,
|
||||||
Icon,
|
ACol: Col,
|
||||||
[Row.name]: Row,
|
CropperAvatar,
|
||||||
[Col.name]: Col,
|
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const { createMessage } = useMessage();
|
const { createMessage } = useMessage();
|
||||||
@ -61,9 +65,17 @@
|
|||||||
return avatar || headerImg;
|
return avatar || headerImg;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function updateAvatar(src: string) {
|
||||||
|
const userinfo = userStore.getUserInfo;
|
||||||
|
userinfo.avatar = src;
|
||||||
|
userStore.setUserInfo(userinfo);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
avatar,
|
avatar,
|
||||||
register,
|
register,
|
||||||
|
uploadApi,
|
||||||
|
updateAvatar,
|
||||||
handleSubmit: () => {
|
handleSubmit: () => {
|
||||||
createMessage.success('更新成功!');
|
createMessage.success('更新成功!');
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user