赞
踩
用户发布帖子之后,页面局部刷新显示帖子内容;
不必刷新浏览器的情况下,实现与服务器之间通讯;
本项目采用AJAX技术实现,局部刷新;
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。即是用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。
Ajax实际上是下面这几种技术的融合:
客户端与服务器,可以在【不必刷新整个浏览器】的情况下,与服务器进行异步通讯的技术
同步交互和异步交互
Ajax核心(XMLHttpRequest)
XMLHttpRequest对象是Ajax中最重要的一个对象。使用Ajax更多的是编写客户端代码,而不是服务端的代码。
XMLHttpRequest 工作原理
传统的web前端与后端的交互中,浏览器直接访问Tomcat的Servlet来获取数据。Servlet通过转发把数据发送给浏览器。
当我们使用AJAX之后,浏览器是先把请求发送到XMLHttpRequest异步对象之中,异步对象对请求进行封装,然后再与发送给服务器。服务器并不是以转发的方式响应,而是以流的方式把数据返回给浏览器
XMLHttpRequest异步对象会不停监听服务器状态的变化,得到服务器返回的数据,就写到浏览器上~因为不是转发的方式,所以是无刷新就能够获取服务器端的数据
为了获取异步传输JSON格式数据,在 CommunityUtil 类中添加getJSONString方法及其重载方法,代码如下:
添加依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
public static String getJSONString(int code, String msg, Map<String, Object> map) { JSONObject json = new JSONObject(); json.put("code", code); json.put("msg", msg); if (map != null) { for (String key : map.keySet()) { json.put(key, map.get(key)); } } return json.toJSONString(); } public static String getJSONString(int code, String msg) { return getJSONString(code, msg, null); } public static String getJSONString(int code) { return getJSONString(code, null, null); }
在 DiscussPostMapper 类中添加 insertDiscussPost 方法,代码如下:
/**
* 新增帖子
* @param discussPost
* @return
*/
int insertDiscussPost(DiscussPost discussPost);
对应DiscussPostMapper.xml文件中新增如下代码:
<sql id="insertFields">
user_id,title,content,type,status,create_time,comment_count,score
</sql>
<insert id="insertDiscussPost" parameterType="DiscussPost">
insert into discuss_post(<include refid="insertFields"></include>)
values(#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score})
</insert>
service层,在DiscussPostService文件中新增如下代码:
/** * 新增帖子 * @param post * @return */ public int addDiscussPost(DiscussPost post) { if (post == null) { throw new IllegalArgumentException("参数不能为空!"); } //将字符串转换为html标签 post.setTitle(HtmlUtils.htmlEscape(post.getTitle())); post.setContent(HtmlUtils.htmlEscape(post.getContent())); // 过滤敏感词 post.setTitle(sensitiveFilter.filter(post.getTitle())); post.setContent(sensitiveFilter.filter(post.getContent())); return discussPostMapper.insertDiscussPost(post); }
controller层,新建DiscussPostController文件,代码如下:
@Controller @RequestMapping("/discuss") public class DiscussPostController { @Autowired private DiscussPostService discussPostService; @Autowired private HostHolder hostHolder; @Autowired private UserService userService; @PostMapping("/add") @ResponseBody //异步请求需要加上该注解 public String addDiscussPost(String title, String content) { User user = hostHolder.getUser(); if (user == null) { return CommunityUtil.getJSONString(403,"你还没有登录哦!"); } DiscussPost post = new DiscussPost(); post.setUserId(user.getId()); post.setTitle(title); post.setContent(content); post.setCreateTime(new Date()); discussPostService.addDiscussPost(post); // 报错的情况,会统一处理 return CommunityUtil.getJSONString(0, "发布成功!"); } }
前端需要对应修改index.html内容,详情见源代码。
启动项目,登录成功后点击我要发布;
查看帖子详情,根据帖子id查询帖子的作者、帖子的内容;
前端页面跳转至帖子详情页;
dao层,新增selectDiscussPosyById方法,代码如下:
/**
* 根据id查询帖子对象
* @param id
* @return
*/
DiscussPost selectDiscussPostById(int id);
DiscussPostMapper.xml文件中新增代码如下:
<select id="selectDiscussPostById" resultType="com.ahtoh.community.entity.DiscussPost">
select <include refid="selectFields"></include>
from discuss_post
where id=#{iod}
</select>
service层,DiscussPostService中新增findDiscussPostById方法,代码如下:
/**
* 根据id查找帖子对象
* @param id
* @return
*/
public DiscussPost findDiscussPostById(int id) {
return discussPostMapper.selectDiscussPostById(id);
}
controller层,新增getDiscussPost方法,代码如下:
/** * 查看帖子详情 * @param discussPostId * @param model * @return */ @GetMapping( "/detail/{discussPostId}") public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model) { // 查询帖子 DiscussPost post = discussPostService.findDiscussPostById(discussPostId); model.addAttribute("post", post); // 查找作者 User user = userService.findUserById(post.getUserId()); model.addAttribute("user", user); return "/site/discuss-detail"; }
前端需要对应修改index.html和 discuss-detail.html内容,详情见源代码。
启动项目,登录用户,查看帖子详情;
新增实体类记录回帖信息和对回帖的回复信息;
根据帖子类型(回帖,回帖的回复)封装回帖对象;
查询不同类型帖子的数量;
在 CommunityConstant 接口中添加两个字段:帖子和评论,代码如下:
/**
* 实体类型: 帖子
*/
int ENTITY_TYPE_POST = 1;
/**
* 实体类型: 评论
*/
int ENTITY_TYPE_COMMENT = 2;
entity包下新增Comment实体类,代码如下:
public class Comment { /**评论id**/ private int id; /**评论人的id**/ private int userId; /**评论类型 1:帖子 2:评论**/ private int entityType; /**评论对象id 针对帖子的评论:帖子的id 针对评论的评论:评论的id**/ private int entityId; /**a回复b,针对评论的评论,评论对象b的user_id,方便前端快速展示b的用户名**/ private int targetId; /**评论人的内容**/ private String content; /**评论人状态**/ private int status; /**评论人的时间**/ private Date createTime; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public int getEntityType() { return entityType; } public void setEntityType(int entityType) { this.entityType = entityType; } public int getEntityId() { return entityId; } public void setEntityId(int entityId) { this.entityId = entityId; } public int getTargetId() { return targetId; } public void setTargetId(int targetId) { this.targetId = targetId; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } @Override public String toString() { return "Comment{" + "id=" + id + ", userId=" + userId + ", entityType=" + entityType + ", entityId=" + entityId + ", targetId=" + targetId + ", content='" + content + '\'' + ", status=" + status + ", createTime=" + createTime + '}'; } }
dao层,新建CommentMapper接口,代码如下:
@Mapper public interface CommentMapper { /** * 根据帖子类型和帖子id查找帖子列表 * @param entityType * @param entityId * @param offset * @param limit * @return */ List<Comment> selectCommentsByEntity(int entityType, int entityId, int offset, int limit); /*** * 根据帖子类型和帖子id查找帖子数量 * @param entityType * @param entityId * @return */ int selectCountByEntity(int entityType, int entityId); }
对应的CommentMapper.xml代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ahtoh.community.dao.CommentMapper"> <sql id="selectFields"> id, user_id, entity_type, entity_id, target_id, content, status, create_time </sql> <select id="selectCommentsByEntity" resultType="com.ahtoh.community.entity.Comment"> select <include refid="selectFields"></include> from comment where status = 0 and entity_type = #{entityType} and entity_id = #{entityId} order by create_time asc limit #{offset}, #{limit} </select> <select id="selectCountByEntity" resultType="int"> select count(id) from comment where status = 0 and entity_type = #{entityType} and entity_id = #{entityId} </select> </mapper>
service层,在service包下新建CommentService,代码如下:
@Service public class CommentService { @Autowired private CommentMapper commentMapper; /** * 根据帖子类型和帖子id查找帖子列表 * @param entityType 帖子类型 * @param entityId 帖子id * @param offset 偏移量 * @param limit 上限 * @return */ public List<Comment> findCommentsByEntity(int entityType, int entityId, int offset, int limit) { return commentMapper.selectCommentsByEntity(entityType, entityId, offset, limit); } /** * 根据帖子类型和帖子id查找帖子数量 * @param entityType 帖子类型 * @param entityId 帖子id * @return */ public int findCommentCount(int entityType, int entityId) { return commentMapper.selectCountByEntity(entityType, entityId); } }
controller层,在DiscussPostController中修改getDiscussPost方法,代码如下:
/** * 查看帖子详情 * @param discussPostId * @param model * @return */ @GetMapping( "/detail/{discussPostId}") public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page) { // 查询帖子 DiscussPost post = discussPostService.findDiscussPostById(discussPostId); model.addAttribute("post", post); // 查找作者 User user = userService.findUserById(post.getUserId()); model.addAttribute("user", user); // 评论分页信息 page.setLimit(5); page.setPath("/discuss/detail/" + discussPostId); page.setRows(post.getCommentCount()); // 评论: 给帖子的评论 // 回复: 给评论的评论 // 评论列表 List<Comment> commentList = commentService.findCommentsByEntity( ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit()); // 评论VO列表 List<Map<String, Object>> commentVoList = new ArrayList<>(); if (commentList != null) { for (Comment comment : commentList) { // 评论VO Map<String, Object> commentVo = new HashMap<>(); // 评论 commentVo.put("comment", comment); // 查询到评论的作者 commentVo.put("user", userService.findUserById(comment.getUserId())); // 回复列表 List<Comment> replyList = commentService.findCommentsByEntity( ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE); // 回复VO列表 List<Map<String, Object>> replyVoList = new ArrayList<>(); if (replyList != null) { for (Comment reply : replyList) { Map<String, Object> replyVo = new HashMap<>(); // 回复 replyVo.put("reply", reply); // 作者 replyVo.put("user", userService.findUserById(reply.getUserId())); // 回复目标 User target = reply.getTargetId() == 0 ? null : userService.findUserById(reply.getTargetId()); replyVo.put("target", target); replyVoList.add(replyVo); } } commentVo.put("replys", replyVoList); // 回复数量 int replyCount = commentService.findCommentCount(ENTITY_TYPE_COMMENT, comment.getId()); commentVo.put("replyCount", replyCount); commentVoList.add(commentVo); } } model.addAttribute("comments", commentVoList); return "/site/discuss-detail"; }
前端需要对应修改index.html和 discuss-detail.html内容,详情见源代码。
启动项目,登录用户,查看帖子详情;
新增帖子数据;
增加帖子数量;
dao层,在CommentMapper新增insertComment接口,代码如下:
/**
* 新增帖子
* @param comment
* @return
*/
int insertComment(Comment comment);
在DiscussPostMapper新增updateCommentCount接口,代码如下:
/**
* 更新帖子数量
* @param id
* @param commentCount
* @return
*/
int updateCommentCount(int id, int commentCount);
对应的CommentMapper.xml文件新增代码如下:
<sql id="insertFields">
user_id, entity_type, entity_id, target_id, content, status, create_time
</sql>
<insert id="insertComment" parameterType="Comment">
insert into comment(<include refid="insertFields"></include>)
values(#{userId},#{entityType},#{entityId},#{targetId},#{content},#{status},#{createTime})
</insert>
对应的DiscussPostMapper.xml新增代码如下:
<update id="updateCommentCount">
update discuss_post set comment_count = #{commentCount} where id = #{id}
</update>
service层,在DiscussPostService类中添加updateCommentCount方法,代码如下:
/**
* 更新评论数量
* @param id
* @param commentCount
* @return
*/
public int updateCommentCount(int id, int commentCount) {
return discussPostMapper.updateCommentCount(id, commentCount);
}
在 CommentService 类中添加 addComment 方法,此方法用到了事务注解,涉及到两张表的操作,代码如下:
@Autowired private SensitiveFilter sensitiveFilter; @Autowired private DiscussPostService discussPostService; /** * 新增帖子 * @param comment * @return */ @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) public int addComment(Comment comment) { if (comment == null) { throw new IllegalArgumentException("参数不能为空!"); } // 添加评论 comment.setContent(HtmlUtils.htmlEscape(comment.getContent())); comment.setContent(sensitiveFilter.filter(comment.getContent())); int rows = commentMapper.insertComment(comment); // 更新帖子评论数量 if (comment.getEntityType() == ENTITY_TYPE_POST) { int count = commentMapper.selectCountByEntity(comment.getEntityType(), comment.getEntityId()); discussPostService.updateCommentCount(comment.getEntityId(), count); } return rows; }
controller层,新建CommentController类,代码如下:
@Controller @RequestMapping("/comment") public class CommentController { @Autowired private CommentService commentService; @Autowired private HostHolder hostHolder; /** * 新增评论 * @param discussPostId * @param comment * @return */ @PostMapping( "/add/{discussPostId}") public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment) { comment.setUserId(hostHolder.getUser().getId()); comment.setStatus(0); comment.setCreateTime(new Date()); commentService.addComment(comment); return "redirect:/discuss/detail/" + discussPostId; } }
前端需要对应修改index.html和 discuss-detail.html内容,详情见源代码。
启动项目,登录用户,评论帖子;
查询用户的私信列表;
分页显示私信;
创建 Message 类,与数据库 message 表中的字段互相对应,代码如下:
@Data public class Message { /**私信id*/ private int id; /**发送方id*/ private int fromId; /**接收方id*/ private int toId; /**会话id*/ private String conversationId; /**会话内容*/ private String content; /**私信状态 0:未读 1:已读 2:删除*/ private int status; /**私信创建时间*/ private Date createTime; }
dao层,新建MessageMapper,新增接口代码如下:
@Mapper
public interface MessageMapper {
// 查询当前用户的会话列表,针对每个会话只返回一条最新的私信
List<Message> selectConversations(int userId, int offset, int limit);
// 查询当前用户的会话数量
int selectConversationCount(int userId);
// 查询某个会话所包含的私信列表
List<Message> selectLetters(String conversationId, int offset, int limit);
//查询某个会话所包含的私信数量
int selectLetterCount(String conversationId);
//查询未读私信的数量
int selectLetterUnreadCount(int userId, String conversationId);
}
在mapper包下新建MessageMapper.xml,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ahtoh.community.dao.MessageMapper"> <sql id="selectFields"> id, from_id, to_id, conversation_id, content, status, create_time </sql> <select id="selectConversations" resultType="com.ahtoh.community.entity.Message"> select <include refid="selectFields"></include> from message where id in( select max(id) from message where status != 2 and from_id != 1 and (from_id = #{userId} or to_id = #{userId}) group by conversation_id ) order by id desc limit #{offset}, #{limit} </select> <select id="selectConversationCount" resultType="int"> select count(m.maxid) from( select max(id) as maxid from message where status != 2 and from_id != 1 and (from_id = #{userId} or to_id = #{userId}) group by conversation_id ) as m </select> <select id="selectLetters" resultType="com.ahtoh.community.entity.Message"> select <include refid="selectFields"></include> from message where status != 2 and from_id != 1 and conversation_id = #{conversationId} order by id asc limit #{offset}, #{limit} </select> <select id="selectLetterCount" resultType="int"> select count(id) from message where status != 2 and from_id != 1 and conversation_id = #{conversationId} </select> <select id="selectLetterUnreadCount" resultType="int"> select count(id) from message where status = 0 and from_id != 1 and to_id = #{userId} <if test="conversationId!=null"> and conversation_id = #{conversationId} </if> </select> </mapper>
service层,新建MessageService服务类,代码如下:
@Service public class MessageService { @Autowired private MessageMapper messageMapper; /** * 根据用户id查询私信列表 * @param userId * @param offset * @param limit * @return */ public List<Message> findConversations(int userId, int offset, int limit) { return messageMapper.selectConversations(userId, offset, limit); } /** * 根据用户id查询消息数量 * @param userId * @return */ public int findConversationCount(int userId) { return messageMapper.selectConversationCount(userId); } /** * 根据会话id查询消息列表 * @param conversationId * @param offset * @param limit * @return */ public List<Message> findLetters(String conversationId, int offset, int limit) { return messageMapper.selectLetters(conversationId, offset, limit); } /** * 根据会话id查询消息数量 * @param conversationId * @return */ public int findLetterCount(String conversationId) { return messageMapper.selectLetterCount(conversationId); } /** * 查询未读消息数量 * @param userId * @param conversationId * @return */ public int findLetterUnreadCount(int userId, String conversationId) { return messageMapper.selectLetterUnreadCount(userId, conversationId); } }
controller层,新建MessageController控制类,代码如下:
@Controller public class MessageController { @Autowired private MessageService messageService; @Autowired private HostHolder hostHolder; @Autowired private UserService userService; /** * 获取私信列表 * @param model * @param page * @return */ @GetMapping("/letter/list") public String getLetterList(Model model, Page page) { User user = hostHolder.getUser(); // 分页信息 page.setLimit(5); page.setPath("/letter/list"); page.setRows(messageService.findConversationCount(user.getId())); // 会话列表 List<Message> conversationList = messageService.findConversations(user.getId(), page.getOffset(), page.getLimit()); List<Map<String, Object>> conversations = new ArrayList<>(); if (conversationList != null) { for (Message message : conversationList) { Map<String, Object> map = new HashMap<>(); map.put("conversation", message); map.put("letterCount", messageService.findLetterCount(message.getConversationId())); map.put("unreadCount", messageService.findLetterUnreadCount(user.getId(), message.getConversationId())); int targetId = user.getId() == message.getFromId() ? message.getToId() : message.getFromId(); map.put("target", userService.findUserById(targetId)); conversations.add(map); } } model.addAttribute("conversations", conversations); // 查询未读消息数量 int letterUnreadCount = messageService.findLetterUnreadCount(user.getId(), null); model.addAttribute("letterUnreadCount", letterUnreadCount); return "/site/letter"; } /** * 私信列表详情 * @param conversationId * @param page * @param model * @return */ @GetMapping(path = "/letter/detail/{conversationId}") public String getLetterDetail(@PathVariable("conversationId") String conversationId, Page page, Model model) { // 分页信息 page.setLimit(5); page.setPath("/letter/detail/" + conversationId); page.setRows(messageService.findLetterCount(conversationId)); // 私信列表 List<Message> letterList = messageService.findLetters(conversationId, page.getOffset(), page.getLimit()); List<Map<String, Object>> letters = new ArrayList<>(); if (letterList != null) { for (Message message : letterList) { Map<String, Object> map = new HashMap<>(); map.put("letter", message); map.put("fromUser", userService.findUserById(message.getFromId())); letters.add(map); } } model.addAttribute("letters", letters); // 私信目标 model.addAttribute("target", getLetterTarget(conversationId)); return "/site/letter-detail"; } /** * 获取私信目标对象 * @param conversationId * @return */ private User getLetterTarget(String conversationId) { String[] ids = conversationId.split("_"); int id0 = Integer.parseInt(ids[0]); int id1 = Integer.parseInt(ids[1]); if (hostHolder.getUser().getId() == id0) { return userService.findUserById(id1); } else { return userService.findUserById(id0); } } }
前端需要对应修改index.html和 discuss-detail.html内容,详情见源代码。
启动项目,登录用户,查看私信;
新增私信内容,更新私信数量;
异步方式发送私信,成功后自动刷新私信列表,显示到前端;
dao层,在MessageMapper中新增isnertMessage 方法和 updateStatus 方法,代码如下:
// 新增消息
int insertMessage(Message message);
// 修改消息的状态
int updateStatus(List<Integer> ids, int status);
对应的MessageMapper.xml中新增代码如下:
<sql id="insertFields">
from_id, to_id, conversation_id, content, status, create_time
</sql>
<insert id="insertMessage" parameterType="com.ahtoh.community.entity.Message" keyProperty="id">
insert into message(<include refid="insertFields"></include> )
values(#{fromId}, #{toId}, #{conversationId}, #{content}, #{status}, #{createTime})
</insert>
<update id="updateStatus">
update message set status = #{status}
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
service层,在 UserService 类中添加 findUserByName 方法;在 MessageService 类中添加 addMessage 和 readMessage 方法;代码如下:
/**
* 根据姓名查询用户
* @param username
* @return
*/
public User findUserByName(String username){
return userMapper.selectByName(username);
}
@Autowired private SensitiveFilter sensitiveFilter; /** * 新增私信 * @param message * @return */ public int addMessage(Message message) { message.setContent(HtmlUtils.htmlEscape(message.getContent())); message.setContent(sensitiveFilter.filter(message.getContent())); return messageMapper.insertMessage(message); } /** * 读取私信 * @param ids * @return */ public int readMessage(List<Integer> ids) { return messageMapper.updateStatus(ids, 1); }
controller层,新增发送私信和设置私信为已读方法,代码如下:
/** * 私信列表详情 * @param conversationId * @param page * @param model * @return */ @GetMapping(path = "/letter/detail/{conversationId}") public String getLetterDetail(@PathVariable("conversationId") String conversationId, Page page, Model model) { // 分页信息 page.setLimit(5); page.setPath("/letter/detail/" + conversationId); page.setRows(messageService.findLetterCount(conversationId)); // 私信列表 List<Message> letterList = messageService.findLetters(conversationId, page.getOffset(), page.getLimit()); List<Map<String, Object>> letters = new ArrayList<>(); if (letterList != null) { for (Message message : letterList) { Map<String, Object> map = new HashMap<>(); map.put("letter", message); map.put("fromUser", userService.findUserById(message.getFromId())); letters.add(map); } } model.addAttribute("letters", letters); // 私信目标 model.addAttribute("target", getLetterTarget(conversationId)); // 查看完私信后,设置已读 List<Integer> ids = getLetterIds(letterList); if (!ids.isEmpty()) { messageService.readMessage(ids); } return "/site/letter-detail"; } /** * 获取未读私信的所有id * @param letterList * @return */ private List<Integer> getLetterIds(List<Message> letterList) { List<Integer> ids = new ArrayList<>(); if (letterList != null) { for (Message message : letterList) { if (hostHolder.getUser().getId() == message.getToId() && message.getStatus() == 0) { ids.add(message.getId()); } } } return ids; } /** * 获取私信目标对象 * @param conversationId * @return */ private User getLetterTarget(String conversationId) { String[] ids = conversationId.split("_"); int id0 = Integer.parseInt(ids[0]); int id1 = Integer.parseInt(ids[1]); if (hostHolder.getUser().getId() == id0) { return userService.findUserById(id1); } else { return userService.findUserById(id0); } } /** * 发送私信 * @param toName * @param content * @return */ @PostMapping(path = "/letter/send") @ResponseBody public String sendLetter(String toName, String content) { User target = userService.findUserByName(toName); if (target == null) { return CommunityUtil.getJSONString(1, "目标用户不存在!"); } Message message = new Message(); message.setFromId(hostHolder.getUser().getId()); message.setToId(target.getId()); if (message.getFromId() < message.getId()) { message.setConversationId(message.getFromId() + "_" + message.getToId()); } else { message.setConversationId(message.getToId() + "_" + message.getFromId()); } message.setContent(content); message.setCreateTime(new Date()); messageService.addMessage(message); return CommunityUtil.getJSONString(0); }
前端修改letter-detai.html 和letter.js文件,详情见源代码;
启动项目,登录用户,发送私信;
查看私信;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。