注意:在模拟器用鼠标滚动是不会切换光标的,因为使用的是触摸滑动。【自定义类型贴在最后了】

script 部分如下:

import { onMounted } from 'vue'import type { orderDetail } from '@/types/category'import type { mainArr } from '@/types/main-arr'import { nextTick, ref } from 'vue'import { getCurrentInstance } from 'vue'//页面加载onMounted(async () => {  await getListData()})//#region 左右联动菜单const instance = getCurrentInstance()//分类列表数据--可以多写几个const categoryList = [  {    id: '1',    name: '即食',    picture: 'el-icon-chicken',    children: [      {        deveicId: 1,        memo: '泸州老窖特曲浓香型白酒',        discount: 100,        id: 2,        inventory: 3,        goodsName: '草莓',        orderNum: 1,        goodsPicPath: '/static/images/locate.png',        price: 8.0,        orderMoney: 0,        oldPrice: 0,        isLimitPromotion: false,      },    ],  },]const mainArray = ref([]) //右侧显示内容(标题+文本)const topArr = ref([]) //每个锚点与到顶部距离const leftIndex = ref(0) //左边光标indexconst isMainScroll = ref(false) // 是否touch到右侧const scrollInto = ref('') //锚点/* 获取列表数据 */const getListData = async () => {  const left = ref([])  const main = ref([])  categoryList.forEach((item) => {    left.value.push(`${item.id + 1}类商品`)    let list: orderDetail[] = []    // for (let i = 0; i  {      list.push(itm)    })    main.value.push({      title: item.name,      list,    })  })  mainArray.value = main.value  await nextTick(() => {    setTimeout(() => {      getElementTop()    }, 10)  })}//获取距离顶部的高度const getScrollTop = (selector: string) => {  const top = new Promise((resolve, reject) => {    let query = uni.createSelectorQuery().in(instance)    query      .select(selector)      .boundingClientRect((data: any) => {        resolve(data.top)      })      .exec()  })  return top}/* 获取元素顶部信息 */const getElementTop = async () => {  /* Promise 对象数组 */  let p_arr: number[] = []  /* 遍历数据,创建相应的 Promise 数组数据 */  for (let i = 0; i  {    let top = res    // #ifdef H5    top += 43 //因固定提示块的需求,H5的默认标题栏是44px    // #endif    /* 所有节点信息返回后调用该方法 */    Promise.all(p_arr).then((data) => {      topArr.value = data    })  })}/* 主区域滚动监听 */const mainScroll = (e: { detail: { scrollTop: any } }) => {  if (!isMainScroll.value) {    return  }  let top = e.detail.scrollTop  let index = -1  if (top >= topArr.value[topArr.value.length - 1]) {    index = topArr.value.length - 1  } else {    index = topArr.value.findIndex((item: any, index: number) => {      return topArr.value[index + 1] >= top    })  }  leftIndex.value = index  {  isMainScroll.value = true}/* 左侧导航点击 */const leftTap = (e: any) => {  let index = e.currentTarget.dataset.index  isMainScroll.value = false  leftIndex.value = Number(index)  scrollInto.value = `item-${index}`}//#endregion

template部分如下:

                                              {{ item.name }}                                                                          {{ item.title }}                                                                                                                      {{ goods.goodsName }}                    {{ goods.memo }}                    限时优惠                                                                                                      ¥                        {{ goods.price.toFixed(2) }}                                            <view                                               v-if="goods.oldPrice != 0 && goods.price                         ¥                        {{ goods.oldPrice!.toFixed(2) }}                                                                                                                                              

scss样式:

page {  height: 100%;  overflow: hidden;  background: #f6f6f6;}.content {  .list_box {    display: flex;    flex-direction: row;    flex-wrap: nowrap;    justify-content: flex-start;    align-items: flex-start;    align-content: flex-start;    font-size: 28rpx;    height: calc(100vh - 380rpx);    .left {      width: 200rpx;      text-align: center;      background-color: #f6f6f6;      line-height: 100rpx;      box-sizing: border-box;      font-size: 32rpx;      color: #666;      height: 100%;      .item {        position: relative;        &:not(:first-child) {          margin-top: 1px;          &::after {            content: '';            display: block;            height: 0;            border-top: #d6d6d6 solid 1px;            width: 620upx;            position: absolute;            top: -1px;            right: 0;            transform: scaleY(0.5);          }        }        &.active,        &:active {          color: #000000;          background-color: #fff;        }      }    }    .main {      height: 100%;      background-color: #fff;      padding: 0 20rpx;      flex-grow: 1;      box-sizing: border-box;      .item-first-box {        position: relative;        padding-top: 20rpx;        width: 100%;      }      .item-first-title {        position: relative;        margin-top: 20rpx;      }      .item-first-content {        position: relative;        padding-top: 20rpx;        margin-bottom: 20rpx;        height: 180rpx;        .goods-image-box {          width: 200rpx;          position: relative;          float: left;          z-index: 999;        }        .goods-image {          position: relative;          width: 170rpx;          height: 170rpx;          border-radius: 10rpx;        }        .goods-inventory {          width: 170rpx;          height: 36rpx;          border-radius: 0 0 10rpx 10rpx;          margin-right: 20rpx;          opacity: 60%;          background-color: #5c9888;          position: absolute;          bottom: 0rpx;          left: 0;          font-size: 24rpx;          color: white;          text-align: center;        }        .goods-inventory-notenough {          position: absolute;          width: 170rpx;          text-align: center;          font-size: 22rpx;          bottom: 4rpx;          left: 0;          color: white;        }        .goods-inventory-zero {          position: absolute;          width: 170rpx;          text-align: center;          font-size: 22rpx;          bottom: 4rpx;          left: 0;          color: white;        }      }      .meta {        position: relative;        display: inline;      }      .name {        height: 40rpx;        font-size: 26rpx;        color: #444;        font-weight: bold;      }      .memo {        display: flex;        margin-top: 6rpx;        font-size: 22rpx;        color: #888;      }      .activity-tips {        display: flex;        margin-top: 15rpx;        font-size: 22rpx;        background-color: #ffd8cb;        color: #fc6d3f;        border-radius: 10rpx;        padding-left: 10rpx;        padding-right: 10rpx;        width: 110rpx;      }      .type {        line-height: 1.8;        padding: 0 15rpx;        font-size: 24rpx;        align-self: flex-start;        border-radius: 4rpx;        color: #888;        background-color: #f7f7f8;      }      .price {        display: flex;        position: relative;        margin-top: 16rpx;        font-size: 24rpx;        .actual {          color: #444;          margin-top: 2rpx;          margin-left: 0rpx;          float: left;        }        .oldprice {          display: inline-block;          font-size: 24rpx;          margin-top: 2rpx;          color: #999;          margin-left: 10rpx;          text-decoration: line-through;        }        .symbol {          font-size: 24rpx;        }        .quantity {          position: absolute;          top: 0;          right: 0;          font-size: 24rpx;          color: #444;          z-index: 999999999;        }      }      .right-scroll:last-child {        border-bottom: 0;      }    }    .scroll {      height: 100%;    }  }}

category.d.ts

/** 通用商品类型 */export type GoodsItem = {  deveicId?: number  /** 商品描述 */  memo: string  /** 商品折扣 */  discount: number  /** id */  id: number  /**库存 */  inventory: number  /** 商品名称 */  goodsName: string  /** 商品已下单数量 */  orderNum: number  /** 商品图片 */  goodsPicPath: string  /** 商品价格 */  price: number  /** 商品原价格 */  oldPrice?: number  /**促销id */  promotionDetialId?: number  /**是否是限时优惠 */  isLimitPromotion: boolean  orderMoney:number  oldPrice:number}

main-arr.d.ts

export type main = {  title: string  list: orderDetail[]}export type mainArr = main[]