完成元素添加,明日完成元素回显和元素切换,属性设置

main
expressgy 2 years ago
parent 71ea3834da
commit 152495d379
  1. 50
      src/components/Atom/index.jsx
  2. 84
      src/components/Atom/index.module.scss
  3. 1
      src/components/DirectoryStructureTree/index.jsx
  4. 3
      src/config/sys22.js
  5. 36
      src/store/defaultStore.js
  6. 230
      src/tools/index.js
  7. 143
      src/view/EditPageNew/index.jsx
  8. 4
      src/view/EditPageNew/index.module.scss

@ -0,0 +1,50 @@
// Nie 2023/2/8
import {useEffect, useState} from "react";
import css from './index.module.scss';
import {defaultStore} from "@/store/index.js";
export default function Atom(props) {
const AtomList = props.AtomList;
const [boxState, setBoxState] = useState(new Array(AtomList.length).fill(false));
//
function handleChangeBoxState(index){
const temporaryState = new Array(boxState.length).fill(false);
if(!boxState[index]){
temporaryState[index] = true;
}
setBoxState(temporaryState)
}
//
function handleAddElement(item){
defaultStore.setNewElementIdentify(item.identify)
}
useEffect(() => {
if(props.state){
setBoxState(new Array(boxState.length).fill(false))
}
}, [props.state])
return AtomList.map((item, index) => {
if (item.children && Array.isArray(item.children)) {
return <div key={index} className={css.atomBox}>
<div className={css.atomTitle} onClick={() => handleChangeBoxState(index)}>{item.name}</div>
<div className={boxState[index] ? [css.atomSon, css.atomBoxOpen].join(' ') : css.atomSon}>
<div className={css.atomDescribe}>{item.describe}</div>
<div>{Atom({AtomList: item.children, state: boxState[index]})}</div>
</div>
</div>
} else {
return <div key={index} className={css.atomBox}>
<div className={css.atomTitle}>
<div className={css.text} onClick={() => handleChangeBoxState(index)}>{item.name}</div>
<div className={css.cmd}>
<div onClick={() => handleAddElement(item)}>+</div>
</div>
</div>
<div className={boxState[index] ? [css.atomSon, css.atomBoxOpen].join(' ') : css.atomSon}>
<div className={css.atomDescribe}>{item.describe}</div>
</div>
</div>
}
})
}

@ -0,0 +1,84 @@
@import '@/assets/default.scss';
$open: open;
$close: close;
.atomBox {
position: relative;
margin: 0.5rem 0;
padding-left: 0.5rem;
.atomTitle {
@include font-serif;
font-weight: 600;
cursor: pointer;
display: flex;
& > div.text {
position: relative;
white-space: nowrap;
flex: 1;
}
& > div.cmd {
position: relative;
flex-shrink: 0;
width: 40px;
@include font-mono;
font-size: 30px;
}
}
.atomSon {
position: relative;
overflow: hidden;
//padding: 1rem 0;
box-sizing: border-box;
max-height: 0;
animation: close linear 300ms;
& > div.atomDescribe {
position: relative;
white-space: nowrap;
font-size: 14px;
padding-left: 1.2rem;
color: #666;
&:before {
content: "";
position: absolute;
left: 0.7rem;
width: 0.3rem;
height: 100%;
background: #1a1a1a;
}
}
}
.atomBoxOpen {
animation: open ease-in-out 300ms forwards;
&:hover {
overflow: overlay;
}
}
}
@keyframes open {
from {
max-height: 0;
}
to {
max-height: 300px;
}
}
@keyframes close {
to {
max-height: 0;
}
from {
max-height: 300px;
}
}

@ -1,3 +1,4 @@
// Nie 2023/2/8
import css from './index.module.scss'
export default function DirectoryStructureTree(props){
return props.tree.map((item, index) => {

@ -1,4 +1,7 @@
import icon from '../assets/react.svg'
export const config = {
projectName:'西安升帮联创技术服务有限公司',
icon,

@ -1,6 +1,6 @@
import { observable, action, computed, makeObservable} from "mobx";
import {observable, action, computed, makeObservable} from "mobx";
import {config} from "@/config/sys22";
import {NE} from "@/tools/index.js";
class DefaultStore {
// 系统配置
@ -9,36 +9,50 @@ class DefaultStore {
// 目录结构树
directoryStructureTree = [
{
name:'菜单1',
icon:'',
children:[
name: '菜单1',
icon: '',
children: [
{
name:'子菜单'
name: '子菜单'
}
]
},
{
name:'菜单2',
icon:'',
name: '菜单2',
icon: '',
},
];
// 元素结构参考
Atom = new NE();
// 新元素转存媒介
newElementIdentify = null;
constructor() {
// mobx6 和以前版本这是最大的区别
makeObservable(this, {
config: observable,
directoryStructureTree: observable,
Atom: observable,
newElementIdentify: observable,
setNewElementIdentify: action,
setName: action,
titleName: computed
});
}
setName(v) {
console.log('触发action');
this.name = v;
}
get titleName(){
return this.name+'___111';
get titleName() {
return this.name + '___111';
}
// 在页面添加新元素
setNewElementIdentify(identify) {
this.newElementIdentify = identify
}
}

@ -0,0 +1,230 @@
// 从元素模板中获取指定元素
export function getTargetElement(identify, AtomTemplate){
let element = null;
for(let i in AtomTemplate){
if(AtomTemplate[i].identify == identify){
element = AtomTemplate[i];
break
}else if(Array.isArray(AtomTemplate[i].children)){
const s = getTargetElement(identify, AtomTemplate[i].children);
if(s) element = s
}
}
return element
}
// 框架基础属性
const style1 = [
'background',
'backdrop-filter',
'position',
'top',
'bottom',
'left',
'right',
'width',
'height',
'margin',
'padding',
'border',
'border-radius',
'href',// 跳转
'overflow',
'全剧中',
]
// 文字属性
const style2 = [
'color', 'font-size', 'font-weight', '是否换行', '下划线', '首行缩进', 'line-height', 'text-align'
]
// 元素原型
export class NE {
constructor() {
return [
{
name: '框架',// 元素名称
// dir:true,// 是否为叶子节点
describe: "此类元素为页面提供骨架",// 描述
identify: 'frame',// 标识
config: {},
depth: '',
children: [
{
name: '单元素结构',// 元素名称
// dir:true,// 是否为叶子节点
describe: "此结构仅可容纳一个子元素",// 描述
identify: 'singleFrame',// 标识
config: {
childrenLength: 1,
childrenType: 'element',
middle: '中间件',
style: [...style1],
styleLimit: {},
event: {}
},
depth: '',
childElement: []
}, {
name: '横向多元素结构',// 元素名称
// dir:true,// 是否为叶子节点
describe: "此结构可在横向上排列多个子元素",// 描述
identify: 'transverseFrame',// 标识
config: {
childrenLength: 'n',
childrenType: 'element',
middle: '中间件',
style: [...style1, 'flex'],
styleLimit: {},
event: {}
},
depth: '',
childElement: []
}, {
name: '纵向多元素结构',// 元素名称
// dir:true,// 是否为叶子节点
describe: "此结构可在纵向上排列多个子元素",// 描述
identify: 'longitudinalFrame',// 标识
config: {
childrenLength: 'n',
childrenType: 'element',
middle: '中间件',
style: [...style1, 'flex'],
styleLimit: {},
event: {}
},
depth: '',
childElement: []
}
]
},
{
name: '内元素',// 元素名称
// dir:true,// 是否为叶子节点
describe: "",// 描述
identify: 'innerElement',// 标识
config: {},
depth: '',
children: [
{
name: '行内文本',// 元素名称
// dir:true,// 是否为叶子节点
describe: "",// 描述
identify: 'span',// 标识
config: {
childrenType: 'text',
middle: '中间件',
style: [...style2],
styleLimit: {},
event: {}
},
depth: '',
}, {
name: '块文本',// 元素名称
// dir:true,// 是否为叶子节点
describe: "",// 描述
identify: 'blockText',// 标识
config: {
childrenType: 'text',
middle: '中间件',
style: [...style2, 'flex'],
styleLimit: {},
event: {}
},
depth: '',
}, {
name: '图像',// 元素名称
// dir:true,// 是否为叶子节点
describe: "",// 描述
identify: 'image',// 标识
config: {
childrenType: 'images',
middle: '中间件',
style: [...style1, 'flex'],
styleLimit: {},
event: {}
},
depth: '',
}, {
name: '视频',// 元素名称
// dir:true,// 是否为叶子节点
describe: "",// 描述
identify: 'video',// 标识
config: {
childrenType: 'video',
middle: '中间件',
style: [...style2, 'flex'],
styleLimit: {},
event: {}
},
depth: '',
}
]
},
{
name: '输入元素',// 元素名称
// dir:true,// 是否为叶子节点
describe: "信息录入元素",// 描述
identify: 'input',// 标识
config: {},
depth: '',
children: [
{
name: '基本输入',// 元素名称
// dir:true,// 是否为叶子节点
describe: "单行文本输入",// 描述
identify: 'input-text',// 标识
config: {
childrenType: 'text',
middle: '中间件',
style: [...style2],
styleLimit: {},
event: {}
},
depth: '',
}, {
name: '块输入',// 元素名称
// dir:true,// 是否为叶子节点
describe: "多行文本输入",// 描述
identify: 'input-textarea',// 标识
config: {
childrenType: 'text',
middle: '中间件',
style: [...style2, 'flex'],
styleLimit: {},
event: {}
},
depth: '',
}, {
name: '代码块',// 元素名称
// dir:true,// 是否为叶子节点
describe: "等宽字体的代码多行输入",// 描述
identify: 'input-code',// 标识
config: {
childrenType: 'text',
middle: '中间件',
style: [...style1, 'flex'],
styleLimit: {},
event: {}
},
depth: '',
}, {
name: '等待开发',// 元素名称
// dir:true,// 是否为叶子节点
describe: "可拓展的输入选项",// 描述
identify: 'nomal',// 标识
config: {
childrenType: 'text',
middle: '中间件',
style: [...style2, 'flex'],
styleLimit: {},
event: {}
},
depth: '',
}
]
},
];
}
}

@ -1,16 +1,20 @@
import {useState, useEffect} from 'react'
import {defaultStore} from '@/store/index'
import {useState, useEffect} from 'react';
import {defaultStore} from '@/store/index';
import {getTargetElement, NE} from "@/tools/index.js";
import DirectoryStructureTree from "@/components/DirectoryStructureTree/index.jsx";
import Modal from '@/components/Modal';
import Button from "@/components/Button/index.jsx";
import Atom from "@/components/Atom/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'
import {autorun} from "mobx";
export default function EditPageNew() {
//
const [thumbnailList, setThumbnailList] = useState([{name: '大纲', choose: false}]);
const [thumbnailList, setThumbnailList] = useState([]);
//
const [addThumbnailState, setAddThumbnailState] = useState(false);
//
@ -18,47 +22,96 @@ export default function EditPageNew() {
// box
const moleculeBoxChoose = [css.moleculeBoxChoose, css.moleculeBox].join(' ');
//
//
const [nowContentList, setNowContentList] = useState({});
//
const [nowContent, setNowContent] = useState({});
//
const [nowElement, setNowElement] = useState(null);
//
const [attributeSwitch, setAttributeSwitch] = useState(false);
//
const [attrLabelPosition, setAttrLabelPosition] = useState(0);
useEffect(() => {
// console.log(thumbnailList)
// console.log(defaultStore.Atom)
}, [thumbnailList])
//
useEffect(() => {
const handleNewElementChange = autorun(() => {
//
if(!defaultStore.newElementIdentify)return;
//
//
if(!nowElement){
alert('未选中元素,无法添加。')
defaultStore.setNewElementIdentify(null);
return;
}
// Element
if(nowElement.config.childrenType != 'element'){
alert('此元素不支持添加子元素。');
defaultStore.setNewElementIdentify(null);
return;
}
//
if(nowElement.config.childrenLength == 1 && nowElement.childElement.length != 0){
alert('此元素无法容纳更多元素');
defaultStore.setNewElementIdentify(null);
return;
}
const newElement = getTargetElement(defaultStore.newElementIdentify, new NE())
nowElement.childElement.push(newElement);
setNowElement(nowElement)
defaultStore.setNewElementIdentify(null);
})
return () => {
handleNewElementChange()
}
}, [nowElement])
//
function closeThumbnailModal() {
setNewThumbnailName('')
setAddThumbnailState(false)
}
//
//
function addThumbnail() {
thumbnailList.push({
name: newThumbnailName
})
const newThumbnail = {name: newThumbnailName, id:Math.random(), choose:false}
//
thumbnailList.push(newThumbnail);
// ()
const newElement = getTargetElement('singleFrame' ,new NE());
nowContentList[newThumbnail.id] = newElement
setNowContentList({...nowContentList})
setThumbnailList(thumbnailList)
closeThumbnailModal()
}
//
//
function chooseThumbnail(index) {
thumbnailList.forEach(item => {
item.choose = false;
})
thumbnailList[index].choose = true
thumbnailList[index].choose = true;
setThumbnailList([...thumbnailList])
setNowContent(thumbnailList[index])
setNowContent(nowContentList[thumbnailList[index].id]);
if(nowContent.identify = 'singleFrame' && nowContentList[thumbnailList[index].id].childElement.length == 0){
setNowElement(nowContentList[thumbnailList[index].id]);
}
}
useEffect(() => {
console.log(thumbnailList)
}, [thumbnailList])
//
const [nowContent, setNowContent] = useState({})
//
const [attributeSwitch, setAttributeSwitch] = useState(false);
//
const [attrLabelPosition, setAttrLabelPosition] = useState(0);
//
function handleChangeAttrLabelPosition(index){
function handleChangeAttrLabelPosition(index) {
setAttrLabelPosition(index)
}
return <div className={css.editPageNew}>
<div className={css.container}>
<div className={css.main}>
@ -146,18 +199,50 @@ export default function EditPageNew() {
</div>
<div className={css.container}>
<header>
<div style={{left:attrLabelPosition * 33.33 + '%'}}></div>
<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.container}
style={{left: attrLabelPosition * -99 + '%'}}>
{/* 页面节点树*/}
<div className={css.elementStructureTree}>
<div>根节点</div>
<div></div>
</div>
<div className={css.linner}></div>
<div className={css.addElement}></div>
{/*新增元素*/}
<div className={css.addElement}>
<Atom AtomList={defaultStore.Atom}></Atom>
</div>
<div className={css.linner}></div>
<div className={css.elementAttribute}></div>
{/*元素属性*/}
<div className={css.elementAttribute}>
<div>
<div>样式属性</div>
<div>
<div>
<div>父元素相关属性</div>
<div></div>
</div>
<div>
<div>元素属性</div>
<div></div>
</div>
</div>
</div>
<div>
<div>事件</div>
<div>
<div>点击事件</div>
</div>
</div>
<div>
<div>数据关系</div>
</div>
</div>
</div>
</div>
</div>

@ -334,6 +334,7 @@
display: flex;
align-items: center;
justify-content: center;
box-shadow: -3px 3px 10px -3px #33333333;
& > img {
width: 25px;
@ -356,6 +357,7 @@
display: flex;
align-items: center;
justify-content: center;
box-shadow: -3px 3px 10px -3px #33333333;
& > img {
width: 25px;
@ -432,7 +434,7 @@
& > div.linner {
flex-shrink: 1 !important;
background: #fff;
//width: 1px;
width: 2px;
height: 100%;
border-radius: 10px;
}

Loading…
Cancel
Save