diff --git a/src/packages/components/Tables/Tables/TableScrollBoard/config.ts b/src/packages/components/Tables/Tables/TableScrollBoard/config.ts
new file mode 100644
index 00000000..e39ddded
--- /dev/null
+++ b/src/packages/components/Tables/Tables/TableScrollBoard/config.ts
@@ -0,0 +1,29 @@
+import { publicConfig } from '@/packages/public'
+import { CreateComponentType } from '@/packages/index.d'
+import { TableScrollBoardConfig } from './index'
+import cloneDeep from 'lodash/cloneDeep'
+import dataJson from './data.json'
+
+export const option = {
+  header: ['列1', '列2', '列3'],
+  dataset: dataJson,
+  index: true,
+  columnWidth: [30, 100, 100],
+  align: ['center','right','right','right'],
+  rowNum: 5,
+  waitTime: 2,
+  headerHeight: 35,
+  carousel: 'single',
+  headerBGC: '#00BAFF',
+  oddRowBGC: '#003B51',
+  evenRowBGC: '#0A2732',
+}
+
+export default class Config
+  extends publicConfig
+  implements CreateComponentType
+{
+  public key = TableScrollBoardConfig.key
+  public chartConfig = cloneDeep(TableScrollBoardConfig)
+  public option = cloneDeep(option)
+}
diff --git a/src/packages/components/Tables/Tables/TableScrollBoard/config.vue b/src/packages/components/Tables/Tables/TableScrollBoard/config.vue
new file mode 100644
index 00000000..9dd1e12d
--- /dev/null
+++ b/src/packages/components/Tables/Tables/TableScrollBoard/config.vue
@@ -0,0 +1,121 @@
+<template>
+  <CollapseItem name="列表" :expanded="true">
+    <SettingItemBox name="基础">
+      <SettingItem name="表行数">
+        <n-input-number
+          v-model:value="optionData.rowNum"
+          :min="1"
+          size="small"
+          placeholder="请输入自动计算"
+        ></n-input-number>
+      </SettingItem>
+      <SettingItem name="轮播时间(s)">
+        <n-input-number
+          v-model:value="optionData.waitTime"
+          :min="1"
+          size="small"
+          placeholder="请输入轮播时间"
+        ></n-input-number>
+      </SettingItem>
+      <SettingItem name="表头高度">
+        <n-input-number
+          v-model:value="optionData.headerHeight"
+          :min="1"
+          size="small"
+          placeholder="请输入表头高度"
+        ></n-input-number>
+      </SettingItem>
+      <SettingItem name="显示行号">
+        <n-switch size="small" v-model:value="optionData.index" />
+      </SettingItem>
+      
+    </SettingItemBox>
+
+    <SettingItemBox name="配置" :alone="true">
+      <SettingItem name="表头数据">
+        <n-input
+          v-model:value="header"
+          :min="1"
+          size="small"
+          placeholder="表头数据(英文','分割)"
+        ></n-input>
+      </SettingItem>
+      <SettingItem name="列对齐方式">
+        <n-input
+          v-model:value="align"
+          :min="1"
+          size="small"
+          placeholder="对齐方式(英文','分割)"
+        ></n-input>
+      </SettingItem>
+      <SettingItem name="列宽度">
+        <n-input
+          v-model:value="columnWidth"
+          :min="1"
+          size="small"
+          placeholder="列宽度(英文','分割)"
+        ></n-input>
+      </SettingItem>
+    </SettingItemBox>
+
+    <SettingItemBox name="样式">
+      <SettingItem name="表头背景色">
+        <n-color-picker
+          size="small"
+          :modes="['hex']"
+          v-model:value="optionData.headerBGC"
+        ></n-color-picker>
+      </SettingItem>
+      <SettingItem name="奇数行背景色">
+        <n-color-picker
+          size="small"
+          :modes="['hex']"
+          v-model:value="optionData.oddRowBGC"
+        ></n-color-picker>
+      </SettingItem>
+      <SettingItem name="偶数行背景色">
+        <n-color-picker
+          size="small"
+          :modes="['hex']"
+          v-model:value="optionData.evenRowBGC"
+        ></n-color-picker>
+      </SettingItem>
+    </SettingItemBox>
+  </CollapseItem>
+</template>
+
+<script setup lang="ts">
+import { PropType, ref, watch } from 'vue'
+import {
+  CollapseItem,
+  SettingItemBox,
+  SettingItem,
+} from '@/components/Pages/ChartItemSetting'
+import { option } from './config'
+
+const props = defineProps({
+  optionData: {
+    type: Object as PropType<typeof option>,
+    required: true,
+  },
+})
+
+const header = ref(props.optionData.header.toString())
+const align = ref(props.optionData.align.toString())
+const columnWidth = ref(props.optionData.columnWidth.toString())
+
+watch([header, align, columnWidth],([headerNew,alignNew,columnWidthNew],[headerOld,alignOld,columnWidthOld])=>{
+  if(headerNew !== headerOld){
+    props.optionData.header = headerNew.split(',')
+  }
+  if(alignNew !== alignOld){
+    props.optionData.align = alignNew.split(',')
+  }
+  if(columnWidthNew !== columnWidthOld){
+    // @ts-ignore
+    props.optionData.columnWidth = columnWidthNew.split(',')
+  }
+})
+
+
+</script>
diff --git a/src/packages/components/Tables/Tables/TableScrollBoard/data.json b/src/packages/components/Tables/Tables/TableScrollBoard/data.json
new file mode 100644
index 00000000..2508e6ff
--- /dev/null
+++ b/src/packages/components/Tables/Tables/TableScrollBoard/data.json
@@ -0,0 +1,12 @@
+[
+  ["行1列1", "行1列2", "行1列3"],
+  ["行2列1", "行2列2", "行2列3"],
+  ["行3列1", "行3列2", "行3列3"],
+  ["行4列1", "行4列2", "行4列3"],
+  ["行5列1", "行5列2", "行5列3"],
+  ["行6列1", "行6列2", "行6列3"],
+  ["行7列1", "行7列2", "行7列3"],
+  ["行8列1", "行8列2", "行8列3"],
+  ["行9列1", "行9列2", "行9列3"],
+  ["行10列1", "行10列2", "行10列3"]
+]
diff --git a/src/packages/components/Tables/Tables/TableScrollBoard/index.ts b/src/packages/components/Tables/Tables/TableScrollBoard/index.ts
new file mode 100644
index 00000000..0ceb94bb
--- /dev/null
+++ b/src/packages/components/Tables/Tables/TableScrollBoard/index.ts
@@ -0,0 +1,14 @@
+import image from '@/assets/images/chart/tables/table_scrollboard.png'
+import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
+import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
+
+export const TableScrollBoardConfig: ConfigType = {
+  key: 'TableScrollBoard',
+  chartKey: 'VTableScrollBoard',
+  conKey: 'VCTableScrollBoard',
+  title: '轮播列表',
+  category: ChatCategoryEnum.TABLE,
+  categoryName: ChatCategoryEnumName.TABLE,
+  package: PackagesCategoryEnum.TABLES,
+  image
+}
diff --git a/src/packages/components/Tables/Tables/TableScrollBoard/index.vue b/src/packages/components/Tables/Tables/TableScrollBoard/index.vue
new file mode 100644
index 00000000..9792e796
--- /dev/null
+++ b/src/packages/components/Tables/Tables/TableScrollBoard/index.vue
@@ -0,0 +1,355 @@
+<template>
+  <div class="dv-scroll-board">
+    <div class="header" v-if="status.header.length && status.mergedConfig"
+      :style="`background-color: ${status.mergedConfig.headerBGC};`">
+      <div class="header-item" v-for="(headerItem, i) in status.header" :key="`${headerItem}${i}`" :style="`
+        height: ${status.mergedConfig.headerHeight}px;
+        line-height: ${status.mergedConfig.headerHeight}px;
+        width: ${status.widths[i]}px;
+      `" :align="status.aligns[i]" v-html="headerItem" />
+    </div>
+
+    <div v-if="status.mergedConfig" class="rows"
+      :style="`height: ${h - (status.header.length ? status.mergedConfig.headerHeight : 0)}px;`">
+      <div class="row-item" v-for="(row, ri) in status.rows" :key="`${row.toString()}${row.scroll}`" :style="`
+        height: ${status.heights[ri]}px;
+        line-height: ${status.heights[ri]}px;
+        background-color: ${status.mergedConfig[row.rowIndex % 2 === 0 ? 'evenRowBGC' : 'oddRowBGC']};
+      `">
+        <div class="ceil" v-for="(ceil, ci) in row.ceils" :key="`${ceil}${ri}${ci}`"
+          :style="`width: ${status.widths[ci]}px;`" :align="status.aligns[ci]" v-html="ceil" />
+
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { PropType, onUnmounted, reactive, toRefs, watch, onMounted } from 'vue'
+import { CreateComponentType } from '@/packages/index.d'
+import { useChartDataFetch } from '@/hooks'
+import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
+import merge from 'lodash/merge'
+import cloneDeep from 'lodash/cloneDeep'
+
+const props = defineProps({
+  chartConfig: {
+    type: Object as PropType<CreateComponentType>,
+    required: true,
+  },
+})
+
+// 这里能拿到图表宽高等
+const { w, h } = toRefs(props.chartConfig.attr)
+// 这里能拿到上面 config.ts 里的 option 数据
+// const { rowNum, headerHeight, index, backgroundColor } = toRefs(props.chartConfig.option)
+
+const status = reactive({
+  defaultConfig: {
+    /**
+     * @description Board header
+     * @type {Array<String>}
+     * @default header = []
+     * @example header = ['column1', 'column2', 'column3']
+     */
+    header: [],
+    /**
+     * @description Board dataset
+     * @type {Array<Array>}
+     * @default dataset = []
+     */
+    dataset: [],
+    /**
+     * @description Row num
+     * @type {Number}
+     * @default rowNum = 5
+     */
+    rowNum: 5,
+    /**
+     * @description Header background color
+     * @type {String}
+     * @default headerBGC = '#00BAFF'
+     */
+    headerBGC: '#00BAFF',
+    /**
+     * @description Odd row background color
+     * @type {String}
+     * @default oddRowBGC = '#003B51'
+     */
+    oddRowBGC: '#003B51',
+    /**
+     * @description Even row background color
+     * @type {String}
+     * @default evenRowBGC = '#003B51'
+     */
+    evenRowBGC: '#0A2732',
+    /**
+     * @description Scroll wait time
+     * @type {Number}
+     * @default waitTime = 2
+     */
+    waitTime: 2,
+    /**
+     * @description Header height
+     * @type {Number}
+     * @default headerHeight = 35
+     */
+    headerHeight: 35,
+    /**
+     * @description Column width
+     * @type {Array<Number>}
+     * @default columnWidth = []
+     */
+    columnWidth: [],
+    /**
+     * @description Column align
+     * @type {Array<String>}
+     * @default align = []
+     * @example align = ['left', 'center', 'right']
+     */
+    align: [],
+    /**
+     * @description Show index
+     * @type {Boolean}
+     * @default index = false
+     */
+    index: false,
+    /**
+     * @description index Header
+     * @type {String}
+     * @default indexHeader = '#'
+     */
+    indexHeader: '#',
+    /**
+     * @description Carousel type
+     * @type {String}
+     * @default carousel = 'single'
+     * @example carousel = 'single' | 'page'
+     */
+    carousel: 'single',
+    /**
+     * @description Pause scroll when mouse hovered
+     * @type {Boolean}
+     * @default hoverPause = true
+     * @example hoverPause = true | false
+     */
+    hoverPause: true
+  },
+  mergedConfig: props.chartConfig.option,
+  header: [],
+  rowsData: [],
+  rows: [{
+    ceils: [],
+    rowIndex: 0,
+    scroll: 0
+  }],
+  widths: [],
+  heights: [0],
+  avgHeight: 0,
+  aligns: [],
+  animationIndex: 0,
+  animationHandler: 0,
+  updater: 0,
+  needCalc: false
+})
+
+const calcData = () => {
+  mergeConfig()
+  calcHeaderData()
+  calcRowsData()
+  calcWidths()
+  calcHeights()
+  calcAligns()
+  animation(true)
+}
+
+onMounted(()=> {
+  calcData()
+})
+
+const mergeConfig = () => {
+  status.mergedConfig = merge(cloneDeep(status.defaultConfig), props.chartConfig.option)
+}
+
+const calcHeaderData = () => {
+  let { header, index, indexHeader } = status.mergedConfig
+  if (!header.length) {
+    status.header = []
+    return
+  }
+  header = [...header]
+  if (index) header.unshift(indexHeader)
+  status.header = header
+}
+
+const calcRowsData = () => {
+  let { dataset, index, headerBGC, rowNum } = status.mergedConfig
+  if (index) {
+    dataset = dataset.map((row:any, i:number) => {
+      row = [...row]
+      const indexTag = `<span class="index" style="background-color: ${headerBGC};border-radius: 3px;padding: 0px 3px;">${i + 1}</span>`
+      row.unshift(indexTag)
+      return row
+    })
+  }
+  dataset = dataset.map((ceils:any, i:number) => ({ ceils, rowIndex: i }))
+  const rowLength = dataset.length
+  if (rowLength > rowNum && rowLength < 2 * rowNum) {
+    dataset = [...dataset, ...dataset]
+  }
+  dataset = dataset.map((d:any, i:number) => ({ ...d, scroll: i }))
+
+  status.rowsData = dataset
+  status.rows = dataset
+}
+
+const calcWidths = () => {
+  const { mergedConfig, rowsData } = status
+  const { columnWidth, header } = mergedConfig
+  const usedWidth = columnWidth.reduce((all:any, ws:number) => all + ws, 0)
+  let columnNum = 0
+  if (rowsData[0]) {
+    columnNum = (rowsData[0] as any).ceils.length
+  } else if (header.length) {
+    columnNum = header.length
+  }
+  const avgWidth = (w.value - usedWidth) / (columnNum - columnWidth.length)
+  const widths = new Array(columnNum).fill(avgWidth)
+  status.widths = merge(widths, columnWidth)
+}
+
+const calcHeights = (onresize = false) => {
+  const { mergedConfig, header } = status
+  const { headerHeight, rowNum, dataset } = mergedConfig
+  let allHeight = h.value
+  if (header.length) allHeight -= headerHeight
+  const avgHeight = allHeight / rowNum
+  status.avgHeight = avgHeight
+  if (!onresize) status.heights = new Array(dataset.length).fill(avgHeight)
+}
+
+const calcAligns = () => {
+  const { header, mergedConfig } = status
+
+  const columnNum = header.length
+
+  let aligns = new Array(columnNum).fill('left')
+
+  const { align } = mergedConfig
+
+  status.aligns = merge(aligns, align)
+}
+
+const animation = async (start = false) => {
+  const { needCalc } = status
+
+  if (needCalc) {
+    calcRowsData()
+    calcHeights()
+    status.needCalc = false
+  }
+  let { avgHeight, animationIndex, mergedConfig, rowsData, updater } = status
+  const { waitTime, carousel, rowNum } = mergedConfig
+
+  const rowLength = rowsData.length
+  if (rowNum >= rowLength) return
+  if (start) {
+    await new Promise(resolve => setTimeout(resolve, waitTime*1000))
+    if (updater !== status.updater) return
+  }
+  const animationNum = carousel === 'single' ? 1 : rowNum
+  let rows = rowsData.slice(animationIndex)
+  rows.push(...rowsData.slice(0, animationIndex))
+  status.rows = rows.slice(0, carousel === 'page' ? rowNum * 2 : rowNum + 1)
+  status.heights = new Array(rowLength).fill(avgHeight)
+  await new Promise(resolve => setTimeout(resolve, 300))
+  if (updater !== status.updater) return
+  status.heights.splice(0, animationNum, ...new Array(animationNum).fill(0))
+  animationIndex += animationNum
+  const back = animationIndex - rowLength
+  if (back >= 0) animationIndex = back
+  status.animationIndex = animationIndex
+  status.animationHandler = setTimeout(animation, waitTime*1000 - 300) as any
+}
+
+const stopAnimation = () => {
+  status.updater = (status.updater + 1) % 999999
+  if (!status.animationHandler) return
+  clearTimeout(status.animationHandler)
+}
+
+const onRestart = async () => {
+  if (!status.mergedConfig) return
+  stopAnimation()
+  calcData()
+}
+
+watch(
+  () => w.value,
+  () => {
+    onRestart()
+  }
+)
+
+watch(
+  () => h.value,
+  () => {
+    onRestart()
+  }
+)
+
+// 数据更新
+watch(
+  () => props.chartConfig.option,
+  () => {
+    onRestart()
+  },
+  {deep:true}
+)
+
+// 数据更新 (默认更新 dataset,若更新之后有其它操作,可添加回调函数)
+useChartDataFetch(props.chartConfig, useChartEditStore)
+
+onUnmounted(() => {
+  stopAnimation()
+})
+
+</script>
+
+<style lang="scss" scoped>
+.dv-scroll-board {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  color: #fff;
+
+  .text {
+    padding: 0 10px;
+    box-sizing: border-box;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  .header {
+    display: flex;
+    flex-direction: row;
+    font-size: 15px;
+
+    .header-item {
+      transition: all 0.3s;
+    }
+  }
+
+  .rows {
+    overflow: hidden;
+
+    .row-item {
+      display: flex;
+      font-size: 14px;
+      transition: all 0.3s;
+      overflow: hidden;
+    }
+  }
+}
+</style>
diff --git a/src/packages/components/Tables/Tables/index.ts b/src/packages/components/Tables/Tables/index.ts
index a2443e35..388951a9 100644
--- a/src/packages/components/Tables/Tables/index.ts
+++ b/src/packages/components/Tables/Tables/index.ts
@@ -1,5 +1,6 @@
 import { TableListConfig } from './TableList'
 import { TableCommonConfig } from './TableCommon'
 import { TableCategoryConfig } from './TableCategory'
+import { TableScrollBoardConfig } from './TableScrollBoard'
 
-export default [TableListConfig, TableCommonConfig, TableCategoryConfig]
+export default [TableListConfig, TableScrollBoardConfig, TableCommonConfig, TableCategoryConfig]