//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> ) }