Преглед на файлове

feat 首页组件添加

jmy преди 4 месеца
родител
ревизия
c60f2c9853

+ 138 - 23
assets/style/global.scss

@@ -243,6 +243,13 @@ view{
 .text-end {
   text-align: end;
 }
+.w-100vw {
+  width: 100vw;
+}
+.h-100vh {
+  height: 100vh;
+}
+
 
 .w-all {
   width: 100%;
@@ -308,6 +315,11 @@ view{
   overflow-y: auto;
 }
 
+.inline-block {
+  display: inline-block;
+}
+
+
 
 /*  (-500 to 1000px, all numbers, auto convert to rpx: px * 2 = rpx) */
 @for $i from -500 through 1000 {
@@ -333,6 +345,7 @@ view{
     bottom: $size;
   }
 }
+
 /* z-index (-100 to 10000 all numbers) */
 @for $i from -100 through 10000 {
   .zi-#{$i} {
@@ -357,7 +370,7 @@ view{
   }
 }
 /* Margins (1px to 100px, all numbers, auto convert to rpx: px * 2 = rpx) */
-@for $i from 1 through 100 {
+@for $i from -500 through 500 {
   $size: $i * 2rpx;
 
   /* Margin */
@@ -453,7 +466,7 @@ view{
 /* #endif */
 
 /* Widths (1px to 375px, all numbers, auto convert to rpx: px * 2 = rpx) */
-@for $i from 1 through 375 {
+@for $i from 1 through 1000 {
   $size: $i * 2rpx;
 
   .w-#{$i} {
@@ -462,7 +475,7 @@ view{
 }
 
 /* Heights (1px to 375px, all numbers, auto convert to rpx: px * 2 = rpx) */
-@for $i from 1 through 375 {
+@for $i from 1 through 1000 {
   $size: $i * 2rpx;
 
   .h-#{$i} {
@@ -504,6 +517,110 @@ view{
   }
 }
 
+/* Border Widths (0.5px to 20px, auto convert to rpx: px * 2 = rpx) */
+// 0.5px border (1rpx)
+.border-w-05 {
+  border-width: 1rpx;
+}
+.border-t-w-05 {
+  border-top-width: 1rpx;
+}
+.border-r-w-05 {
+  border-right-width: 1rpx;
+}
+.border-b-w-05 {
+  border-bottom-width: 1rpx;
+}
+.border-l-w-05 {
+  border-left-width: 1rpx;
+}
+
+//borders 1px to 20px
+@for $i from 1 through 20 {
+  $size: $i * 2rpx;
+
+  /* all borders */
+  .border-w-#{$i} {
+    border-width: $size;
+  }
+
+  /* border top */
+  .border-t-w-#{$i} {
+    border-top-width: $size;
+  }
+
+  /* border right */
+  .border-r-w-#{$i} {
+    border-right-width: $size;
+  }
+
+  /* border bottom */
+  .border-b-w-#{$i} {
+    border-bottom-width: $size;
+  }
+
+  /* border left */
+  .border-l-w-#{$i} {
+    border-left-width: $size;
+  }
+}
+
+/* Border Styles */
+.border-solid {
+  border-style: solid;
+}
+.border-dashed {
+  border-style: dashed;
+}
+.border-dotted {
+  border-style: dotted;
+}
+.border-double {
+  border-style: double;
+}
+.border-none {
+  border-style: none;
+}
+
+/* Border Directions */
+.border {
+  border-style: solid;
+  border-width: 1rpx;
+}
+.border-t {
+  border-top-style: solid;
+  border-top-width: 1rpx;
+}
+.border-r {
+  border-right-style: solid;
+  border-right-width: 1rpx;
+}
+.border-b {
+  border-bottom-style: solid;
+  border-bottom-width: 1rpx;
+}
+.border-l {
+  border-left-style: solid;
+  border-left-width: 1rpx;
+}
+
+/* Clear Border Directions */
+.border-t-0 {
+  border-top-width: 0;
+}
+.border-r-0 {
+  border-right-width: 0;
+}
+.border-b-0 {
+  border-bottom-width: 0;
+}
+.border-l-0 {
+  border-left-width: 0;
+}
+.border-0 {
+  border-width: 0;
+}
+
 // 定义颜色列表
 $colors: (
   '000000', '333333', '666666', '999999', 'CCCCCC', 'FFFFFF',
@@ -515,7 +632,8 @@ $colors: (
   '1E7E34', '155724', '0F3D1F', 'DC3545', 'C82333', 'BD2130',
   'A71E2A', '6E001D', 'FFC107', 'E0A800', 'D39E00', 'B8860B',
   '6C5600', '17A2B8', '138496', '117A8B', '0C5460', '062B36',
-  'F5F7FA', 'EEEEEE', '02B176', 'FA341E', 'D46C0D',
+  'F5F7FA', 'EEEEEE', '02B176', 'FA341E', 'D46C0D', 'FFA599',
+  'FF4B33', '38D97D',
 );
 
 // 文字颜色类 - 标准化格式
@@ -540,67 +658,64 @@ $colors: (
 }
 
 /* 主题色彩快捷类 */
-.text-primary {
+.text-blue {
   color: #007bff;
 }
 
-.text-success {
+.text-green {
   color: #28a745;
 }
 
-.text-danger {
+.text-red {
   color: #dc3545;
 }
 
-.text-warning {
+.text-yellow {
   color: #ffc107;
 }
 
-.text-info {
+.text-cyan {
   color: #17a2b8;
 }
 
-.text-light {
+.text-gray {
   color: #f8f9fa;
 }
 
-.text-dark {
-  color: #343a40;
+.text-black {
+  color: #000000;
 }
 
-.text-muted {
-  color: #6c757d;
-}
 .text-white {
   color: #ffffff;
 }
 
-.bg-primary {
+.bg-blue {
   background-color: #007bff;
 }
 
-.bg-success {
+.bg-green {
   background-color: #28a745;
 }
 
-.bg-danger {
+.bg-red {
   background-color: #dc3545;
 }
 
-.bg-warning {
+.bg-yellow {
   background-color: #ffc107;
 }
 
-.bg-info {
+.bg-cyan {
   background-color: #17a2b8;
 }
 
-.bg-light {
+.bg-gray {
   background-color: #f8f9fa;
 }
 
-.bg-dark {
-  background-color: #343a40;
+.bg-black {
+  background-color: #000000;
 }
 
 .bg-white {

+ 7 - 2
assets/style/index.scss

@@ -8,8 +8,8 @@
 //加载动画
 .scroll-loading {
     display: inline-block;
-    width: 30rpx;
-    height: 30rpx;
+    width: 26rpx;
+    height: 26rpx;
     border-radius: 50%;
     border: 4rpx solid #999999;
     border-bottom-color: transparent !important;
@@ -155,4 +155,9 @@
         }
     }
 
+}
+// 文字删除线
+.text-line-through::after {
+  content: ""; /* 必须有content属性,即使是空字符串 */
+  display: block; /* 或者 inline-block, 根据需要 */
 }

+ 31 - 0
components/public/scs-scroll-top.vue

@@ -0,0 +1,31 @@
+<template>
+    <uni-transition name="fade" :duration="300" :show="isShowToTop">
+        <image src="https://www.mescroll.com/img/mescroll-totop.png" class="scroll-totop" @click="goTop" />
+    </uni-transition>
+</template>
+<script>
+export default {
+    props: {
+        isShowToTop: {
+            type: Boolean,
+            default: false,
+        },
+    },
+    methods: {
+        goTop() {
+            this.$emit('goTop')
+        },
+    },
+}
+</script>
+<style lang="scss" scoped>
+.scroll-totop {
+    position: fixed;
+    bottom: 80rpx;
+    right: 50rpx;
+    width: 72rpx;
+    height: 72rpx;
+    z-index: 999;
+    border-radius: 50%;
+}
+</style>

+ 161 - 0
components/public/scs-scroll-view.vue

@@ -0,0 +1,161 @@
+<!--
+ * @Author: jmy
+ * @Date: 2026-01-07 12:02:41
+ * @LastEditors: Please set LastEditors
+ * @LastEditTime: 2026-01-07 15:02:10
+ * @Description: 
+-->
+<template>
+  <!--
+   scroll-view 组件内部,在uni-app/小程序中,
+   scroll-view 会创建独立的滚动上下文,导致内部的 fixed 定位元素相对于
+   scroll-view 容器定位,而非整个视窗,因此会随内容滚动。 -->
+  <scroll-view @scrolltolower="loadMore" :scroll-y="isScroll" :scroll-with-animation="true"
+      @refresherpulling="handlePulling" @refresherrefresh="handleRefresh" @refresherrestore="handleRefreshStore"
+      @refresherabort="handleRefreshAbort" @scroll="onScroll" :refresher-enabled="refresherEnabled"
+      :scroll-top="scrollTop" :refresher-threshold="80" :refresher-triggered="isTrigger" refresher-default-style="none"
+      :refresher-background="refresherBackground" class="scs-scroll">
+      <template slot="refresher">
+        <view class="scs-scroll-refresher">
+          {{ refresherText }}
+          <text class="scroll-loading" :class="{ 'scroll-rotate': refresherText === '正在加载' }"></text>
+        </view>
+      </template>
+      <slot name="list" />
+    </scroll-view>
+</template>
+
+<script>
+export default {
+  props: {
+    isScroll: {
+      type: Boolean,
+      default: true,
+    },
+    list: {
+      type: Array,
+      default: () => {
+        return [];
+      },
+    },
+    refresherBackground: {
+      type: String,
+      default: "#FFF",
+    },
+    refresherEnabled: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      isTrigger: false,
+      scrollTop: 0,
+      oldScrollTop: 0, //记录上次滚动位置
+      refresherText: "下拉刷新",
+      threshold: 15, // 设定触发显示隐藏的滚动距离阈值
+      isShowToTop: true, //是否显示返回顶部按钮
+    };
+  },
+  methods: {
+
+    //返回顶部
+    goTop(e) {
+      //视图会发生重新渲染
+      this.scrollTop = this.oldScrollTop;
+      //当视图渲染结束 重新设置为0
+      this.$nextTick(() => {
+        this.scrollTop = 0;
+      });
+    },
+    //滚动时触发
+    onScroll(e) {
+      this.isShowToTop = e.detail.scrollTop > 200;
+      this.$emit("scroll");
+      // console.log("scroll",  e.detail.scrollTop);
+      // e.detail.scrollTop 是当前滚动位置
+      const scrollTop = e.detail.scrollTop;
+      // 只有当滚动距离超过阈值时才触发显示隐藏
+      if (Math.abs(scrollTop - this.oldScrollTop) > this.threshold) {
+        // 根据滚动方向来确定是显示还是隐藏
+        let isShow = !(scrollTop > this.oldScrollTop);
+        this.$emit("updateIsTab", isShow);
+      }
+      // 记录上次滚动位置
+      this.oldScrollTop = scrollTop;
+      //判断是否显示返回顶部按钮
+
+      this.$emit("handleScroll", scrollTop);
+    },
+    goTop(e) {
+      //视图会发生重新渲染
+      this.scrollTop = this.oldScrollTop;
+      //当视图渲染结束 重新设置为0
+      this.$nextTick(() => {
+        this.scrollTop = 0;
+      });
+    },
+    //滚动到底部加载更多
+    loadMore() {
+      this.$emit("loadMore");
+    },
+    //自定义下拉刷新控件被下拉
+    handlePulling(e) {
+      const self = this;
+      let y = e.detail.deltaY;
+      this.$emit("updateIsTab", true);
+      if (y >= 80) {
+        self.refresherText = "释放刷新";
+      } else {
+        self.refresherText = "下拉刷新";
+      }
+    },
+    //自定义下拉刷新被触发
+    handleRefresh() {
+      const self = this;
+      // self.goTop();
+      self.$emit("onfresher");
+      self.refresherText = "正在加载";
+    },
+    //自定义下拉刷新被复位
+    handleRefreshStore() {
+      const self = this;
+      self.refresherText = "下拉刷新";
+    },
+    //自定义下拉刷新被中止
+    handleRefreshAbort() {
+      const self = this;
+      self.refresherText = "下拉刷新";
+      self.isTrigger = false;
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+// 隐藏scroll-view滚动条
+::v-deep.scs-scroll {
+  height: 99.9%;
+  .uni-scroll-view::-webkit-scrollbar {
+    width: 0;
+    height: 0;
+    display: none;
+  }
+}
+
+
+
+
+
+::v-deep .scs-scroll-refresher {
+  height: 100%;
+  width: 100vw;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 10rpx;
+  color: #999999;
+  font-size: 30rpx;
+  text-align: center;
+}
+</style>

+ 8 - 0
main.js

@@ -11,6 +11,14 @@ Vue.prototype.$scsUtils = scsUtils;
 Vue.prototype.utils = utils;
 import store from './store'
 import {isLoginCourse} from './utils/common.js'
+import ScsScrollView from '@/components/public/scs-scroll-view.vue'
+import ScsScrollTop from '@/components/public/scs-scroll-top.vue'
+import ScsScrollNavbar from '@/components/public/scs-scroll-navbar.vue'
+import ScsScrollIconbar from '@/components/public/scs-scroll-iconbar.vue'
+Vue.component('scs-scroll-top', ScsScrollTop)
+Vue.component('scs-scroll-view', ScsScrollView)
+Vue.component('scs-scroll-navbar', ScsScrollNavbar)
+Vue.component('scs-scroll-iconbar', ScsScrollIconbar)
 Vue.prototype.$isLoginCourse = isLoginCourse
 import {
 	setData

+ 119 - 30
pages/home/index.vue

@@ -1,3 +1,10 @@
+<!--
+ * @Author: jmy
+ * @Date: 2026-01-06 12:02:41
+ * @LastEditors: Please set LastEditors
+ * @LastEditTime: 2026-01-06 15:02:20
+ * @Description: 首页
+-->
 <template>
 	<view class="w-all h-all flex flex-column bg-F5F7FA">
 		<image src="/static/images/yuexuan_home_top_bg.png" class="absolute top-0 left-0 zi-1 w-all h-266"></image>
@@ -17,36 +24,97 @@
 		</uni-nav-bar>
 		<!-- 搜索栏 -->
 		<HomeSearch @onSearch="onSearch" />
-		<!-- tab栏 -->
-		<view class="scs-nav-bar px-12 mt-11 rounded-8 zi-10">
-			<view class="flex items-center relative ">
-				<scsScrollNavbar :tabsData="tabsData" :tabCurrentIndex="tabCurrentIndex" nameKey="name"
-					key="tabCurrentIndex" @tabChange="tabChange" />
-				<image class="w-32 h-32  absolute bottom-2.5 right-40 zi-10" src="/static/images/white_gradient_bg.png">
-				</image>
-				<view class="flex items-center justify-center">
-					<image class="w-16 h-16 ml-5 mr-12" src="/static/images/product_section_icon.png"></image>
-					<image class="w-16 h-16 mr-7" src="/static/images/home_filter_icon.png"></image>
-				</view>
-			</view>
-			<homeMenu :autoplay="false" :swiperList="menusData" />
-		</view>
-		<!-- 商品栏 -->
-		<homeProduct />
-		<!-- tab栏 -->
-		<view class="bg-white w-all flex-1 mt-10 rounded-t-8 px-28">
-			<scsScrollIconbar :tabsData="tabsProduct" :tabCurrentIndex="tabsProductIndex" nameKey="name"
-				key="tabsProductIndex" @tabChange="tabProductChange"  />
+		<view class="zi-10 flex-1 overflow-auto">
+			<ScsScrollView ref="fresher" refresherBackground="transparent" :refresherEnabled="false"
+				@onfresher="onfresher" @loadMore="loadMore" @handleScroll="(flag) => isShowToTop = flag">
+				<template v-slot:list>
+					<!-- tab栏 -->
+					<view class="scs-nav-bar px-12 mt-11 rounded-8">
+						<view class="flex items-center relative ">
+							<ScsScrollNavbar :tabsData="tabsData" :tabCurrentIndex="tabCurrentIndex" nameKey="name"
+								key="tabCurrentIndex" @tabChange="tabChange" />
+							<image class="w-32 h-32  absolute bottom-2.5 right-40"
+								src="/static/images/white_gradient_bg.png">
+							</image>
+							<view class="flex items-center justify-center">
+								<image class="w-16 h-16 ml-5 mr-12" src="/static/images/product_section_icon.png">
+								</image>
+								<image class="w-16 h-16 mr-7" src="/static/images/home_filter_icon.png"></image>
+							</view>
+						</view>
+						<HomeMenu :autoplay="false" :swiperList="menusData" />
+					</view>
+					<!-- 商品栏 -->
+					<HomeProduct />
+					<!-- tab栏 -->
+
+					<view class="tab-goods w-all mt-10 rounded-t-8 px-28">
+						<ScsScrollIconbar :tabsData="tabsProduct" :tabCurrentIndex="tabsProductIndex" nameKey="name"
+							key="tabsProductIndex" @tabChange="tabProductChange" />
+					</view>
+					<view class="px-12 mt-14">
+						<view class="bg-white  pb-10 rounded-8 overflow-hidden">
+							<image class="w-all h-195"
+								src="https://img1.baidu.com/it/u=2172818577,3783888802&fm=253&app=138&f=JPEG?w=800&h=1422">
+							</image>
+							<view
+								class="goods-count px-12 flex items-center justify-between text-white w-all h-32 rounded-6 mt--15 zi-2 relative">
+								<view class="fw-500 fs-18">热卖爆品</view>
+								<view class="fw-400 fs-13">已售1000件</view>
+							</view>
+							<view class="fw-500 fs-13 text-333333 mt-11 px-12">
+								[广州康和药业 GKH PHARMACEUTICAL LTD]盐酸多西环素片 0.1g*12片 1盒装
+							</view>
+							<view class="fw-400 fs-12 text-D46C0D mt-7 px-12">
+								处方药须凭处方在药师指导下购买和使用
+							</view>
+							<view class="flex items-center justify-between mt-7 px-12">
+								<view class="flex items-center gap-4 text-FF4B33 fs-11">
+									<view class="px-4 ph-2 border border-FFA599 rounded-2 ">9.5折</view>
+									<view class="px-4 ph-2 border border-FFA599 rounded-2 ">限购1份</view>
+								</view>
+								<view class="flex items-center gap-2">
+									<view class="fs-12 text-FA341E fw-400">领卷</view>
+									<image class="w-12 h-12" src="/static/images/home/sdyhzq_bg@2x.png"></image>
+								</view>
+							</view>
+							<view class="flex items-center justify-between mt-8 px-12">
+								<view class="flex items-end gap-8">
+									<view class="text-FA341E ">
+										<text class="fs-12 fw-600">¥</text>
+										<text class="fs-24 fw-600">105</text>
+										<text class="fs-15 fw-600">.36</text>
+									</view>
+									<view class="text-999999 fs-13 fw-400 text-line-through pb-3">
+										¥128.00
+									</view>
+								</view>
+								<view class="flex items-center w-110 h-34 rounded-34 overflow-hidden">
+									<view class="w-44 h-all flex items-center justify-center bg-38D97D">
+										<image class="w-20 h-20" src="/static/images/home/shopping_car_icon24@2x.png">
+										</image>
+									</view>
+									<view
+										class="flex-1 h-all flex items-center justify-center bg-02B176 fw-500 text-white fs-14">
+										去购买</view>
+								</view>
+							</view>
+						</view>
+					</view>
+
+					<u-gap height="15"></u-gap>
+				</template>
+			</ScsScrollView>
+			<ScsScrollTop :isShowToTop="isShowToTop" @goTop="$refs.fresher.goTop" />
 		</view>
 
 
 	</view>
 </template>
 <script>
-import scsScrollNavbar from '@/components/public/scs-scroll-navbar.vue'
-import scsScrollIconbar from '@/components/public/scs-scroll-iconbar.vue'
-import homeMenu from './components/home-menu.vue'
-import homeProduct from './components/home-product.vue'
+
+import HomeMenu from './components/home-menu.vue'
+import HomeProduct from './components/home-product.vue'
 import HomeSearch from './components/home-search.vue'
 import {
 	getMenu,
@@ -59,11 +127,9 @@ import {
 } from '@/api/index'
 export default {
 	components: {
-		scsScrollNavbar,
-		homeMenu,
-		homeProduct,
+		HomeMenu,
+		HomeProduct,
 		HomeSearch,
-		scsScrollIconbar,
 	},
 	data() {
 		return {
@@ -82,10 +148,11 @@ export default {
 				{ name: '营养保健' },
 			],
 			tabsProductIndex: 0,
+			goodList: [1, 2],
+			isShowToTop: false,
 		}
 	},
 	onLoad() {
-
 	},
 	onShow() {
 		this.getMenuData();
@@ -108,7 +175,6 @@ export default {
 			const { code, data, msg } = await getMenu();
 			if (code == 200) {
 				this.menusData = this.$scsUtils.splitArrayIntoSubarrays(data, 5) || []
-				console.log("---------11111111-------", this.menusData)
 			} else {
 				uni.showToast({
 					title: msg,
@@ -116,6 +182,19 @@ export default {
 				})
 			}
 		},
+		// 下拉刷新
+		onfresher() {
+			const self = this;
+			self.$refs.fresher.isTrigger = true;
+			setTimeout(() => {
+				self.$refs.fresher.refresherText = "刷新成功";
+				self.$refs.fresher.isTrigger = false;
+			}, 500);
+		},
+		// 上拉加载更多
+		loadMore() {
+
+		}
 	}
 }
 </script>
@@ -124,7 +203,17 @@ export default {
 	width: calc(100vw - 140rpx) !important;
 }
 
+
+
 .scs-nav-bar {
 	background: linear-gradient(180deg, #FFFFFF 0%, #FFFFFF 49.52%, #F5F7FA 100%);
 }
+
+.tab-goods {
+	background: linear-gradient(180deg, #FFFFFF 0%, #FFFFFF 49.52%, #F5F7FA 100%);
+}
+
+.goods-count {
+	background: linear-gradient(to right, #FA341E, #F4A007)
+}
 </style>

BIN
static/images/home/purple_right_arrow_right_icon12@2x.png


BIN
static/images/home/shopping_car_icon24@2x.png