Commit b96ea0753bfd769693a368cf1e3d8316688c0dcb
1 parent
6cbac4b7
feat(avatar-cropper): more props added
为头像剪裁组件添加value属性、onChange事件以及用于自定义上传按钮的属性;更新个人设置页的头像设置
Showing
4 changed files
with
104 additions
and
22 deletions
CHANGELOG.zh_CN.md
... | ... | @@ -7,6 +7,11 @@ |
7 | 7 | - 增加`ignoreRoute`用于在`ROUTE_MAPPING`或`BACK`权限模式下仅生成菜单 |
8 | 8 | - 增加`hidePathForChildren`配置,标识为子项目生成菜单时忽略本级`path` |
9 | 9 | - **TableAction** 新增`tooltip`配置,可以为按钮增加 tooltip 提示 |
10 | +- **CropperAvatar** | |
11 | + - 新增`value`用于设置当前头像 | |
12 | + - 新增`onChange`用于接受头像剪裁并上传成功事件 | |
13 | + - 新增`btnText`、`btnProps` 用于自定义上传按钮文案和属性 | |
14 | + - 为剪裁`Modal`内的操作按钮添加工具提示 | |
10 | 15 | |
11 | 16 | ### 🐛 Bug Fixes |
12 | 17 | ... | ... |
src/components/Cropper/src/CropperAvatar.vue
1 | 1 | <template> |
2 | 2 | <div :class="getClass" :style="getStyle"> |
3 | 3 | <div :class="`${prefixCls}-image-wrapper`" :style="getImageWrapperStyle" @click="openModal"> |
4 | + <div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle"> | |
5 | + <Icon | |
6 | + icon="ant-design:cloud-upload-outlined" | |
7 | + :size="getIconWidth" | |
8 | + :style="getImageWrapperStyle" | |
9 | + color="#d6d6d6" | |
10 | + /> | |
11 | + </div> | |
4 | 12 | <img :src="sourceValue" v-if="sourceValue" alt="avatar" /> |
5 | 13 | </div> |
6 | - <a-button :class="`${prefixCls}-upload-btn`" @click="openModal"> | |
7 | - {{ t('component.cropper.selectImage') }} | |
14 | + <a-button | |
15 | + :class="`${prefixCls}-upload-btn`" | |
16 | + @click="openModal" | |
17 | + v-if="showBtn" | |
18 | + v-bind="btnProps" | |
19 | + > | |
20 | + {{ btnText ? btnText : t('component.cropper.selectImage') }} | |
8 | 21 | </a-button> |
9 | - <CopperModal @register="register" @uploadSuccess="handleUploadSuccess" :uploadApi="uploadApi" /> | |
22 | + | |
23 | + <CopperModal | |
24 | + @register="register" | |
25 | + @uploadSuccess="handleUploadSuccess" | |
26 | + :uploadApi="uploadApi" | |
27 | + :src="sourceValue" | |
28 | + /> | |
10 | 29 | </div> |
11 | 30 | </template> |
12 | 31 | <script lang="ts"> |
13 | - import { defineComponent, computed, CSSProperties, unref, ref } from 'vue'; | |
32 | + import { defineComponent, computed, CSSProperties, unref, ref, watchEffect, watch } from 'vue'; | |
14 | 33 | import CopperModal from './CopperModal.vue'; |
15 | 34 | import { useDesign } from '/@/hooks/web/useDesign'; |
16 | 35 | import { useModal } from '/@/components/Modal'; |
17 | 36 | import { useMessage } from '/@/hooks/web/useMessage'; |
18 | 37 | import { useI18n } from '/@/hooks/web/useI18n'; |
38 | + import type { ButtonProps } from '/@/components/Button'; | |
39 | + import Icon from '/@/components/Icon'; | |
19 | 40 | |
20 | 41 | const props = { |
21 | 42 | width: { type: [String, Number], default: '200px' }, |
43 | + value: { type: String }, | |
44 | + showBtn: { type: Boolean, default: true }, | |
45 | + btnProps: { type: Object as ButtonProps }, | |
46 | + btnText: { type: String, default: '' }, | |
22 | 47 | uploadApi: { type: Function as PropType<({ file: Blob, name: string }) => Promise<void>> }, |
23 | 48 | }; |
24 | 49 | |
25 | 50 | export default defineComponent({ |
26 | 51 | name: 'CropperAvatar', |
27 | - components: { CopperModal }, | |
52 | + components: { CopperModal, Icon }, | |
28 | 53 | props, |
29 | - setup(props) { | |
30 | - const sourceValue = ref(''); | |
54 | + emits: ['update:value', 'change'], | |
55 | + setup(props, { emit }) { | |
56 | + const sourceValue = ref(props.value || ''); | |
31 | 57 | const { prefixCls } = useDesign('cropper-avatar'); |
32 | 58 | const [register, { openModal }] = useModal(); |
33 | 59 | const { createMessage } = useMessage(); |
... | ... | @@ -37,14 +63,28 @@ |
37 | 63 | |
38 | 64 | const getWidth = computed(() => `${props.width}`.replace(/px/, '') + 'px'); |
39 | 65 | |
66 | + const getIconWidth = computed(() => parseInt(`${props.width}`.replace(/px/, '')) / 2 + 'px'); | |
67 | + | |
40 | 68 | const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) })); |
41 | 69 | |
42 | 70 | const getImageWrapperStyle = computed( |
43 | 71 | (): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }) |
44 | 72 | ); |
45 | 73 | |
74 | + watchEffect(() => { | |
75 | + sourceValue.value = props.value; | |
76 | + }); | |
77 | + | |
78 | + watch( | |
79 | + () => sourceValue.value, | |
80 | + (v: string) => { | |
81 | + emit('update:value', v); | |
82 | + } | |
83 | + ); | |
84 | + | |
46 | 85 | function handleUploadSuccess({ source }) { |
47 | 86 | sourceValue.value = source; |
87 | + emit('change', source); | |
48 | 88 | createMessage.success(t('component.cropper.uploadSuccess')); |
49 | 89 | } |
50 | 90 | |
... | ... | @@ -53,6 +93,7 @@ |
53 | 93 | prefixCls, |
54 | 94 | register, |
55 | 95 | openModal, |
96 | + getIconWidth, | |
56 | 97 | sourceValue, |
57 | 98 | getClass, |
58 | 99 | getImageWrapperStyle, |
... | ... | @@ -82,6 +123,27 @@ |
82 | 123 | } |
83 | 124 | } |
84 | 125 | |
126 | + &-image-mask { | |
127 | + opacity: 0; | |
128 | + position: absolute; | |
129 | + width: inherit; | |
130 | + height: inherit; | |
131 | + border-radius: inherit; | |
132 | + border: inherit; | |
133 | + background: rgba(0, 0, 0, 0.4); | |
134 | + cursor: pointer; | |
135 | + -webkit-transition: opacity 0.4s; | |
136 | + transition: opacity 0.4s; | |
137 | + | |
138 | + ::v-deep(svg) { | |
139 | + margin: auto; | |
140 | + } | |
141 | + } | |
142 | + | |
143 | + &-image-mask:hover { | |
144 | + opacity: 40; | |
145 | + } | |
146 | + | |
85 | 147 | &-upload-btn { |
86 | 148 | margin: 10px auto; |
87 | 149 | } | ... | ... |
src/views/demo/comp/cropper/index.vue
1 | 1 | <template> |
2 | 2 | <PageWrapper title="图片裁剪示例" content="需要开启测试接口服务才能进行上传测试!"> |
3 | 3 | <CollapseContainer title="头像裁剪"> |
4 | - <CropperAvatar :uploadApi="uploadApi" /> | |
4 | + <CropperAvatar :uploadApi="uploadApi" :value="avatar" /> | |
5 | 5 | </CollapseContainer> |
6 | 6 | |
7 | 7 | <CollapseContainer title="矩形裁剪" class="my-4"> |
... | ... | @@ -9,7 +9,7 @@ |
9 | 9 | <div class="cropper-container mr-10"> |
10 | 10 | <CropperImage ref="refCropper" :src="img" @cropend="handleCropend" style="width: 40vw" /> |
11 | 11 | </div> |
12 | - <img :src="cropperImg" class="croppered" v-if="cropperImg" /> | |
12 | + <img :src="cropperImg" class="croppered" v-if="cropperImg" alt="" /> | |
13 | 13 | </div> |
14 | 14 | <p v-if="cropperImg">裁剪后图片信息:{{ info }}</p> |
15 | 15 | </CollapseContainer> |
... | ... | @@ -34,10 +34,11 @@ |
34 | 34 | <script lang="ts"> |
35 | 35 | import { defineComponent, ref } from 'vue'; |
36 | 36 | import { PageWrapper } from '/@/components/Page'; |
37 | - import { CollapseContainer } from '/@/components/Container/index'; | |
37 | + import { CollapseContainer } from '/@/components/Container'; | |
38 | 38 | import { CropperImage, CropperAvatar } from '/@/components/Cropper'; |
39 | 39 | import { uploadApi } from '/@/api/sys/upload'; |
40 | 40 | import img from '/@/assets/images/header.jpg'; |
41 | + import { useUserStore } from '/@/store/modules/user'; | |
41 | 42 | |
42 | 43 | export default defineComponent({ |
43 | 44 | components: { |
... | ... | @@ -51,7 +52,8 @@ |
51 | 52 | const cropperImg = ref(''); |
52 | 53 | const circleInfo = ref(''); |
53 | 54 | const circleImg = ref(''); |
54 | - | |
55 | + const userStore = useUserStore(); | |
56 | + const avatar = ref(userStore.getUserInfo?.avatar || ''); | |
55 | 57 | function handleCropend({ imgBase64, imgInfo }) { |
56 | 58 | info.value = imgInfo; |
57 | 59 | cropperImg.value = imgBase64; |
... | ... | @@ -70,6 +72,7 @@ |
70 | 72 | circleImg, |
71 | 73 | handleCropend, |
72 | 74 | handleCircleCropend, |
75 | + avatar, | |
73 | 76 | uploadApi, |
74 | 77 | }; |
75 | 78 | }, | ... | ... |
src/views/demo/page/account/setting/BaseSetting.vue
... | ... | @@ -7,10 +7,14 @@ |
7 | 7 | <a-col :span="10"> |
8 | 8 | <div class="change-avatar"> |
9 | 9 | <div class="mb-2"> 头像 </div> |
10 | - <img width="140" :src="avatar" /> | |
11 | - <Upload :showUploadList="false"> | |
12 | - <Button class="ml-5"> <Icon icon="feather:upload" />更换头像 </Button> | |
13 | - </Upload> | |
10 | + <CropperAvatar | |
11 | + :uploadApi="uploadApi" | |
12 | + :value="avatar" | |
13 | + btnText="更换头像" | |
14 | + :btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }" | |
15 | + @change="updateAvatar" | |
16 | + width="150" | |
17 | + /> | |
14 | 18 | </div> |
15 | 19 | </a-col> |
16 | 20 | </a-row> |
... | ... | @@ -18,11 +22,11 @@ |
18 | 22 | </CollapseContainer> |
19 | 23 | </template> |
20 | 24 | <script lang="ts"> |
21 | - import { Button, Upload, Row, Col } from 'ant-design-vue'; | |
25 | + import { Button, Row, Col } from 'ant-design-vue'; | |
22 | 26 | import { computed, defineComponent, onMounted } from 'vue'; |
23 | 27 | import { BasicForm, useForm } from '/@/components/Form/index'; |
24 | - import { CollapseContainer } from '/@/components/Container/index'; | |
25 | - import Icon from '/@/components/Icon/index'; | |
28 | + import { CollapseContainer } from '/@/components/Container'; | |
29 | + import { CropperAvatar } from '/@/components/Cropper'; | |
26 | 30 | |
27 | 31 | import { useMessage } from '/@/hooks/web/useMessage'; |
28 | 32 | |
... | ... | @@ -30,16 +34,16 @@ |
30 | 34 | import { accountInfoApi } from '/@/api/demo/account'; |
31 | 35 | import { baseSetschemas } from './data'; |
32 | 36 | import { useUserStore } from '/@/store/modules/user'; |
37 | + import { uploadApi } from '/@/api/sys/upload'; | |
33 | 38 | |
34 | 39 | export default defineComponent({ |
35 | 40 | components: { |
36 | 41 | BasicForm, |
37 | 42 | CollapseContainer, |
38 | 43 | Button, |
39 | - Upload, | |
40 | - Icon, | |
41 | - [Row.name]: Row, | |
42 | - [Col.name]: Col, | |
44 | + ARow: Row, | |
45 | + ACol: Col, | |
46 | + CropperAvatar, | |
43 | 47 | }, |
44 | 48 | setup() { |
45 | 49 | const { createMessage } = useMessage(); |
... | ... | @@ -61,9 +65,17 @@ |
61 | 65 | return avatar || headerImg; |
62 | 66 | }); |
63 | 67 | |
68 | + function updateAvatar(src: string) { | |
69 | + const userinfo = userStore.getUserInfo; | |
70 | + userinfo.avatar = src; | |
71 | + userStore.setUserInfo(userinfo); | |
72 | + } | |
73 | + | |
64 | 74 | return { |
65 | 75 | avatar, |
66 | 76 | register, |
77 | + uploadApi, | |
78 | + updateAvatar, | |
67 | 79 | handleSubmit: () => { |
68 | 80 | createMessage.success('更新成功!'); |
69 | 81 | }, | ... | ... |