uniapp-通知滚动组件+轮播图组件封装-案例

通知组件

代码封装

javascript 复制代码
<template>
  <!-- 通知 -->
  <view class="notice">
    <image
      class="notice-icon"
      src="@/assets/images/tz.png"
      mode="aspectFit"
    />
    <view class="notice-ticker">
      <view
        class="ticker-list"
        :class="{ 'is-no-transition': noTransition }"
        :style="{ transform: `translateY(-${current * 40}rpx)` }"
      >
        <view
          v-for="(item, index) in tickerList"
          :key="index"
          class="ticker-item"
          @tap="onItemClick(item)"
        >
          <text class="ticker-text">{{ item.title }}</text>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref, computed, watch, onMounted, onUnmounted } from "vue";

const props = defineProps({
  notices: {
    type: Array,
    default: () => [],
  },
});

const emit = defineEmits(["onChange"]);

/* 点击某条通知 */
const onItemClick = (item) => {
  console.log(item);
  emit("onChange", item);
};

const tickerList = computed(() => {
  if (!props.notices.length) return [];
  return [...props.notices, props.notices[0]];
});

const current = ref(0);
const noTransition = ref(false);
const DELAY = 4000;
let timer = null;

const tick = () => {
  if (current.value >= tickerList.value.length - 1) {
    noTransition.value = true;
    current.value = 0;
    setTimeout(() => {
      noTransition.value = false;
    }, 50);
  } else {
    current.value++;
  }
};

const start = () => {
  stop();
  if (props.notices.length > 1) {
    timer = setInterval(tick, DELAY);
  }
};

const stop = () => {
  if (timer) {
    clearInterval(timer);
    timer = null;
  }
};

watch(() => props.notices, start);

onMounted(start);
onUnmounted(stop);
</script>

<style lang="scss" scoped>
.notice {
  display: flex;
  align-items: center;
  gap: 6rpx;
  height: 60rpx;
  padding: 0 16rpx;
  box-sizing: border-box;
  background: #f7faff;
  border-radius: 12rpx;

  .notice-icon {
    width: 48rpx;
    height: 48rpx;
    flex-shrink: 0;
  }

  .notice-ticker {
    flex: 1;
    min-width: 0;
    height: 40rpx;
    padding-left: 6rpx;
    overflow: hidden;

    .ticker-list {
      display: flex;
      flex-direction: column;
      transition: transform 0.4s ease;

      &.is-no-transition {
        transition: none;
      }

      .ticker-item {
        height: 40rpx;
        line-height: 40rpx;

        .ticker-text {
          display: block;
          font-size: 22rpx;
          color: rgba(0, 104, 254, 0.9);
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
        }
      }
    }
  }
}
</style>

使用

html 复制代码
<!-- 通知 -->
<notice :notices="noticeList" />

/* 通知 */
const noticeList = [
  { id: 1, title: "沪锡大涨激活工业金属涨价逻辑,这背后是全球增长预期" },
  { id: 2, title: "BTC 站上 68000 美元关口,市场看多情绪持续升温撒旦飞洒飞洒发生发士大夫撒飞洒发生" },
  { id: 3, title: "ETH 生态活跃度持续提升,链上交易量创近期新高" },
];

轮播图组件

代码封装

html 复制代码
<template>
  <!-- 轮播图 -->
  <view class="banner-wrap">
    <uni-swiper-dot
      :info="banners"
      :current="current"
      mode="round"
      field="content"
      :dotsStyles="dotStyles"
      @clickItem="clickItem"
    >
      <swiper
        class="banner-swiper"
        :current="current"
        @change="change"
        circular
        autoplay
        :interval="8000"
        :duration="300"
      >
        <swiper-item v-for="(item, index) in banners" :key="index">
          <image class="banner-img" :src="item.src" mode="scaleToFill" @click="handleClick(item)" />
        </swiper-item>
      </swiper>
    </uni-swiper-dot>
  </view>
</template>

<script setup>
import { ref } from "vue";

const props = defineProps({
  banners: {
    type: Array,
    default: () => [],
  },
});

const current = ref(0);

const emit = defineEmits(["click"]);

const dotStyles = {
  backgroundColor: "#0068FE",
  selectedBackgroundColor: "#0068FE",
  border: "none",
  selectedBorder: "none",
};

const change = (e) => {
  current.value = e.detail.current;
};

const clickItem = (e) => {
  current.value = e;
};

const handleClick = (item) => {
  if (!item.type) return;
  if (item.type === "page") {
    goPath(item.url);
  } else if (item.type === "link") {
    const title = item.title || "";
    goPath(
      `/pages/webview/webview?url=${encodeURIComponent(item.url)}&title=${encodeURIComponent(title)}`
    );
  }
};

const goPath = (path) => {
  uni.navigateTo({
    url: path,
    fail: () => uni.switchTab({ url: path }),
  });
};
</script>

<style lang="scss" scoped>
.banner-wrap {
  padding: 24rpx 0 0;

  .banner-swiper {
    width: 100%;
    height: 176rpx;
    border-radius: 16rpx;
    overflow: hidden;
  }

  .banner-img {
    width: 100%;
    height: 176rpx;
    display: block;
  }
}
</style>

使用

html 复制代码
<banner :banners="banners" />

/* 轮播图 */
const banners = [
  { src: "/assets/images/lbt.png", type: "page", url: "/pages/asset/asset" },
  { src: "/assets/images/lbt.png", type: "link", url: "https://www.xxxx.com" },
  { src: "/assets/images/lbt.png" },
];
相关推荐
2501_9159214316 小时前
appuploader-cli 命令行上传 IPA 到 App Store Connect upload CI 集成
android·ci/cd·小程序·https·uni-app·iphone·webview
结网的兔子1 天前
【前端开发】UniApp 项目地图选型与Web-APP跨端迁移方案
前端·uni-app
2501_916007472 天前
Bundle ID 注册与管理 App ID 创建指南 命名规则 权限开关与删除注意事项
android·ios·小程序·https·uni-app·iphone·webview
PedroQue992 天前
uni-app路由插件化:解锁高效开发新姿势
前端·uni-app
2501_915909062 天前
iOS 证书创建与管理 类型限制 p12 密码与云备份实操
android·ios·小程序·https·uni-app·iphone·webview
anyup2 天前
【2026年8月】uView Pro 千星,还拿到了 GVP
前端·uni-app·github
andr_gale3 天前
02_uniapp自定义上凸效果的底部TabBar
前端·javascript·uni-app·移动端
不爱说话郭德纲3 天前
uni-app x iOS 扫码模糊怎么办?AVFoundation 灰尘级别摄像头助你超广角聚焦
前端·uni-app·app
CHB4 天前
uni-app x 蒸汽模式 性能测试基准报告 Benchmark【Android版】
android·ios·uni-app