feat(avatar-cropper): more props added
为头像剪裁组件添加value属性、onChange事件以及用于自定义上传按钮的属性;更新个人设置页的头像设置
| | |
| | | - 增加`ignoreRoute`用于在`ROUTE_MAPPING`或`BACK`权限模式下仅生成菜单 |
| | | - 增加`hidePathForChildren`配置,标识为子项目生成菜单时忽略本级`path` |
| | | - **TableAction** 新增`tooltip`配置,可以为按钮增加 tooltip 提示 |
| | | - **CropperAvatar** |
| | | - 新增`value`用于设置当前头像 |
| | | - 新增`onChange`用于接受头像剪裁并上传成功事件 |
| | | - 新增`btnText`、`btnProps` 用于自定义上传按钮文案和属性 |
| | | - 为剪裁`Modal`内的操作按钮添加工具提示 |
| | | |
| | | ### 🐛 Bug Fixes |
| | | |
| | |
| | | <template> |
| | | <div :class="getClass" :style="getStyle"> |
| | | <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" /> |
| | | </div> |
| | | <a-button :class="`${prefixCls}-upload-btn`" @click="openModal"> |
| | | {{ t('component.cropper.selectImage') }} |
| | | <a-button |
| | | :class="`${prefixCls}-upload-btn`" |
| | | @click="openModal" |
| | | v-if="showBtn" |
| | | v-bind="btnProps" |
| | | > |
| | | {{ btnText ? btnText : t('component.cropper.selectImage') }} |
| | | </a-button> |
| | | <CopperModal @register="register" @uploadSuccess="handleUploadSuccess" :uploadApi="uploadApi" /> |
| | | |
| | | <CopperModal |
| | | @register="register" |
| | | @uploadSuccess="handleUploadSuccess" |
| | | :uploadApi="uploadApi" |
| | | :src="sourceValue" |
| | | /> |
| | | </div> |
| | | </template> |
| | | <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 { useDesign } from '/@/hooks/web/useDesign'; |
| | | import { useModal } from '/@/components/Modal'; |
| | | import { useMessage } from '/@/hooks/web/useMessage'; |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | import type { ButtonProps } from '/@/components/Button'; |
| | | import Icon from '/@/components/Icon'; |
| | | |
| | | const props = { |
| | | 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>> }, |
| | | }; |
| | | |
| | | export default defineComponent({ |
| | | name: 'CropperAvatar', |
| | | components: { CopperModal }, |
| | | components: { CopperModal, Icon }, |
| | | props, |
| | | setup(props) { |
| | | const sourceValue = ref(''); |
| | | emits: ['update:value', 'change'], |
| | | setup(props, { emit }) { |
| | | const sourceValue = ref(props.value || ''); |
| | | const { prefixCls } = useDesign('cropper-avatar'); |
| | | const [register, { openModal }] = useModal(); |
| | | const { createMessage } = useMessage(); |
| | |
| | | |
| | | 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 getImageWrapperStyle = computed( |
| | | (): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }) |
| | | ); |
| | | |
| | | watchEffect(() => { |
| | | sourceValue.value = props.value; |
| | | }); |
| | | |
| | | watch( |
| | | () => sourceValue.value, |
| | | (v: string) => { |
| | | emit('update:value', v); |
| | | } |
| | | ); |
| | | |
| | | function handleUploadSuccess({ source }) { |
| | | sourceValue.value = source; |
| | | emit('change', source); |
| | | createMessage.success(t('component.cropper.uploadSuccess')); |
| | | } |
| | | |
| | |
| | | prefixCls, |
| | | register, |
| | | openModal, |
| | | getIconWidth, |
| | | sourceValue, |
| | | getClass, |
| | | getImageWrapperStyle, |
| | |
| | | } |
| | | } |
| | | |
| | | &-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 { |
| | | margin: 10px auto; |
| | | } |
| | |
| | | <template> |
| | | <PageWrapper title="图片裁剪示例" content="需要开启测试接口服务才能进行上传测试!"> |
| | | <CollapseContainer title="头像裁剪"> |
| | | <CropperAvatar :uploadApi="uploadApi" /> |
| | | <CropperAvatar :uploadApi="uploadApi" :value="avatar" /> |
| | | </CollapseContainer> |
| | | |
| | | <CollapseContainer title="矩形裁剪" class="my-4"> |
| | |
| | | <div class="cropper-container mr-10"> |
| | | <CropperImage ref="refCropper" :src="img" @cropend="handleCropend" style="width: 40vw" /> |
| | | </div> |
| | | <img :src="cropperImg" class="croppered" v-if="cropperImg" /> |
| | | <img :src="cropperImg" class="croppered" v-if="cropperImg" alt="" /> |
| | | </div> |
| | | <p v-if="cropperImg">裁剪后图片信息:{{ info }}</p> |
| | | </CollapseContainer> |
| | |
| | | <script lang="ts"> |
| | | import { defineComponent, ref } from 'vue'; |
| | | import { PageWrapper } from '/@/components/Page'; |
| | | import { CollapseContainer } from '/@/components/Container/index'; |
| | | import { CollapseContainer } from '/@/components/Container'; |
| | | import { CropperImage, CropperAvatar } from '/@/components/Cropper'; |
| | | import { uploadApi } from '/@/api/sys/upload'; |
| | | import img from '/@/assets/images/header.jpg'; |
| | | import { useUserStore } from '/@/store/modules/user'; |
| | | |
| | | export default defineComponent({ |
| | | components: { |
| | |
| | | const cropperImg = ref(''); |
| | | const circleInfo = ref(''); |
| | | const circleImg = ref(''); |
| | | |
| | | const userStore = useUserStore(); |
| | | const avatar = ref(userStore.getUserInfo?.avatar || ''); |
| | | function handleCropend({ imgBase64, imgInfo }) { |
| | | info.value = imgInfo; |
| | | cropperImg.value = imgBase64; |
| | |
| | | circleImg, |
| | | handleCropend, |
| | | handleCircleCropend, |
| | | avatar, |
| | | uploadApi, |
| | | }; |
| | | }, |
| | |
| | | <a-col :span="10"> |
| | | <div class="change-avatar"> |
| | | <div class="mb-2"> 头像 </div> |
| | | <img width="140" :src="avatar" /> |
| | | <Upload :showUploadList="false"> |
| | | <Button class="ml-5"> <Icon icon="feather:upload" />更换头像 </Button> |
| | | </Upload> |
| | | <CropperAvatar |
| | | :uploadApi="uploadApi" |
| | | :value="avatar" |
| | | btnText="更换头像" |
| | | :btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }" |
| | | @change="updateAvatar" |
| | | width="150" |
| | | /> |
| | | </div> |
| | | </a-col> |
| | | </a-row> |
| | |
| | | </CollapseContainer> |
| | | </template> |
| | | <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 { BasicForm, useForm } from '/@/components/Form/index'; |
| | | import { CollapseContainer } from '/@/components/Container/index'; |
| | | import Icon from '/@/components/Icon/index'; |
| | | import { CollapseContainer } from '/@/components/Container'; |
| | | import { CropperAvatar } from '/@/components/Cropper'; |
| | | |
| | | import { useMessage } from '/@/hooks/web/useMessage'; |
| | | |
| | |
| | | import { accountInfoApi } from '/@/api/demo/account'; |
| | | import { baseSetschemas } from './data'; |
| | | import { useUserStore } from '/@/store/modules/user'; |
| | | import { uploadApi } from '/@/api/sys/upload'; |
| | | |
| | | export default defineComponent({ |
| | | components: { |
| | | BasicForm, |
| | | CollapseContainer, |
| | | Button, |
| | | Upload, |
| | | Icon, |
| | | [Row.name]: Row, |
| | | [Col.name]: Col, |
| | | ARow: Row, |
| | | ACol: Col, |
| | | CropperAvatar, |
| | | }, |
| | | setup() { |
| | | const { createMessage } = useMessage(); |
| | |
| | | return avatar || headerImg; |
| | | }); |
| | | |
| | | function updateAvatar(src: string) { |
| | | const userinfo = userStore.getUserInfo; |
| | | userinfo.avatar = src; |
| | | userStore.setUserInfo(userinfo); |
| | | } |
| | | |
| | | return { |
| | | avatar, |
| | | register, |
| | | uploadApi, |
| | | updateAvatar, |
| | | handleSubmit: () => { |
| | | createMessage.success('更新成功!'); |
| | | }, |