Nodejs之MEAN栈开发(九)---- 用户评论的增加/删除/修改
由于工作中做实时通信的项目,需要用到Nodejs做通讯转接功能,刚开始接触,很多都不懂,于是我和同事就准备去学习nodejs,结合nodejs之MEAN栈实战书籍《Getting.MEAN.with.Mongo.Express.Angular.and.Node.2015.11》,我们完成了一个小型的ReadClubing项目,结合书中讲解和步骤,我们完成了不同的功能,当然由于时间原因,还有很多不完善的地方,后续我们会继续开发。
同事负责开发的内容为:
Nodejs之MEAN栈开发(一)---- 路由与控制器
Nodejs之MEAN栈开发(二)----视图与模型
Nodejs之MEAN栈开发(三)---- 使用Mongoose创建模型及API
Nodejs之MEAN栈开发(四)---- form验证及图片上传
Nodejs之MEAN栈开发(五)---- Angular入门与页面改造
Nodejs之MEAN栈开发(六)---- 用Angular创建单页应用(上)
Nodejs之MEAN栈开发(七)---- 用Angular创建单页应用(下)
Nodejs之MEAN栈开发(八)---- 用户认证与会话管理详解
我开发的内容为:
Nodejs之MEAN栈开发(九)---- 用户评论的增加/删除/修改
针对这次Nodejs之MEAN栈开发ReadClubing项目,我主要负责是标题详情页面,包括页面布局、数据展示、评论的增加/删除/修改等工作。
1、标题详情页面布局和数据展示
标题详情页操作如图:

因为ReadClub的项目的结构是按照MVC的形式来开发的,首先找到程序入口,即路由app_client/app.js,app.js主要是按照Angular提供的内置模块$routeProvider实现路由转接功能,链接到详情页的代码如下:
(function() {
angular.module('readApp', ['ngRoute', 'ngSanitize'])
.config(['$routeProvider', '$locationProvider', config]);
function config($routeProvider, $locationProvider) {
$routeProvider
.when('/topicDetail/:topicid', {
templateUrl: 'topicDetail/topicDetail.html',
controller: 'topicDetailCtrl',
caseInsensitiveMatch: true,
controllerAs: 'vm'
})
.otherwise({ redirectTo: '/' });
$locationProvider.html5Mode(true);
}
)();
因此页面被跳转到详情页topicDetail.html,详情页的数据依赖于控制器topicDetailCtrl。
同样是遵循Angular页面获取数据的方式,页面中需要展示数据的地方都以{{value}}的形式表示,中间value的输出是依靠控制器topicDetailCtrl从数据库获取到的数据。topicDetail.html代码如下:
<navigation></navigation>
<div id="bodycontent" class="container">
<div class="row">
<div class="col-md-9">
<div class="content">
<div class="topic_top backcolor">
<div class="title">{{vm.topic.title}}</div>
<div class="topic_content">{{vm.topic.content}}</div>
</div>
<div class="comment backcolor">
<div class="commenttip">{{vm.topic.comments.length}} 回复</div>
<div id="commentcontent" ng-repeat="comments in vm.topic.comments">
<div class="cell reply_area reply_item ">
<div class="author_content">
<a href="" class="user_avatar">
<img src="https://avatars.githubusercontent.com/u/3088175?v=3&s=120"/>
</a>
<div class="user_info">
<a class="dark reply_author" href="/user/{{vm.topic.author}}">{{vm.topic.author}}</a>
<a class="reply_time">{{comments.createdOn | jsonDate:'yyyy-MM-dd HH:mm:ss'}}</a>
</div>
<div class="user_action">
<a ng-href="#" ng-click="vm.editReply(comments._id)" id="{{comments._id}}">
<i class="fa fa-pencil-square-o" title="编辑"></i>
</a>
<a ng-href="#" ng-click="vm.deleteReply(comments._id)">
<i class="fa fa-trash" title="删除"></i>
</a>
</div>
</div>
<div class="reply_content from-rochael">
<div class="markdown-text" ng-bind-html="comments.content | trustHtml">
</div>
</div>
</div>
</div>
</div>
<div class="commentarea backcolor">
<div class="header">添加回复</div>
<div class="inner">
<div id="summernote" ng-model="summernote" ng-summernote ></div>
<input id="topicid" type="hidden" value="{{vm.topic._id}}" />
<input id="username" type="hidden" value="Smartlin" />
<div class="editor_buttons">
<span ng-show="vm.reply" class="submit" ng-click="vm.submitReply()">回复</span>
<span ng-show="!vm.reply" class="submit" ng-click="vm.updateReply()">回复</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="userinfo">
<p>stoneniqiu</p>
</div>
</div>
</div>
<footer-nav></footer-nav>
理解控制器获取数据:同理,在topicDetail文件下新建topicDetail.controller.js。遵循Angular的路由规则,由于实现的视图和控制器的绑定关系,视图加载时,控制器会立即调取数据给页面输出。
我们来看看topicDetail.controller.js的逻辑,代码如下:
(function () {
angular.module('readApp')
.controller('topicDetailCtrl', topicDetailCtrl)
.directive('ngSummernote', getSummernote)
topicDetailCtrl.$inject = ['$scope','$http', '$routeParams', 'topicData'];
function topicDetailCtrl($scope,$http, $routeParams, topicData) {
var vm = this;
vm.topicid = $routeParams.topicid;
vm.reply = true;
topicData.getTopicById(vm.topicid).success(function (data) {
vm.topic = data;
}).error(function (e) {
vm.message = "Sorry, something's gone wrong ";
})
}
function getSummernote(){
return {
restrict : 'A',
require : 'ngModel',
link : function($scope, $element, $attrs, $ngModel){
if (!$ngModel) {
return;
}
$($element).summernote({
height: ,
minHeight: ,
maxHeight: ,
focus: true
})
},
};
}
})();
topicData.getTopicById(vm.topicid),通过传入topicid查询数据库,链接到app_client/common/services/ReadData.service.js,主要通过angular定义的service,请求数据模块$http,实现路由转接功能,代码如下:
angular
.module('readApp')
.service('topicData', topicData) topicData.$inject = ['$http'];
function topicData($http) {
var getTopicById = function (topicid) {
return $http.get('/api/topics/' + topicid);
};
return {
getTopicById: getTopicById
};
};
$http.get('/api/topics/' + topicid),根据路由跳转到app_api/routes/index.js,通过node的router规则实现跳转。代码如下:
router.get('/topics/:topicid', topicCtrl.topicReadOne);
在app_api/controller/topic.js中实现路由方法,代码如下:
module.exports.topicReadOne = function (req, res) {
var topicid = req.params.topicid;
console.log("topicid:"+topicid);
if (!topicid) {
sendJSONresponse(res, , {
"message": "Not found, topicid is required"
});
return;
}
TopicModel.findById(topicid).exec(function (err, topic) {
if (!topic) {
sendJSONresponse(res, , {
"message": "topicid not found"
});
return;
} else if (err) {
sendJSONresponse(res, , err);
return;
}
sendJSONresponse(res, , topic);
});
}
返回的数据topic在topicDetail.controller.js中通过vm.topic = data来接收,到这里,视图中通过vm.topic依赖的数据就会展现出来。
详情页面展示需要注意的几点细节:
(1)、富文本框summernote的显示
没有运用angular的html页面,显示summernote的方法:首先,在html中写入标签 <div id="summernote"></div>;其次,引入支持相应的summernote.css和summernote.js文件即可,代码如下:
<link rel='stylesheet' href='http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.1/summernote.css'/>
<div id="summernote"></div>
<script src='http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.1/summernote.js'></script>
但在使用angular框架加载html页面时,会导致富文本框summernote无法显示,解决方法为:
第一步:在html页面中增加ng-model,ng-summernote,代码如下:
<div id="summernote" ng-model="summernote" ng-summernote ></div>
第二步:在控制器topicDetail.controller.js中,嵌入指令directive('ngSummernote', getSummernote),代码如下:
angular.module('readApp',[])
.directive('ngSummernote', getSummernote)
function getSummernote(){
return {
restrict : 'A',
require : 'ngModel',
link : function($scope, $element, $attrs, $ngModel){
if (!$ngModel) {
return;
}
$($element).summernote({//初始化方法
height: ,
minHeight: ,
maxHeight: ,
focus: true
})
},
};
}
(2)、
编辑和删除图标的显示
编辑和删除图标的显示,需要引用font-awesome.min.css,fontawesome是一套绝佳的图标字体库和CSS框架,为您提供可缩放的矢量图标,您可以使用CSS所提供的所有特性对它们进行更改,包括:大小、颜色、阴影或者其它任何支持的效果。引入的方式如下:
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css"/>
(3)、编辑事件editReply()和删除事件deleteReply()传参
在ng-repeat执行循环过程中,因为这两个事件需要传入评论的id,方法为:vm.editReply(comments._id)和vm.deleteReply(comments._id),直接传入comments._id,不需要用{{}};而其他不需要作为参数传递给方法的值,显示的方法是需要加{{}},如:{{comments.createdOn}},代码如下:
(4)、关于angularJS绑定数据时自动转义html标签
因为我们追加的评论保存到数据库时会有带html标签的情况,如加粗、设置颜色的字体,图片等,当取出展示在评论区时会带有html标签,因此需要转义,实现转义的方法如下:
第一步:在html中绑定过滤器trustHtml,代码如下:
<div class="markdown-text" ng-bind-html="comments.content | trustHtml"></div>
第二步:在控制器topicDetail.controller.js中,设置过滤器,代码如下:
angular.module('readApp',[])
.filter('trustHtml', getTrust)
getTrust.$inject = ['$sce'];
function getTrust($sce) {
return function (input) {
return $sce.trustAsHtml(input);
}
}
2、追加评论
追加评论通过回复按钮,但是当我们修改评论时,将要修改的评论内容放到富文本框中,也要点击回复按钮,因此会出现冲突,解决方法:
首先,在html页面中定义两个绑定不同事件的回复按钮,通过angular内置指令ng-show来实现两个按钮的显示和隐藏,从而来实现不同的功能,代码如下:
<span ng-show="vm.reply" class="submit" ng-click="vm.submitReply()">回复</span>
<span ng-show="!vm.reply" class="submit" ng-click="vm.updateReply()">回复</span>
其次,在初进详情页时,设置vm.reply = true来显示增加评论的按钮,当点击编辑评论时,则设置vm.reply = false来显示编辑评论的按钮,最后修改的评论成功提交后,又设置vm.reply = true切换到增加评论的按钮。
通过入口回复按钮submitReply(),链接到控制器topicDetail.controller.js,代码如下:
vm.submitReply = function () {
var code = $('#summernote').summernote('code');
if (code == "") {
alert("输入的内容不能为空~");
return;
}
var params = {
topicid: vm.topicid,
content: code
};
topicData.addComment(params).success(function (data) {
vm.message = data.length > ? "" : "暂无数据";
vm.topic.comments.push(data);//数据库和页面上的comments对象都要进行数据更新;
$('#summernote').summernote('code', '');
}).error(function (e) {
console.log(e);
});
};
通过topicData.addComment(params),链接到ReadData.service.js,通过绑定service()显示路由,代码如下:
function topicData ($http) {
var addComment = function(data){
return $http({
method: 'POST',
url: "/api/topics/" + data.topicid,
data: data
})
};
return {
addComment: addComment,
}
}
通过$http()请求,链接到app_api/routes/index.js,代码如下:
router.post('/topics/:topicid', topicCtrl.commentAppendOne);
通过topicCtrl.commentAppendOne,链接到app_api/controller/topic.js,代码如下:
//读取所有的有关这个topicid的评论
module.exports.commentAppendOne = function (req, res) {
var topicid = req.params.topicid;
if (!topicid) {
sendJSONresponse(res, , {
"message": "Not found, topicid is required"
});
return;
}
TopicModel.findById(topicid)
.select('comments')
.exec(function (err, topic) {
if (!topic) {
sendJSONresponse(res, , {
"message": "topic not found"
});
return;
} else if (err) {
sendJSONresponse(res, , err);
return;
}
doAddComment(req, res, topic);
});
} var doAddComment = function (req, res, topic) {
if (!topic) {
sendJSONresponse(res, , "topicid not found");
} else {
//console.log("user:", req.body.user);
//console.log(req.body.content);
topic.comments.push({
user: req.body.user,
createdOn: req.body.createdOn,
content: req.body.content
});
topic.save(function (err, topic) {
var thisReview;
if (err) {
sendJSONresponse(res, , err);
} else {
var length = topic.comments.length - ;
//console.log("length:",length);
thisReview = topic.comments[length];
//console.log("thisReview:", querystring.stringify(thisReview));
sendJSONresponse(res, , thisReview);
}
});
}
}
最后,在控制器中通过成功之后的回调函数,通过push()方法将要追加的数据放到数组topic.comments中,实现数据更新;评论追加成功后,记得清空富文本框里面的值,方法为:$('#summernote').summernote('code', '');
3、删除评论
点击删除图标,触发删除评论事件vm.deleteReply(comments._id),需要传入评论id,确保要删除的是哪条评论。根据入口,链接到控制器topicDetail.controller.js,代码如下:
vm.deleteReply = function (commentid) {
var params = {
topicid: vm.topicid,
commentid: commentid
};
topicData.deleteComment(params).success(function (data) {
if (confirm("确定删除?")) {
for (var i = ; i < vm.topic.comments.length; i++) {
if (vm.topic.comments[i]._id == commentid) {
vm.topic.comments.splice(vm.topic.comments.indexOf(vm.topic.comments[i]._id), );
}
}
}
}).error(function (e) {
console.log(e);
});
}
通过topicData.deleteComment(params),链接到ReadData.service.js,代码如下:
function topicData ($http) {
var deleteComment = function (data) {
console.log("deleteComment,topicid:", data.topicid, "commentid:", data.commentid);
return $http.delete('/api/topics/' + data.topicid + '/comments/' + data.commentid);
};
return {
deleteComment: deleteComment
}
}
通过$http.delete()请求,链接到app_api/routes/index.js,代码如下:
router.delete('/topics/:topicid/comments/:commentid', topicCtrl.commentDeleteOne);
通过topicCtrl.commentDeleteOne,链接到app_api/controllers/topic.js,代码如下:
module.exports.commentDeleteOne = function (req, res) {
if (!req.params.topicid || !req.params.commentid) {
sendJSONresponse(res, , {
"message": "Not found, topicid and commentid are both required"
});
return;
}
TopicModel
.findById(req.params.topicid)
.select('comments')
.exec(
function (err, topic) {
if (!topic) {
sendJSONresponse(res, , {
"message": "topicid not found"
});
return;
} else if (err) {
sendJSONresponse(res, , err);
return;
}
if (topic.comments && topic.comments.length > ) {
console.log("length:", topic.comments.length);
if (!topic.comments.id(req.params.commentid)) {
sendJSONresponse(res, , {
"message": "commentid not found"
});
} else {
topic.comments.id(req.params.commentid).remove();
topic.save(function (err) {
if (err) {
sendJSONresponse(res, , err);
} else {
sendJSONresponse(res, , null);
}
});
}
} else {
sendJSONresponse(res, , {
"message": "No comment to delete"
});
}
}
);
};
最后,在控制器中通过成功之后的回调函数,变量评论数组找到要删除的那条评论,通过splice()从数组中删除,实现页面数据更新。
4、修改评论
点击编辑图标,触发编辑评论事件vm.editReply(comments._id),需要传入评论id,确保要编辑的是哪条评论。根据入口,链接到控制器topicDetail.controller.js,注意编辑评论需要分为两步:
第一步、将要编辑的评论内容追加到富文本框中,进行修改,代码如下:
vm.editReply = function (commentid) {
document.getElementsByTagName('BODY')[].scrollTop=document.getElementsByTagName('BODY')[].scrollHeight;
var editContent = angular.element(document.getElementById(commentid)).parent('.user_action').parent('.author_content').next('.reply_content').children().text();
$('#summernote').summernote('code', editContent);
vm.reply = false;
vm.commentid = commentid;
}
第二步、通过点击编辑评论的回复按钮,来实现修改评论的提交,代码如下:
vm.updateReply = function () {
vm.code = $('#summernote').summernote('code');
var params = {
topicid: vm.topicid,
commentid: vm.commentid,
editContent: vm.code
};
topicData.updateComment(params).success(function (data) {
console.log(data);
for(var i = ;i < data.comments.length;i++){
if(data.comments[i]._id == vm.commentid){
console.log('id into');
data.comments[i].content = vm.code;
vm.topic.comments = data.comments;
$('#summernote').summernote('code', '')
}
}
vm.reply = true;
}).error(function (e) {
console.log(e);
});
}
根据topicData.updateComment(params),链接到ReadData.service.js,代码如下:
function topicData ($http) {
var updateComment = function (data) {
return $http({
method:'POST',
url:'/api/topics/' + data.topicid + '/comments/' + data.commentid,
data: { editContent: data.editContent}
})
};
return {
updateComment: updateComment
};
}
通过$http(),post方式请求,链接到app_api/routes/index.js,代码如下:
router.post('/topics/:topicid/comments/:commentid', topicCtrl.commentUpdateOne);
通过topicCtrl.commentUpdateOne,链接到app_api/controllers/topic.js,代码如下:
module.exports.commentUpdateOne = function (req, res) {
if (!req.params.topicid || !req.params.commentid) {
sendJSONresponse(res, , {
"message": "Not found, topicid and commentid are both required"
});
return;
}
TopicModel
.findById(req.params.topicid)
.select('comments')
.exec(
function (err, topic) {
if (!topic) {
sendJSONresponse(res, , {
"message": "topicid not found"
});
return;
} else if (err) {
sendJSONresponse(res, , err);
return;
}
if (topic.comments && topic.comments.length > ) {
if (!topic.comments.id(req.params.commentid)) {
sendJSONresponse(res, , {
"message": "commentid not found"
});
} else {
for(var i= ;i<topic.comments.length;i++){
if(topic.comments[i]._id == req.params.commentid){
topic.comments[i].content = req.body.editContent;
topic.save(function (err, topic) {
if (err) {
sendJSONresponse(res, , err);
} else {
sendJSONresponse(res, , topic);
}
});
}
}
}
} else {
sendJSONresponse(res, , {
"message": "No comment to update"
});
}
}
);
};
var updateContentById = function (req, res, commentid) {
CommentModel
.findById(commentid)
.select('content')
.exec(
function (err, content) {
if (!content) {
sendJSONresponse(res, , {
"message": "content not found"
});
return;
} else if (err) {
sendJSONresponse(res, , err);
return;
}
sendJSONresponse(res, , content);
topic.save(function (err, comments) {
if (err) {
sendJSONresponse(res, , err);
} else {
sendJSONresponse(res, , book);
}
});
}
);
}
最后,通过控制器中执行成功后的回调函数,通过循环找到要修改的那个评论id,根据那条id去更新对应的评论内容,实现页面数据更新。
细节:如果在node页面console.log("汉字")或者后端接收到带有中文的数据或者使用node提供的代码压缩uglifyJs.minify(),cmd窗口会报错,原因是windows系统自带的cmd窗口不能识别utf-8字符编码,解决方法如下:
1、打开CMD.exe命令行窗口;
2、通过 chcp命令改变代码页,UTF-8的代码页为65001;

3、修改窗口属性,改变字体
在命令行标题栏上点击右键,选择"属性"->"字体",将字体修改为True Type字体"Lucida Console",然后点击确定将属性应用到当前窗口。如下图所示:

这时使用type命令就可以显示UTF-8文本文件的内容了:
type filename.txt
4、通过以上操作并不能完全解决问题,因为显示出来的内容有可能不完全。可以先最小化,然后最大化命令行窗口,文件的内容就完整的显示出来了。
5、另外提供一些chcp命令的参考:
chcp 65001 就是换成UTF-8代码页
chcp 936 可以换回默认的GBK
chcp 437 是美国英语
源码:https://github.com/stoneniqiu/ReadingClub (注意不同分支)
小结:这一节主要讲到了如何在Angular页面来加载Jquery组件如富文本框summernote,调取数据和展示数据时应该注意的几点细节,评论的增加、删除、修改,其实内容不是很多,主要要理清思路,根据项目mvc的逻辑结构,自己走一遍,应该就没问题了。
Nodejs之MEAN栈开发(九)---- 用户评论的增加/删除/修改的更多相关文章
- Nodejs之MEAN栈开发(三)---- 使用Mongoose创建模型及API
继续开扒我们的MEAN栈开发之路,前面两节我们学习了Express.Jade引擎并创建了几个静态页面,最后通过Heroku部署了应用. Nodejs之MEAN栈开发(一)---- 路由与控制器 Nod ...
- AutoCad 二次开发 .net 之层表的增加 删除 修改图层颜色 遍历 设置当前层
AutoCad 二次开发 .net 之层表的增加 删除 修改图层颜色 遍历 设置当前层 AutoCad 二次开发 .net 之层表的增加 删除 修改图层颜色 遍历 设置当前层我理解的图层的作用大概是把 ...
- Nodejs之MEAN栈开发(二)----视图与模型
上一节做了对Express做了简单的介绍,提出了controller,介绍了路由.这一节将重点放到视图和模型上,完成几个静态页面并部署到heroku上. 导航 前端布局使用bootstrap,从官网下 ...
- Nodejs之MEAN栈开发(八)---- 用户认证与会话管理详解
用户认证与会话管理基本上是每个网站必备的一个功能.在Asp.net下做的比较多,大体的思路都是先根据用户提供的用户名和密码到数据库找到用户信息,然后校验,校验成功之后记住用户的姓名和相关信息,这个信息 ...
- Nodejs之MEAN栈开发(一)---- 路由与控制器
因为工作需要,最近再次学习了node,上一次学习node是2014年,纯粹是个人兴趣,学了入门之后没有运用,加上赶别的项目又不了了之.这次正好捡起来.废话不多说,这里的MEAN指的是Mongodb.E ...
- Python 全栈开发九 日志模块
日志是一种可以追踪某些软件运行时所发生事件的方法.软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情.一个事件可以用一个可包含可选变量数据的消息来描述.此外,事件也有重要性的概念 ...
- Nodejs之MEAN栈开发(七)---- 用Angular创建单页应用(下)
上一节我们走通了基本的SPA基础结构,这一节会更彻底的将后端的视图.路由.控制器全部移到前端.篇幅比较长,主要分页面改造.使用AngularUI两大部分以及一些优化路由.使用Angular的其他指令的 ...
- Nodejs之MEAN栈开发(六)---- 用Angular创建单页应用(上)
在上一节中我们学会了如何在页面中添加一个组件以及一些基本的Angular知识,而这一节将用Angular来创建一个单页应用(SPA).这意味着,取代我们之前用Express在服务端运行整个网站逻辑的方 ...
- Nodejs之MEAN栈开发(五)---- Angular入门与页面改造
这个系列一共会涉及两个JavaScript框架的讲解,一个是Express用做后端,一个是Angular用于前端.和Express一样,Angular分离内容,处理视图.数据和逻辑.和MVC模式很相似 ...
随机推荐
- MD5编码工具类 MD5Code.java
代码如下: package com.util; /** * MD5编码工具类 * http://www.cnblogs.com/sosoft/ */ public class MD5Code { st ...
- Asp.net Session 与Cookie的应用
写程序的很多人都知道的,Session是服务器端的东西而Cooike是客户端的东西.因为B/S模式是无状态模式,它们的应用都是要存储客户端的某些登录或是加密的信息. Session存在于服务器端,当然 ...
- 速战速决 (1) - PHP: 概述, 常量, 变量, 运算符, 表达式, 控制语句
[源码下载] 速战速决 (1) - PHP: 概述, 常量, 变量, 运算符, 表达式, 控制语句 作者:webabcd 介绍速战速决 之 PHP 概述 常量 变量 运算符 表达式 控制语句 示例1. ...
- css: 照片有如层叠效果
显示上面照片效果css <!DOCTYPE html> <!--headTrap<body></body><head></head>& ...
- android 6.0添加权限
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissio ...
- TestNG官方文档中文版(2)-annotation
TestNG的官方文档的中文翻译版第二章,原文请见 http://testng.org/doc/documentation-main.html 2 - Annotation 这里是TestNG中用到的 ...
- Java Collection或Map的同步
新集合也提供了能防止多个进程同时修改一个容器内容的机制.若在一个容器里反复,同时另一些进程介入,并在那个容器中插入.删除或修改一个对象,便会面临发生冲突的危险.我们可能已传递了那个对象,可能它位于我们 ...
- Linux学习笔记13-用户和组
操作系统通过用户.组概念来管理使用计算机的人 用户代表一个使用计算机的使用者,操作系统通过用户概念限制一个使用者能够访问的资源 组用来组织管理用户 用户具有以下概念: 每个用户有一个用户名和一个用户I ...
- 图标集锦:10套免费的社交媒体 & 社交网站图标
社交网络是最近几年互联网领域最热门的关键词之一,如今社会网络化媒体也成为我们信息获取和传播的重要途径,很多网站都有把内容分享到社交媒体的功能. 社交媒体图标作为向用户传递信息的重要媒介,不管是在网页还 ...
- [deviceone开发]-do_FrameAnimtionView的简单动画示例
一.简介 do_FrameAnimtionView组件是用加载GIF动态图片和加载一系列图片形成动画效果的展示组件,这个示例直观的展示组件基本的使用方式.适合初学者. 二.效果图 三.相关下载 htt ...