From f26f29d84e0a68831a6af14dab3eec5500496d2e Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期三, 28 五月 2025 16:48:52 +0800
Subject: [PATCH] 初始化项目

---
 uview-ui/components/u-image/u-image.vue |  267 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 267 insertions(+), 0 deletions(-)

diff --git a/uview-ui/components/u-image/u-image.vue b/uview-ui/components/u-image/u-image.vue
new file mode 100644
index 0000000..c729c3c
--- /dev/null
+++ b/uview-ui/components/u-image/u-image.vue
@@ -0,0 +1,267 @@
+<template>
+	<view class="u-image" @tap="onClick" :style="[wrapStyle, backgroundStyle]">
+		<image
+			v-if="!isError"
+			:src="src"
+			:mode="mode"
+			@error="onErrorHandler"
+			@load="onLoadHandler"
+			:lazy-load="lazyLoad"
+			class="u-image__image"
+			:show-menu-by-longpress="showMenuByLongpress"
+			:style="{
+				borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius)
+			}"
+		></image>
+		<view
+			v-if="showLoading && loading"
+			class="u-image__loading"
+			:style="{
+				borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius),
+				backgroundColor: this.bgColor
+			}"
+		>
+			<slot v-if="$slots.loading" name="loading" />
+			<u-icon v-else :name="loadingIcon" :width="width" :height="height"></u-icon>
+		</view>
+		<view
+			v-if="showError && isError && !loading"
+			class="u-image__error"
+			:style="{
+				borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius)
+			}"
+		>
+			<slot v-if="$slots.error" name="error" />
+			<u-icon v-else :name="errorIcon" :width="width" :height="height"></u-icon>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * Image 鍥剧墖
+ * @description 姝ょ粍浠朵负uni-app鐨刬mage缁勪欢鐨勫姞寮虹増锛屽湪缁ф壙浜嗗師鏈夊姛鑳藉锛岃繕鏀寔娣″叆鍔ㄧ敾銆佸姞杞戒腑銆佸姞杞藉け璐ユ彁绀恒�佸渾瑙掑�煎拰褰㈢姸绛夈��
+ * @tutorial https://uviewui.com/components/image.html
+ * @property {String} src 鍥剧墖鍦板潃
+ * @property {String} mode 瑁佸壀妯″紡锛岃瀹樼綉璇存槑
+ * @property {String | Number} width 瀹藉害锛屽崟浣嶄换鎰忥紝濡傛灉涓烘暟鍊硷紝鍒欎负rpx鍗曚綅锛堥粯璁�100%锛�
+ * @property {String | Number} height 楂樺害锛屽崟浣嶄换鎰忥紝濡傛灉涓烘暟鍊硷紝鍒欎负rpx鍗曚綅锛堥粯璁� auto锛�
+ * @property {String} shape 鍥剧墖褰㈢姸锛宑ircle-鍦嗗舰锛宻quare-鏂瑰舰锛堥粯璁quare锛�
+ * @property {String | Number} border-radius 鍦嗚鍊硷紝鍗曚綅浠绘剰锛屽鏋滀负鏁板�硷紝鍒欎负rpx鍗曚綅锛堥粯璁� 0锛�
+ * @property {Boolean} lazy-load 鏄惁鎳掑姞杞斤紝浠呭井淇″皬绋嬪簭銆丄pp銆佺櫨搴﹀皬绋嬪簭銆佸瓧鑺傝烦鍔ㄥ皬绋嬪簭鏈夋晥锛堥粯璁� true锛�
+ * @property {Boolean} show-menu-by-longpress 鏄惁寮�鍚暱鎸夊浘鐗囨樉绀鸿瘑鍒皬绋嬪簭鐮佽彍鍗曪紝浠呭井淇″皬绋嬪簭鏈夋晥锛堥粯璁� false锛�
+ * @property {String} loading-icon 鍔犺浇涓殑鍥炬爣锛屾垨鑰呭皬鍥剧墖锛堥粯璁� photo锛�
+ * @property {String} error-icon 鍔犺浇澶辫触鐨勫浘鏍囷紝鎴栬�呭皬鍥剧墖锛堥粯璁� error-circle锛�
+ * @property {Boolean} show-loading 鏄惁鏄剧ず鍔犺浇涓殑鍥炬爣鎴栬�呰嚜瀹氫箟鐨剆lot锛堥粯璁� true锛�
+ * @property {Boolean} show-error 鏄惁鏄剧ず鍔犺浇閿欒鐨勫浘鏍囨垨鑰呰嚜瀹氫箟鐨剆lot锛堥粯璁� true锛�
+ * @property {Boolean} fade 鏄惁闇�瑕佹贰鍏ユ晥鏋滐紙榛樿 true锛�
+ * @property {String Number} width 浼犲叆鍥剧墖璺緞鏃跺浘鐗囩殑瀹藉害
+ * @property {String Number} height 浼犲叆鍥剧墖璺緞鏃跺浘鐗囩殑楂樺害
+ * @property {Boolean} webp 鍙敮鎸佺綉缁滆祫婧愶紝鍙寰俊灏忕▼搴忔湁鏁堬紙榛樿 false锛�
+ * @property {String | Number} duration 鎼厤fade鍙傛暟鐨勮繃娓℃椂闂达紝鍗曚綅ms锛堥粯璁� 500锛�
+ * @event {Function} click 鐐瑰嚮鍥剧墖鏃惰Е鍙�
+ * @event {Function} error 鍥剧墖鍔犺浇澶辫触鏃惰Е鍙�
+ * @event {Function} load 鍥剧墖鍔犺浇鎴愬姛鏃惰Е鍙�
+ * @example <u-image width="100%" height="300rpx" :src="src"></u-image>
+ */
+export default {
+	name: 'u-image',
+	props: {
+		// 鍥剧墖鍦板潃
+		src: {
+			type: String,
+			default: ''
+		},
+		// 瑁佸壀妯″紡
+		mode: {
+			type: String,
+			default: 'aspectFill'
+		},
+		// 瀹藉害锛屽崟浣嶄换鎰�
+		width: {
+			type: [String, Number],
+			default: '100%'
+		},
+		// 楂樺害锛屽崟浣嶄换鎰�
+		height: {
+			type: [String, Number],
+			default: 'auto'
+		},
+		// 鍥剧墖褰㈢姸锛宑ircle-鍦嗗舰锛宻quare-鏂瑰舰
+		shape: {
+			type: String,
+			default: 'square'
+		},
+		// 鍦嗚锛屽崟浣嶄换鎰�
+		borderRadius: {
+			type: [String, Number],
+			default: 0
+		},
+		// 鏄惁鎳掑姞杞斤紝寰俊灏忕▼搴忋�丄pp銆佺櫨搴﹀皬绋嬪簭銆佸瓧鑺傝烦鍔ㄥ皬绋嬪簭
+		lazyLoad: {
+			type: Boolean,
+			default: true
+		},
+		// 寮�鍚暱鎸夊浘鐗囨樉绀鸿瘑鍒井淇″皬绋嬪簭鐮佽彍鍗�
+		showMenuByLongpress: {
+			type: Boolean,
+			default: true
+		},
+		// 鍔犺浇涓殑鍥炬爣锛屾垨鑰呭皬鍥剧墖
+		loadingIcon: {
+			type: String,
+			default: 'photo'
+		},
+		// 鍔犺浇澶辫触鐨勫浘鏍囷紝鎴栬�呭皬鍥剧墖
+		errorIcon: {
+			type: String,
+			default: 'error-circle'
+		},
+		// 鏄惁鏄剧ず鍔犺浇涓殑鍥炬爣鎴栬�呰嚜瀹氫箟鐨剆lot
+		showLoading: {
+			type: Boolean,
+			default: true
+		},
+		// 鏄惁鏄剧ず鍔犺浇閿欒鐨勫浘鏍囨垨鑰呰嚜瀹氫箟鐨剆lot
+		showError: {
+			type: Boolean,
+			default: true
+		},
+		// 鏄惁闇�瑕佹贰鍏ユ晥鏋�
+		fade: {
+			type: Boolean,
+			default: true
+		},
+		// 鍙敮鎸佺綉缁滆祫婧愶紝鍙寰俊灏忕▼搴忔湁鏁�
+		webp: {
+			type: Boolean,
+			default: false
+		},
+		// 杩囨浮鏃堕棿锛屽崟浣峬s
+		duration: {
+			type: [String, Number],
+			default: 500
+		},
+		// 鑳屾櫙棰滆壊锛岀敤浜庢繁鑹查〉闈㈠姞杞藉浘鐗囨椂锛屼负浜嗗拰鑳屾櫙鑹茶瀺鍚�
+		bgColor: {
+			type: String,
+			default: '#f3f4f6'
+		}
+	},
+	data() {
+		return {
+			// 鍥剧墖鏄惁鍔犺浇閿欒锛屽鏋滄槸锛屽垯鏄剧ず閿欒鍗犱綅鍥�
+			isError: false,
+			// 鍒濆鍖栫粍浠舵椂锛岄粯璁や负鍔犺浇涓姸鎬�
+			loading: true,
+			// 涓嶉�忔槑搴︼紝涓轰簡瀹炵幇娣″叆娣″嚭鐨勬晥鏋�
+			opacity: 1,
+			// 杩囨浮鏃堕棿锛屽洜涓簆rops鐨勫�兼棤娉曚慨鏀癸紝鏁呴渶瑕佷竴涓腑闂村��
+			durationTime: this.duration,
+			// 鍥剧墖鍔犺浇瀹屾垚鏃讹紝鍘绘帀鑳屾櫙棰滆壊锛屽洜涓哄鏋滄槸png鍥剧墖锛屽氨浼氭樉绀虹伆鑹茬殑鑳屾櫙
+			backgroundStyle: {}
+		};
+	},
+	watch: {
+		src: {
+			immediate: true,
+			handler (n) {
+				if(!n) {
+					// 濡傛灉浼犲叆null鎴栬��''锛屾垨鑰協alse锛屾垨鑰卽ndefined锛屾爣璁颁负閿欒鐘舵��
+					this.isError = true;
+					this.loading = false;
+				} else {
+					this.isError = false;
+				}
+			}
+		}
+	},
+	computed: {
+		wrapStyle() {
+			let style = {};
+			// 閫氳繃璋冪敤addUnit()鏂规硶锛屽鏋滄湁鍗曚綅锛屽鐧惧垎姣旓紝px鍗曚綅绛夛紝鐩存帴杩斿洖锛屽鏋滄槸绾补鐨勬暟鍊硷紝鍒欏姞涓妑px鍗曚綅
+			style.width = this.$u.addUnit(this.width);
+			style.height = this.$u.addUnit(this.height);
+			// 濡傛灉鏄厤缃簡鍦嗗舰锛岃缃�50%鐨勫渾瑙掞紝鍚﹀垯鎸夌収榛樿鐨勯厤缃��
+			style.borderRadius = this.shape == 'circle' ? '50%' : this.$u.addUnit(this.borderRadius);
+			// 濡傛灉璁剧疆鍦嗚锛屽繀椤昏鏈塰idden锛屽惁鍒欏彲鑳藉渾瑙掓棤鏁�
+			style.overflow = this.borderRadius > 0 ? 'hidden' : 'visible';
+			if (this.fade) {
+				style.opacity = this.opacity;
+				style.transition = `opacity ${Number(this.durationTime) / 1000}s ease-in-out`;
+			}
+			return style;
+		}
+	},
+	methods: {
+		// 鐐瑰嚮鍥剧墖
+		onClick() {
+			this.$emit('click');
+		},
+		// 鍥剧墖鍔犺浇澶辫触
+		onErrorHandler(err) {
+			this.loading = false;
+			this.isError = true;
+			this.$emit('error', err);
+		},
+		// 鍥剧墖鍔犺浇瀹屾垚锛屾爣璁發oading缁撴潫
+		onLoadHandler() {
+			this.loading = false;
+			this.isError = false;
+			this.$emit('load');
+			// 濡傛灉涓嶉渶瑕佸姩鐢绘晥鏋滐紝灏变笉鎵ц涓嬫柟浠g爜锛屽悓鏃剁Щ闄ゅ姞杞芥椂鐨勮儗鏅鑹�
+			// 鍚﹀垯鏃犻渶fade鏁堟灉鏃讹紝png鍥剧墖渚濈劧鑳界湅鍒颁笅鏂圭殑鑳屾櫙鑹�
+			if (!this.fade) return this.removeBgColor();
+			// 鍘熸潵opacity涓�1(涓嶉�忔槑锛屾槸涓轰簡鏄剧ず鍗犱綅鍥�)锛屾敼鎴�0(閫忔槑锛屾剰鍛崇潃璇ュ厓绱犳樉绀虹殑鏄儗鏅鑹诧紝榛樿鐨勭伆鑹�)锛屽啀鏀规垚1锛屾槸涓轰簡鑾峰緱杩囨浮鏁堟灉
+			this.opacity = 0;
+			// 杩欓噷璁剧疆涓�0锛屾槸涓轰簡鍥剧墖灞曠ず鍒拌儗鏅叏閫忔槑杩欎釜杩囩▼鏃堕棿涓�0锛屽欢鏃朵箣鍚庡欢鏃朵箣鍚庨噸鏂拌缃负duration锛屾槸涓轰簡鑾峰緱鑳屾櫙閫忔槑(鐏拌壊)
+			// 鍒板浘鐗囧睍绀虹殑杩囩▼涓殑娣″叆鏁堟灉
+			this.durationTime = 0;
+			// 寤舵椂50ms锛屽惁鍒欏湪娴忚鍣℉5锛岃繃娓℃晥鏋滄棤鏁�
+			setTimeout(() => {
+				this.durationTime = this.duration;
+				this.opacity = 1;
+				setTimeout(() => {
+					this.removeBgColor();
+				}, this.durationTime);
+			}, 50);
+		},
+		// 绉婚櫎鍥剧墖鐨勮儗鏅壊
+		removeBgColor() {
+			// 娣″叆鍔ㄧ敾杩囨浮瀹屾垚鍚庯紝灏嗚儗鏅缃负閫忔槑鑹诧紝鍚﹀垯png鍥剧墖浼氱湅鍒扮伆鑹茬殑鑳屾櫙
+			this.backgroundStyle = {
+				backgroundColor: 'transparent'
+			};
+		}
+	}
+};
+</script>
+
+<style scoped lang="scss">
+@import '../../libs/css/style.components.scss';
+
+.u-image {
+	position: relative;
+	transition: opacity 0.5s ease-in-out;
+
+	&__image {
+		width: 100%;
+		height: 100%;
+	}
+
+	&__loading,
+	&__error {
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		@include vue-flex;
+		align-items: center;
+		justify-content: center;
+		background-color: $u-bg-color;
+		color: $u-tips-color;
+		font-size: 46rpx;
+	}
+}
+</style>

--
Gitblit v1.9.3