架构完成

main
expressgy 2 years ago
parent 9bea6e0e53
commit 71ea3834da
  1. 7
      src/assets/arrow.svg
  2. 50
      src/assets/default.scss
  3. 1
      src/assets/help.svg
  4. 6
      src/assets/index.css
  5. 1
      src/assets/lolipop.svg
  6. 13
      src/components/DirectoryStructureTree/index.jsx
  7. 7
      src/components/DirectoryStructureTree/index.module.scss
  8. 2
      src/components/Modal/index.jsx
  9. 2
      src/components/Modal/index.module.scss
  10. 45
      src/store/defaultStore.js
  11. 10
      src/store/index.js
  12. BIN
      src/view/EditPageNew/bg.jpg
  13. 173
      src/view/EditPageNew/index.jsx
  14. 500
      src/view/EditPageNew/index.module.scss

@ -0,0 +1,7 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1675764697806" class="icon" viewBox="0 0 1267 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6645"
xmlns:xlink="http://www.w3.org/1999/xlink" width="247.4609375" height="200">
<path d="M1170.529524 73.094095L97.304381 499.809524 1170.529524 926.524952 978.236952 499.809524z" fill="#ffffff"
p-id="6646" data-spm-anchor-id="a313x.7781069.0.i4" class="selected"></path>
</svg>

After

Width:  |  Height:  |  Size: 559 B

@ -0,0 +1,50 @@
@font-face {
font-family: Emoji;
src: local("Apple Color Emojiji"), local("Segoe UI Emoji"), local("Segoe UI Symbol"), local("Noto Color Emoji");
unicode-range: U+1F000-1F644, U+203C-3299;
}
//无线字体
body {
font-family: system-ui, apple-system, Segoe UI, Rototo, Emoji, Helvetica, Arial, sans-serif;
}
//衬线字体
@mixin font-serif {
font-family: Georgia, Cambria, "Times New Roman", Times, serif;
}
//等宽字体
@mixin font-mono {
font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
//以上字体总结 来源于B站Upzhangxinxu https://www.bilibili.com/video/BV1b54y1Z7pu/?spm_id_from=333.337.search-card.all.click&vd_source=2f8cd1eff8d87be6af7bdcbccbd60ade
@mixin widthAuto{
/*div宽度适应文字*/
width:fit-content;
width:-webkit-fit-content;
width:-moz-fit-content;
}
@mixin noSelect{
/*无法选中*/
-webkit-touch-callout:none;
-webkit-user-select:none;
-khtml-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
}
//浸水模糊效果
@mixin beautifulBlurWhite{
background-color: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(20px) saturate(5);
}
//浸水模糊效果
@mixin beautifulBlur{
backdrop-filter: blur(20px) saturate(1);
}
//壁纸适应效果
@mixin autoBackgroundImg{
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1675741114492" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2747" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 972.8c253.952 0 460.8-206.848 460.8-460.8s-206.848-460.8-460.8-460.8S51.2 258.048 51.2 512s206.848 460.8 460.8 460.8z m20.48-317.44c0 12.288-8.192 20.48-20.48 20.48s-20.48-8.192-20.48-20.48c0-59.392 22.528-102.4 61.44-141.312 8.192-8.192 40.96-36.864 47.104-43.008 16.384-20.48 26.624-45.056 26.624-71.68 0-61.44-51.2-112.64-112.64-112.64s-112.64 51.2-112.64 112.64c0 12.288-8.192 20.48-20.48 20.48s-20.48-8.192-20.48-20.48c0-83.968 69.632-153.6 153.6-153.6s153.6 69.632 153.6 153.6c0 36.864-12.288 69.632-34.816 98.304-6.144 8.192-43.008 38.912-49.152 45.056-34.816 32.768-51.2 65.536-51.2 112.64z m-20.48 163.84c-16.384 0-30.72-14.336-30.72-30.72s14.336-30.72 30.72-30.72 30.72 14.336 30.72 30.72-14.336 30.72-30.72 30.72z" fill="#333333" p-id="2748" data-spm-anchor-id="a313x.7781069.0.i1" class=""></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -88,7 +88,7 @@ button:focus-visible {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
display: none; display: none;
backdrop-filter: blur(50px); backdrop-filter: blur(10px) saturate(5);
animation: modalRootShow ease-in-out 500ms forwards; animation: modalRootShow ease-in-out 500ms forwards;
} }
@keyframes modalRootShow { @keyframes modalRootShow {
@ -96,12 +96,12 @@ button:focus-visible {
background: #33333300; background: #33333300;
} }
to{ to{
background: #66666633; background: #bbbbbbcc;
} }
} }
@keyframes modalRootHide { @keyframes modalRootHide {
from{ from{
background: #33333333; background: #aaaaaacc;
} }
to{ to{
background: #66666600; background: #66666600;

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1675738216271" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5268" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M823.53152 808.3456a475.78112 475.78112 0 0 1-77.5168 62.84288 478.91968 478.91968 0 0 1-42.65984 24.7296l53.53984 82.29888c21.87776 33.62816 67.47648 42.75712 101.84704 20.39808 34.37056-22.35904 44.49792-67.74784 22.62528-101.376l-57.83552-88.89344z" fill="#8D3F07" p-id="5269"></path><path d="M512.80384 486.69184m-463.69792 0a463.69792 463.69792 0 1 0 927.39584 0 463.69792 463.69792 0 1 0-927.39584 0Z" fill="#F9949E" p-id="5270"></path><path d="M512.80384 486.69184m-352.1536 0a352.1536 352.1536 0 1 0 704.3072 0 352.1536 352.1536 0 1 0-704.3072 0Z" fill="#FFFFFF" p-id="5271"></path><path d="M512.80384 486.69184m-222.86848 0a222.86848 222.86848 0 1 0 445.73696 0 222.86848 222.86848 0 1 0-445.73696 0Z" fill="#F9949E" p-id="5272"></path><path d="M512.80384 486.69184m-96.77824 0a96.77824 96.77824 0 1 0 193.55648 0 96.77824 96.77824 0 1 0-193.55648 0Z" fill="#FFFFFF" p-id="5273"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,13 @@
import css from './index.module.scss'
export default function DirectoryStructureTree(props){
return props.tree.map((item, index) => {
if(item.children && Array.isArray(item.children)){
return <div key={index} className={css.directoryStructureTree}>
<div>{item.name}</div>
<div>{DirectoryStructureTree({tree: item.children})}</div>
</div>
}else{
return <div key={index} className={css.directoryStructureTree}>{item.name}</div>
}
})
}

@ -0,0 +1,7 @@
.directoryStructureTree{
position: relative;
line-height: 1.5em;
border-radius: 10px;
margin: 1em;
cursor: pointer;
}

@ -61,7 +61,7 @@ export default function Dialog(props){
return createPortal( return createPortal(
<div className={className} style={{width:width + 'px', height:width * 0.618 + 'px'}}> <div className={className} style={{width:width + 'px', height:width * 0.618 + 'px'}}>
<div className={BPEMR.close} onClick={close}><img src={closeSvg} alt=""/></div> <div className={BPEMR.close} onClick={close}><img src={closeSvg} alt=""/></div>
<div>{props.children}</div> <div style={{height:'100%'}}>{props.children}</div>
</div>, </div>,
node node
); );

@ -1,4 +1,4 @@
@import '@/assets/default.scss';
$modalShow:modalShow; $modalShow:modalShow;
$modalHide:modalHide; $modalHide:modalHide;

@ -0,0 +1,45 @@
import { observable, action, computed, makeObservable} from "mobx";
import {config} from "@/config/sys22";
class DefaultStore {
// 系统配置
config = config;
// 目录结构树
directoryStructureTree = [
{
name:'菜单1',
icon:'',
children:[
{
name:'子菜单'
}
]
},
{
name:'菜单2',
icon:'',
},
];
constructor() {
// mobx6 和以前版本这是最大的区别
makeObservable(this, {
config: observable,
directoryStructureTree: observable,
setName: action,
titleName: computed
});
}
setName(v) {
console.log('触发action');
this.name = v;
}
get titleName(){
return this.name+'___111';
}
}
export default DefaultStore

@ -1,5 +1,7 @@
import UserStore from './userStore' // import UserStore from './userStore'
import NetStore from "./netStore"; // import NetStore from "./netStore";
import DefaultStore from "@/store/defaultStore.js";
export const userStore = new UserStore() // export const userStore = new UserStore()
export const netStore = new NetStore() // export const netStore = new NetStore()
export const defaultStore = new DefaultStore()

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

@ -1,3 +1,172 @@
export default function EditPageNew(){ import {useState, useEffect} from 'react'
return <div>Edit</div> import {defaultStore} from '@/store/index'
import DirectoryStructureTree from "@/components/DirectoryStructureTree/index.jsx";
import Modal from '@/components/Modal';
import Button from "@/components/Button/index.jsx";
import css from './index.module.scss'
import svgLolipop from '@/assets/lolipop.svg'
import svgHelp from '@/assets/help.svg'
import svgArrow from '@/assets/arrow.svg'
export default function EditPageNew() {
//
const [thumbnailList, setThumbnailList] = useState([{name: '大纲', choose: false}]);
//
const [addThumbnailState, setAddThumbnailState] = useState(false);
//
const [newThumbnailName, setNewThumbnailName] = useState('');
// box
const moleculeBoxChoose = [css.moleculeBoxChoose, css.moleculeBox].join(' ');
//
function closeThumbnailModal() {
setNewThumbnailName('')
setAddThumbnailState(false)
}
//
function addThumbnail() {
thumbnailList.push({
name: newThumbnailName
})
setThumbnailList(thumbnailList)
closeThumbnailModal()
}
//
function chooseThumbnail(index) {
thumbnailList.forEach(item => {
item.choose = false;
})
thumbnailList[index].choose = true
setThumbnailList([...thumbnailList])
setNowContent(thumbnailList[index])
}
useEffect(() => {
console.log(thumbnailList)
}, [thumbnailList])
//
const [nowContent, setNowContent] = useState({})
//
const [attributeSwitch, setAttributeSwitch] = useState(false);
//
const [attrLabelPosition, setAttrLabelPosition] = useState(0);
//
function handleChangeAttrLabelPosition(index){
setAttrLabelPosition(index)
}
return <div className={css.editPageNew}>
<div className={css.container}>
<div className={css.main}>
<div className={css.directoryStructureTree}>
<div className={css.container}>
<header>
<div>
<div className={css.svg}><img src={svgLolipop} alt=""/></div>
<div className={css.title}>目录结构树</div>
</div>
</header>
<div className={css.main}>
<div className={css.container}>
<DirectoryStructureTree
tree={defaultStore.directoryStructureTree}></DirectoryStructureTree>
</div>
</div>
<footer>
<div>
<div className={css.svg}><img src={svgHelp} alt=""/></div>
<div className={css.title}>{defaultStore.config.projectName}</div>
</div>
</footer>
</div>
</div>
<div className={css.graphicEditor}>
<div className={css.container}>
<header>页面名称</header>
<div className={css.linner}></div>
<div className={css.main}>
<div className={css.container}>
<div className={css.left}>
<div className={css.title}>大纲</div>
<div className={css.thumbnail}>
<div className={css.container}>
<div className={css.moleculeList}>
{
thumbnailList.map((item, index) => {
return <div
className={item.choose ? moleculeBoxChoose : css.moleculeBox}
key={index} onClick={() => chooseThumbnail(index)}>
<div>
<div className={css.molecule}>{item.name}</div>
<div className={css.moleculeCmd}>{item.choose}</div>
</div>
</div>
})
}
</div>
<div className={css.moleculeAdd} onClick={() => {
setAddThumbnailState(true)
}}>
<div>+</div>
</div>
<Modal state={addThumbnailState} close={closeThumbnailModal} width={"300"}>
<div className={css.addThumbnailState}>
<div className={css.title}>添加大纲</div>
<div className={css.main}>
<div>大纲名称</div>
<div><input type="text" value={newThumbnailName}
onChange={event => {
setNewThumbnailName(event.target.value)
}}/></div>
</div>
<div className={css.ok}><Button onClick={addThumbnail}>确认</Button>
</div>
</div>
</Modal>
</div>
</div>
</div>
<div className={css.middle}>
</div>
<div className={css.right} style={{
width: attributeSwitch ? '0' : '300px',
boxShadow: attributeSwitch ? '' : '2px 2px 10px 1px #33333333',
marginLeft: attributeSwitch ? '0' : '1rem',
// padding:attributeSwitch ? '0' : '1rem',
}}>
<div className={attributeSwitch ? css.switch : css.switchClose} onClick={() => {
setAttributeSwitch(!attributeSwitch)
}}>
<img src={svgLolipop} alt=""/>
</div>
<div className={css.container}>
<header>
<div style={{left:attrLabelPosition * 33.33 + '%'}}></div>
<div onClick={() => handleChangeAttrLabelPosition(0)}>元素结构树</div>
<div onClick={() => handleChangeAttrLabelPosition(1)}>添加元素</div>
<div onClick={() => handleChangeAttrLabelPosition(2)}>元素属性</div>
</header>
<div className={css.main}>
<div className={css.container} style={{left:attrLabelPosition * -99 + '%'}}>
<div className={css.elementStructureTree}></div>
<div className={css.linner}></div>
<div className={css.addElement}></div>
<div className={css.linner}></div>
<div className={css.elementAttribute}></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
} }

@ -0,0 +1,500 @@
@import '@/assets/default.scss';
.editPageNew {
@include noSelect;
position: relative;
height: 100%;
width: 100%;
overflow: hidden;
//background: #00ADB5;
display: flex;
flex-direction: column;
padding: 1rem;
box-sizing: border-box;
& > div.container {
position: relative;
flex: 1;
//
//background: #556270; /* fallback for old browsers */
//background: -webkit-linear-gradient(to right, #556270, #ff6b6b); /* Chrome 10-25, Safari 5.1-6 */
//background: linear-gradient(to right, #556270, #ff6b6b); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
background: #780206; /* fallback for old browsers */
background: -webkit-linear-gradient(to right, #061161, #780206); /* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to right, #061161, #780206); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
background: #B993D6; /* fallback for old browsers */
background: -webkit-linear-gradient(to right, #8CA6DB, #B993D6); /* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to right, #8CA6DB33, #B993D633); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
border-radius: 16px;
overflow: hidden;
box-shadow: 2px 2px 10px 2px #33333333;
& > div.main {
position: relative;
height: 100%;
display: flex;
& > div {
position: relative;
display: flex;
flex-direction: column;
overflow: hidden;
}
// 左侧目录结构树
& > div.directoryStructureTree {
position: relative;
flex-shrink: 0;
width: 260px;
border-right: 1px solid #fff;
box-sizing: border-box;
padding: 1rem;
& > div.container {
position: relative;
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
border-radius: 10px;
overflow: hidden;
& > * {
position: relative;
}
// 头部标题和logo
& > header {
flex-shrink: 0;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
&:before {
content: "";
position: absolute;
background-color: #f9949e;
width: 50px;
height: 30px;
}
& > div {
position: relative;
padding: 1rem;
@include beautifulBlur;
& > div.svg {
text-align: center;
& > img {
width: 30px;
}
}
& > div.title {
line-height: 2rem;
color: #333;
@include font-serif;
font-weight: 600;
font-size: 1.1rem;
}
}
}
// 主体目录
& > div.main {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
&:before, &:after {
content: '';
position: relative;
width: 80%;
height: 3px;
border-radius: 10px;
background-color: #fff;
margin: 0 auto;
}
div.container {
position: relative;
flex: 1;
}
}
// 说明和备注
& > footer {
flex-shrink: 0;
min-height: 50px;
display: flex;
flex-direction: column-reverse;
align-items: center;
& > div {
position: relative;
display: flex;
& > div.svg {
& > img {
width: 1.5rem;
}
}
& > div.title {
@include font-serif;
color: #333;
font-size: 14px;
}
}
}
}
}
// 右侧图形编辑器
& > div.graphicEditor {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 1rem 1rem 1rem 2rem;
box-sizing: border-box;
& > div.container {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
// 头部页面描述
& > header {
position: relative;
flex-shrink: 0;
height: 100px;
}
// 分割线
& > div.linner {
position: relative;
flex-shrink: 0;
height: 3px;
background-color: #fefefe;
width: 100%;
border-radius: 10px;
margin: 0 auto;
}
// 图形编辑器主体
& > div.main {
position: relative;
flex: 1;
//overflow: hidden;
display: flex;
flex-direction: column;
padding: 1rem 0 0 0;
box-sizing: border-box;
& > div.container {
position: relative;
flex: 1;
display: flex;
& > div {
position: relative;
}
// 左部缩略图
& > div.left {
width: 200px;
display: flex;
flex-direction: column;
& > div.title {
position: relative;
@include font-serif;
font-weight: 600;
flex-shrink: 0;
line-height: 2rem;
}
// 缩略图容器
& > div.thumbnail {
position: relative;
flex: 1;
overflow: hidden;
padding: 1rem 0;
display: flex;
flex-direction: column;
& > div.container {
flex: 1;
// 缩略图列表
& > div.moleculeList {
// 缩略图
& > div.moleculeBox {
position: relative;
margin: 1rem;
box-sizing: border-box;
box-shadow: 1px 1px 8px 0px #33333333;
height: 100px;
border-radius: 0.5rem;
cursor: pointer;
//border: 1px solid #17B97800;
transition: box-shadow ease-in-out 300ms, border ease-in-out 300ms;
overflow: hidden;
display: flex;
flex-direction: column;
&:hover {
box-shadow: 1px 1px 8px 1px #33333344;
}
& > div {
position: relative;
flex: 1;
margin: 0.5rem;
}
}
& > div.moleculeBoxChoose {
background: #FC354C33; /* fallback for old browsers */
background: -webkit-linear-gradient(to right, #0ABFBC33, #FC354C33); /* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to right, #0ABFBC33, #FC354C33); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
@include beautifulBlur;
}
}
// 添加缩略图
& > div.moleculeAdd {
position: relative;
margin: 1rem;
box-shadow: 1px 1px 8px 0px #33333333;
height: 100px;
border-radius: 0.5rem;
cursor: pointer;
//border: 1px solid #cdcdcd00;
display: flex;
align-items: center;
justify-content: center;
transition: box-shadow ease-in-out 300ms;
&:hover {
box-shadow: 1px 1px 8px 1px #33333344;
}
& > div {
font-size: 6rem;
line-height: 6rem;
padding-bottom: 0.5rem;
}
}
// 缩略图对象
}
}
}
// 中部编辑器
& > div.middle {
flex: 1;
min-width: 600px;
box-shadow: 2px 2px 10px 1px #33333333;
border-radius: 1rem;
margin-left: 1rem;
}
// 右部属性区
& > div.right {
position: relative;
display: flex;
flex-direction: column;
margin-left: 1rem;
//box-shadow: 2px 2px 10px 1px #33333333;
border-radius: 1rem;
transition: width ease-in-out 500ms, box-shadow ease-in-out 500ms, margin-left ease-in-out 500ms;
//padding: 1rem;
// 属性展示开关
& > div.switch {
position: absolute;
left: -40px;
background: #FFC93C;
height: 60px;
width: 40px;
top: 0;
bottom: 0;
margin: auto 0;
border-radius: 50px 0 0 50px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
& > img {
width: 25px;
line-height: 50px;
transition: transform ease-in-out 500ms;
}
}
& > div.switchClose {
position: absolute;
left: -40px;
background: #FFC93C;
height: 60px;
width: 40px;
top: 0;
bottom: 0;
margin: auto 0;
border-radius: 50px 0 0 50px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
& > img {
width: 25px;
line-height: 50px;
transform: rotate(180deg);
transition: transform ease-in-out 500ms;
}
}
// 属性主体
& > div.container {
position: relative;
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
margin: 1rem;
// 属性切换开关
& > header {
position: relative;
flex-shrink: 0;
display: flex;
overflow: hidden;
//border-radius: 1rem;
line-height: 2rem;
//background: #fefefe99;
width: 100%;
margin-bottom: 1rem;
//box-shadow: 1px 1px 8px 0px #33333333;
& > div {
@include font-serif;
font-weight: 600;
position: relative;
flex: 1;
text-align: center;
cursor: pointer;
overflow: hidden;
white-space: nowrap;
}
& > div:first-child {
position: absolute;
background: #3EC1D3;
width: 33.33%;
border-radius: 1rem;
height: 3px;
bottom: 0;
transition: left ease-in-out 300ms;
}
}
// 属性切换内容
& > div.main {
position: relative;
overflow: hidden;
flex: 1;
padding: 1rem;
box-sizing: border-box;
background: #66666611;
border-radius: 1rem;
box-shadow: 0px 0px 10px 1px #66666611 inset;
& > div.container {
position: relative;
width: 298%;
height: 100%;
left: 0;
display: flex;
transition: left ease-in-out 300ms;
& > div{
position: relative;
}
& > div.linner {
flex-shrink: 1 !important;
background: #fff;
//width: 1px;
height: 100%;
border-radius: 10px;
}
& > div.elementStructureTree {
flex: 1;
}
& > div.addElement {
flex: 1;
}
& > div.elementAttribute {
flex: 1;
}
}
}
}
}
}
}
}
}
}
}
}
// 添加大纲弹窗
.addThumbnailState {
position: relative;
height: 100%;
display: flex;
flex-direction: column;
.title {
position: relative;
flex-shrink: 0;
text-align: center;
@include font-serif;
font-weight: 600;
font-size: 1.2rem;
margin: 5px 0;
}
.main {
position: relative;
flex: 1;
display: flex;
align-items: center;
& input {
border-bottom: 1px solid #cdcdcd;
margin-left: 1rem;
line-height: 1.2rem;
}
}
.ok {
position: relative;
flex-shrink: 0;
display: flex;
justify-content: center;
}
}
Loading…
Cancel
Save