刘嘉威
2022-10-27 796f1fce7951ca1dd4ba5785f25d86bf5ca9dcd5
feat:增加无缝轮播组件 增加左下 右下组件
10个文件已修改
5个文件已添加
1076 ■■■■■ 已修改文件
components.d.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/modules/index.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/main.scss 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/variable.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/empty-com/empty-com.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/empty-com/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/seamless-scroll/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/seamless-scroll/seamless-scroll.vue 407 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mock/mock-index.ts 81 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/stores/setting/setting.ts 53 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index/index.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index/left-bottom.vue 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index/right-bottom.vue 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/setting.vue 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
types/global.d.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components.d.ts
@@ -12,10 +12,12 @@
    ElDrawer: typeof import('element-plus/es')['ElDrawer']
    ElRadio: typeof import('element-plus/es')['ElRadio']
    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
    EmptyCom: typeof import('./src/components/empty-com/empty-com.vue')['default']
    ItemWrap: typeof import('./src/components/item-wrap/item-wrap.vue')['default']
    MessageContent: typeof import('./src/components/Plugins/MessageContent/index.vue')['default']
    RouterLink: typeof import('vue-router')['RouterLink']
    RouterView: typeof import('vue-router')['RouterView']
    ScaleScreen: typeof import('./src/components/scale-screen/scale-screen.vue')['default']
    SeamlessScroll: typeof import('./src/components/seamless-scroll/seamless-scroll.vue')['default']
  }
}
src/api/modules/index.ts
@@ -4,8 +4,8 @@
    "centerMap":"/bigscreen/centerMap",
    "centerBottom":"/bigscreen/installationPlan",
    // 'big3':"/bigscreen/sbtx", //设备提醒
    'leftBottom':"/bigscreen/leftBottom", //坐下
    'rightTop':"/bigscreen/alarmNum", //报警次数
    // 'big5':'/bigscreen/ssyj',//实时预警
    'rightBottom':'/bigscreen/rightBottom',//右下
    'rightCenter':'/bigscreen/ranking',// 报警排名
}
src/assets/css/main.scss
@@ -51,4 +51,48 @@
    .el-message__content {
      color: var(--yh-text-color-primary);
    }
  }
  .beautify-scroll-def {
    overflow-y: auto;
    &::-webkit-scrollbar-thumb {
      //滚动条的设置
      background-color: rgba(14, 59, 150, 0);
      background-clip: padding-box;
      border-radius: 4px;
    }
    &:hover {
      &::-webkit-scrollbar-thumb {
        //滚动条的设置
        background-color: rgba(14, 59, 150, 0.5);
        background-clip: padding-box;
        border-radius: 4px;
      }
    }
    &::-webkit-scrollbar-track-piece {
      //滚动条凹槽的颜色,还可以设置边框属性
      background-color: transparent;
    }
    &::-webkit-scrollbar {
      //滚动条的宽度
      width: 8px;
      height: 8px;
    }
    &::-webkit-scrollbar-thumb:hover {
      background-color: rgba(14, 59, 150, .8);
    }
  }
  .text-content {
    width: 100%;
    display: flex;
    min-height: calc(100% - 60px);
    justify-content: space-between;
  }
src/assets/css/variable.scss
@@ -0,0 +1 @@
$primary-color: #1890ff;
src/components/empty-com/empty-com.vue
New file
@@ -0,0 +1,9 @@
<script setup lang="ts"></script>
<template>
  <div>
    <slot></slot>
  </div>
</template>
<style scoped lang="scss"></style>
src/components/empty-com/index.ts
New file
@@ -0,0 +1,2 @@
import EmptyCom from "./empty-com.vue"
export default EmptyCom
src/components/seamless-scroll/index.ts
New file
@@ -0,0 +1,2 @@
import SeamlessScroll from "./seamless-scroll.vue"
export default SeamlessScroll
src/components/seamless-scroll/seamless-scroll.vue
New file
@@ -0,0 +1,407 @@
<script setup lang="ts">
import {
  computed,
  defineComponent,
  onBeforeMount,
  onMounted,
  ref,
  watch,
  nextTick,
} from "vue";
import type { CSSProperties } from "vue";
import throttle from "lodash/throttle";
type propsType = {
  modelValue?: boolean;
  list: Array<any>;
  step?: number;
  limitScrollNum?: number;
  hover?: boolean;
  direction?: string;
  singleHeight?: number;
  singleWidth?: number;
  singleWaitTime?: number;
  isRemUnit?: boolean;
  isWatch?: boolean;
  delay?: number;
  ease?: any;
  count?: number;
  copyNum?: number;
  wheel?: boolean;
  singleLine?: boolean;
};
const props = withDefaults(defineProps<propsType>(), {
  // 是否开启自动滚动
  modelValue: true,
  // 原始数据列表
  list: () => [],
  // 步进速度,step 需是单步大小的约数
  step: 1,
  // 开启滚动的数据量
  limitScrollNum: 3,
  // 是否开启鼠标悬停
  hover: false,
  // 控制滚动方向
  direction: "up",
  // 单步运动停止的高度
  singleHeight: 0,
  // 单步运动停止的宽度
  singleWidth: 0,
  // 单步停止等待时间 (默认值 1000ms)
  singleWaitTime: 1000,
  // 是否开启 rem 度量
  isRemUnit: false,
  // 开启数据更新监听
  isWatch: true,
  // 动画时间
  delay: 0,
  // 动画方式
  ease: "ease-in",
  // 动画循环次数,-1 表示一直动画
  count: -1,
  // 拷贝几份滚动列表
  copyNum: 1,
  // 开启鼠标悬停时支持滚轮滚动
  wheel: false,
  // 启用单行滚动
  singleLine: false,
});
interface Emits {
  (event: "count", _count: number): void;
  (event: "stop", _count: number): void;
}
const emit = defineEmits<Emits>();
const scrollRef = ref(null);
const slotListRef = ref<HTMLDivElement | null>(null);
const realBoxRef = ref<HTMLDivElement | null>(null);
const reqFrame = ref<number | null>(null);
const singleWaitTimeout = ref<TimeProp | null>(null);
const realBoxWidth = ref(0);
const realBoxHeight = ref(0);
const xPos = ref(0);
const yPos = ref(0);
const isHover = ref(false);
const _count = ref(0);
const isScroll = computed(() =>
  props.list ? props.list.length >= props.limitScrollNum : false
);
const realBoxStyle = computed(() => {
  return {
    width: realBoxWidth.value ? `${realBoxWidth.value}px` : "auto",
    transform: `translate(${xPos.value}px,${yPos.value}px)`,
    transition: `all ${
      typeof props.ease === "string"
        ? props.ease
        : "cubic-bezier(" +
          props.ease.x1 +
          "," +
          props.ease.y1 +
          "," +
          props.ease.x2 +
          "," +
          props.ease.y2 +
          ")"
    } ${props.delay}ms`,
    overflow: "hidden",
    display: props.singleLine ? "flex" : "block",
  };
});
const isHorizontal = computed(
  () => props.direction == "left" || props.direction == "right"
);
function dataWarm(list: any) {
  if (list && typeof list !== "boolean" && list.length > 100) {
    console.warn(
      `数据达到了${list.length}条有点多哦~,可能会造成部分老旧浏览器卡顿。`
    );
  }
}
const floatStyle = computed<CSSProperties>(() => {
  return isHorizontal.value
    ? {
        float: "left",
        overflow: "hidden",
        display: props.singleLine ? "flex" : "block",
        flexShrink: props.singleLine ? 0 : 1,
      }
    : { overflow: "hidden" };
});
const baseFontSize = computed(() => {
  return props.isRemUnit
    ? parseInt(
        globalThis.window.getComputedStyle(
          globalThis.document.documentElement,
          null
        ).fontSize
      )
    : 1;
});
const realSingleStopWidth = computed(
  () => props.singleWidth * baseFontSize.value
);
const realSingleStopHeight = computed(
  () => props.singleHeight * baseFontSize.value
);
const step = computed(() => {
  let singleStep: number;
  let _step = props.step;
  if (isHorizontal.value) {
    singleStep = realSingleStopWidth.value;
  } else {
    singleStep = realSingleStopHeight.value;
  }
  if (singleStep > 0 && singleStep % _step > 0) {
    console.error(
      "如果设置了单步滚动,step 需是单步大小的约数,否则无法保证单步滚动结束的位置是否准确。~~~~~"
    );
  }
  return _step;
});
const cancle = () => {
  cancelAnimationFrame(reqFrame.value as number);
  reqFrame.value = null;
};
const animation = (
  _direction: "up" | "down" | "left" | "right",
  _step: number,
  isWheel?: boolean
) => {
  // console.log("animation",_direction,_step,isWheel);
  reqFrame.value = requestAnimationFrame(function () {
    const h = realBoxHeight.value / 2;
    const w = realBoxWidth.value / 2;
    if (_direction === "up") {
      if (Math.abs(yPos.value) >= h) {
        yPos.value = 0;
        _count.value += 1;
        emit("count", _count.value);
      }
      yPos.value -= _step;
    } else if (_direction === "down") {
      if (yPos.value >= 0) {
        yPos.value = h * -1;
        _count.value += 1;
        emit("count", _count.value);
      }
      yPos.value += _step;
    } else if (_direction === "left") {
      if (Math.abs(xPos.value) >= w) {
        xPos.value = 0;
        _count.value += 1;
        emit("count", _count.value);
      }
      xPos.value -= _step;
    } else if (_direction === "right") {
      if (xPos.value >= 0) {
        xPos.value = w * -1;
        _count.value += 1;
        emit("count", _count.value);
      }
      xPos.value += _step;
    }
    if (isWheel) {
      return;
    }
    let { singleWaitTime } = props;
    if (singleWaitTimeout.value) {
      clearTimeout(singleWaitTimeout.value);
    }
    if (!!realSingleStopHeight.value) {
      if (Math.abs(yPos.value) % realSingleStopHeight.value < _step) {
        singleWaitTimeout.value = setTimeout(() => {
          move();
        }, singleWaitTime);
      } else {
        move();
      }
    } else if (!!realSingleStopWidth.value) {
      if (Math.abs(xPos.value) % realSingleStopWidth.value < _step) {
        singleWaitTimeout.value = setTimeout(() => {
          move();
        }, singleWaitTime);
      } else {
        move();
      }
    } else {
      move();
    }
  });
};
const move = () => {
  cancle();
  if (isHover.value || !isScroll.value || _count.value === props.count) {
    emit("stop", _count.value);
    _count.value = 0;
    return;
  }
  animation(
    props.direction as "up" | "down" | "left" | "right",
    step.value,
    false
  );
};
const initMove = () => {
  dataWarm(props.list);
  if (isHorizontal.value) {
    let slotListWidth = (slotListRef.value as HTMLDivElement).offsetWidth;
    slotListWidth = slotListWidth * 2 + 1;
    realBoxWidth.value = slotListWidth;
  }
  if (isScroll.value) {
    realBoxHeight.value = (realBoxRef.value as HTMLDivElement).offsetHeight;
    if (props.modelValue) {
      move();
    }
  } else {
    cancle();
    yPos.value = xPos.value = 0;
  }
  // console.log("initMove","isHorizontal",isHorizontal.value,"isScroll",isScroll.value,realBoxRef.value?.offsetHeight);
};
const startMove = () => {
  isHover.value = false;
  move();
};
const stopMove = () => {
  isHover.value = true;
  if (singleWaitTimeout.value) {
    clearTimeout(singleWaitTimeout.value);
  }
  cancle();
};
const hoverStop = computed(
  () => props.hover && props.modelValue && isScroll.value
);
const throttleFunc = throttle((e: WheelEvent) => {
  cancle();
  const singleHeight = !!realSingleStopHeight.value
    ? realSingleStopHeight.value
    : 15;
  if (e.deltaY < 0) {
    animation("down", singleHeight, true);
  }
  if (e.deltaY > 0) {
    animation("up", singleHeight, true);
  }
}, 30);
const onWheel = (e: WheelEvent) => {
  throttleFunc(e);
};
const reset = () => {
  cancle();
  isHover.value = false;
  initMove();
};
const Reset = () => {
  reset();
};
defineExpose({
  Reset,
});
watch(
  () => props.list,
  () => {
    if (props.isWatch) {
      nextTick(() => {
        reset();
      });
    }
  },
  {
    deep: true,
  }
);
watch(
  () => props.modelValue,
  (newValue) => {
    if (newValue) {
      startMove();
    } else {
      stopMove();
    }
  }
);
watch(
  () => props.count,
  (newValue) => {
    if (newValue !== 0) {
      startMove();
    }
  }
);
onBeforeMount(() => {
  cancle();
  clearTimeout(singleWaitTimeout.value as unknown as number);
});
onMounted(() => {
  if (isScroll.value) {
    initMove();
  }
});
</script>
<template>
  <div
    v-if="props.wheel && props.hover"
    ref="realBoxRef"
    :style="realBoxStyle"
    @mouseenter="
      () => {
        hoverStop && stopMove();
      }
    "
    @mouseleave="
      () => {
        hoverStop && startMove();
      }
    "
    @wheel="
      (e) => {
        hoverStop && onWheel(e);
      }
    "
  >
    <div ref="slotListRef" :style="floatStyle">
      <slot></slot>
    </div>
    <div :style="floatStyle">
      <slot></slot>
    </div>
  </div>
  <div
    v-else
    :style="realBoxStyle"
    ref="realBoxRef"
    @mouseenter="
      () => {
        hoverStop && stopMove();
      }
    "
    @mouseleave="
      () => {
        hoverStop && startMove();
      }
    "
  >
    <div ref="slotListRef" :style="floatStyle">
      <slot></slot>
    </div>
    <div :style="floatStyle">
      <slot></slot>
    </div>
  </div>
</template>
<style scoped lang="scss"></style>
src/mock/mock-index.ts
@@ -57,6 +57,31 @@
            return a
        }
    },
    //左下
    {
        url: "/bigscreen/leftBottom",
        type: "get",
        response: () => {
            const a = Mock.mock({
                success: true,
                data: {
                    "list|20": [
                        {
                            provinceName: "@province()",
                            cityName: '@city()',
                            countyName: "@county()",
                            createTime: "@datetime('yyyy-MM-dd HH:mm:ss')",
                            deviceId: "6c512d754bbcd6d7cd86abce0e0cac58",
                            "gatewayno|+1": 10000,
                            "onlineState|1": [0, 1],
                        }
                    ]
                }
            })
            return a
        }
    },
    //右上
    {
        url: "/bigscreen/alarmNum",
@@ -82,23 +107,51 @@
        url: "/bigscreen/ranking",
        type: "get",
        response: () => {
            let num =Mock.mock({"list|80":[{ value:"@integer(50,1000)",name:"@city()"}]}).list
            let num = Mock.mock({ "list|80": [{ value: "@integer(50,1000)", name: "@city()" }] }).list
            //   console.log("ranking",num);
              let newNum:any =[],numObj:any ={}
              num.map((item:any )=>{
                if(!numObj[item.name] && newNum.length<8){
                    numObj[item.name] =true
            let newNum: any = [], numObj: any = {}
            num.map((item: any) => {
                if (!numObj[item.name] && newNum.length < 8) {
                    numObj[item.name] = true
                    newNum.push(item)
                }
              })
              let arr = newNum.sort((a:any ,b:any )=>{
                return b.value-a.value
              })
              let a ={
                  success:true,
                  data:arr
              }
              return a
            })
            let arr = newNum.sort((a: any, b: any) => {
                return b.value - a.value
            })
            let a = {
                success: true,
                data: arr
            }
            return a
        }
    },
    //右下
    {
        url: "/bigscreen/rightBottom",
        type: "get",
        response: () => {
            const a = Mock.mock({
                success: true,
                data: {
                    "list|40": [{
                        alertdetail: "@csentence(5,10)",
                        "alertname|1": ["水浸告警", "各种报警"],
                        alertvalue: "@float(60, 200)",
                        createtime: "2022-04-19 08:38:33",
                        deviceid: null,
                        "gatewayno|+1": 10000,
                        phase: "A1",
                        sbInfo: "@csentence(10,18)",
                        "terminalno|+1": 100,
                        provinceName: "@province()",
                        cityName: '@city()',
                        countyName: "@county()",
                    }],
                }
            })
            return a
        }
    },
    //安装计划
src/stores/setting/setting.ts
@@ -1,28 +1,51 @@
import { ref, computed } from 'vue'
import { ref, computed, reactive } from 'vue'
import { defineStore } from 'pinia'
// import { storeToRefs } from 'pinia';
export const useSettingStore = defineStore('setting', () => {
  const settingShow = ref(false);//设置弹窗显隐
  const isScale = ref(false);//是否进行全局适配
  const setSettingShow = (flag:boolean)=>{
    settingShow.value=flag
  const indexConfig = ref({
    leftBottomSwiper: true,//左轮播
    rightBottomSwiper: true,//右下轮播
  })
  const defaultOption = ref({
    step: 4, // 数值越大速度滚动越快
    hover: true, // 是否开启鼠标悬停stop
    wheel: false,//在开启鼠标悬停的情况下是否开启滚轮滚动,默认不开启
    openWatch: true, // 开启数据实时监控刷新dom
    direction: 1, // 0向下 1向上 2向左 3向右
    limitScrollNum: 4, // 开始无缝滚动的数据量 this.dataList.length
    singleHeight: 0, // 单步运动停止的高度(默认值0是无缝不停止的滚动) direction => 0/1
    singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3
    singleWaitTime: 3000 // 单步运动停止的时间(默认值1000ms)
  })
  const setSettingShow = (flag: boolean) => {
    settingShow.value = flag
  }
  const setIsScale = (flag:boolean)=>{
    isScale.value=flag
  const setIsScale = (flag: boolean) => {
    isScale.value = flag
    setSettingData()
  }
  const initSetting=()=>{
    let settingData:any = localStorage.getItem('loftv-settingData')
    if(settingData){
        settingData=JSON.parse(settingData)
        setIsScale(settingData.isScale)
  const setIndexConfig = (Config: any) => {
    indexConfig.value = Config
    localStorage.setItem('loftv-indexConfig', JSON.stringify(indexConfig.value))
  }
  const initSetting = () => {
    let settingData: any = localStorage.getItem('loftv-settingData')
    if (settingData) {
      settingData = JSON.parse(settingData)
      setIsScale(settingData.isScale)
    }
    let settingIndexConfig: any = localStorage.getItem('loftv-indexConfig')
    if (settingIndexConfig) {
      settingIndexConfig = JSON.parse(settingIndexConfig)
      setIndexConfig(settingIndexConfig)
    }
  }
  const setSettingData=()=>{
    localStorage.setItem('loftv-settingData',JSON.stringify({
        isScale:isScale.value
  const setSettingData = () => {
    localStorage.setItem('loftv-settingData', JSON.stringify({
      isScale: isScale.value
    }))
  }
  return { settingShow, setSettingShow,isScale ,setIsScale,initSetting,setSettingData}
  return { settingShow, setSettingShow, isScale, setIsScale, initSetting, setSettingData, defaultOption, indexConfig, setIndexConfig }
})
src/views/index/index.vue
@@ -2,6 +2,7 @@
import ItemWrap from "@/components/item-wrap";
import LeftTop from "./left-top.vue";
import LeftCenter from "./left-center.vue";
import LeftBottom from "./left-bottom.vue";
import CenterMap from "./center-map.vue";
import CenterBottom from "./center-bottom.vue";
import RightTop from "./right-top.vue";
@@ -32,7 +33,7 @@
        title="设备提醒"
        style="padding: 0 10px 16px 10px"
      >
        <!-- <LeftBottom /> -->
        <LeftBottom />
      </ItemWrap>
    </div>
    <div class="contetn_center">
src/views/index/left-bottom.vue
New file
@@ -0,0 +1,228 @@
<script setup lang="ts">
import { currentGET } from "@/api";
import SeamlessScroll from "@/components/seamless-scroll";
import { computed, onMounted, reactive } from "vue";
import { useSettingStore } from "@/stores";
import { storeToRefs } from "pinia";
import EmptyCom from "@/components/empty-com"
const settingStore = useSettingStore();
const { defaultOption,indexConfig } = storeToRefs(settingStore);
const state = reactive<any>({
  list: [],
  defaultOption: {
    ...defaultOption.value,
    singleHeight: 256,
    limitScrollNum: 4,
  },
  scroll: true,
});
const getData = () => {
  currentGET("leftBottom", { limitNum: 20 }).then((res) => {
    console.log("设备提醒", res);
    if (res.success) {
      state.list = res.data.list;
    } else {
      window.$message({
        text: res.msg,
        type: "warning",
      });
    }
  });
};
const addressHandle = (item: any) => {
  let name = item.provinceName;
  if (item.cityName) {
    name += "/" + item.cityName;
    if (item.countyName) {
      name += "/" + item.countyName;
    }
  }
  return name;
};
const comName = computed(()=>{
    if(indexConfig.value.leftBottomSwiper){
        return SeamlessScroll
    }else{
        return EmptyCom
    }
})
onMounted(() => {
  getData();
});
</script>
<template>
  <div class="left_boottom_wrap beautify-scroll-def"  :class="{ 'overflow-y-auto': !indexConfig.leftBottomSwiper }">
    <component
     :is="comName"
      :list="state.list"
      v-model="state.scroll"
      :singleHeight="state.defaultOption.singleHeight"
      :step="state.defaultOption.step"
      :limitScrollNum="state.defaultOption.limitScrollNum"
      :hover="state.defaultOption.hover"
      :singleWaitTime="state.defaultOption.singleWaitTime"
      :wheel="state.defaultOption.wheel"
    >
      <ul class="left_boottom">
        <li class="left_boottom_item" v-for="(item, i) in state.list" :key="i">
          <span class="orderNum doudong">{{ i + 1 }}</span>
          <div class="inner_right">
            <div class="dibu"></div>
            <div class="flex">
              <div class="info">
                <span class="labels">设备ID:</span>
                <span class="text-content zhuyao doudong wangguan">
                  {{ item.gatewayno }}</span
                >
              </div>
              <div class="info">
                <span class="labels">时间:</span>
                <span class="text-content" style="font-size: 12px">
                  {{ item.createTime }}</span
                >
              </div>
            </div>
            <span
              class="types doudong"
              :class="{
                typeRed: item.onlineState == 0,
                typeGreen: item.onlineState == 1,
              }"
              >{{ item.onlineState == 1 ? "上线" : "下线" }}</span
            >
            <div class="info addresswrap">
              <span class="labels">地址:</span>
              <span class="text-content ciyao" style="font-size: 12px">
                {{ addressHandle(item) }}</span
              >
            </div>
          </div>
        </li>
      </ul>
    </component>
  </div>
</template>
<style scoped lang="scss">
.left_boottom_wrap {
  overflow: hidden;
  width: 100%;
  height: 100%;
}
.doudong {
  overflow: hidden;
  backface-visibility: hidden;
}
.overflow-y-auto {
  overflow-y: auto;
}
.left_boottom {
  width: 100%;
  height: 100%;
  .left_boottom_item {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 8px;
    font-size: 14px;
    margin: 10px 0;
    .orderNum {
      margin: 0 16px 0 -20px;
    }
    .info {
      margin-right: 10px;
      display: flex;
      align-items: center;
      color: #fff;
      .labels {
        flex-shrink: 0;
        font-size: 12px;
        color: rgba(255, 255, 255, 0.6);
      }
      .zhuyao {
        color: $primary-color;
        font-size: 15px;
      }
      .ciyao {
        color: rgba(255, 255, 255, 0.8);
      }
      .warning {
        color: #e6a23c;
        font-size: 15px;
      }
    }
    .inner_right {
      position: relative;
      height: 100%;
      width: 380px;
      flex-shrink: 0;
      line-height: 1;
      display: flex;
      align-items: center;
      justify-content: space-between;
      flex-wrap: wrap;
      .dibu {
        position: absolute;
        height: 2px;
        width: 104%;
        background-image: url("@/assets/img/zuo_xuxian.png");
        bottom: -10px;
        left: -2%;
        background-size: cover;
      }
      .addresswrap {
        width: 100%;
        display: flex;
        margin-top: 8px;
      }
    }
    .wangguan {
      color: #1890ff;
      font-weight: 900;
      font-size: 15px;
      width: 80px;
      flex-shrink: 0;
    }
    .time {
      font-size: 12px;
      // color: rgba(211, 210, 210,.8);
      color: #fff;
    }
    .address {
      font-size: 12px;
      cursor: pointer;
      // @include text-overflow(1);
    }
    .types {
      width: 30px;
      flex-shrink: 0;
    }
    .typeRed {
      color: #fc1a1a;
    }
    .typeGreen {
      color: #29fc29;
    }
  }
}
</style>
src/views/index/right-bottom.vue
@@ -1,13 +1,198 @@
<script setup lang='ts'>
<script setup lang="ts">
import { currentGET } from "@/api";
import SeamlessScroll from "@/components/seamless-scroll";
import { computed, onMounted, reactive } from "vue";
import { useSettingStore } from "@/stores";
import { storeToRefs } from "pinia";
import EmptyCom from "@/components/empty-com";
const settingStore = useSettingStore();
const { defaultOption, indexConfig } = storeToRefs(settingStore);
const state = reactive<any>({
  list: [],
  defaultOption: {
    ...defaultOption.value,
    singleHeight: 252,
    limitScrollNum: 3,
    // step:3
  },
  scroll: true,
});
const getData = () => {
  currentGET("rightBottom", { limitNum: 20 }).then((res) => {
    console.log("右下", res);
    if (res.success) {
      state.list = res.data.list;
    } else {
      window.$message({
        text: res.msg,
        type: "warning",
      });
    }
  });
};
const comName = computed(() => {
  if (indexConfig.value.rightBottomSwiper) {
    return SeamlessScroll;
  } else {
    return EmptyCom;
  }
});
function montionFilter (val:any) {
    // console.log(val);
    return val ? Number(val).toFixed(2) : '--'
}
const handleAddress=(item:any)=>{
  return `${ item.provinceName }/${item.cityName }/${item.countyName}`
}
onMounted(() => {
  getData();
});
</script>
<template>
  <div class=''>
  <div
    class="right_bottom_wrap beautify-scroll-def"
    :class="{ 'overflow-y-auto': !indexConfig.rightBottomSwiper }"
  >
    <component
      :is="comName"
      :list="state.list"
      v-model="state.scroll"
      :singleHeight="state.defaultOption.singleHeight"
      :step="state.defaultOption.step"
      :limitScrollNum="state.defaultOption.limitScrollNum"
      :hover="state.defaultOption.hover"
      :singleWaitTime="state.defaultOption.singleWaitTime"
      :wheel="state.defaultOption.wheel"
    >
      <ul class="right_bottom">
        <li class="right_center_item" v-for="(item, i) in state.list" :key="i">
          <span class="orderNum">{{ i + 1 }}</span>
          <div class="inner_right">
            <div class="dibu"></div>
            <div class="flex">
              <div class="info">
                <span class="labels">设备ID:</span>
                <span class="text-content zhuyao"> {{ item.gatewayno }}</span>
              </div>
              <div class="info">
                <span class="labels">型号:</span>
                <span class="text-content"> {{ item.terminalno }}</span>
              </div>
              <div class="info">
                <span class="labels">告警值:</span>
                <span class="text-content warning">
                  {{  montionFilter(item.alertvalue) }}</span
                >
              </div>
            </div>
            <div class="flex">
              <div class="info">
                <span class="labels shrink-0"> 地址:</span>
                <span class=" ciyao truncate" style="font-size: 12px;width: 220px;" :title="handleAddress(item)">
                  {{ handleAddress(item)}}</span
                >
              </div>
              <div class="info time shrink-0">
                <span class="labels">时间:</span>
                <span class="text-content" style="font-size: 12px">
                  {{ item.createtime }}</span
                >
              </div>
            </div>
            <div class="flex">
              <div class="info">
                <span class="labels">报警内容:</span>
                <span
                  class="text-content ciyao"
                  :class="{ warning: item.alertdetail }"
                >
                  {{ item.alertdetail || "无" }}</span
                >
              </div>
            </div>
          </div>
        </li>
      </ul>
    </component>
  </div>
</template>
<style scoped lang='scss'>
<style scoped lang="scss">
.right_bottom {
  width: 100%;
  height: 100%;
</style>
  .right_center_item {
    display: flex;
    align-items: center;
    justify-content: center;
    height: auto;
    padding: 10px;
    font-size: 14px;
    color: #fff;
    .orderNum {
      margin: 0 20px 0 -20px;
    }
    .inner_right {
      position: relative;
      height: 100%;
      width: 400px;
      flex-shrink: 0;
      line-height: 1.5;
      .dibu {
        position: absolute;
        height: 2px;
        width: 104%;
        background-image: url("@/assets/img/zuo_xuxian.png");
        bottom: -12px;
        left: -2%;
        background-size: cover;
      }
    }
    .info {
      margin-right: 10px;
      display: flex;
      align-items: center;
      .labels {
        flex-shrink: 0;
        font-size: 12px;
        color: rgba(255, 255, 255, 0.6);
      }
      .zhuyao {
        color: $primary-color;
        font-size: 15px;
      }
      .ciyao {
        color: rgba(255, 255, 255, 0.8);
      }
      .warning {
        color: #e6a23c;
        font-size: 15px;
      }
    }
  }
}
.right_bottom_wrap {
  overflow: hidden;
  width: 100%;
  height: 252px;
}
.overflow-y-auto {
  overflow-y: auto;
}
</style>
src/views/setting.vue
@@ -1,11 +1,20 @@
<script setup lang="ts">
import { useSettingStore } from "@/stores/index";
import { ref } from "vue";
import {storeToRefs} from "pinia"
const isScaleRadio = ref(false);
const leftBottomRadio=ref(true)
const rightBottomRadio=ref(true)
const settingStore = useSettingStore();
const {indexConfig}=storeToRefs(settingStore)
const init = () => {
  settingStore.initSetting();
  isScaleRadio.value = settingStore.isScale;
  leftBottomRadio.value=indexConfig.value.leftBottomSwiper
  rightBottomRadio.value=indexConfig.value.rightBottomSwiper
};
init();
const handleClose = () => {};
@@ -18,11 +27,16 @@
const isScaleChange = (flag: boolean) => {
  settingStore.setIsScale(flag);
};
const radiochange = (blag: boolean, type: string) => {
  console.log(blag, type);
const radiochange = (blag: boolean) => {
  settingStore.setIsScale(blag);
  // this.$store.commit('setting/updateSwiper', { val, type })
};
const indexRadioChange=(flag: boolean)=>{
  settingStore.setIndexConfig({
    leftBottomSwiper: leftBottomRadio.value,//左轮播
    rightBottomSwiper:rightBottomRadio.value,//右下轮播
  });
}
</script>
<template>
@@ -45,6 +59,33 @@
          </el-radio-group>
        </div>
      </div>
      <div class="left_shu">实时监测</div>
      <div class="setting_item">
        <span class="setting_label">
          设备提醒自动轮询: <span class="setting_label_tip"></span>
        </span>
        <div class="setting_content">
          <el-radio-group
            v-model="leftBottomRadio"
            @change="indexRadioChange"
          >
            <el-radio :label="true">是</el-radio>
            <el-radio :label="false">否</el-radio>
          </el-radio-group>
        </div>
      </div>
      <div class="setting_item">
        <span class="setting_label"> 实时预警轮播: </span>
        <div class="setting_content">
          <el-radio-group
            v-model="rightBottomRadio"
            @change="indexRadioChange"
          >
            <el-radio :label="true">是</el-radio>
            <el-radio :label="false">否</el-radio>
          </el-radio-group>
        </div>
      </div>
    </template>
    <!-- <template #footer>
      <div style="flex: auto">
types/global.d.ts
@@ -1,3 +1,5 @@
interface Window {
  $message: any
}
type TimeProp= NodeJS.Timeout