大屏前端开发架构-大屏响应式布局组件

我们在做大屏项目的时候,通常会遇到这些问题

  1. 如何布局,包括常规布局,是px 还是vw,还是rem,还是%
  2. 是否支持页面响应式,每次要浪费大量时间编码,调试
  3. 样式能否复用,如果封装成主题,还可以支持主题切换的功能

综合多个大屏项目的实际开发和落地,我封装了大屏响应式布局组件(React),经过几个版本的迭代和优化,现已开源和发布到npm

本文给大家介绍下这个组件的功能和设计,希望大家可以找到大屏组件开发的灵感

源码地址

github npm

1.栅格系统/布局系统

tsx 复制代码
 <DashboardAppResponsive 
    cols={12}
    resource={{
        A1: TestChildA1
     }}
     layout={[
        {
            i: "A1",
            w: 3,
            h: 4,
            x: 0,
            y: 0,
        },
       ]}
 /> 
  1. 这是一个最简单的布局案例:cols属性指定栅格列数(默认12);
  2. resource 配置格子组件,key是id, TestChildA1是函数组件;
  3. layout 配置布局信息, i是id,w h分别是宽高,x y分别是坐标,左上角是0,0开始算,单位都是栅格
  4. 参考 https://github.com/react-grid-layout/react-grid-layout?tab=readme-ov-file#usage 的设计

maxRows是什么意思呢?由于大屏一般有两种模式,一种是在pc/电视设备,一屏展示,不需要滚动条,另一种是移动端设备,需要流式滚动展示,所以需要区别计算

部分源码

tsx 复制代码
 // 计算出一共多少行,最底下的格子组件在什么位置
 const computedMaxRow = (layout: Partial<IDashboardItemProps>[]) => {
  return layout.map((l) => (l.y || 0) + (l.h || 0)).sort((a, b) => b - a)[0];
};

 const rows = computedMaxRow(layout);
 // pc设备,移动端设备分别计算
 const rowHeight = breakpoint === 'desktop' || breakpoint === 'showroom'
        ? sizeFormat((contentHeight) / rows)
        : typeof rowHeightParam === "function"
            ? rowHeightParam({ breakpoint, themeMode, themeName })
            : rowHeightParam;

后续扩展

resource前端控制, layout存储在服务端,可以进行位置配置

2.响应式系统

tsx 复制代码
<DashboardAppResponsive 
	breakpoints={{
        showroom: 2600,
        desktop: 1300,
        tablet: 500,
        mobile: 0,
      }}
      cols={({ breakpoint })=>{
	if (breakpoint === "tablet" || breakpoint == "mobile") {
          return 6
        }
        return 12;
      }}
    layout={({ breakpoint }) => {
        if (breakpoint === "tablet" || breakpoint == "mobile") {
          return tabletLayout
        }
        return pcLayout;
     }}
       
/>

设计说明

  1. 内置断点系统,layout , cols 等属性可以写成函数的形式

  2. 组件只有一套,布局有n套

  3. 参考antd-table的components属性

默认断点值说明

名称 值(px)
showroom 展厅 大于2600
desktop pc端 大于1300,小于2600
tablet 平板 大于500,小于1300
mobile 手机 大于0,小于500

覆盖默认组件

tsx 复制代码
 <DashboardAppResponsive
        components={{
          content: ({ children, contentRef, breakpoint }: any) => {
            return (
              <div
                ref={contentRef}
                style={{
                  height: "calc( 100% - 120px)",
                  overflowX: "hidden",
                  overflowY:
                    breakpoint === "tablet" || breakpoint === "mobile"
                      ? "auto"
                      : "hidden",
                  position: "relative",
                }}
              >
                {children}
              </div>
            );
          },
          headerInner: () => (
            <HeaderWrap>
              <Header
                data={{
                  activeMenu: [2, 1],
                  projectId: "",
                  projectName: "",
                  ...props.data,
                }}
                emit={props?.emit}
              />
            </HeaderWrap>
          ),
        }}
      />

自定义组件说明

layout : 最外面的容器组件

content : 除头部以外的内容区域

headerWrapper: 头部外部组件,一般定义背景图片

headerInner : 头部内部组件(由于带业务需求,往往需要子系统自定义)

containerWrapper: 卡片容器组件,一般由主题提供

3.主题预设系统

设计思路

  1. 每个主题可以提供组件的任意属性,和样式文件,将会与用户传递给组件的属性进行合并

  2. 用户属性大于主题属性

  3. 参考设计 https://github.com/nosferatu500/react-sortable-tree/blob/stable/src/react-sortable-tree.tsx#L35C7-L35C17

优势

  1. 快速开发
  2. UI验收过的主题不会出错

使用一个主题

这部分文档尚未完善,先以内部主题为样例

tsx 复制代码
import './App.css'
import { DashboardAppResponsive } from 'rc-dashboard'
import { TestChildBase } from './components/TestChildBase'
import { tabletLayout } from './utils/tabletLayout'
import { pcLayout } from './utils/pcLayout'

import { jfDarkTheme } from 'rc-dashboard/dist/themes'
import 'rc-dashboard/dist/themes/index.css'

function App() {
  return (
    <DashboardAppResponsive
      minHeight={555}
      resource={{
        A1: TestChildBase,
      }}
      layout={({ breakpoint }) => {
        if (breakpoint === "tablet" || breakpoint == "mobile") {
          return tabletLayout
        }
        return pcLayout;
      }}
      themeProvider={jfDarkTheme}
    />
  )
}

export default App

我们可以看到 themeProvider传入了 jfDarkTheme ,同时还引用了一个css,接下来来看下主题内部源码是怎么样的

tsx 复制代码
// 一般会提供一份主题样式
import "./jfDarkThemeGlobal.less";

export const jfDarkTheme: DashBoardTheme = {
   // themeName会做为className
  themeName: "jfDarkTheme",
  wrapperStyle: {
    backgroundColor: "#052B54",
  },
  components: {
    containerWrapper: NodeContentRenderer,
    headerWrapper: TitleNodeRenderer,
    content: JfDarkThemeContent
  }
};
export * from "../interface";

jfDarkThemeGlobal.less , 主要是提供css变量和基础样式

less 复制代码
@text-common: #c3d4e5;
@text-primary: #3b78ef;
@text-num-light: #f4a52e;
@text-num: #ffdc2f;
@text-light: #34daff;

html[data-theme="dark"] body ,
html[data-theme-name="jfDarkTheme"] body 
{
  /* // 交发黑暗色 */
  /* 下拉框的背景颜色 */
  --popover: 209 96% 18%;
}

.jfDarkTheme {
  background-image: url("@/assets/jfDarkTheme/main-bg.jpg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  font-size: 14px;
  color: #c3d4e5;
  // 有用的
  
  --text-num: @text-num;
  --text-num-light: @text-num-light;

  --thumb-color: rgba(59, 120, 239, 0.5);
  /* // 交发黑暗色 */
  /* 下拉框的背景颜色 */
  --popover: 209 96% 18%;
  
  &.mobile {
    background-repeat: repeat;
    background-size: contain;
    background-position: unset;
    background-image: url("@/assets/jfDarkTheme/main-bg-m.png");
  }
}

4.主题切换系统

本质上是动态修改themeProvider属性

模式1: 亮色/暗色切换

提供hooks

tsx 复制代码
 const { themeMode, setThemeMode } = useThemeMode()
  
 type ThemeMode = "dark" | "light" | "system";

使用hooks

tsx 复制代码
export const TitleNodeChildRenderer = (props: any) => {
 const { themeMode, setThemeMode } = useThemeMode()
 return <div style={{
   display: 'flex',

 }}>
   <p>当前模式{themeMode}</p>
   <div>
     <Button onClick={() => {
       setThemeMode('light')
     }}>明色</Button>
     <Button
       onClick={() => {
         setThemeMode('dark')
       }}
     >暗色</Button>
   </div>
 </div>;
};

模式2,主题切换

提供hooks

scss 复制代码
const { themeName, setThemeName } = useThemeMode()

使用hooks

typescript 复制代码
export const TitleNodeChangeTheme = (props: any) => {
 const { themeName, setThemeName } = useThemeMode()
 return <div style={{
   display: 'flex',

 }}>
   <p>当前主题{themeName}</p>
   <div>
     <Button onClick={() => {
       setThemeName('jfDarkTheme')
     }}>蓝深色</Button>
     <Button
       onClick={() => {
         setThemeName('jfLightTheme')
       }}
     >蓝亮色</Button>
   </div>
 </div>;
};

模式2下,外层需要判断当前主题

tsx 复制代码
export const DashboardAppResponsiveThemeDemo = () => {
 const [themeName, setThemeName] = useState(localStorage.getItem("vite-ui-theme-name") || 'jfDarkTheme')
 return (
   <DashboardAppResponsive
     onThemeNameChange={(name = '') => {
       name && setThemeName(name)
     }}
     themeProvider={() => {
       return themesMap[themeName] || jfDarkTheme
     }}
   />
 );
};
相关推荐
蟾宫曲4 小时前
在 Vue3 项目中实现计时器组件的使用(Vite+Vue3+Node+npm+Element-plus,附测试代码)
前端·npm·vue3·vite·element-plus·计时器
秋雨凉人心4 小时前
简单发布一个npm包
前端·javascript·webpack·npm·node.js
liuxin334455664 小时前
学籍管理系统:实现教育管理现代化
java·开发语言·前端·数据库·安全
qq13267029404 小时前
运行Zr.Admin项目(前端)
前端·vue2·zradmin前端·zradmin vue·运行zradmin·vue2版本zradmin
魏时烟5 小时前
css文字折行以及双端对齐实现方式
前端·css
2401_882726486 小时前
低代码配置式组态软件-BY组态
前端·物联网·低代码·前端框架·编辑器·web
web130933203986 小时前
ctfshow-web入门-文件包含(web82-web86)条件竞争实现session会话文件包含
前端·github
胡西风_foxww6 小时前
【ES6复习笔记】迭代器(10)
前端·笔记·迭代器·es6·iterator
前端没钱6 小时前
探索 ES6 基础:开启 JavaScript 新篇章
前端·javascript·es6
m0_748255267 小时前
vue3导入excel并解析excel数据渲染到表格中,纯前端实现。
前端·excel