使用react做管理后台项目, 一点点感悟与问题
1.全选翻页问题
场景描述: 对一个table列表数据进行操作, 将选中的数据加到另一个列表中, 并可以对源列表进行全选和查询操作.
问题描述:当使用全选功能, 对数据进行翻页, 会导致原来选中的数据被清空, 原因是antd的全选按钮, 只选择到了当前页的数据, 当切换之后又是另一页数据了.
解决方案一: 将列表数据变成二维数组, 实现多页操作, 并将数据扁平化之后加到需要添加进去的列表中, 关键代码如下:
说明: 在state中有以下值:
propsParameter.selectedRowKeys , //存放被选中项列表(二维数组)
propsParameter.onItem, // 扁平化后的列表数据, 用于在后一个列表展示数据
list:[], 源数据列表
在后一个列表删除数据的关键代码如下:
基本实现需求, 但仍然存在问题, 查询之后会删除被选择的值, 原因是翻页问题导致.查询时当前一般只有一页,选中之后按页码来增删数据,当前查询数据会覆盖原来的第一页数据.
解决方案二: 穿梭框方案
总结: 使用穿梭框基本上就实现了业务需求, 但由于没有分页功能, 数据加载可能会相对有影响,如果之后有更好的方式,再补上.
路由跳转问题
在菜单内部实现跳转时,会出现数据未加载问题.原因是内部切换菜单时没有触发model层的入口函数, 导致触发请求来获取数据.
解决方案: 写一个有生命周期的子组件, 通过子组件的componentDidMount来触发父组件, 从而触发请求数据的接口. 因为子组件的componentDidMount函数会在每一次进入时触发.
总结: 这个解决方式有些投机取巧, 借助有生命周期的子组件来实现接口请求触发,不是一个很明智的选择.
名称过长导致的样式问题
解决方式: word-break:break-all; white-space:normal
调用浏览器的打印问题
调用浏览器的打印: window.print(),
设置打印样式: @media print{}
5.关于树形结构的问题
场景需求: 在选择战区省份城市时,需要动态加载城市下的经销商数据, 并且编辑时需要回显被选中的经销商。
解决方案: antd的tree树形选择组件
具体实现方式请查看这篇文章 antd的tree树形组件异步加载数据小案例
6.关于前端数据处理问题
场景需求:点击新增按钮之后新增一个问题, 其中包含题目、类型和选项等,需要对问题进行删减,对选项进行删减上下移动操作,并且选项中可以包含图片。
view.jsx
//页面方法
//状态值变化
function updateModel(value, name, modHierarchy) {
if(name==='attendType'){
updateModel(0, 'attendBenefit', 'attendInfo')
}
let obj = modelObj;
if (modHierarchy) {
modHierarchy = modHierarchy.split(".");
if(modHierarchy&&modHierarchy.length > 0){
modHierarchy.map(e => {
obj = obj[e];
});
}
}
obj[name] = value;
// 分发到model
dispatch({
type: `${namespace}/updateModel`,
payload: obj
});
};
//更换questions问题集合专用方法
const changeQuestion = (value, ind, wrapLabel, label, deepLabel)=> {
let ques = cloneDeep(modelObj[wrapLabel]);
ques.map((item,index)=> {
if(index === ind){
item[label] = value;
if(value == 3 && label == 'questionType'){
item.options.length = 1;
}
}
})
updateModel(ques, wrapLabel);
}
// 修改指定问题的选项
const changeOptions = (value, quIndex, opIndex, label) => {
let ques = cloneDeep(modelObj['questions']);
ques[quIndex].options[opIndex][label] = value;
updateModel(ques, 'questions');
}
//问卷中的图片上传处理
const changeOptionImg =(info, quIndex, opIndex, label) => {
let fileUrl = '';
if (info.file.status === 'done') {
message.success('上传成功');
fileUrl = info.file.response.data;
}
changeOptions(fileUrl, quIndex,opIndex, label);
}
//添加问题
const addQuestion = () => {
dispatch({
type: `${namespace}/addQuestion`,
payload: {
}
});
}
//删除问题
const removeQuestion = (index) => {
dispatch({
type: `${namespace}/removeQuestion`,
payload: {
index
}
});
}
//添加奖项
const addPrizes = () => {
dispatch({
type: `${namespace}/addPrizes`,
payload: {
}
});
}
//删除奖项
const removePrizes = (index) => {
if(modelObj.prizes.length>2){
dispatch({
type: `${namespace}/removePrizes`,
payload:{
index
}
});
}else{
message.error('奖项配置不能少于两个!')
}
}
//页面渲染
{
modelObj.questions && modelObj.questions.map((item,index) => {
return (
removeQuestion(index)} type="close-circle" theme="filled" style={{fontSize:'20px',color:'#fff',cursor:'pointer'}}/>}
title={`问题${index+1}`} key={"Index_" + index}>
changeQuestion(e.target.value, index, 'questions', 'questionType')}>
单选
多选
填空
changeQuestion(e.target.value, index, 'questions','questionTitle')}/>
{let num = checked ? 1:0; changeQuestion(num, index, 'questions','required')} }/>
{item.questionType===2 &&
changeQuestion(value, index,'questions','choiceMinNum')}/>
changeQuestion(value,index, 'questions','choiceMaxNum')}/>
}
{
item.options.map((tem,dex) => {
return (
{
item.questionType!==3 &&
changeOptions(e.target.value, index, dex, 'optionTitle')}/>
beforeUpload(file,2)}
onChange={(info)=>changeOptionImg(info,index, dex,'imageUrl')}
>
{
tem.imageUrl?
:
上传
}
addOptions(index, item.options.length)}/>
deleteOptions(index,dex,tem)}/>
upOptions(index,dex)} />
downOptions(index,dex)} />
说明:图片建议尺寸:220x170px, 大小小于200kb, 仅支持jpg、png格式
}
{
item.questionType === 3 &&
changeOptions(value, index, dex, 'leastLength')}/>
changeOptions(value, index, dex, 'maxLength')}/>
}
)
})
}
{
item.questionType!==3 &&
}
)
})
}
//mod.js(model层)
//添加问题
*addQuestion({ payload }, { put, call, select }) {
let { questions } = yield select(e => e[tmpModule.namespace]);
console.log('questions-->',questions.length+1);
let tempObj = {
questionSeq:questions.length+1, //问题排序
questionType: 1, //问题类型:1.单选题,2.多选题.3.填空题
questionTitle: "", //问题标题
required: 0, //问题必填:0非必填,1必填
choiceMinNum: '', //最少选择数量
choiceMaxNum: '', //最多选择数量
options: [//选项集合
{
optionTitle: "", //选项标题
imageUrl: "",//图片
optionSep:1,//排序
localTitle:"选项1",//选项描述
inputTips: "",//输入提示
leastLength: '',//最少字数
maxLength: '',//最多字数
}
]
}
questions.push(tempObj);
yield put({type: 'store',payload: {questions} });
},
//删除问题
*removeQuestion({ payload }, { put, call, select }) {
let { questions } = yield select(e => e[tmpModule.namespace]);
questions.splice(payload.index,1);
yield put({type: 'store',payload: {questions} });
},
//添加问卷中的选项
*addOption({payload}, {put,call, select}) {
let { questions, optionId} = yield select(e => e[tmpModule.namespace]);
let {options} = questions[payload.index];
console.log('options=--->', options);
optionId++;
payload.obj.optionId = optionId;
// console.log('OptionId-->', optionId)
options.push(payload.obj)
yield put({
type: 'store',
payload: {
questions,
optionId
}
});
},
//删除问卷中的选项
*deleteOptions({payload}, {put,call, select}) {
let { questions, optionId} = yield select(e => e[tmpModule.namespace]);
let {queIndex,OpIndex,optionsItem} = payload;
questions.filter((item,index)=>{
if(index+''===queIndex+''){
//return item.options[OpIndex].optionSep === optionsItem.optionSep
if(item.options.length===1){
return message.error('至少保留一个选项!');
}else{
return item.options.splice(OpIndex,1);
}
}
})
yield put({
type: 'store',
payload: {
questions
}
});
},
//上移问卷中的选项
*upOptions({payload}, {put,call, select}) {
let { questions} = yield select(e => e[tmpModule.namespace]);
let {queIndex,OpIndex} = payload;
questions.filter((item,index)=>{
if(index+''===queIndex+''){
item.options[OpIndex-1].optionSep = OpIndex+1;
item.options[OpIndex].optionSep = OpIndex;
let tmp = item.options[OpIndex];
item.options[OpIndex] = item.options[OpIndex-1];
item.options[OpIndex-1] = tmp;
//return item.options.splice(OpIndex,1);
}
})
yield put({
type: 'store',
payload: {
questions
}
});
},
//下移问卷中的选项
*downOptions({payload}, {put,call, select}) {
let { questions} = yield select(e => e[tmpModule.namespace]);
let {queIndex,OpIndex} = payload;
questions.filter((item,index)=>{
if(index+''===queIndex+''){
item.options[OpIndex].optionSep = OpIndex+2;
item.options[OpIndex+1].optionSep = OpIndex+1;
let tmp = item.options[OpIndex];
item.options[OpIndex] = item.options[OpIndex+1];
item.options[OpIndex+1] = tmp;
//return item.options.splice(OpIndex,1);
}
})
yield put({
type: 'store',
payload: {
questions
}
});
},
//新增奖项
*addPrizes({ payload }, { put, call, select }) {
let { prizes } = yield select(e => e[tmpModule.namespace]);
let tempObj = {
prizeId: "1", //奖品id
prizeType: 2, //奖项类型:1.积分,2.礼品.3.抽奖次数,4谢谢参与
prizeName: '', //奖品名称(赠送积分/礼品名称/赠送次数)
prizeNumber: '', //奖品数量
numerator: '',//中奖概率
seq:1,//奖品排序
prizeTips: '',//中奖提醒
prizeImageUrl:'',//奖品图片
prizeDelivery:1, //礼品发放方式:1验证码,2邮寄
useBeginDatetime:'', //核销开始时间
useEndDatetime:'', //核销结束时间
attendIntegralName:'',//积分名称
attendIntegralCode:'',//积分编码
}
prizes.push(tempObj);
yield put({type: 'store',payload: {prizes} });
},
//删除奖项
*removePrizes({ payload }, { put, call, select }) {
let { prizes } = yield select(e => e[tmpModule.namespace]);
prizes.splice(payload.index,1);
yield put({type: 'store',payload: {prizes} });
},