CSS Grid 二维布局实战:从核心概念到完整页面搭建

发布时间:2026/9/18 11:35:31
CSS Grid 二维布局实战:从核心概念到完整页面搭建 做了这么多年前端要说哪个CSS特性真正改变了我写页面的方式CSS Grid 网格布局绝对排第一。这个被称作二维布局神器的CSS模块让我终于能像画图纸一样去规划整个网页排版而不是像以前那样拿浮动、定位、Flexbox 轮番上阵最后还得靠各种 hack 去补漏洞。如果你还在为一个页面里侧边栏、顶部栏、主内容区怎么排而头疼或者看着一堆float和flex的嵌套代码欲哭无泪那 Grid 就是你要找的那个答案。这篇文章我不会从头到尾念文档而是按我实际做项目的思路把 Grid 的核心概念、常用属性、完整实战和踩坑经验串起来讲。无论你是刚接触 CSS 没多久的新手还是已经写了几年页面但一直没系统学 Grid 的老手都能从里面拿到可以直接上手的方案。学完之后你会发现复杂的网页排版不但不复杂甚至可以像拼积木一样清楚。1. 为什么说 Grid 是二维布局神器1.1 一次看似简单的页面需求让我重新理解了排版前几年我接到一个产品详情页的需求页面结构大概是顶部商品图轮播右边是商品信息和购买按钮下方是详情参数和用户评价最底部还有相关推荐。如果用 Flexbox 去搭通常得用很多个容器把页面切成一层一层的条每个条里面再用flex横着或竖着排内容。做成是能做出来但改起来特别费劲。产品经理说把评价挪到右下角你就要调整好几个容器的位置和宽度比例一不小心就崩了。后来我在一次技术分享里看到别人用 Grid 重构了类似页面几个关键属性一下去整个页面的骨架就出来了。我当时的第一反应是原来布局还可以这么写直接从宏观上控制哪些区块占第几行第几列而不是一层层去包容器。从那时起我开始在真实项目里大量使用 Grid越用越觉得它就是为解决这种页面整体排版而生的。还有个很直观的对比用表格布局的年代我们也能做二维排版但表格的语义和样式控制能力都很差改起来牵一发动全身浮动布局则是完全绕道走边边角角都要用clearfix去处理Flexbox 解决了主轴方向排列的问题但它本质上是一维的想同时管住行和列就得嵌套。Grid 的出现相当于把表和列的二维坐标系带回了容器布局而且每一个网格单元都可以单独控制这才是它配得上神器这个称呼的原因。1.2 Grid 和 Flexbox 到底谁取代谁这个问题我在博客、评论区、甚至线下面试里都被反复问过。先说结论它们不互相取代而是互补。Grid 擅长的是页面的骨架也就是你先决定整个页面划分为几个区域、每个区域大概占多大Flexbox 擅长的是骨架内部的内容排列也就是一个导航栏里的菜单项怎么均匀分布、一组按钮怎么居中、一行小图标怎么对齐。我习惯的做法是用 Grid 搭页面整体结构用 Flexbox 处理局部的组件内部布局。比如后台管理页面整个页面用grid-template-columns和grid-template-rows划分出侧边栏、顶栏、内容区这是 Grid 的活儿而顶栏右侧的用户头像、通知图标、下拉菜单这些用一个flex容器排一排就非常顺手。这种Grid 在外、Flex 在内的组合在实际开发里是出现频率最高的布局套路。从设计思路去看Flexbox 的主轴和交叉轴解决的是一条线上的排列问题而 Grid 的行线和列线解决的是一张网上的定位问题。如果你已经在一个方向比如横向上工作了且内容量不一定固定那 Flexbox 会更灵活如果你明确知道页面要分成几块、横向纵向都要控制那就是 Grid 的主场。把这两者区分清楚写布局的时候脑子会特别清醒。1.3 Grid 最适合解决的场景基于这么长时间的实践我总结了几个 Grid 优势特别明显的场景页面级布局后台管理系统、营销活动页、个人博客、作品集官网。这类页面通常有明确的头部、导航、主体、侧栏、底部页面必须铺满屏幕Grid 的二维划分特别契合。卡片式布局商品列表、图片瀑布流、文章列表。以前用浮动做卡片墙要么每次都要算百分比要么需要借助第三方栅格框架。Grid 配合repeat(auto-fill, minmax(...))几行代码就能实现自动换行、自动等宽的卡片网格。复杂不对称布局有的设计稿为了视觉冲击力会让某个区块跨多列或多行比如杂志风格的页面。Grid 的grid-column和grid-row可以直接让一个项目从第1列跨到第3列这在以前要用负 margin 加宽度百分比才能勉强实现。需要严格对齐的布局比如仪表盘里的数据面板要求所有卡片同一行等宽等高Grid 默认每个网格单元就是等宽的不用额外去算。当然也不是所有布局都非 Grid 不可。单纯一行按钮、一段文字和一个图片的垂直居中用 Flexbox 或 line-height 就够了没必要杀鸡用牛刀。工具是死的人的判断才是关键。2. 上手前必须搞懂的几个核心概念2.1 容器与项目谁负责布局、谁被布局Grid 最基础的一对概念是容器container和项目item。给一个元素设置display: grid;这个元素就成了网格容器它的所有直接子元素都自动变成网格项目。这里有个新手最容易犯的误区项目只看直接子元素孙元素并不会直接参与网格布局它会被当作某个单元格内部的普通内容去处理。比如下面的结构div classgrid-container div classbox-aA/div div classbox-b div classnested嵌套内容/div /div div classbox-cC/div /div.grid-container是容器.box-a、.box-b、.box-c是三个网格项目.nested不是网格项目它只是.box-b内部的内容。如果你想让.nested也在网格里就要修改 DOM 结构或者把.box-b变成一个内部 Grid 容器。理解这一点以后排查为什么这个元素不受网格控制的时候会非常快。2.2 网格线、单元格、区域把页面想象成 Excel在 Grid 里容器被横向和纵向的网格线切分成了多个单元格。我特别喜欢用 Excel 来打比方网格线就是表格里的边框线单元格就是一个格子多个相邻单元格可以合并成区域。Grid 里的区域定义grid-area就相当于 Excel 里拖动鼠标选择一片范围然后给它起个名字。比如一个容器设置了两列三行那它就有 3 条纵向网格线编号从左到右是 1、2、3和 4 条横向网格线编号从上到下是 1、2、3、4。编号规则很直觉默认从 1 开始这也是新手最容易理解的方式。实际上网格线还支持负数编号从最后一个方向往前数-1就是最后一条线。比如让一个项目从第1条线跨到最后一条线可以写grid-column: 1 / -1;这样就不用管到底有多少列了直接通栏。网格单元和区域的差别在于单元是最小的格子区域则是一个矩形区域可以由多个单元组成。Grid 的grid-area属性既可以用数字指定起止网格线也可以用名字指定命名区域。后面实战部分我会详细演示到时候你就能感受到给区域起名字再对号入座这种思路到底有多舒服。2.3 显式网格与隐式网格没写明的行和列是怎么来的用grid-template-columns和grid-template-rows明确画出来的行和列叫显式网格。但现实里经常发生这样的情况你明明只定义了两列三行可容器里塞了 8 个网格项目多出来的两个项目会自动新开一行或一列来存放这些自动产生的网格就是隐式网格。隐式网格的行高或列宽默认是auto意思就是按内容高度/宽度来。所以有时候你会遇到一种很迷惑的现象明明设置了grid-template-rows: 200px 200px 200px;但内容多出几行之后那些多出来的行高居然根据内容长得很高把页面撑得乱七八糟。这其实不是 Bug而是隐式网格在起作用。控制隐式网格的大小对应的属性是grid-auto-rows和grid-auto-columns。如果你希望多出来的行也有固定高度就设置grid-auto-rows: 200px;。如果你希望多出来的行自动适应内容保持auto也没问题。理解了显式和隐式的区别很多莫名其妙多出一行/一列的问题都能迎刃而解。3. 核心属性一把梭从零开始写 Grid 布局3.1 定义行与列grid-template-columns / grid-template-rows吃过苦头的前端应该都有这种经历以前想做两栏布局要么浮动加 margin要么 flex 加 flex-basis然后祈祷两边内容不一样高时页面不出岔子。Grid 用grid-template-columns和grid-template-rows直接定义列宽和行高一切豁然开朗。.container { display: grid; grid-template-columns: 200px 1fr 2fr; grid-template-rows: 100px auto; }上面的代码定义了一个 3 列 2 行的网格。列宽分别是200px、1fr、2fr行高分别是100px和auto。auto的意思是这一行的高度由内容决定因为有了这个值grid-template-rows不需要每行都写死像素值可以非常灵活地处理头部固定、内容区自适应的页面。关于fr单位它是fraction的缩写表示剩余可用空间的份额。你可以把它理解成比例分配。上面的1fr和2fr会先减去200px剩下的空间再按 1:2 分给第2列和第3列。这个属性是我用 Grid 之后依赖度最高的单位因为它彻底解决了一边固定一边自适应的老大难问题不需要任何calc运算。在实际项目中我定义容器列的写法一般长这样.app-layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); }这样做的意思是左侧固定 240px 作为侧边栏右侧minmax(0, 1fr)吃掉剩余空间。注意这里我写了minmax(0, 1fr)而不是单纯的1fr原因很关键当右侧内容里有很长的英文单词、大表格或者图片时1fr有可能被内容的最小宽度撑破导致布局溢出。用minmax(0, 1fr)之后这一列会被压缩到最小为 0再按剩余空间去分配从根上避免了溢出。这个问题我在后面避坑部分还会详细讲因为太多人在这上面栽过跟头了。3.2 让尺寸更聪明fr 单位的组合玩法、repeat() 和 minmax()如果每列都要手写那 12 列的栅格系统写起来手都要断掉。Grid 为此提供了repeat()函数专门用来简化重复值。.container { display: grid; grid-template-columns: repeat(3, 1fr); }这段代码等价于1fr 1fr 1fr也就是三等分。如果配合auto-fill或auto-fit还能实现特别惊艳的自动换行效果.container { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }这行代码的意思是每一列至少宽 250px如果容器足够宽就继续往下排更多的列塞满一排放不下就自动换行。这就是 CSS Grid 实现响应式卡片墙的经典写法。以前你要用 JavaScript 监听窗口宽度变化再动态改每张卡片的宽度百分比现在几行 CSS 全搞定。那auto-fill和auto-fit有什么区别俩兄弟容易让人混淆。简单说auto-fill在容器还有剩余空间时会保留一排空白轨道像占座一样auto-fit则会把空白轨道完全折叠掉让已有项目拉伸填满整个容器。同样一行容器卡片少的时候auto-fit显示的卡片会更大视觉上更饱满。我一般做商品列表喜欢用auto-fill做后台数据面板喜欢用auto-fit你可以根据自己的设计倾向去选。3.3 把内容放到指定位置grid-column / grid-row 与 span 的用法网格容器把版面划分好了之后接下来就是让每个网格项目对号入座。最直接的方法是用grid-row和grid-column指定项目占据哪条网格线到哪条网格线。.item-1 { grid-column: 1 / 3; /* 从第1条线到第3条线约占两列 */ grid-row: 1 / 2; /* 从第1条线到第2条线约占一行 */ }还有一种写法用span关键字更直观.item-1 { grid-column: 1 / span 2; /* 从第1列开始占据2列 */ }我更喜欢缩写形式。grid-column: 1 / -1这种用法我项目里经常出现它的意思是从第1条线到倒数第1条线也就是横跨整行。在设计那种大标题横贯全页的区块时特别方便。如果说grid-template-columns是画格子那grid-column和grid-row就是把项目往格子里放。但每次都要写数字网格线项目一多还是容易出错。所以有了下面这个更贴近人类思维的工具命名网格区域。3.4 用 grid-template-areas 给布局区域起名字像拼图一样排版grid-template-areas应该是我在整个 Grid 体系里最喜欢的一个属性因为它真正把写代码变成了画图。先看代码.app-layout { display: grid; grid-template-columns: 220px 1fr; grid-template-rows: 60px 1fr 40px; grid-template-areas: header header sidebar main footer footer; min-height: 100vh; } .app-header { grid-area: header; } .app-sidebar { grid-area: sidebar; } .app-main { grid-area: main; } .app-footer { grid-area: footer; }看到没有grid-template-areas里用 ASCII 风格的字符串直接把页面的区块形状描述了出来第一行是 header 跨两列第二行是 sidebar 在左、main 在右第三行 footer 跨两列。每个网格项目通过grid-area: header这种命名方式直接把自己放到了对应的区域里。这种写法的好处太明显了第一可读性极强哪怕你 3 个月之后再回看代码一眼就能读懂页面骨架第二调整布局非常方便。产品经理说把 sidebar 挪到右边你只需要把grid-template-areas里的两列顺序换一下再把grid-template-columns里的宽度顺序调一调整个侧边栏就换边了完全不用动 HTML 结构。有一点要注意grid-template-areas的每个字符串代表一行每行的列数必须一致而且对应的区域名要能形成一个矩形。比如你写了header header下面就不能只写main那样列数对不上Grid 直接罢工。如果某个位置想留空可以用.占位grid-template-areas: header header sidebar main footer footer;这里每行都是两个区域名列数就对齐了。留空时变成grid-template-areas: header header sidebar main footer footer;不过说实话grid-template-areas在处理大面积区域时是神器但如果页面布局特别碎比如一个区域内又要分五六个小块就不太适合了那种情况还是回到grid-column/grid-row去单独控制更好。3.5 间距、对齐与自动排列被很多人忽略的加分项网格间距用gap属性设置它是row-gap和column-gap的简写。以前用 margin 做间距要么第一个元素的 margin 需要特殊处理要么项目之间间距会出现双倍问题。网格里直接设置间距干净利落.container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }这里的gap: 16px表示列间距和行间距都是 16px。如果行列不同就写gap: 20px 16px;前面是行距后面是列距。用gap之后网格项目之间的间距永远是被均匀分割的不会出现左右两边贴边、中间间距宽的尴尬。对齐属性这块Grid 比 Flexbox 更复杂一点因为它是二维的。它分成两组一组控制整个网格在容器里的对齐方式分别叫justify-content水平和align-content垂直另一组控制网格项目在各自单元格里的对齐方式分别叫justify-items水平和align-items垂直。下面这个表格能帮你快速对照属性控制对象典型值类似 Flexbox 中的属性justify-content整体网格在容器里的水平位置start / center / end / space-betweenjustify-contentalign-content整体网格在容器里的垂直位置start / center / end / stretchalign-contentjustify-items项目在自己单元格内的水平对齐start / center / end / stretch无align-items项目在自己单元格内的垂直对齐start / center / end / stretchalign-items我特别想说一下默认值。网格项目的align-items默认是stretch也就是说如果没有显式设置高度项目会自动拉伸填满整个单元格的高度。这对做卡片墙非常友好——不同卡片内容多少不一但站在同一行里高度天然都是相同的视觉上特别整齐。如果你想改掉这个默认行为比如让某个项目缩在自己内容的高度里再单独设置align-self: start就可以了。grid-auto-flow这个属性也值得单独提一句。默认情况下网格项目按照 DOM 顺序从左到右、自上而下自动排列这是grid-auto-flow: row的行为。如果你做瀑布流或图片墙希望项目尽量紧凑地填充空白可以设置grid-auto-flow: dense;它会尝试把后面的项目往前塞到空格子里。不过要注意dense会改变项目在视觉上的顺序如果语义上必须保持 DOM 顺序就别乱用。4. 实战20 分钟搭出一个后台管理页面4.1 先想清楚到底要画什么光讲概念不过瘾我拿一个常见的后台管理系统页面来走一遍完整流程。需求很简单左边是侧边导航栏右边顶部是用户信息栏中间是内容区域最下面是一行版权信息。所有区域的高度需要铺满整个屏幕也就是常说的 Holy Grail Layout 圣杯布局。先不要急着写代码先在脑子里或纸上画一下网格。这个页面是一个很典型的二维划分列方向分两列左列固定 220px右列自适应行方向分三行顶栏 60px内容区撑满剩余空间底栏 40px。画好图之后代码就顺理成章了。4.2 用 grid-template-areas 先搭骨架HTML 结构保持简洁div classapp-layout header classapp-header顶部栏/header aside classapp-sidebar侧边导航/aside main classapp-main div classcard主要内容卡片/div /main footer classapp-footer版权信息/footer /div然后写下核心 CSS.app-layout { display: grid; grid-template-columns: 220px minmax(0, 1fr); grid-template-rows: 60px minmax(0, 1fr) 40px; grid-template-areas: header header sidebar main footer footer; height: 100vh; gap: 0; } .app-header { grid-area: header; background: #2c3e50; color: #fff; display: flex; align-items: center; justify-content: space-between; padding: 0 20px; } .app-sidebar { grid-area: sidebar; background: #f8f9fa; padding: 20px; } .app-main { grid-area: main; background: #e9ecef; padding: 20px; overflow: auto; } .app-footer { grid-area: footer; background: #2c3e50; color: #fff; display: flex; align-items: center; justify-content: center; }注意我在grid-template-rows里也用了minmax(0, 1fr)而不是1fr这一行的min如果写 0内容区就不会因为没有足够空间而撑破网格结构内部如果内容很多靠各自容器的overflow: auto去滚动。这就是一个标准的满屏后台布局了高度永远 100vh不会出现页面出现两个滚动条的问题。4.3 响应式把侧边栏挪到顶部或者直接隐藏后台页面在手机上的常见做法是把长侧边栏收起来或者让它变成顶部横向导航。Grid 处理这个场景相当优雅只需要在媒体查询里改一下grid-template-columns、grid-template-rows和grid-template-areasmedia (max-width: 768px) { .app-layout { grid-template-columns: 1fr; grid-template-rows: 60px auto minmax(0, 1fr) 40px; grid-template-areas: header sidebar main footer; } .app-sidebar { overflow-x: auto; display: flex; gap: 12px; } }这里你体会一下grid-template-areas的好处移动端只是把原来的 2 列折叠成 1 列行数从 3 行变成 4 行区域顺序是 header、sidebar、main、footer 依次排列。改动量非常小肉眼可见地直接完成了一次响应式重排。4.4 内容区内部的卡片用 Grid 自动排列布局骨架完成后内容区里的一组卡片我也习惯继续用 Grid 排。比如有 8 张数据卡片希望桌面端一行 4 张平板一行 2 张手机一行 1 张.dashboard-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; } .card { background: #fff; border-radius: 8px; padding: 16px; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); }这几行代码做到了什么卡片数量无论多少页面都会自动换行最小 240px 宽能塞几列就塞几列。不需要为平板写媒体查询因为auto-fill和minmax已经替你做了大部分响应式工作。你唯一要操心的可能就是卡片内容多出来的高度会不会让同一行里的卡片参差不齐这时候 Grid 默认的stretch对齐帮了大忙每张卡片的高度会自动拉平到同一行最高的那张。到这里一个完整后台页面已经从骨到肉都搭完了。整个过程里我没有用过一个float没有手写过一份百分比宽度也没有为清浮动加多余的标签。这就是 Grid 的威力。5. 常见问题与避坑实录5.1 1fr 并不是永远等分小心隐性的最小宽度陷阱这是我在真实项目里踩过最深的一个坑也值得所有 Grid 新手警惕。正如前面提到的1fr的分母并不是容器的全部宽度减去固定列后剩下的值这么简单因为网格项有一个默认的min-width: auto。当单元格里放着长 URL、长英文单词、大表格或者不可压缩的图片时内容的最小内容宽度会反过来把整个网格撑破导致横向滚动条出现。解决办法有两种。一是把对应列的1fr改成minmax(0, 1fr)意思是这列可以收缩到 0 以下严格说是可以压缩到 0把分配空间的主动权完全交给网格算法。二是给单元格内的内容元素加上min-width: 0或overflow: hidden之类的限制。我个人的习惯是凡是用来做自适应主内容区的列通常直接写minmax(0, 1fr)一劳永逸。5.2 隐式网格行高失控另一个常见问题就是隐式网格的轨道尺寸没被控制住。比如你定义了grid-template-rows: 200px 200px;但实际内容有很多行多出来的行会自动创建。因为隐式行默认高度是auto所以一旦内容区里有超高元素页面就会突然长出老高的一截尤其是在列表渲染场景里特别明显。解决办法很直接给容器设置grid-auto-rows比如grid-auto-rows: 200px或者更灵活的grid-auto-rows: minmax(150px, auto)。前者适合固定卡片高度的场景后者适合希望行高有下限但能随内容增高的场景。只要你在写 Grid 之前就意识到会有隐式轨道这个概念绝大多数类似问题都能提前避免。5.3 对齐与间隙的双重困惑Grid 里对齐相关属性比较多新手容易把justify-items和justify-content搞混。记住一个口诀content管大局items管个体。justify-content管整个网格在容器里靠左靠右它生效的前提是网格总宽度小于容器宽度justify-items管每个项目在它自己的单元格里靠左靠右前提是项目自己本身的宽度小于单元格宽度。实际写代码时如果发现自己设置的justify-content: center没生效先看看是不是项目默认stretch导致单元格都被填满了网格宽度等于容器宽度那justify-content当然没有发挥空间。先改成justify-items: start或者限制项目宽度再调整整体对齐问题就没了。这属于思路层面的误区比单纯记属性重要得多。5.4 兼容性现状放心用但得留个心眼CSS Grid 从 2017 年开始就在所有主流浏览器里得到了稳定支持现在的 Chrome、Firefox、Safari、Edge 新版本都能放心使用。真正要留意的历史包袱是 IE11它支持的是早期-ms-前缀版本很多属性和现在的标准行为不一样比如grid-template-areas在 IE11 里就不支持。我的做法是如果项目明确要支持 IE11要么放弃 Grid 降低兼容要求要么用supports做特性检测给旧浏览器写一套 Flexbox 后备方案。如果是新项目或者是已经不需要兼容老浏览器的后台系统那 Grid 可以直接上不用太纠结。另外建议大家关注一下你是否用了会影响语义的网格重排——网格是可以让 DOM 顺序和视觉顺序不一致的这对屏幕阅读器和键盘导航并不友好如果项目对可访问性有要求请尽量保持视觉顺序和 DOM 顺序一致或者用正确的order逻辑去弥补。5.5 Grid 和 Flexbox 的黄金组合套路最后分享一下我的个人搭配套路。页面最外层结构比如整体框架、内容区内的多列多行卡片网格基本用 Grid 来搭因为这种地方要的是区域划分。而到具体的组件内部比如导航栏里的菜单、按钮组、卡片里的标题和操作按钮用 Flexbox 排起来更轻快。举个具体的例子首页有一排统计卡片最外层用repeat(auto-fill, minmax(240px, 1fr))排列每张卡片内部左上角是数字右上角是图标底部是标题和趋势说明这些横向纵向的小排列用 Flexbox 处理。这样的组合不仅代码清晰而且后期修改范围很小想调整卡片间距改 Grid 的gap想把卡片里的图标从右上角挪到右下角只改卡片内部的 Flexbox 属性互不牵连。写在最后我个人的体会是如果你学 CSS 布局学了很久总感觉页面的排布方式还停留在一个容器套一个容器的旧思路里那一定要花几天时间把 Grid 用熟。它改变的不只是你写代码的方式更是你看待页面结构的方式——从一个一个盒子往外套变成先画一张二维表格再把内容填进去。这个思维转变完成之后你会发现自己排起版来从容得多也敢接以前觉得特别复杂的页面了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询