From cdf6f355221baa0979d71087e847e6d45f9b04cf Mon Sep 17 00:00:00 2001 From: Sagi Date: Thu, 22 Sep 2022 23:37:52 +0800 Subject: [PATCH] feature: add avatar component --- packages/ui-vue/components/avatar/index.ts | 0 .../components/avatar/src/avatar.component.ts | 308 ++++++++++++++++++ .../avatar/src/avatar.component.tsx | 66 ++++ .../components/avatar/src/avatar.props.ts | 40 +++ .../avatar/src/composition/types.ts | 9 + .../avatar/src/composition/use-file.ts | 74 +++++ .../avatar/src/composition/use-image.ts | 60 ++++ 7 files changed, 557 insertions(+) create mode 100644 packages/ui-vue/components/avatar/index.ts create mode 100644 packages/ui-vue/components/avatar/src/avatar.component.ts create mode 100644 packages/ui-vue/components/avatar/src/avatar.component.tsx create mode 100644 packages/ui-vue/components/avatar/src/avatar.props.ts create mode 100644 packages/ui-vue/components/avatar/src/composition/types.ts create mode 100644 packages/ui-vue/components/avatar/src/composition/use-file.ts create mode 100644 packages/ui-vue/components/avatar/src/composition/use-image.ts diff --git a/packages/ui-vue/components/avatar/index.ts b/packages/ui-vue/components/avatar/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/ui-vue/components/avatar/src/avatar.component.ts b/packages/ui-vue/components/avatar/src/avatar.component.ts new file mode 100644 index 0000000..2bc6655 --- /dev/null +++ b/packages/ui-vue/components/avatar/src/avatar.component.ts @@ -0,0 +1,308 @@ +import { Component, OnInit, ViewChildren, ElementRef, Input, Output, EventEmitter, HostListener, ViewChild, forwardRef } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; +import { NotifyService } from '@farris/ui-notify'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { LocaleService } from '@farris/ui-locale'; + +export interface upImageFile { + size: number; + name: string; + type: string; + lastModified?: string; + lastModifiedDate?: Date; + state?: string; + base64?: string; +} + +@Component({ + selector: 'farris-avatar', + templateUrl: './avatar.component.html', + styleUrls: ['./avatar.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AvatarComponent), + multi: true + } + ] +}) +export class AvatarComponent implements ControlValueAccessor, OnInit { + private defaultImgSrc = 'data:image/jpeg;base64,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' + private errorImgSrc = 'data:image/jpeg;base64,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'; + public imgSrc; + // public fileBinary: string; + @ViewChild('file') file: ElementRef; + // @Input() cover = ''; + tReadOnly: boolean = false; + @Input() + set readonly(value: boolean) { + if (value !== this.tReadOnly) { + let localeTitle = this.localeService.getValue('avatar.imgtitle'); + this.imgtitle = value ? '' : (this.imgTitle ? this.imgTitle : localeTitle); + this.tReadOnly = value; + } + + }; + get readonly(): boolean { + return this.tReadOnly; + } + // @Input() type; + @Input() size: number = 1; + @Input() imgTitle: string; + @Input() avatarWidth: number = 100; + @Input() avatarHeight: number = 100; + @Input() imgShape: string = 'circle'; + imgtitle: string = '点击修改'; + // @Input() isBase64:boolean = true; + @Output('imgChange') imgChange = new EventEmitter(); + + _type; + @Input() + set type(val) { + if (val && val.length) { + let types = val; + if (typeof val === 'string') { + types = val.split(','); + } + if (types && types.length) { + this.currentImgType = []; + types.forEach(t => { + if ((typeof t == 'string') && t.constructor == String) { + let tImgtype = 'image/' + t; + if (t === 'jpg') { + let jpgType = 'image/jpeg'; + this.currentImgType.push(jpgType); + } + this.currentImgType.push(tImgtype); + } + }); + if (this.currentImgType.length > 0) { + this.imgType = this.currentImgType.join(',') + } + } + } + } + get type() { + return this._type; + } + _cover; + @Input() + set cover(val) { + if (val) { + this._cover = val; + this.imgsrcInit(this.cover); + } + else { + this.imgSrc = this.defaultImgSrc; + } + } + get cover() { + return this._cover; + } + + private onChangeCallback: Function = () => { } + private onTouchedCallback: Function = () => { } + //是否加载中 + loadingImg: boolean; + imgType = 'image/*'; + imgFileObj: upImageFile; + currentImgType = ['image/image', 'image/webp', 'image/png', 'image/svg', 'image/gif', 'image/jpg', 'image/jpeg', 'image/bmp']; + constructor(private notifyService: NotifyService, public localeService: LocaleService) { + this.notifyService.config.position = 'top-center'; + } + + ngOnInit() { + // if(this.cover){ + // this.imgsrcInit(this.cover); + // } + // else{ + // this.imgSrc = this.defaultImgSrc; + // } + if (this.readonly) { + this.imgtitle = ''; + } + else if (this.imgTitle) { + this.imgtitle = this.imgTitle; + } + else { + this.imgtitle = this.localeService.getValue('avatar.imgtitle'); + } + } + /*判断cover数据格式,做出修改赋值给imgSrc + */ + imgsrcInit(val) { + let isImg = this.isSrc(val); + if (isImg) { + this.imgSrc = val; + } + else { + let isFullBase64 = this.isBaseSrc(val); + if (isFullBase64) { + this.imgSrc = val + } + else { + this.imgSrc = this.addBase64(val); + } + } + } + + @HostListener('click') + onClick(): void { + if (this.readonly) { + return; + } + (this.file.nativeElement as HTMLInputElement).click(); + } + + getfiledata(event) { + if (this.readonly) { + return; + } + const filetarget = event.target as HTMLInputElement; + let getfile = filetarget.files; + if (!getfile[0]) { + return; + } + let fileType = getfile[0].type; + const isLtSize = getfile[0].size / 1024 / 1024 < this.size; + if (this.currentImgType.indexOf(fileType) < 0) { + let typeerrorText = this.localeService.getValue('avatar.typeError'); + this.notifyService.error({ + type: 'error', + title: '', + msg: typeerrorText + }); + filetarget.value = ''; + // this.notifyService.error('上传图片类型不正确'); + return; + } + + if (!isLtSize) { + let sizeerrorText = this.localeService.getValue('avatar.sizeError'); + let errormes: string = sizeerrorText + this.size + "M!"; + this.notifyService.error({ + type: 'error', + title: '', + msg: errormes + }); + filetarget.value = ''; + // this.notifyService.error(`上传图片不能大于${this.size}M!`); + return; + } + this.transformFile(getfile[0]); + filetarget.value = ''; + } + + public getImgFileObj() { + return this.imgFileObj; + } + + transformFile(getfile: any) { + // const subject = new Subject(); + this.imgFileObj = { + size: getfile.size, + name: getfile.name, + type: getfile.type, + lastModified: getfile.lastModified, + lastModifiedDate: getfile.lastModifiedDate + } + this.do(getfile).subscribe(res => { + this.loadingImg = false; + if (res['state'] === 'done') { + this.imgSrc = res['result']; + + //this.onChangeCallback(this.imgSrc); + this.onChangeCallback(this.removeBase64(this.imgSrc)); + this.onTouchedCallback(); + } + else if (res['state'] === 'error') { + let uploaderrorText = this.localeService.getValue('avatar.uploadError'); + this.notifyService.error({ + type: 'error', + title: '', + msg: uploaderrorText + }) + // this.notifyService.error('图片上传失败,请重试!'); + } + this.imgFileObj.state = res['state']; + this.imgFileObj.base64 = res['result']; + this.imgChange.emit(this.imgFileObj); + }); + } + + read(file: File): Observable { + return Observable.create(observer => { + const reader = new FileReader(); + reader.readAsDataURL(file); + //this.loadingImg = true; + // reader.onloadstart=function(){} + reader.onload = () => { + //console.log(reader.result); + observer.next({ state: 'done', 'result': reader.result }); + observer.complete(); + }; + reader.onerror = function () { + observer.next({ state: 'error', 'result': reader.result }); + observer.complete(); + } + }); + } + do(file: any): Observable { + return this.read(file as File); + } + + writeValue(val: any): void { + if (val && val.length) { + // if(this.isBase64){ + // this.imgSrc = this.addBase64(val); + // } + // else{ + // this.imgSrc = val; + // } + this.imgsrcInit(val); + } + else if (this.cover) { + this.imgsrcInit(this.cover); + } + else { + this.imgSrc = this.defaultImgSrc; + } + } + registerOnChange(fn: any): void { + this.onChangeCallback = fn; + } + registerOnTouched(fn: any): void { + this.onTouchedCallback = fn; + } + // setDisabledState?(isDisabled: boolean): void { + // this.disabled = isDisabled; + // } + + addBase64(val) { + if (!val) return; + return 'data:image/jpeg;base64,' + val; + } + + removeBase64(val) { + if (!val) return; + let img_arr = val.split(','); + if (img_arr.length) { + return img_arr[1]; + } + + } + //判断是否是图片路径 + isSrc(url) { + return (url.match(/\.(jpeg|jpg|gif|png|svg|bmp|webp)$/) != null) + } + //判断是否是完成base64 + isBaseSrc(url) { + return (url.indexOf('data:image/') > -1 ? true : false) + } + + errorSrc() { + this.imgSrc = this.errorImgSrc; + this.imgtitle = this.localeService.getValue('avatar.loadError'); + } + +} diff --git a/packages/ui-vue/components/avatar/src/avatar.component.tsx b/packages/ui-vue/components/avatar/src/avatar.component.tsx new file mode 100644 index 0000000..4b63715 --- /dev/null +++ b/packages/ui-vue/components/avatar/src/avatar.component.tsx @@ -0,0 +1,66 @@ +import { defineComponent, computed, ref, SetupContext } from 'vue'; +import { avatarProps, AvatarProps } from './avatar.props'; + +export default defineComponent({ + name: 'Avatar', + props: avatarProps, + emits: ['change'], + setup(props: AvatarProps, context: SetupContext) { + const avatarClass = computed(() => ({ + 'f-avatar': true, + 'f-avatar-readonly': props.readonly, + 'f-avatar-circle': props.shape === 'circle', + 'f-avatar-square': props.shape === 'square', + })); + + const avatarStyle = computed(() => ({ + width: props.avatarWidth + 'px', + height: props.avatarHeight + 'px', + })); + + let showLoading = false; + let imgSrc = ''; + + const currentImgType = ['image/image', 'image/webp', 'image/png', 'image/svg', 'image/gif', 'image/jpg', 'image/jpeg', 'image/bmp']; + + function errorSrc() { + return ''; + } + + function getfiledata() {} + + const defaultImgSrc = + 'data:image/jpeg;base64,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'; + const errorImgSrc = + 'data:image/jpeg;base64,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'; + + const imageType = computed(() => props.type.join()); + + + return () => { + return ( +
+ {showLoading && ( +
+
加载中
+
+ )} + + {!props.readonly && ( +
+ +
+ )} + +
+ ); + }; + }, +}); diff --git a/packages/ui-vue/components/avatar/src/avatar.props.ts b/packages/ui-vue/components/avatar/src/avatar.props.ts new file mode 100644 index 0000000..dec7561 --- /dev/null +++ b/packages/ui-vue/components/avatar/src/avatar.props.ts @@ -0,0 +1,40 @@ +import { ExtractPropTypes, PropType } from 'vue'; + +type AvatarShap = 'square' | 'circle'; + +export const avatarProps = { + /** + * 头像宽度 + */ + avatarWidth: { type: Number, default: 100 }, + /** + * 头像高度 + */ + avatarHeight: { type: Number, default: 100 }, + /** + * 组件标识 + */ + cover: { type: String }, + /** + * 只读 + */ + readonly: { type: Boolean, default: false }, + /** + * 头像形状 + */ + shape: { type: String as PropType, default: 'circle' }, + /** + * 头像最大尺寸, 单位MB + */ + maxSize: { type: Number, default: 1 }, + /** + * 头像标题 + */ + tile: { type: String, default: '' }, + /** + * 支持的头像类型 + */ + type: { type: Array, default: [] }, +}; + +export type AvatarProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/avatar/src/composition/types.ts b/packages/ui-vue/components/avatar/src/composition/types.ts new file mode 100644 index 0000000..8331d5b --- /dev/null +++ b/packages/ui-vue/components/avatar/src/composition/types.ts @@ -0,0 +1,9 @@ +import { ComputedRef } from 'vue'; + +export interface UseImage { + acceptTypes: ComputedRef; + + imageSrc: ComputedRef; + + imageTitle: ComputedRef; +} diff --git a/packages/ui-vue/components/avatar/src/composition/use-file.ts b/packages/ui-vue/components/avatar/src/composition/use-file.ts new file mode 100644 index 0000000..de1dfdf --- /dev/null +++ b/packages/ui-vue/components/avatar/src/composition/use-file.ts @@ -0,0 +1,74 @@ +import { SetupContext } from 'vue'; +import { AvatarProps } from '../avatar.props'; + +export function useFile(props: AvatarProps, context: SetupContext, allowTypes: string[]) { + function getFileData($event: Event) { + if (props.readonly) { + return; + } + const fileInput = $event.target as HTMLInputElement; + const selectedFiles = fileInput.files; + if (!selectedFiles || !selectedFiles[0]) { + return; + } + const fileType = selectedFiles[0].type; + const isLtSize = selectedFiles[0].size / 1024 / 1024 < props.maxSize; + if (allowTypes.indexOf(fileType) < 0) { + const typeErrorMessage = this.localeService.getValue('avatar.typeError'); + this.notifyService.error({ + type: 'error', + title: '', + msg: typeErrorMessage, + }); + fileInput.value = ''; + return; + } + + if (!isLtSize) { + const sizeErrorMessageTemplate = this.localeService.getValue('avatar.sizeError'); + const sizeErrorMessage: string = sizeErrorMessageTemplate + props.maxSize + 'MB!'; + this.notifyService.error({ + type: 'error', + title: '', + msg: sizeErrorMessage, + }); + fileInput.value = ''; + return; + } + this.transformFile(selectedFiles[0]); + fileInput.value = ''; + } + + transformFile(getfile: any) { + // const subject = new Subject(); + this.imgFileObj = { + size: getfile.size, + name: getfile.name, + type: getfile.type, + lastModified: getfile.lastModified, + lastModifiedDate: getfile.lastModifiedDate + } + this.do(getfile).subscribe(res => { + this.loadingImg = false; + if (res['state'] === 'done') { + this.imgSrc = res['result']; + + //this.onChangeCallback(this.imgSrc); + this.onChangeCallback(this.removeBase64(this.imgSrc)); + this.onTouchedCallback(); + } + else if (res['state'] === 'error') { + let uploaderrorText = this.localeService.getValue('avatar.uploadError'); + this.notifyService.error({ + type: 'error', + title: '', + msg: uploaderrorText + }) + // this.notifyService.error('图片上传失败,请重试!'); + } + this.imgFileObj.state = res['state']; + this.imgFileObj.base64 = res['result']; + this.imgChange.emit(this.imgFileObj); + }); + } +} diff --git a/packages/ui-vue/components/avatar/src/composition/use-image.ts b/packages/ui-vue/components/avatar/src/composition/use-image.ts new file mode 100644 index 0000000..774d537 --- /dev/null +++ b/packages/ui-vue/components/avatar/src/composition/use-image.ts @@ -0,0 +1,60 @@ +import { computed, SetupContext } from 'vue'; +import { AvatarProps } from '../avatar.props'; +import { UseImage } from './types'; + +export function useImage(props: AvatarProps, context: SetupContext): UseImage { + const defaultImage = ''; + const errorImage = ''; + + // 判断是否是图片路径 + function isUrl(url: string) { + return url.match(/\.(jpeg|jpg|gif|png|svg|bmp|webp)$/) != null; + } + + // 判断是否是完成base64 + function isBase64Image(url: string) { + return url.indexOf('data:image/') > -1; + } + + function appendBase64ImageHeader(val) { + if (!val) { + return ''; + } + return 'data:image/jpeg;base64,' + val; + } + + const acceptTypes = computed(() => { + if (!props.type || !props.type.length) { + return ''; + } + const imageTypesArray = props.type.map((fileType: string) => { + if (fileType === 'jpg') { + fileType = 'jpeg'; + } + return `image/${fileType}`; + }); + if (!imageTypesArray || !imageTypesArray.length) { + return 'image/*'; + } + return imageTypesArray.join(','); + }); + + const imageSrc = computed(() => { + if (!props.cover) { + return defaultImage; + } + if (isUrl(props.cover)) { + return props.cover; + } + if (isBase64Image(props.cover)) { + return props.cover; + } + return appendBase64ImageHeader(props.cover); + }); + + const imageTitle = computed(() => { + return props.readonly ? '' : props.tile; + }); + + return { acceptTypes, imageSrc, imageTitle }; +}