jianshu-react-mongoose/Addition and deletion of first-level authority and second-level authority

//backend
//db.js
const mongoose = require('mongoose')
mongoose.set('strictQuery',true)

mongoose.connect('mongodb://127.0.0.1:27017/db_jianshu')

conn=mongoose.connection
conn.on('open',()=>{<!-- -->
    console.log('Database connection succeeded')
})

conn.on('error',err=>{<!-- -->
    console.log('Database connection failed')
})

module.exports=mongoose
//model.js
const mongoose = require('./db')

const Schema = mongoose.Schema

const jianshuSchema=new Schema({<!-- -->
    title: String,
    content: String,
    checked: Boolean,
    level:Number,
    pid:{<!-- -->
        type:Schema.Types.ObjectId,
        ref:'jianshu'
    }
},{<!-- -->versionKey:false})

let jianshuModel=mongoose.model('jianshu',jianshuSchema,'jianshu')

// jianshuModel.create({<!-- -->
// title:'Vue two-way data binding',
// content:'two-way data binding',
// checked: false,
//level:2,
// pid:'64671c9e5708278b1206224c'
// })

module.exports={<!-- -->
    jianshuModel
}

//index.js
var express = require('express');
var router = express. Router();
var jwt = require('jsonwebtoken')
var multiparty = require('multiparty')
var {<!-- --> jianshuModel} = require('../model/model')

//Display level one permissions
router.get('/level1', async function(req, res) {<!-- -->
  let level1=await jianshuModel.find({<!-- -->level:1})
  res.send({<!-- -->
    code:200,
    level1
  });
});

//Display secondary permissions
router.post('/level2', async function(req, res) {<!-- -->
  let level1_id=req.body.id
  console.log(level1_id,'11111');
  let level2=await jianshuModel.find({<!-- -->pid:level1_id,level:2})
  console.log(level2,'22222');
  res.send({<!-- -->
    code:200,
    level2
  });
});

//Add a level of permission
router. post('/add1', async function(req, res) {<!-- -->
  let body=req.body
  await jianshuModel.create({<!-- -->...body})
  res.send({<!-- -->
    code:200,
  });
});

//Add secondary permissions
router. post('/add2', async function(req, res) {<!-- -->
  let body=req.body
  await jianshuModel.create({<!-- -->...body})
  res.send({<!-- -->
    code:200,
  });
});

//Delete level one permission
router.delete('/del1/:id', async function (req, res){<!-- -->
  let id=req.params.id
  await jianshuModel.deleteOne({<!-- -->_id:id})
  res.send({<!-- -->
    code:200
  })
})

//Delete secondary permissions
router.delete('/del2/:id', async function (req, res){<!-- -->
  let id=req.params.id
  await jianshuModel.deleteOne({<!-- -->_id:id})
  res.send({<!-- -->
    code:200
  })
})

module.exports = router;

// front-end react
//index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css'
import {<!-- -->BrowserRouter as Router} from 'react-router-dom'
import {<!-- --> Provider } from 'react-redux'
import store from './store/counterSlice'

const root = ReactDOM.createRoot(document.getElementById('root'));
root. render(
  <>
  <Provider store={<!-- -->store}>
    <Router>
      <App />
    </Router>
  </Provider>
  </>
);
//index.css
*{<!-- -->
    margin: 0;
    padding: 0;
}

html,body,#root{<!-- -->
    height: 800px;
    
}
//router.js
import Home from "./components/Home"
import List from "./components/List"
import Login from "./components/Login"
import Tip from "./components/Tip"

const routes=[
    {<!-- -->
        path:'/',
        element:<Home></Home>,
    },
    {<!-- -->
        path:'/login',
        element:<Login></Login>
    },
    {<!-- -->
        path:'/list',
        element:<List></List>
    },
    {<!-- -->
        path:'/tip',
        element:<Tip></Tip>
    }
        
]

export default routes

//App.js
import React from 'react'

import {<!-- --> useRoutes } from 'react-router-dom'
import routes from './router'

export default function App(props) {<!-- -->
  return (
    <>
        {<!-- -->useRoutes(routes)}
    </>
  )
}

//Home.js
import React from 'react'
import './Home.scss'
import {<!-- --> Button} from 'antd-mobile'
import {<!-- --> message} from 'antd';
import {<!-- -->useNavigate} from 'react-router-dom'
export default function Home() {<!-- -->
  const navigate = useNavigate()
  const [messageApi, contextHolder] = message. useMessage();
  const warning = () => {<!-- -->
    if(!sessionStorage.getItem('token')){<!-- -->
      messageApi.open({<!-- -->
        type: 'warning',
        content: 'Please log in first to continue',
      });
    }else{<!-- -->
      navigate('/list')
    }
  };

  return (
    <div className='container'>
      {<!-- -->/* message prompt box */}
      {<!-- -->contextHolder}
      <div className="top">
        <img src="/img/jianshu.png" alt="" />
        <div className="btn">
          <Button onClick={<!-- -->()=>navigate('/login')}>Login</Button>
          <Button >Register</Button>
          <Button color='warning' onClick={<!-- -->warning}>Write an article</Button>
        </div>
      </div>
      <div className="content">
        <img src="/img/jianshu2.png" alt="" />
      </div>
    </div>
  )
}
//Home.scss
.container{<!-- -->
    width: 100%;
    height: 100%;
    
    .top{<!-- -->
        height: 60px;
        width: 100%;
        display: flex;
        align-items: center;
        img{<!-- -->
            width: 60%;
            height: 100%;
        }
        .btn{<!-- -->
            height: 40px;
            width: 40%;
            display: flex;
            align-items: center;
            justify-content: space-around;
        }
    }

    .content{<!-- -->
        width: 100%;
        padding: 0 70px;
        box-sizing: border-box;
        img{<!-- -->
            height: 650px;
            width: 100%;
        }
    }
}
//List.js
import React,{<!-- -->useEffect, useState} from 'react'
import {<!-- -->useNavigate} from 'react-router-dom'
import './List.scss'
import axios from 'axios'
import {<!-- -->SettingOutlined} from '@ant-design/icons';
import {<!-- --> DownOutlined } from '@ant-design/icons';
import {<!-- --> Dropdown, Space } from 'antd';
export default function List() {<!-- -->
    const navigate = useNavigate()
    const [flag,setFlag]=useState(false) //Add the display and hide of the collection form
    const [flag2,setFlag2]=useState(false) //Add the display and hide of the article form
    const [id,setId]=useState(0) //Currently click on the _id of the first-level permission
    const [id2,setId2]=useState(0) //Currently click on the _id of the first-level permission
    const [title,setTitle]=useState('')
    
    useEffect(()=>{<!-- -->
        getlevel1()
    },[])

    const [level1,setLevel1]=useState([])
    const [level2,setLevel2]=useState([])
    const [item,setItem]=useState({<!-- -->})
    
    const [title2, setTitle2] = useState('')
    const [content2, setContent2] = useState('')
    const [checked, setChecked] = useState(false)

    // const items = [
    // {<!-- -->
    // label: <a href="https://www.antgroup.com">delete</a>,
    // key: '0',
    // },
    // {<!-- -->
    // label: <a href="https://www.aliyun.com">2nd menu item</a>,
    // key: '1',
    // },
       
    // ];
    
    async function getlevel1(){<!-- -->
        let {<!-- -->data}=await axios.get('http://127.0.0.1:8000/level1')
        console.log(data,'dddddd');
        if(data.code==200){<!-- -->
            setLevel1(data.level1)
        }
    }
    async function show2(idx){<!-- -->
        console.log(idx,'11id');
        setId(idx)
        let {<!-- -->data}=await axios.post('http://127.0.0.1:8000/level2',{<!-- -->id:idx})
        if(data.code==200){<!-- -->
            setLevel2(data.level2)
        }
    }

    function show2info(item,idx){<!-- -->
        setItem(item)
        setId2(idx)
    }
    //Add a level of permission
    async function add1(){<!-- -->
        let obj={<!-- -->
            title: title,
            checked: false,
            content:'',
            level: 1
        }

        await axios. post('http://127.0.0.1:8000/add1', obj)
        setFlag(false)
        getlevel1()
    }

    //Add a level of permission
    async function add2(){<!-- -->
        let obj={<!-- -->
            title: title2,
            checked: false,
            content: content2,
            level: 2,
            pid:id
        }

        await axios. post('http://127.0.0.1:8000/add2', obj)
        setFlag2(false)
        getlevel1()
        show2()
        show2info()
        alert('add successfully')
        navigate('/tip')
    }
    async function del1(idx){<!-- -->
        await axios.delete('http://127.0.0.1:8000/del1/' + idx)
        getlevel1()
        show2()

    }

    async function del2(idx){<!-- -->
        let {<!-- -->data}=await axios.delete('http://127.0.0.1:8000/del2/' + idx)
        if(data.code==200){<!-- -->
            // setTitle2('')
            // setContent2('')
            //After deleting the secondary permission, the content of the prompt box on the right has not disappeared
            getlevel1()
            show2()
        }
        
        
    }
  return (
    <div className='container3'>
        <div className="left">
            <div className="top">
                <div>Back to Homepage</div>
            </div>
            <div className="addbtn" onClick={<!-- -->()=>setFlag(!flag)}>
                 + New Collection
            </div>
            {<!-- -->/* Add the display and hide of the collection form */}
            {<!-- -->
                flag?(
                    <div className="inputbox">
                        <div className="input">
                            <input type="text" value={<!-- -->title} onChange={<!-- -->(e)=>setTitle(e.target.value)}/>
                        </div>

                        <div className='sub'>
                            <button onClick={<!-- -->add1}>Submit</button>
                            <button onClick={<!-- -->()=>setTitle('')}>Cancel</button>
                        </div>
                    </div>
                ):''
            }
            <ul>
                {<!-- -->
                    level1.map(item=><li key={<!-- -->item._id} onClick={<!-- -->()=>show2(item._id)}
                                    className={<!-- -->id==item._id?'activeli':''}
                                   >
                                   {<!-- -->item.title}
                                   <SettingOutlined onClick={<!-- -->()=>{<!-- -->del1(item._id)}}/>
                                   </li>)
                }
                
            </ul>
        </div>
        <div className="middle">
            <div className="addbtn1">
                 + New article
            </div>
            {<!-- -->/* level2 */}
            <ul>
                {<!-- -->
                    level2.map(item=><li key={<!-- -->item._id} onClick={<!-- -->()=>{<!-- -->show2info(item,item ._id)}}
                    className={<!-- -->id2==item._id?'activeli':''}
                   >
                    <div className='d1'>
                        <div><input type="checkbox" /></div>
                        
                        <div>Word Count{<!-- -->item.content.length}</div>
                    </div>
                    <div className='d2'>
                        <div><input type="text" value={<!-- -->item.title} /></div>
                        <div><input type="text" value={<!-- -->item. content} /></div>
                        {<!-- -->/* <div><h3>{item.title}</h3></div> */}
                        {<!-- -->/* <div><h5>{item. content}</h5></div> */}
                    </div>
                    <div className='d3'>
                        {<!-- -->/* <SettingOutlined onClick={(e) => e.preventDefault()}/> */}
                        <Dropdown
                            menu={<!-- -->{<!-- -->
                                items:[
                                    {<!-- -->label:<a onClick={<!-- -->()=>{<!-- -->del2(item._id)}}>delete</a>,key :'0'},
                                    {<!-- -->label:<a>Mobile Collection</a>,key:'1'}
                            
                                    ]
                            }}
                            trigger={<!-- -->['click']}
                        >
                            <SettingOutlined onClick={<!-- -->(e) => e.preventDefault()}/>
                        </dropdown>
                    </div>
                   </li>)
                }
                {<!-- -->
                    flag2?(
                        <li>
                            <div className='d1'>
                                {<!-- -->/* <div><input type="checkbox" /></div> */}
                                
                                <div>Word count{<!-- -->content2.length}</div>
                            </div>
                            <div className='d2'>
                                <div><input type="text" value={<!-- -->title2} onChange={<!-- -->(e)=>setTitle2(e.target.value)}/></ div>
                                <div><input type="text" value={<!-- -->content2} onChange={<!-- -->(e)=>setContent2(e.target.value)}/></ div>
                            </div>
                            <div className='d3'>
                                <SettingOutlined />
                            </div>
                        </li>

                    ):''
                }
                
            </ul>
            <div className="addbtn2" onClick={<!-- -->()=>setFlag2(true)}>
                 + Create a new article below
            </div>
        </div>
        
        {<!-- -->
            flag2?(
                <div className="right2">
                    <div className="title">
                        <input type="text" value={<!-- -->title2} onChange={<!-- -->(e)=>setTitle2(e.target.value)}/>
                    </div>
                    <div className="wrist">
                        <span onClick={<!-- -->add2}>Publish article</span>
                    </div>
                    <div className="text">
                        <textarea cols="100" rows="50" value={<!-- -->content2} onChange={<!-- -->(e)=>setContent2(e.target.value)}>< /textarea>
                    </div>
                </div>

            ):(
                <div className="right">
                    <div className="title">
                        <input type="text" value={<!-- -->item.title} />
                    </div>
                    <div className="wrist">
                        <span >Publish an article</span>
                    </div>
                    <div className="text">
                        <textarea cols="100" rows="50" value={<!-- -->item.content}></textarea>
                    </div>
                </div>
            )
        }
        

    </div>
  )
}

//List.scss
.container3{<!-- -->
    width: 100%;
    height: 100%;
    display: flex;
    .left{<!-- -->
        flex:0.17;
        height: 100%;
        background-color: rgba(64,64,64,0.9);
        .top{<!-- -->
            height: 70px;
            width: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
            div{<!-- -->
                height: 40px;
                width: 70%;
                border-radius: 30px;
                border: 1px solid rgb(242, 119, 75);
                color: rgb(242, 119, 75);
                display: flex;
                justify-content: center;
                align-items: center;
            }
        }

        .addbtn{<!-- -->
            height: 50px;
            font-size: 15px;
            color: #fff;
            font-weight: bolder;
            padding-left: 8px;
            line-height: 50px;
        }
        .inputbox{<!-- -->
            width: 100%;
            padding: 0 5px;
            box-sizing: border-box;
            .input{<!-- -->
                height: 30px;
                width: 100%;
                input{<!-- -->
                    height: 100%;
                    width: 100%;
                    background-color: #666;
                    border: 0;
                }
            }

            .sub{<!-- -->
                height: 40px;
                width: 100%;
                display: flex;
                justify-content: center;
                align-items: center;
                margin-top: 10px;
                button{<!-- -->
                    height: 30px;
                    width: 60px;
                    background-color: #666;
                    border: 1px solid green;
                    border-radius: 15px;
                    margin: 0 5px;

                }
            }
        }

        ul{<!-- -->
            width: 100%;
            li{<!-- -->
                height: 40px;
                width: 100%;
                padding: 0 10px;
                box-sizing: border-box;
                line-height: 40px;
                color:#fff;
                display: flex;
                justify-content: space-between;
                align-items: center;
            }

            .activeli{<!-- -->
                height: 40px;
                width: 100%;
                padding: 0 10px;
                line-height: 40px;
                color:#fff;
                background-color: #666;
                display: flex;
                justify-content: space-between;
                align-items: center;
            }
        }

    }
    .middle{<!-- -->
        flex:0.2;
        border-right: 1px solid #eee;
        .addbtn1{<!-- -->
            height: 50px;
            font-size: 15px;
            color: #666;
            font-weight: bolder;
            padding-left: 8px;
            line-height: 50px;
            border-bottom: 1px solid #eee;
        }
        ul{<!-- -->
            width: 100%;
            li{<!-- -->
                height: 70px;
                width: 100%;
                padding-left: 10px;
                box-sizing: border-box;
                color:#666;
                border-bottom: 1px solid #eee;
                display: flex;
               .d1{<!-- -->
                flex:0.3;
                height: 70px;
                display: flex;
                flex-direction: column;
                justify-content: center;
                
                
               }

               .d2{<!-- -->
                flex:0.5;
                height: 100%;
                display: flex;
                flex-direction: column;
                justify-content: center;
                input{<!-- -->
                    border: 0;
                    outline: none;
                    background-color: none;
                }
                
                // /* Eliminate the input background color of autofill */
                // input:-webkit-autofill {<!-- -->
                // -webkit-box-shadow: 0 0 0px 1000px transparent inset;
                // transition: background-color 5000s ease-in-out 0s;
                // }
  
                
               }

               .d3{<!-- -->
                flex:0.2;
                height: 100%;
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;

               }

            }

            .activeli{<!-- -->
                height: 70px;
                width: 100%;
                padding-left: 10px;
                color:#666;
                background-color: #ccc;
                border-bottom: 1px solid #eee;

            }
        }

        .addbtn2{<!-- -->
            height: 50px;
            font-size: 15px;
            color: #ccc;
            font-weight: bolder;
            padding-left: 8px;
            line-height: 50px;
            
        }
    }
    .right{<!-- -->
        flex:0.5;
        height: 100%;
        .title{<!-- -->
            height: 60px;
            width: 100%;
            border-bottom: 1px solid #eee;
            input{<!-- -->
                height: 100%;
                width: 90%;
                outline: none;
                border: 0;
            }
        }
        .wrist{<!-- -->
            height: 40px;
            line-height: 40px;
            width: 100%;
            background-color: #c1c1c1;
            span{<!-- -->
                margin-left: 680px;
            }

        }

        .text{<!-- -->
            // height: calc(100% - 60px);
            width: 100%;
            textarea{<!-- -->
                height: 100%;
                width: 1005;
                border: 0;
                outline: 0;
            }
        }
    }

    .right2{<!-- -->
        flex:0.5;
        height: 100%;
        .title{<!-- -->
            height: 60px;
            width: 100%;
            border-bottom: 1px solid #eee;
            input{<!-- -->
                height: 100%;
                width: 90%;
                outline: none;
                border: 0;
            }
        }
        .wrist{<!-- -->
            height: 40px;
            line-height: 40px;
            width: 100%;
            background-color: #c1c1c1;
            span{<!-- -->
                margin-left: 680px;
            }

        }

        .text{<!-- -->
            // height: calc(100% - 60px);
            width: 100%;
            textarea{<!-- -->
                height: 100%;
                width: 1005;
                border: 0;
                outline: 0;
            }
        }
    }
}

//Login.js
import React from 'react'
import './Login.scss'
import {<!-- -->useNavigate} from 'react-router-dom'
import {<!-- --> Button, Checkbox, Form, Input } from 'antd';
export default function Login () {<!-- -->
  const navigate = useNavigate()

  const onFinish = (values) => {<!-- -->
    console.log('Success:', values);
    sessionStorage.setItem('token','xxxxxx')
    navigate('/list')

  };
  const onFinishFailed = (errorInfo) => {<!-- -->
    console.log('Failed:', errorInfo);
  };

  function validateTel(rule,value,cb){<!-- -->
    const reg=/^(?:(?:\ + |00)86)?1\d{10}$/
    if(!reg.test(value)){<!-- -->
      cb('The phone number is incorrect')
    }else{<!-- -->
      cb()
    }

  }
  return (
    <div className='container2'>
      <div className="left">
        <img src="/img/jianshu3.png" alt="" />
      </div>
      <div className="right">
          <div className="loginbox">
            <Form
              name="basic"
              labelCol={<!-- -->{<!-- -->
                span: 8,
              }}
              wrapperCol={<!-- -->{<!-- -->
                span: 16,
              }}
              style={<!-- -->{<!-- -->
                maxWidth: 600,
              }}
              initialValues={<!-- -->{<!-- -->
                remember: true,
              }}
              onFinish={<!-- -->onFinish}
              onFinishFailed={<!-- -->onFinishFailed}
              autoComplete="off"
            >
              <Form.Item
                label="Tel"
                name="tel"
                rules={<!-- -->[
                  {<!-- -->
                    required: true,
                    message: 'Mobile phone number cannot be empty',
                  },
                  {<!-- -->
                    validator:validateTel
                  }
                ]}
              >
                <Input />
              </Form.Item>

              <Form.Item
                label="Password"
                name="password"
                rules={<!-- -->[
                  {<!-- -->
                    required: true,
                    message: 'Password cannot be empty',
                  },
                  {<!-- -->
                    min:3,max:5,message:'The length is 3-5 characters'
                  }
                ]}
              >
                <Input. Password />
              </Form.Item>

              <Form.Item
                name="remember"
                valuePropName="checked"
                wrapperCol={<!-- -->{<!-- -->
                  offset: 8,
                  span: 16,
                }}
              >
                <Checkbox>Remember me</Checkbox>
              </Form.Item>

              <Form.Item
                wrapperCol={<!-- -->{<!-- -->
                  offset: 8,
                  span: 16,
                }}
              >
                <Button type="primary" htmlType="submit">
                  Log in
                </Button>
              </Form.Item>
            </Form>
          </div>
      </div>
        
    </div>
  )
}

//Login.scss
.container2{<!-- -->
    width: 100%;
    height: 100%;
    background: #f2f2f2;
    display: flex;
    padding: 50px 80px;
    box-sizing: border-box;
    .left{<!-- -->
        flex: 0.5;
        // height: 500px;
        display: flex;
        justify-content: center;
        align-items: center;
        img{<!-- -->
            // height: 100%;
            width: 100%;
        }
    }

    .right{<!-- -->
        flex:0.45;
        display: flex;
        justify-content: center;
        align-items: center;
        margin-left: 10px;
        .loginbox{<!-- -->
            height: 400px;
            width: 350px;
            background-color: #fff;
            display: flex;
            justify-content: center;
            align-items: center;
        }
    }
}

//Tip.js
import React from 'react'

export default function Tip() {<!-- -->
  return (
    <div>
        Added successfully
    </div>
  )
}