先看样子:
一、后端
package com.example.readinglist.cms.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.readinglist.cms.dto.ResponseDTO;
import com.example.readinglist.cms.entity.MyAppUser;
import com.example.readinglist.cms.service.MyAppUserService;
import com.example.readinglist.util.ListUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.Date;
import java.util.List;
/**
* <p>
* 用户 前端控制器
* </p>
*
* @author *cruder
* @since 2021-04-13
*/
@RestController
@RequestMapping("/app/user")
public class MyAppUserController {
@Autowired
private MyAppUserService myAppUserService;
@GetMapping("findList")
public ResponseDTO findList(MyAppUser myAppUser){
List<MyAppUser> myAppUsers = myAppUserService.list(new QueryWrapper<>(myAppUser)
.orderByDesc("create_time"));
if(ListUtils.isNotEmpty(myAppUsers)){
myAppUsers.forEach(user->{
user.setTagList(user.getTags().split(","));
});
}
return ResponseDTO.success(myAppUsers);
}
@GetMapping("findPage")
public ResponseDTO findPage(Page<MyAppUser> page, MyAppUser myAppUser){
IPage<MyAppUser> appUserIPage = myAppUserService.page(page,new QueryWrapper<MyAppUser>()
.like(!StringUtils.isEmpty(myAppUser.getUserName()),"user_name",myAppUser.getUserName())
.eq(myAppUser.getAge()!=null,"age",myAppUser.getAge())
.orderByDesc("create_time"));
return ResponseDTO.success(appUserIPage);
}
@PostMapping("edit")
public ResponseDTO edit(@Valid @RequestBody MyAppUser myAppUser){
myAppUser.setUpdateTime(new Date());
myAppUserService.updateById(myAppUser);
return ResponseDTO.success();
}
@PostMapping("add")
public ResponseDTO add(@RequestBody MyAppUser myAppUser){
MyAppUser one = myAppUserService.getOne(new QueryWrapper<MyAppUser>()
.eq("user_name", myAppUser.getUserName()));
if(one!=null){
return ResponseDTO.failed("此用户名此存在!");
}
myAppUser.setCreateTime(new Date());
myAppUserService.save(myAppUser);
return ResponseDTO.success();
}
@GetMapping("delete")
public ResponseDTO delete(@RequestParam Integer userId){
return ResponseDTO.success(myAppUserService.removeById(userId));
}
}
针对分页器无效,需要加如下配置
package com.example.readinglist.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Mybatis Plus 配置类
*
* @author *cruder
*/
@EnableTransactionManagement
@Configuration
@MapperScan("com.example.readinglist.cms.dao.*")
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
二、前端
信息卡片组件
UserInfo.tsx
import React, { useEffect ,useState} from 'react';
import {Modal,Toast,InputItem} from 'antd-mobile'
import style from './userinfo.less';
import {deleteById,edit} from '@/pages/index/users/service'
const alert = Modal.alert;
const UserInfo = (props) => {
const {user} = props;
const[modalVisible,setModalVisible] = useState(false);
const[userInfo,setUserInfo] = useState({...user});
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
const handleEdit = (user)=>{
console.log('监听到编辑事件',user);
setModalVisible(true);
};
const deleteHandle = (userId)=>{
console.log('监听到点击删除按钮事件',userId);
alert('Delete', 'Are you sure???', [
{ text: 'Cancel', onPress: () => console.log('cancel') },
{
text: 'Ok',
onPress: () =>
new Promise((resolve) => {
console.log('点击确认',userId);
const response = deleteById({userId:userId});
console.log(response);
Toast.info('delete successfully', 1);
setTimeout(resolve, 2000);
window.location.href = '/myapp/index/users';
}),
},
])
};
const onClose = async() => {
const response = await edit(userInfo);
console.log(response);
if(response.status===1){
setModalVisible(false);
window.location.href = '/myapp/index/users';
}else {
Toast.fail('edit fail!'+response.msg,Toast.LONG)
}
};
const handleChange = (name,val) =>{
console.log('监听到输入框变化',name,val);
userInfo[name]=val;
};
return (
<div className={style.outDiv}>
<div><span className={style.title}>{user.userName}</span><span className={style.tags}>{user.tags}</span></div>
<div>年龄:{user.age}</div>
<div>邮箱:{user.email}</div>
<div>住址:{user.address}</div>
<button className={style.button_edit} onClick={() => {
handleEdit(user);
}}>编辑
</button>
<button className={style.button_delete} onClick={()=>{deleteHandle(user.userId);}}>删除</button>
<Modal visible={modalVisible}
transparent
maskClosable={false}
title= "编辑个人信息"
footer={[{ text: '确认', onPress: () => { console.log('ok');onClose(); } }
,{ text: '取消', onPress: () => { console.log('cancel');setModalVisible(false); } }]}>
<div className={style.inputDiv}>
<div><label>昵称</label><input defaultValue={user.userName} onChange={val => handleChange('userName',val.target.value)}/></div>
<div><label>年龄</label><input defaultValue={user.age} onChange={val => handleChange('age',val.target.value)}/></div>
<div><label>标签</label><input defaultValue={user.tags} onChange={val => handleChange('tags',val.target.value)}/></div>
<div><label>住址</label><input defaultValue={user.address} onChange={val => handleChange('address',val.target.value)}/></div>
<div><label>邮箱</label><input defaultValue={user.email} onChange={val => handleChange('email',val.target.value)}/></div>
</div>
</Modal>
</div>
)
};
export default UserInfo;
userinfo.less
.outDiv{
width: 80%;
border: 1px solid white;
border-radius: 10px;
margin: 15px auto 0;
background-color: white;
position: relative;
padding: 10px;
}
.title{
font-size: large;
font-weight: bolder;
margin-right: 10px;
color: #1d39c4;
}
.outDiv div{
padding-right: 50px;
}
.tags{
font-size: smaller;
color: darkgreen;
border: 1px solid darkgreen;
}
.tags:hover{
font-size: large;
color: #d46b08;
}
.button_edit{
position: absolute;
top: 30px;
right: 10px;
color: white;
border: white 1px solid;
padding: 2px 8px;
border-radius: 5px;
background-color: #0e80d2;
}
.button_delete{
position: absolute;
top: 60px;
right: 10px;
color: white;
padding: 2px 8px;
border-radius: 5px;
border: white 1px solid;
background-color: orangered;
}
.inputDiv{
div{
margin-top: 5px;
input{
width:80%;
}
label{
width: 20%;
color: black;
margin-right: 3px;
}
}
}
users页
index.tsx
import {Pagination,Button,SearchBar} from 'antd-mobile'
import {useState} from 'react';
import {connect} from 'dva';
import style from './users.less'
import {add} from './service'
import UserModal from '../../../component/UserModal/UserModal'
import UserInfo from '../../../component/UserInfo/UserInfo'
const index = ({users1,dispatch}) =>{
const[modalVisible,setModalVisible] = useState(false);
const[record,setRecord] = useState(undefined);
const[userNameDisable,setUserNameDisable] = useState(false);
const array = users1.data;
console.log(array);
const locale = {
prevText: '上一页',
nextText: '下一页',
};
const editHandler = (record)=>{
setModalVisible(true);
setUserNameDisable(true);
console.log(record);
setRecord(record);
};
const addHandler = ()=>{
setModalVisible(true);
setUserNameDisable(false);
};
const deleteHandler = (userId:number)=>{
dispatch({
type: 'users1/deleteById',
payload: {
userId,
},
});
};
const okClose = () =>{
setModalVisible(false);
};
const cancelClose = () =>{
setModalVisible(false);
setRecord(undefined)
};
const onFinish = async values =>{
if(record){
values['userId'] = record.userId;
dispatch({
type:'users1/edit',
payload: values,
});
setModalVisible(false);
}else {
// dispatch({
// type:'users1/add',
// payload: values,
// });
const response = await add(values);
if(response.status){
setModalVisible(false);
}
dispatch({
type:'users1/getPage',
payload:{current:1,size:6}
});
}
};
const handlePageChange = current=>{
dispatch({type:'users1/getPage',payload:{current:current,size:6}});
};
const handleSearch = val =>{
dispatch({type:'users1/getPage',payload:{userName:val}});
};
return (<div>
<div className={style.topDiv}>
<Button type='primary' size='small' className={style.btn_add} onClick={()=>{addHandler();}}>添加</Button>
<SearchBar onSubmit={val=>handleSearch(val)}/>
</div>
<div>
{array!==undefined?users1.data.records.map(item=>(
<UserInfo key={item.userId} user={item}/>
)):null}
</div>
<UserModal
visible={modalVisible}
okClose={okClose}
cancelClose={cancelClose}
record={record}
onFinish = {onFinish}
userNameDisable = {userNameDisable}
/>
{array!==undefined?<Pagination
style={{position:'fixed',width:'100%',bottom:'0'}}
total={array.pages}
current={array.current}
locale={locale} onChange={e=>handlePageChange(e)} />:null}
</div>
)
};
// const mapStateToProps = ({users})=>{
// console.log(users);
// return {
// users,
// }
// };
const mapStateToProps = ({users1})=>({users1});
export default connect(mapStateToProps)(index);
model.ts
import {Reducer,Effect,Subscription} from 'umi'
import {getRemote, edit, deleteById, add, getPage} from './service'
interface UserModelType{
namespace: 'users1',
state:{},
reducers:{
getList: Reducer
findPage: Reducer
},
effects:{
getRemote: Effect
getPage: Effect
edit: Effect
add: Effect
deleteById: Effect
},
subscriptions:{
setup: Subscription;
}
}
const userModel:UserModelType ={
namespace: 'users1',
state:{},
reducers:{
getList(state,{payload}){
return payload;
},
findPage(state,{payload}){
return payload;
},
},
effects: {
*getRemote(action,{put,call}){
const response = yield call(getRemote);
console.log(response.data);
const data = response.data;
yield put({
type: 'getList',
payload: {data},
});
},
*getPage({payload: values},{put,call}){
const response = yield call(getPage,values);
console.log(response.data);
const data = response.data;
yield put({
type: 'findPage',
payload: {data},
});
},
*edit({payload: values},{put,call}){
console.log('edit here',values);
const response = yield call(edit,values);
yield put({
type: 'getRemote',
});
},
*add({payload: values},{put,call}){
console.log('add here',values);
const response = yield call(add,values);
yield put({
type: 'getPage',
});
},
*deleteById({payload: userId},{put,call}){
console.log('delete',userId);
const response = yield call(deleteById,userId);
yield put({
type: 'getRemote',
});
},
},
subscriptions:{
setup({dispatch,history}){
return history.listen(({pathname})=>{
console.log(pathname,history);
if(pathname==='/index/users'){
dispatch({type: 'getPage',payload:{current:1,size:6}});
}
})
}
},
};
export default userModel;
service.ts
import {request} from 'umi'
import {message} from 'antd'
export const getRemote = async () =>{
return request('/app/user/findList',{method:'get'}).then(function (response) {
console.log(response);
return response;
}).catch(function (error) {
console.log(error);
});
};
export const getPage = async (page) =>{
return request('/app/user/findPage',{method:'get',params:page}).then(function (response) {
console.log(response);
return response;
}).catch(function (error) {
console.log(error);
});
};
export const edit = async (values) =>{
return request('/app/user/edit',{method:'post',data:values}).then(function (response) {
if(response.status){
message.success('编辑成功');
}else {
message.error(response.msg);
}
return response;
}).catch(function (error) {
message.error('编辑失败');
console.log(error);
});
};
export const add = async (values) =>{
return request('/app/user/add',{method:'post',data:values}).then(function (response) {
if(response.status){
message.success('添加成功');
}else {
message.error(response.msg);
}
return response;
}).catch(function (error) {
message.error('添加失败');
console.log(error);
});
};
export const deleteById = async (userId) =>{
console.log('点击确认',userId);
return request('/app/user/delete',{method:'get',params:userId}).then(function (response) {
message.success('删除成功');
return response;
}).catch(function (error) {
message.error('删除失败');
console.log(error);
});
};
users.less
.topDiv{
margin: 15px auto 0;
width: 80%;
position: relative;
.btn_add{
background-color: lightgreen;
width:25%;
color: darkblue;
font-size: larger;
margin-left: 0;
}
}
矬就矬在,编辑和删除的弹窗组件是放在UserInfo卡片组件里的,直接在卡片组件里面调用接口,没用到model里的接口。总之关系很乱,要单独弄一个弹窗组件并实现组件之间通信这一点实际上没搞明白。