尚-react项目后台管理系统_第1页
尚-react项目后台管理系统_第2页
尚-react项目后台管理系统_第3页
尚-react项目后台管理系统_第4页
尚-react项目后台管理系统_第5页
已阅读5页,还剩148页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1章:准

项目-管理系. 管理的 包括前端PC应用和后端应 /// 前端:ReactAntdAxiosES6后端NodeExpressMongodb1.2.其它的界面功能运行其它的界面功能运行final技术选前端路API/接你能从此项目中学到什么流程及开发方法熟悉一个项目的开发流程学会模块化、组件化、工程化的开发模npmconfigsetregistryyarnconfigsetyarninit-ynpminit- npm掌握使用create-react-app脚手架初始化react学会使用node+express+mongoose+mongodb搭 应1.6.2.React插件或第库掌握使用react-router-dom开发单页应学会使用redux+react-redux+redux-thunk掌握使用axios/jsonp与后端进行数据交掌握使用antd学会使用echarts/bizcharts学会使用react-draft-wysiwyg实现富文本编辑1.7.npm/yarnyarn命令文档/"usernamegitconfigglobal gitinitgitadd //添加gitcommitmmessagegitremoteaddoriginurl//关联 gitpushoriginmaster//推送本地master分支 gitcheckoutbdevgitpushogigindev//推送本地dev分支 gitpullorigindev// gitcloneurl// yarnadd npminstall ck@3.2.1-yarnaddw ck@3.2.1-Dnpminstallw ck@3.2.1-D yarnglobaladdw npminstallw ck-gyarnremovew npmremovew ck-Syarnglobalremove npmremove ck-yarnrunnpmrunnpminfo1.8.git参考手册/2章:应用开发详开启项目开使用create-react-app(脚手架)搭建项目create-react-app是 提供的用于搭建基于 ck+es6项目的脚手操作npmnpminstall-gcreate-react-app:全 工create-react-appreact-admin cdreact-adminnpmstart:部分同学可能会出现包版本差异的导致异常的问解决:添加.env编码测试与打包发布项目npm:(live-reload),npmrunnpminstall-gserveservebuild:功能需求分,项目源码基 设基本结构importimportReact,{Component}from应用根组classAppextends{render()returnreturn)}}exportdefaultimportimportReactfromimportReactDOMfrom'react-importAppfromReactDOM.render(<App 2.3.3..参考文档组件库包2.4.3.实现组件的按需打yarnadd 依赖模 constconst{override,fixBabelImports}=require('customize-modulemodule.exportsoverride(fixBabelImports('import',{libraryName:'antd',libraryDirectory:'es',style:'css',修改配置.4.在应用中使用antdimportReact,{Component}from'react'import{Button,message}from'antd'classAppextendsComponenthandleClick=()}render()return')}}"scripts":"start":"react-app-rewiredstart","build":"react-app-rewiredbuild","test":"react-app-rewiredtest","eject":"react-scriptseject"自定antd需求yarnaddyarnaddlessless-const{override,fixBabelImports,const{override,fixBabelImports,addLessLoader}=require('customize-module.exportsoverride(fixBabelImports('import',{libraryName:'antd',libraryDirectory:'es',style:true,javascriptEnabled:modifyVars:{'@primary-color':应用中使用的组件项目中用到的antd组.引入路路由包:react-router-2.5.2.前台应用路由.3.路由组件用户登陆的路由组importReact,{Component}fromexportdefaultclassLoginextends{render()return)}}importReact,{Component}importReact,{Component}fromexportdefaultclassAdminextends{render()return)}}路由:importReact,{Component}fromimport{BrowserRouter,Switch,Route}from'react-router-importLoginfrom'./pages/login/login'importAdminfrom应用根组classAppextendsComponentrender()return<Routepath='/login'<Routepath='/')}}exportexportdefault2.6. 组件(不与交互.6.1.静态组件资——————————————————————————h6margin:padding:}h6font-size:font-weight:}ullist-style:}textarea{margin:}htmlbox-sizing:border-}*,*:before,*:afterbox-sizing:}}{height:auto;max-width:100%;}audiomax-width:}iframeborder:}tableborder-collapse:collapse;border-spacing:0;}thpadding:text-align:}html,bodyheight:}#root注意:index.htmlheight:}width:.loginwidth:height:background-image:url('./images/bg.jpg');background-size:100%100%;{display:flex;align-items:center;height:80px;background-color:rgba(21,20,13,imgwidth:40px;height:40px;margin-left:50px;}h1font-size:30px;color:white;margin:00015px;}}.login-content{margin:50pxauto;width:400px;height:300px;background-color:#fff;padding:20px40px;h3font-size:30px;font-weight:bold;text-align:center;margin-bottom:20px;}.login-form.login-form-buttonwidth:}}}}importimportReact,{Component}from'react'import{}fromimportlogofrom'./images/logo.png'import'./login.less'constItem=登陆classLoginextends{render()return<div<imgsrc={logo} <Inputprefix={<Icontype="user"style={{color: placeholder="用户名<Inputprefix={<Icontype="lock"style={{color: type="password" .6.2.前台表单验证与数据收集用户名的要求的要必须大于等于4必须小于等于12importReact,{Component}from'react'import{}fromimportlogofrom'./images/logo.png'import'./login.less'constItem=<Buttontype="primary"htmlType="submit"className="login-form-)}}exportdefault——————————————————————————classLoginextendsComponent登login=(e)=> 事件默认行为(不提交表单//进行表单ps.form.validateFields(async(err,values)=>if(!err)校验成const{username,password}=console.log('提交登陆请求'username}else校验失}}*自定validator=(rule,value,callback)=>//console.log(rule,constlength=value&&value.lengthconstpwdReg=/^[a-zA-Z0-9_]+$/if(!value)//callback如果不传参代表校验成功,如果传参代表校验失败,并且会callback('必须输 }elseif(length<4){ 必须4位')}elseif(length> }elseif }elsecallback()//必须调用}——————————————————————————}render()const{getFieldDecorator}=ps.formreturn(<div<imgsrc={logo} {将被form组成

getFieldDecorator包装的表单控件会自动添value和onChange//根据内置验证规则进 式验rules:{requiredtruewhitespacetruemessage'必须输入用户名{min4message'4位{max12,message'12位{pattern:/^[a-zA-Z0-9_]+$/,message:']<Inputprefix={<Icontype="user"style={{color:'rgba(0,0,0,.25)'}}/>}placeholder="用户名"/>)}{rules:{validator:]<Icontype="lock" )}<Buttontype="primary"htmlType="submit"className="login-form-)}}用户名 必须大于4exportdefault说 咱们的项目是一个前分离的项目 前台应用与应 应用负责处理前台应用提交的请求 并给前台应用返回json数前台应用负责展现数据,与用户交互,与应用交运行应用确保启动mongodb服API接口文:登根据分类ID根据ID/Name上删20)..postman前交互依赖包2.15.2.ajax请求模 能发送ajax请求的函数模块函数的返回值是promise对象axios.get()/post()返回的就是promise对象返回自己创建的promise对象:异步返回结果数据,而不是包含结果数据的importaxiosfrom'axios'import{message}from'antd'exportdefaultfunctionajax(url,data={},method='GET')returnnewPromise(function(resolve,reject)let//执行异步ajax请if(method==='GET')}elsepromise=axios.post(url,yarnadd}}promise.then(response=>如果成功了调用}).catch(error=>{//对所有ajax请求出错 message.error('请求错误} 2.15.3.配2.15.4.请求测试"proxy": 包含n个接口请求函数的模块importajaxfrom登exportconstreqLogin=(username,password)=>ajax('/login',{username,////请 login=async(username,password)=>console.log('发送登陆的ajax请求'usernamepassword)constresult=awaitreqLogin(username,password)console.log('login()',result)} 组件(完成登陆功能2.9.1.依..3.importReact,{Component}from'react'import{}fromimportimportlogofromimportmemoryUtilsfrom'../../utils/memoryUtils'import{reqLogin}from'../../api'exportdefaultuser:{}//内存中保存登陆的user信息}yarnadd登陆的路由组classLoginextendsComponenthandleSubmit=(event)=> //对所有表ps.form.validateFields(async(err,values)=>检验成if(!err)//console.log('提交登陆的ajax请求',const{username,password}=constresult=awaitreqLogin(username,//console.log('login()',if(result.status===0)提示,保存用户登memoryUtils.user= 跳转}else}}elseconsole.log('检验失败}//得到form对//constform=//获取表单项的输入数//constvalues=//console.log('handleSubmit()',} ——————————————————————————用户名 必须必须大于等于4必须小于等于12必须是英文、数字或validatePwd=(rule,value,callback)=>{console.log('validatePwd()',rule,value)if(!value){ 必须输入}elseif }elseif }elseif(!/^[a-zA-Z0- }elsecallback()验证} x')//验证失败并指定}render()//得到具强大功能的form对constform=const{getFieldDecorator}=return<div<imgsrc={logo} {——————————————————————————用户名 必须必须大于等于4必须小于等于12必须是英文、数字或}{线组成

getFieldDecorator('username',{//配置对象:属性名是特定的一 式验证:直接使用别人定义好的验证rules:requiredtruewhitespacetruemessage'用户名必须输入min4message'4位max12message'12位pattern/^[a-zA-Z0-9_]+$/message' prefix={<Icontype="user"style={{color:'rgba(0,0,0,.25)'}} placeholder="用户名 )}{rules:{validator:}] prefix={<Icontype="lock"style={{color:'rgba(0,0,0,.25)'}} )}importimportReact,{Component}from'react'import{Redirect}from'react-router-dom'importmemeoryUtilsfromexportdefaultclassAdminextends{render()constuser=memeoryUtils.userif(!user._id){return<Redirect}return o)}}.9.4.<Buttontype="primary"htmlType="submit"className="login-form-)}}constWrapLogin=exportdefault维持登陆与自动登登陆,刷新后依然是已登陆状态(维持登陆,关闭浏览器后打开浏览器依然是已登陆状态(自动登登陆 登陆路径自动跳转到管理界importimportstorefromconstUSER_KEY=包含n个操作localstorage的工export{{//localStroage只能保存string,如果传递是对象,会自动调用对象的toString()并保//localStorage.setItem(USER_KEY,JSON.stringify(user))//保存的必须是对象的jsonstore.set(USER_KEY,user) 会自动转换成json再保//returnJSON.parse(localStorage.getItem(USER_KEY)||'{}')//[object,returnstore.get(USER_KEY)||removeUser()//}.10.3.importstorageUtilsfrom'./utils/storageUtils'importmemoryUtilsfrom'./utils/memoryUtils'//如果local中保存了user,将user保存到内存中constuser=storageUtils.getUser()if(user&&user._id){memoryUtils.user=}判断登录是if(result.status===0)登录成 提示登录成功,保存用户登录信message.success('登录成功保存constuser=resu memoryUtils.user=user//跳转 管理路由(已经登录成功,不需要回退了}render()if(memoryUtils.user&&memoryUtils.user._id)return<Redirect}} 组件(搭建整体结构整体组件组成 importReact,{Component}from'react'import'./index.less'左侧exportdefaultclassLeftNavextendsComponentrender()return<divclassName="left-nav">.left-navcolor:}))}}Header组 importReact,{Component}from'react'import'./index.less'头部exportdefaultclassHeaderextendsComponentrender()return<divclassName='header'>)}}.headerheight:}Admin组 importimportReact,{Component}from'react'import{Redirect}from'react-router-dom'import{Layout}fromimportimportmemeoryUtilsfrom'../../utils/memoryUtils'importHeaderfrom' importLeftNavfrom' const{Footer,Sider,Content}=exportdefaultclassAdminextends{render()constuser=memeoryUtils.userif(!user._id){return<Redirect}return<Layoutstyle={{height:<Contentstyle={{backgroundColor:<Footerstyle={{textAlign:'center',color:'#aaaaaa'}}> )}}组定义各个子路由组件路由:importReact,{Component}fromimport{Redirect,Route,Switch}from'react-router-dom'import{Layout}from'antd'importmemeoryUtilsfrom'../../utils/memoryUtils'importHeaderfrom' importLeftNavfrom' importHomefrom'../home/home'importCategoryfrom'../category/category'importProductfrom'../product/product'importRolefrom'../role/role'importUserfrom'../user/user'importBarfrom'../charts/bar'importLinefrom'../charts/line'importPiefrom'../charts/pie'const{Footer,Sider,Content}=exportdefaultclassAdminextends{render()constuser=memeoryUtils.userif(!user._id){return<Redirect}return<Layoutstyle={{height:<Contentstyle={{backgroundColor:<Routepath='/home'<Routepath='/category'<Routepath='/product'<Routepath='/role'<Routepath='/user'<Routepath='/charts/bar'<Routepath='/charts/line'<Routepath='/charts/pie'<Redirectto='/home'<Footerstyle={{textAlign:'center',color:'#aaaaaa'}}> )}}导航菜单配置: {List=title:'首页',//菜单标题key'/home'对应的icon'home'图标名{title:'商品',key'/products',iconchildren//{title'品类管理',key:'/category',icon:'bars'{key:'/product',icon:'tool']{title'用户管理key:'/user',icon:'user'{title'角色管理key:'/role',{titletitle'图形图表key:'/charts',children:[{title:'柱形图',icon:'bar-chart'{title:'折线图',icon:'line-chart'{title'饼图icon:'pie-chart']]export.left-nav.logo-linkdisplay:flex;height:80px;background-color:#002140;img{width:height:margin:015px0}h1margin-bottom:color:whitecolor:white;font-size:20px;}}}2.13.3.导航菜单组件:left-importimportReact,{Component}fromimport{Link,withRouter}from'react-router-dom'import{ ,Icon}from'antd'Configfrom importlogofromimportconst = 左侧classLeftNavextendsComponent 使用reduce()+递归 Nodes= List)=>constpath=List.reduce((pre,item)=>if{ .Item<Link<Icon ——————————————————————————}{ <Icon} if(item.children.find(cItem=>path.indexOf(cItem.key)===0))this.openKey=}}return},} 使用map()+递归 Nodes2= List)=>constpath= List.map(item=>if(!item.children)return .Item<Link<Icon—————————————————————————— )}elseif(item.children.find(cItem=>path.indexOf(cItem.key)===0))this.openKey=}return <Icon} )}}在第一次render()之前执行一componentWillMount()// Nodes= Nodes= }render()//得到当前请求路径,作为选中菜单项的constselectKey=ps.location.pathnameconstopenKey=this.openKeyreturn<divclassName="left-<Linkto='/home'className='logo-<imgsrc={logo}2.142.14Header依 < { } )}}history/location/matchexportdefaultyarnyarnadd2.14.2. importimportjsonpfromexportfunctionreqWeather(city)consturl/returnnewPromise((resolve,reject){jsonp(url,param:},(error,response)=>if(!error&&response.status=='success')const{dayPictureUrl,weather}=response.results[0].weather_data[0]resolve({dayPictureUrl,weather})}elsealert('获取天气信息失败}}2.14.3..headerheight:background-color:.header-topheight:line-height:40px;text-align:right;border-bottom:1pxsolid}}{display:flex;height:40px; font-size:20px;&::after{content:'';top:30px;right:transform:translateX(50%);border-top:20pxsolidwhite;border-right:20pxsolidtransparent;border-bottom:20pxsolidtransparent;border-left:20pxsolidtransparent;}}.header-bottom-rightwidth:text-align:right;img{width:30px;height:20px;margin:015px;}}}}.4.5.抽取通用组件:link-1).2).{border:none;outline:none;color:#1DA57A;cursor:}importReactfrom'react'import'./index.less' 的button组exportdefaultfunctionLinkButton(props)return<button{...props}className='link-}包含nexportfunctionformateDate(time)if(!time)returnletdate=newreturndate.getFullYear()+'-'+(date.getMonth()+1)+'-'++''+date.getHours()+':'+date.getMinutes()+':'+}2.14.6.importimportReact,{Component}from'react'import{Modal}from'antd'import{withRouter}from'react-router-importLinkButtonfrom'../link-button' Listfrom'../../config/ import{reqWeather}from'../../api'import{formateDate}from'../../utils/dateUtils'importmemoryUtilsfrom'../../utils/memoryUtils'importstorageUtilsfrom'../../utils/storageUtils'import'./index.less'头部classHeaderextendsComponentstate=sysTime:formateDate(Date.now()),dayPictureUrl:'',//天气 weather:''}发异步ajax获取天气数据并更新状getWeather=async()=>const{dayPictureUrl,weather}=awaitreqWeather(' this.setState({dayP}启动循环定时器,每隔1s更新一次getSysTime=()=>ervalId=setInterval(()=>sysTime:},}退出logout=()=>{Modal.confirm({content'确定退出吗?'()=>//移除保存的usermemoryUtils.user={}跳转到onCancel()}根据请求的path得到对应的标getTitle=(path)=>let enu=> .key===path){title= }elseif {title=}}——————————————————————————return}}componentWillUnmount()清除定}render()const{sysTime,dayPictureUrl,weather}=得到constuser=得到当前constpath=得到对应的标consttitle=return<div<span>欢迎<divclassName="header-<imgsrc={dayPictureUrl})}importimportReactfrom'react'import'./home.less'Home路由组exportdefaultfunctionHome(props)return<div )exportexportdefault.152.15Home.15.1.width:height:100%;display:flex;justify-content:center;font-size:30px;}<Contentstyle={{backgroundColor:'white',margin:'20px20px分类管.16.2.importReact,{Component}from'react'import{}fromimportUpdateFormfrom'./update-//获取一级或某个二级exportconstreqCategorys=(parentId)=>ajax('/manage/category/list',添加分{},更新exportconstreqUpdateCategory=({categoryId,categoryName})},——————————————————————————importAddFormfrom'./add-importLinkButtonfrom ponents/link-import{reqCategorys,reqAddCategory,reqUpdateCategory}fromexportdefaultclassCategoryextendsComponentstate=categorys:[],//一级分类列表subCategorys:[],//二级分类列表parentId:'0',//父分类的IDparentName:'',//父分类的名称示 示}getCategorys=async(parentId)=>g:trueparentId=parentId||constresult=awaitreqCategorys(parentId)//{status:0,data:g:falseif(result.status==={constcategorys ataif(parentId'0')——————————————————————————}elses:categorys}}elsemessage.error('获取列表失败}}showSubCates=(category)=>//console.log('set之前',this.state.parentId)//this.setState({:parentName: //console.log('set之后',this.state.parentId)}showCategorys=()=>tId:'0',parentName:'',showStatus:0,} ——————————————————————————showAdd=()({1} showUpdate=(category)=>this.category=s:2}addCategory=async()=>const{parentId,categoryName}= s:0constresult=awaitreqAddCategory(categoryName,if(result.status===0)if(parentId===this.state.parentId)}elseif(parentId==='0')——————————————————————————}}}updateCategory=async()=>constcategoryId=const{categoryName}= 0constresult=awaitreqUpdateCategory({categoryId,if(result.status===0)}}componentWillMount()this.columns={title'分类名称',dataIndex:{width:300,render:(category)=><LinkButtononClick={()=>this.showUpdate(category)}>修改分 ——————————————————————————{this.state.parentId==='0'<LinkButtononClick={()=>this.showSubCates(category)}>查看子分类</LinkButton>:)}componentDidMount()}render()const{categorys,subCategorys,parentId,parentName,loading,showStatus}=constcategory=this.category||consttitleparentId'0'一级分类列表<LinkButtononClick={this.showCategorys}>一级分类列表</LinkButton> <Icontype='arrow- )constextra=<Buttontype='primary'<Icontype='plus'/>添)return<Cardtitle={title} dataSource={parentId==='0'?categorys: pagination={{pageSize:5,showQuickJumper:true,showSizeChanger: ———————————————————————————————————————————————————— title="添加分类 visible={showStatus=== onCancel={()=>this.setState({showStatus: setForm={form=>this.form= title="修改分类 visible={showStatus=== onCancel={()=>this.setState({showStatus: setForm={form=>this.form= )}}importimportReact,{Component}from'react'import{Form,Select,Input}from'antd'importPropTypesfrom'prop-——————————————————————————constItem=constOption=添加分类的Form组classAddFormextendsComponentstaticpropTypes=categorys:PropTypes.array.isRequired,parentId:PropTypes.string.isRequired,setForm:PropTypes.func.isRequired,}componentWillMount()}render()const{getFieldDecorator}=ps.formconst{categorys,parentId}=psreturn(<Itemlabel='所属分类{initialValue:<Optionkey='0'value='0'>一级分类{categorys.map(c=><Optionkey={c._id}})}<Itemlabel='分类名称)}}exportdefaultAddForm=importReact,{Component}from'react'import{Form,Input}from'antd'importPropTypesfrom'prop-types'constItem=更新分类的Form组classUpdateFormextendsComponentstaticpropTypes=categoryName:setForm:}componentWillMount()}render(){initialValue:)}initialValue:initialValue:<Input )})}}2.17.商2.17.商品管依2.17.2.const{getFieldDecorator}=ps.formconst{categoryName}=psreturn{exportdefaultUpdateForm=根据分类ID获取获取商品分//根据ID/Name搜索产品分页列exportconstreqAddOrUpdateProduct=(product)=>ajax('/manage/product/'(product._id?'update':'add'),product,exportconstreqUpdateProductStatus=(productId,status){productId,},.17.3.importReact,{Component}fromimport{Switch,Route,Redirect}from'react-router-importProductHomefromimportProductAddUpdatefrom'./add-update'importProductDetailfrom'./detail'import管理的商品管理路由exportdefaultclassProductextends{render()return<Routepath='/product'exactexportconstreqSearchProducts=({pageNum,pageSize,searchType,searchName}){pageNum,[searchType]:exportconstreqDeleteImg=(name)=>ajax('/manage/img/delete',{name},<Route<Routepath='/product/addupdate'<Routepath='/product/detail'<Redirect)}}importimportReact,{Component}fromimport{}fromimportLinkButtonfromimport{reqProducts,reqSearchProducts,reqUpdateProductStatus}fromimport{PAGE_SIZE}fromconstOption=exportdefaultclassProductHomeextendsComponentstate=total0商品的总products当前页列表数searchType'productName'搜索类型productNamesearchName'',//}2.17.4.——————————————————————————更新指定产品的状updateProductStatus=async(productId,status)=>constresult=awaitreqUpdateProductStatus(productId,if(result.status===}}initColumns=()=>this.columns={dataIndex{dataIndex{title:'价格',render:(price)=>{title:'状态',width:100,dataIndex'status',renderstatusproduct1:在售2:letb ext='下架'letstatusText'在售'if(status==={ ext'上架statusText'已下架}——————————————————————————status=status===1?2:return<Buttontype='primary'onClick={() )}{width:100,render:(product)=><LinkButtononClick=ps.history.push('/product/detail', <LinkButtononClick=ps.history.push('/product/addupdate',)]}getProducts=async(pageNum)=>this.pageNum=const{searchType,searchName}=this.stateletresultresult=awaitreqSearchProducts({pageNum,pageSize:PAGE_SIZE,searchType,else一般result=awaitreqProducts(pageNum,}——————————————————————————if(result.status===0)const{total,list}=resu products:}}componentWillMount()}componentDidMount()}render()const{products,total,searchType}=this.stateconsttitle=(<Selectvalue={searchType}onChange={value=>this.setState({searchType:关键字

''<Inputstyle={{width:150,marginLeft:10,marginRight:10}} onChange={(e)=>this.setState({searchName: <Buttontype='primaryonClick=this.getProducts(1)}>搜索)constextra=<Buttontype='primary'style={{float:'right'}}onClick={()<Icon)defaultPageSize:onChange:)}}.17.5.importReact,{Component}from'react'import{List,Icon,Card}from'antd'import{reqCategory}fromimport{BASE_IMG_PATH}from'../../utils/constants'importLinkButtonfrom" 商品exportdefaultclassProductDetailextendsComponentstate=,return<Cardtitle={title} ——————————————————————————cName2二级分类名}getCategoryName=async()=>const{categoryId,pCategoryId}=ps.location.stateif(pCategoryId==='0'){获取一级分constresult=awaitreqCategory(categoryId)constcName1=resu }else获取一级分/*constresult1=awaitreqCategory(pCategoryId)constcName1=获取二级分constresult2=awaitreqCategory(categoryId)constcName2=this.setState({cName1,cName2})*/一次发多个请求,等所有请求都返回后一起处理,如果有一个请求出错了,Promise.all([promise1,promise2])返回值一个promise对象,异步成功返[result1,constresults=awaitconstresult1=constresult2=constcName1=constcName2=this.setState({cName1,cName2})}}componentDidMount()}render()——————————————————————————const{name,desc,price,imgs,detail}=const{cName1,cName2}=this.stateconsttitle=(<LinkButtononClick={()=><Icontype="arrow-left"style={{fontSize: )return<CardclassName='product-detail'<span>{price'元<span>{cName1+(cName2?'-->'+cName2:<spanclassName='left'>商 {imgs.map(img=>}<divdangerouslySetInnerHTML={{html:)}}product/add-importReact,{Component}from'react'import{}fromimportLinkButtonfrom' importPicturesWallfrom'./pictures-wall'importRichTextEditorfrom'./rich-text-import{reqCategorys,reqAddOrUpdateProduct}fromconst{Item}=const{TextArea}=商品添加/更新的路由组classProductAddUpdateextendsComponentstate=options:[],//用来显}constructor(props)this.pw=this.editor=}loadData=async(selectedOptions)=>//console.log('loadDate()', OptionselectedOptions[selectedOptions.length1]Option.loading=true//显示loading——————————————————————————//异步请求获取对constsubCategorys=awaitthis.getCategorys( constcOptions=subCategorys.map(c=>value:c._id,label:,isLeaf:true,//添加为对应的option的children(子Option.children=Option.isLeaf=}//更新options状options:}获取指定分类id的子分类列如果parentId为0时获取一级列getCategorys=async(parentId)=>constresult=awaitif(result.status===0)constcategorys=resu if(parentId==='0'){//根据一级分类数组初始化生成options数}else{当前得到是二级分类列return}}——————————————————————————}生成级联的一级列initOptions=async(categorys)=>//根据一级分类数组生成option的数constoptions=categorys.map(c=>value:c._id,label:,isLeaf://如果当前是更新,且商品是一个二级分类的const{product,isUpdate}=thisif(isUpdate&&product.pCategoryId!=='0'){//异步获取product.pCategoryId的二级分类列constsubCategorys=awaitthis.getCategorys(product.pCategoryId)if(subCategorys&&subCategorys.length>0){//生成二级的option数constcOptions=subCategorys.map(c=>value:c._id,isLeaf:true,找到对应的 //将cOptions添加为对应的一级option的Option.children=}}更新状}——————————————————————————对商品价格进行自定义验validatePrice=(rule,value,callback){value=value*{}elsecallback('价格必须是大于0的数值}}submit=()=>ps.form.validateFields(async(err,values)=>if(!err)收集产品相const{name,desc,price,categoryIds}=//在父组件中得到子组件对象,调用子组constimgs=constdetail=letpCategoryId=''letcategoryId=''if(categoryIds.length===1){//选择的是一级分pCategoryId=categoryId=}else{//选择的是二级分类pCategoryIdcategoryIds[0]categoryId=categoryIds[1]}封装成constproduct={name,desc,price,pCategoryId,categoryId,detail,if(this.isUpdate)product._id=}请求保constresult=await}else——————————————————————————message.success('保存商品失败}}}componentDidMount()}componentWillMount()取出跳转constproduct=ps.location.stateduct=product||{}this.isUpdate }render()const{product,isUpdate}=const{pCategoryId,categoryId}=productconst{options}=this.stateconst{getFieldDecorator}=//准备用于级联列表显示的数组constcategoryIds=[]if(isUpdate){{)}}consttitle=<LinkButtononClick={()=><Icontype='arrow-left'style={{fontSize:——————————————————————————{isUpdate'修改商品'添加商品)//指定form的item布局的constformItemLayout{labelCol:{span:2wrapperCol:{span:8}return<Card<Itemlabel="商品名称{{initialValue:,rules:[{requiredtruemessage'商品名称必须输入])}<Itemlabel="商品描述{{initialValue:product.desc,rules:[{requiredtruemessage'商品描述必须输入]<TextAreaplaceholder="请输入商品描述autosize)}<Itemlabel="商品价格{{initialValue:product.price,rules:[{requiredtruemessage'商品价格必须输入{validator:]'')}<Itemlabel="商品分类{{initialValue:rules:{requiredtruemessage'商品分类必须输入] )}<Itemlabel="商 "<PicturesWallref={this.pw} label="商品详情 labelCol={{span:2 wrapperCol={{span:20<RichTextEditorref={this.editor}')}}exportdefaultproduct/pictures-importimportReactfromimportPropTypesfrom'prop-import{Upload,Icon,Modal,message}fromimport{BASE_IMG_PATH,UPLOAD_IMG_NAME}fromimport{reqDeleteImg}from)exportdefaultclassPicturesWallponentstaticpropTypes=imgs:}constructor(props)letfileList=如果传入了imgs,生成一个对应的constimgs=ps.imgsif(imgs&&imgs.length>0){fileList=imgs.map((img,index)=>uid:-name:status'done'loading上传中done:上传完成remove删url:BASE_IMG_PATH+}this.state=previewVisible:false,//是否显示大图预previewImage:'',//大图的fileList:fileList//所有需要显示 }} 文件名的数getImgs=()=>this.state.fileList.map(file=>关闭handleCancel=()=>this.setState({previewVisible:预览handlePreview=(file)=>previewVisible:}file:当前操fileList:所有文件信息对象的数handleChange=async({file,fileList})=>console.log('handleChange()',file,//如果上 if(file.status==={constresult=file.responseif(result.status===0){message.success('上传成功了const{name,url}=resu file=fileList[fileList.length-1]file.url=url}elsemessage.error('上传失败了}elseif(file.status'removed'删除constresult=awaitreqDeleteImg()if(result.status===0){message.success('删 成功}}elsemessage.error('删 失败}}//更新fileList}render()const{previewVisible,previewImage,fileList}=constuploadButton=<Icon )return name= <Modalvisible={previewVisible}footer={null}<imgalt="example"style={{width:'100%'}})}}product/rich-text-importimportReact,{Component}from'react'importPropTypesfrom'prop-types'import{Editor}from'react-draft-import{EditorState,convertToRaw,ContentState}from'draft-js'importdraftToHtmlfrom'draftjs-to-html'importhtmlToDraftfrom'html-to-用来指定商品详情信exportdefaultclassRichTextEditorextendsComponentstaticpropTypes=detail:}constructor(props)//根据传入的html文本初始显示ps.detailleteditorStateif(detail){//如果传入才需要constblocksFromHtml=const{contentBlocks,entityMap}=editorState=EditorState.createWithContent(contentState)}elseeditorState=}初始化{editorStat}}当输入改变时立即保.17.9..product-detail.leftfont-size:18px;font-weight:bold;}}onEditorStateChange=(editorState)=>}getDetail=()=>return}render()const{editorState}=return editorStyle={{height:250,border:'1pxsolid#000',padding:'0 )}}2.17.10.包包含n个应用中的常量字符串exportconstUPLOAD_IMG_NAME'image上'//上传 的参importReact,ponent}fromimportPropTypesfrom'prop-{Form,}from2.18.角色管2.18.1.添加角exportconstreqAddRole=(roleName)=>ajax('/manage/role/add',{roleName},获取exportconstreqRoles=()=>exportconstreqUpdateRole=(role)=>ajax('/manage/role/update',role,classclassAddFormponentstaticpropTypes=setForm:}componentWillMount()}render()const{getFieldDecorator}=ps.formconstformItemLayout={labelCol:{span:wrapperCol:{span:}return<Form.Itemlabel="角色名称{initialValue:)})}}exportdefaultAddForm=importimportReact,ponent}fromimportPropTypesfrom'prop-import——————————————————————————}from Listfrom'../../config/ constItem=Form.Itemconst{TreeNode}=添加分类的form组exportdefaultclassAuthForm ponentstaticpropTypes=role:}constructor(props)//根据传入角色的 s生成初始状态const{ s}=ps.rolethis.state={ }} s数据的方 s=()=>getTreeNodes= List)=> List.reduce((pre,item){<TreeNodetitl

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论