无木
2021-07-03 b96ea0753bfd769693a368cf1e3d8316688c0dcb
feat(avatar-cropper): more props added

为头像剪裁组件添加value属性、onChange事件以及用于自定义上传按钮的属性;更新个人设置页的头像设置
4个文件已修改
126 ■■■■ 已修改文件
CHANGELOG.zh_CN.md 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Cropper/src/CropperAvatar.vue 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demo/comp/cropper/index.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demo/page/account/setting/BaseSetting.vue 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CHANGELOG.zh_CN.md
@@ -7,6 +7,11 @@
  - 增加`ignoreRoute`用于在`ROUTE_MAPPING`或`BACK`权限模式下仅生成菜单
  - 增加`hidePathForChildren`配置,标识为子项目生成菜单时忽略本级`path`
- **TableAction** 新增`tooltip`配置,可以为按钮增加 tooltip 提示
- **CropperAvatar**
  - 新增`value`用于设置当前头像
  - 新增`onChange`用于接受头像剪裁并上传成功事件
  - 新增`btnText`、`btnProps` 用于自定义上传按钮文案和属性
  - 为剪裁`Modal`内的操作按钮添加工具提示
### 🐛 Bug Fixes
src/components/Cropper/src/CropperAvatar.vue
@@ -1,33 +1,59 @@
<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();
@@ -37,14 +63,28 @@
      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'));
      }
@@ -53,6 +93,7 @@
        prefixCls,
        register,
        openModal,
        getIconWidth,
        sourceValue,
        getClass,
        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 {
      margin: 10px auto;
    }
src/views/demo/comp/cropper/index.vue
@@ -1,7 +1,7 @@
<template>
  <PageWrapper title="图片裁剪示例" content="需要开启测试接口服务才能进行上传测试!">
    <CollapseContainer title="头像裁剪">
      <CropperAvatar :uploadApi="uploadApi" />
      <CropperAvatar :uploadApi="uploadApi" :value="avatar" />
    </CollapseContainer>
    <CollapseContainer title="矩形裁剪" class="my-4">
@@ -9,7 +9,7 @@
        <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>
@@ -34,10 +34,11 @@
<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: {
@@ -51,7 +52,8 @@
      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;
@@ -70,6 +72,7 @@
        circleImg,
        handleCropend,
        handleCircleCropend,
        avatar,
        uploadApi,
      };
    },
src/views/demo/page/account/setting/BaseSetting.vue
@@ -7,10 +7,14 @@
      <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>
@@ -18,11 +22,11 @@
  </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';
@@ -30,16 +34,16 @@
  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();
@@ -61,9 +65,17 @@
        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('更新成功!');
        },