Ver código fonte

广告相关

baijunde 3 meses atrás
pai
commit
db60630a5c
4 arquivos alterados com 359 adições e 133 exclusões
  1. 6 5
      components/index_nav.vue
  2. 308 94
      components/upload/index.vue
  3. 44 33
      pages/index/index.vue
  4. 1 1
      pages/store/advertising.vue

+ 6 - 5
components/index_nav.vue

@@ -8,7 +8,7 @@
 	<view class="item">
 		<view class="card" v-for="(item,index) in swiperList" :key="index" @click="jump(item)">
 			<image :src="item.url" mode="aspectFill"
-				:style="{width:item.name=='广告管理'?'52rpx':'56rpx',height:item.name=='广告管理'?'52rpx':'56rpx',}">
+				>
 			</image>
 			{{item.name}}
 		</view>
@@ -173,21 +173,22 @@
 <style scoped lang="scss">
 	.item {
 		width: 100%;
+		height: 100%;
 		display: flex;
 		flex-wrap: wrap;
 		justify-content: space-between;
-		padding-top: 32rpx;
+		column-gap: 30rpx;
+		
 	}
 
 	.card {
-		width: 20%;
+		width: 100rpx;
 		display: flex;
 		flex-direction: column;
-		flex-wrap: wrap;
+		
 		justify-content: space-between;
 		align-items: center;
 		font-size: 24rpx;
-
 		image {
 			width: 60rpx;
 			height: 60rpx;

+ 308 - 94
components/upload/index.vue

@@ -1,105 +1,319 @@
 <template>
-	<u-upload :action="action" :fileList="fileListData" 
-		v-bind="$attrs" 
-		@on-remove="onRemove"
-		@on-success="afterRead"
-		@on-list-change="onListChange">
-		<slot></slot>
-	</u-upload>
+  <u-upload
+    :name="name" 
+    :width="width"
+    :height="height"
+    :accept="accept"
+    :fileList="fileList"
+    :multiple="multiple" 
+    :maxCount="maxCount"
+    :disabled="disabled"
+    v-if="$slots.content"
+    @afterRead="afterRead" 
+    @delete="onDelete">
+    <slot name="content" :data="fileList"></slot>
+  </u-upload>
+  <u-upload
+    :name="name" 
+    :width="width"
+    :height="height"
+    :accept="accept"
+    :fileList="fileList"
+    :multiple="multiple" 
+    :maxCount="maxCount"
+    :disabled="disabled"
+    v-else
+    @afterRead="afterRead" 
+    @delete="onDelete">
+  </u-upload>
 </template>
 
 <script>
+// import baseUrl from '@/common/config.js';
 import configService from '@/common/service/config.service';
 export default {
-	name: "",
-	props: {
-		api: {
-			type: String,
-			default: '/sys/common/upload'
-		},
-		fileList: {
-			type: Array,
-			default: () => []
-		},
-	},
-	data() {
-		return {
-			fileListData: [],
-		};
-	},
-	watch: {
-		fileList: {
-			immediate: true,
-			handler(n) {
-				if (n.length > 0) {
-					this.fileListData = n;
-				}
-			}
-		}
-	},
-	computed: {
-		action() {
-			return configService.apiUrl + this.api
-		}
-	},
-	mounted() {
-		
-	},
-	beforeDestroy() {
-		
-	},
-	methods: {
-		afterRead(event, index, list) {
-			// url是组件的固定字段
-			this.fileListData[index] = {
-				...event,
-				url: list[index].url
-			};
-			this.$emit('update:fileList', this.fileListData);
-			this.$emit('complete', this.fileListData);
-		},
-		onRemove(index, lists) {
-			this.fileListData.splice(index, 1);
-			this.$emit('update:fileList', this.fileListData);
-			this.$emit('complete', this.fileListData);
-		},
-		// onListChange早于onSuccess
-		onListChange(lists) {
-			// this.$emit('complete', lists);
-		},
-		// 手动上传
-		uploadFilePromise(url) {
-			return new Promise((resolve, reject) => {
-				let a = uni.uploadFile({
-					url: this.action,
-					name: "file",
-					header:{
-						'AppType': '5',
-						'X-Access-Token': uni.getStorageSync('token'),
-					},
-					filePath: url,
-					// formData: {
-					// 	user: "test",
-					// },
-					success: (res) => {
-						uni.hideLoading();
-						let data = JSON.parse(res.data);
-						if (data.success) {
-							resolve(data);
-						} else {
-							uni.showToast({
-								icon: 'none',
-								title: data.message || '文件导入失败',
-							});
-						}
-					},
-				});
-			});
-		},
-	}
+  name: "",
+  props: {
+    api: {
+      type: String,
+      default: '/sys/common/upload'
+    },
+    name: {
+      type: String,
+      default: uni.$u.props.upload.name
+    },
+    accept: {
+      type: String,
+      default: uni.$u.props.upload.accept
+    },
+    width: {
+      type: [String, Number],
+      default: uni.$u.props.upload.width
+    },
+    height: {
+      type: [String, Number],
+      default: uni.$u.props.upload.height
+    },
+    maxCount: {
+      type: Number,
+      default: 5
+    },
+    multiple: {
+      type: Boolean,
+      default: true
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    // 初始图片列表
+    initList: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      fileList: [],
+    };
+  },
+  watch: {
+    // 更新fileList
+    initList: {
+      handler(newVal) {
+        if (newVal && newVal.length > 0) {
+          this.fileList = newVal.map(item => ({
+            ...item,
+            status: 'success',
+            message: ''
+          }));
+        } else {
+          this.fileList = [];
+        }
+      },
+      immediate: true,
+      deep: true
+    }
+  },
+  computed: {
+
+  },
+  mounted() {
+    
+  },
+  beforeDestroy() {
+
+  },
+  methods: {
+    // 上传后的处理函数
+    async afterRead(event) {
+      // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+      let lists = [].concat(event.file);
+      let fileListLen = this[`fileList`].length;
+      
+      // 遍历文件,对图片进行压缩(支持H5、App、小程序三端)
+      // 支持两种判断方式:1. MIME类型以image/开头  2. 文件后缀为.jpg
+      for (let i = 0; i < lists.length; i++) {
+        const item = lists[i];
+        const isImageType = item.type && item.type.startsWith('image');
+        const isJpgFile = item.name && (item.name.toLowerCase().endsWith('.jpg') || item.name.toLowerCase().endsWith('.jpeg'));
+        if (isImageType || isJpgFile) {
+          // 压缩后可能是 File 对象(H5)或 URL 字符串(App/小程序)
+          const compressedResult = await this.compressImage(item.url);
+          // 保存原始 URL 用于预览,压缩结果用于上传
+          lists[i].compressedFile = compressedResult;
+        }
+      }
+      
+      lists.map((item) => {
+        this[`fileList`].push({
+          ...item,
+          status: "uploading",
+          message: "上传中",
+          localUrl: item.url, // 保存原始本地路径用于预览
+        });
+      });
+      // 无预览时,使用toast
+      if (!this.$attrs.previewImage) {
+        uni.showLoading({
+          title: '上传中...'
+        });
+      }
+      for (let i = 0; i < lists.length; i++) {
+        // 使用压缩后的文件或原文件上传
+        const uploadTarget = lists[i].compressedFile || lists[i].url;
+        const result = await this.uploadFilePromise(uploadTarget);
+        let item = this[`fileList`][fileListLen];
+        this[`fileList`].splice(
+          fileListLen,
+          1,
+          Object.assign(item, {
+            status: "success",
+            message: "",
+            url:  configService.apiUrl  + '/' + result,
+             localUrl: item.localUrl
+          })
+        );
+        fileListLen++;
+        // 无预览时,使用toast
+        if (!this.$attrs.previewImage) {
+          uni.hideLoading();
+          uni.showToast({
+            icon: 'none',
+            title: result.message || '上传完成',
+            duration: 2000
+          });
+          this.$emit('complete');
+        }
+      }
+      this.$emit('fileList', this.fileList);
+    },
+    // 图片压缩 - 支持H5、App、小程序三端
+    // 使用条件编译区分平台
+    async compressImage(url) {
+      // #ifdef H5
+      // H5 环境直接使用 Canvas 压缩
+      try {
+        return await this.canvasCompress(url, 0.8);
+      } catch (err) {
+        console.warn('Canvas 压缩失败,使用原图片:', err);
+        return url;
+      }
+      // #endif
+      
+      // #ifndef H5
+      // App 和小程序使用 uni.compressImage
+      return new Promise((resolve) => {
+        uni.compressImage({
+          src: url,
+          quality: 0.8,
+          success: (res) => {
+            resolve(res.tempFilePath);
+          },
+          fail: (err) => {
+            console.warn('uni.compressImage 失败,使用原图片:', err);
+            resolve(url);
+          }
+        });
+      });
+      // #endif
+    },
+    // Canvas 图片压缩(H5 专用)
+    canvasCompress(url, quality) {
+      return new Promise((resolve, reject) => {
+        const img = new Image();
+        img.crossOrigin = 'anonymous';
+        img.onload = () => {
+          const canvas = document.createElement('canvas');
+          const ctx = canvas.getContext('2d');
+          
+          // 计算压缩后的尺寸(保持宽高比)
+          const maxWidth = 1200;
+          const maxHeight = 1200;
+          let width = img.width;
+          let height = img.height;
+          
+          if (width > maxWidth) {
+            height = (maxWidth / width) * height;
+            width = maxWidth;
+          }
+          if (height > maxHeight) {
+            width = (maxHeight / height) * width;
+            height = maxHeight;
+          }
+          
+          canvas.width = width;
+          canvas.height = height;
+          ctx.drawImage(img, 0, 0, width, height);
+          
+          // 将 Canvas 转换为 File 对象
+          canvas.toBlob((blob) => {
+            if (blob) {
+              // 确保文件名以 .jpg 结尾
+              const originalName = url.split('/').pop() || 'image';
+              const fileName = originalName.endsWith('.jpg') || originalName.endsWith('.jpeg') 
+                ? originalName 
+                : originalName + '.jpg';
+              const file = new File([blob], fileName, { type: 'image/jpeg' });
+              resolve(file);
+            } else {
+              reject('Blob 创建失败');
+            }
+          }, 'image/jpeg', quality);
+        };
+        img.onerror = (err) => {
+          reject('图片加载失败: ' + err);
+        };
+        img.src = url;
+      });
+    },
+    uploadFilePromise(fileOrUrl) {
+      return new Promise((resolve, reject) => {
+        // #ifdef H5
+        // H5 环境使用原生 FormData 上传
+        if (fileOrUrl instanceof File) {
+          const formData = new FormData();
+          formData.append('file', fileOrUrl);
+          formData.append('biz', 'temp');
+          
+          fetch( configService.apiUrl  + this.api, {
+            method: 'POST',
+            body: formData,
+            headers: {
+              'Accept': 'application/json'
+            }
+          }).then(response => response.json())
+            .then(data => {
+              if (data.success) {
+                resolve(data.message);
+              } else {
+                reject(data.message);
+              }
+            })
+            .catch(err => {
+              reject('上传失败: ' + err);
+            });
+          return;
+        }
+        // #endif
+        
+        // App 和小程序使用 uni.uploadFile
+        uni.uploadFile({
+          url:  configService.apiUrl  + this.api,
+          name: "file",
+          formData: {
+            'biz': 'temp',
+          },
+          filePath: fileOrUrl,
+          success: (res) => {
+            let data = JSON.parse(res.data);
+            if (data.success) {
+              resolve(data.message);
+            }
+          },
+          fail: (err) => {
+            reject(err);
+          }
+        });
+      });
+    },
+    onDelete(event) {
+      uni.showModal({
+        title: '提示',
+        content: '确定要删除?',
+        cancelText: '取消',
+        confirmText: '确定',
+        success: res => {
+          if (res.confirm) {
+            this[`fileList`].splice(event.index, 1);
+            this.$emit('fileList', this.fileList);
+          }
+        }
+      })
+    },
+  }
 }
 </script>
 
 <style lang="scss" scoped>
-
 </style>

+ 44 - 33
pages/index/index.vue

@@ -27,10 +27,10 @@
 		      </view>
 		    </view>
 		  </view>
-		  <!-- 背景图 改为绝对定位 + 父级相对定位 -->
-		  <view class="head-back">
-		    <image src="/static/index/top-back.png" mode="widthFix"></image>
-		  </view>
+		  
+		</view>
+		<view class="head-back">
+		  <image src="/static/index/top-back.png" mode="widthFix"></image>
 		</view>
 
 		<view class="service">
@@ -247,14 +247,29 @@
 <style scoped lang="scss">
 	.index {
 		width: 100vw;
-		// height: 94vh;
-		// background: #F2F3F5;
-
+		height: 100vh;
+		background: #F2F3F5;
+		z-index: 1;
+		.head-back {
+		  position: fixed;
+		  top: 0;
+		  left: 0;
+		  width: 100%;
+		  // height: 100%;
+		  z-index: 1;
+		  
+		  image {
+		    width: 100%;
+		    height: 100%;
+		    // object-fit: cover; 
+		  }
+		}
 		.head {
 		  width: 100%;
 		  height: auto; 
 		  position: relative; 
 		  overflow: hidden;
+			z-index: 2;
 		  
 		  .top {
 		    width: 100%;
@@ -262,20 +277,7 @@
 		    background-color: transparent;
 		  }
 		  
-		  .head-back {
-		    position: absolute;
-		    top: 0;
-		    left: 0;
-		    width: 100%;
-		    height: 100%;
-		    z-index: 1;
-		    
-		    image {
-		      width: 100%;
-		      height: 100%;
-		      // object-fit: cover; 
-		    }
-		  }
+		  
 		
 		  .header {
 		    width: 100%;
@@ -287,7 +289,7 @@
 		    font-weight: 600;
 		    position: relative;
 		    z-index: 10;
-		    margin-top: 44rpx;
+		    // margin-top: 44rpx;
 		
 		    .logo {
 		      width: 56rpx;
@@ -313,13 +315,12 @@
 		  .data_box {
 		    width: 686rpx;
 		    height: 180rpx;
-		    margin: 36rpx auto;
+		    margin: 36rpx auto 16rpx;
 		    background: #ffffff;
 		    box-shadow: 0px 2rpx 8rpx 0px rgba(214, 225, 237, 0.1);
 		    border-radius: 16rpx;
 		    position: relative;
 		    z-index: 19;
-		    margin-bottom: 42rpx;
 		
 		    .title {
 		      display: flex;
@@ -371,19 +372,20 @@
 			// height: 468rpx;
 			box-shadow: 0px 2rpx 8rpx 0px rgba(214, 225, 237, 0.1);
 			border-radius: 16rpx;
-			padding: 24rpx 18rpx;
 			margin: 0 auto;
-
+			
 			.main {
 				height: auto;
 				display: flex;
 				justify-content: space-between;
 				margin-bottom: 16rpx;
 				align-items: stretch;
-
+				background: #FFFFFF;
+				padding: 20rpx;
+				border-radius: 20rpx;
 				.left {
 					flex: 1;
-					margin-right: 24rpx;
+					margin-right: 22rpx;
 					height: 340rpx;
 					display: flex;
 					flex-direction: column;
@@ -392,15 +394,16 @@
 					.scan {
 						font-size: 20rpx;
 						font-weight: 600;
-						width: 100%;
+						width: 352rpx;
 						height: 160rpx;
-						padding: 12rpx 12rpx 12rpx 28rpx;
+						padding: 10rpx 12rpx 16rpx 28rpx;
 						display: flex;
 						justify-content: space-between;
 						align-items: center;
 						position: relative;
 						z-index: 2;
-
+						border-radius: 8rpx;
+						overflow: hidden;
 						.back {
 							width: 100%;
 							height: 100%;
@@ -438,14 +441,15 @@
 
 				.right {
 					width: 272rpx;
-					height: 338rpx;
+					height: 340rpx;
 					color: #624FE3;
 					font-size: 20rpx;
 					padding: 28rpx;
 					background: #F6F4FF;
 					position: relative;
 					z-index: 2;
-
+					border-radius: 8rpx;
+					overflow: hidden;
 					.back {
 						width: 100%;
 						height: 100%;
@@ -473,6 +477,13 @@
 					}
 				}
 			}
+			.other{
+				width: 100%;
+				height: 304rpx;
+				padding: 32rpx ;
+				background: #FFFFFF;
+				border-radius: 16rpx;
+			}
 		}
 
 		.message {

+ 1 - 1
pages/store/advertising.vue

@@ -175,7 +175,7 @@
 			ChooseImage() {
 				uni.chooseImage({
 					// count: 4, //默认9
-					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
+					sizeType: ['compressed'],
 					sourceType: ['album'], //从相册选择
 					success: (res) => {
 						if (res) {