最近研究下JS日期级联效果 感觉还不错,然后看了下kissy也正好有这么一个组件,也看了下源码,写的还不错,通过google最早是在2011年 淘宝的虎牙(花名)用原审JS写了一个(貌似据说是从YUI那边重构下的) 具体的可以看他的 博客园 , 感觉kissy组件源码 思路也是和YUI类似 所以我今天的基本思路也和他们的一样 只是通过自己分析下及用自己的方式包装下。

基本原理

1.传参中有 '年份下拉框dom节点', '月份下拉框dom节点', '天数下拉框dom节点', "开始日期","结束日期","默认日期"配置项

1.如果开始传参日期为空 那么默认是从"1900-01-01"开始

2.如果"结束日期为空" 那么默认结束日期为当前的时间。

3. 如果默认日期为空 那么默认日期默认为当前的时间。

2. 月份对应的天数可以直接写死 如:_dayInMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ; 分别为1月份到12月份的各个月份的默认天数,当然还有2月份闰年29天的情况 待会在代码中会有判断的。

3. 分别渲染出年份区间,月份区间,及相应的天数。(如果有默认的日期的话 且默认日期大于或者等于开始日期 且小于或者等于结束日期的话) 那么页面加载的时候 显示默认日期。

4. 绑定change事件 当切换到不同年份的时候 月份和天数也要分别渲染出来。

基本配置项如下:

   nodeYear
'#year',    年份下拉框dom节点
 nodeMonth  '#month',  月份下拉框dom节点
 nodeDay  '#day',      日期下拉框dom节点
 dateStart   '',             开始日期(为空 默认日期从1900-01-01开始)
 dateEnd  '',             结束日期(可选 默认为空就为当前时间)
dateDefault   ''             默认日期(可选 默认为空就为当前时间)

对外提供的方法

1. getDate()  返回当前时间,格式为yyyy-mm-dd

2. getYear() 返回当前的年份

3. getMonth() 返回当前的月份

4. getDay() 返回当前月份中的天数.

JSFiddle demo链接如下:

查看demo 请点击我!

下面代码分析如下:

1. 初始化调用init方法:分别获取开始时间 结束时间 默认时间的 "年,月,天"。如下代码:

// 开始时间可选 如果为空的话 那么默认开始时间是1900-01-01
if(_config.dateStart != '') { this.startDate = {
y: new Date(_config.dateStart).getFullYear(),
m: new Date(_config.dateStart).getMonth() + 1,
d: new Date(_config.dateStart).getDate()
};
}else {
var dateStart = '1900/01/01'; this.startDate = {
y: new Date(dateStart).getFullYear(),
m: new Date(dateStart).getMonth() + 1,
d: new Date(dateStart).getDate()
};
} // dateEnd 默认为空 如果没有传入的话 就取当前的时间
if(_config.dateEnd == '') {
this.endDate = {
y: new Date().getFullYear(),
m: new Date().getMonth() + 1,
d: new Date().getDate()
};
}else {
this.endDate = {
y: new Date(_config.dateEnd).getFullYear(),
m: new Date(_config.dateEnd).getMonth() + 1,
d: new Date(_config.dateEnd).getDate()
};
} // 默认时间可选 如果默认时间为空的话 那么就取当前的时间
if(_config.dateDefault != '') {
this.defaultDate = {
y: new Date(_config.dateDefault).getFullYear(),
m: new Date(_config.dateDefault).getMonth() + 1,
d: new Date(_config.dateDefault).getDate()
};
}else {
this.defaultDate = {
y: new Date().getFullYear(),
m: new Date().getMonth() + 1,
d: new Date().getDate()
};
}
// 判断时间是否合理
if((Date.parse(self._changeFormat(_config.dateStart)) > Date.parse(self._changeFormat(_config.dateEnd))) ||
(Date.parse(self._changeFormat(_config.dateDefault)) > Date.parse(self._changeFormat(_config.dateEnd)))){
return;
}

2. 渲染下拉框的年份:调用 y = self._renderYear();这个方法。

1. 获取年份的区间范围,获取方法就是:获取开始时间的年份 和 结束时的年份 如下代码:

/*
* 获取年份的范围 最小-最大
* @method _getYearRange
* @return {min,max}
*/
_getYearRange: function(){
var self = this,
_config = self.config;
return {
min: self.startDate.y,
max: self.endDate.y
}
},

2. 接着渲染年份,从最近的年份开始渲染,如果有默认的年份 且 满足条件的话 那么默认的年份显示出来。如下代码:

/*
* 渲染年份下拉框
* @method _renderYear
* private
*/
_renderYear: function(){
var self = this,
_config = self.config,
_cache = self.cache;
var nodeyear = $(_config.nodeYear)[0],
y = self.defaultDate.y,
range,
option; if(nodeyear) {
range = self._getYearRange();
for(var i = range.max; i >= range.min; i--) {
option = new Option(i,i); // 如果有默认年份的话
if(i == y) {
option.selected = true;
}
// 兼容所有浏览器 插入到最后
nodeyear.add(option,undefined);
}
}
$(nodeyear).attr('year',y);
return y;
},

3. 接着渲染月份 调用这个方法  y参数就是刚刚返回的年份  m = self._renderMonth(y);

1. 同理 渲染月份也要获取月份的范围 默认都是从1月份到12月份 但是也有列外。比如如下2个判断。

/*
* 获取月份的范围
* @method _getMonthRange
* @param {y} Number
*/
_getMonthRange: function(y){
var self = this,
_config = self.config;
var startDate = self.startDate,
endDate = self.endDate,
min = 1,
max = 12;
/*
* 如果默认年份等于开始年份的话 那么月份最小取得是开始的月份
* 因为如果开始是1900-05-01 如果默认的是 1900-03-02 那么最小月份肯定取得是5
* 因为默认时间不可能小于开始时间
*/
if(y == startDate.y) { // 开始年份
min = startDate.m;
} /*
* 同理 如果默认年份等于2014-04-01 那么取得是当前的年份(endDate未传的情况下)
* 那么最大的肯定取得是当前年份的 月份 不可能取的是4 因为只渲染出当前月份出来
* 后面的月份没有渲染出来
*/
if(y == endDate.y) {
max = endDate.m;
}
return {
min: min,
max: max
}
},

2. 知道月份的范围后 然后根据上面的年份渲染相应的月份:代码如下:

/*
* 根据年份 渲染所有的月份
* @method _renderMonth
* @param {y} 年份
*/
_renderMonth: function(y){
var self = this,
_config = self.config;
var nodeMonth = $(_config.nodeMonth)[0],
m = $(nodeMonth).attr('month') || self.defaultDate.m,
range,
option,
t = false;
if(nodeMonth) {
range = self._getMonthRange(y); nodeMonth.innerHTML = '';
for(var i = range.min; i <= range.max; i++) {
option = new Option(self.bitExpand(i),self.bitExpand(i)); // 如果有默认的月份的话
if(i == m) {
option.selected = true;
m = i;
t = true;
}
// 兼容所有浏览器 插入到最后
nodeMonth.add(option,undefined);
}
if(!t) {
m = range.min;
}
} $(nodeMonth).attr('month',m);
return m;
},

上面的代码 用了这句判断  m = $(nodeMonth).attr('month') || self.defaultDate.m, 默认情况下 也就是说页面一加载的时候 可以获取默认的月份,但是当我触发change事件后 我取的月份 是从m = $(nodeMonth).attr('month') 这个里面取得。上面代码 nodeMonth.innerHTML = ''; 也是为了change时候 请清空掉 然后重新生成的。

4.  渲染天数 通过这个方法: self._renderDay(y,m);

1. 渲染天数 同理也要获得相应的天数。调用_getDayRange方法。此方法中有判断是闰年的情况的。如下代码:

/*
* 获得天数的范围
* @method _getDayRange
* @param {y,m} {number,number}
*/
_getDayRange: function(y,m){
var self = this,
_config = self.config,
_cache = self.cache;
var startDate = self.startDate,
endDate = self.endDate,
min = 1,
max; if(m) {
if(m == 2) {
max = self._isLeapYear(y) ? 29 : 28;
}else {
max = _cache._dayInMonth[m-1];
}
// 如果年月份都等于开始日期的话 那么min也等于开始日
if(y == startDate.y && m == startDate.m) {
min = startDate.d;
}
// 如果年月份都等于结束日期的话 那么max也等于结束日
if(y == endDate.y && m == endDate.m) {
max = endDate.d;
}
}
return {
min: min,
max: max
}
},

2.接着渲染天数的方法如下:

_renderDay: function(y,m) {
var self = this,
_config = self.config;
var nodeDay = $(_config.nodeDay)[0],
d = $(nodeDay).attr('day') || self.defaultDate.d,
range,
option,
t = false;
if(nodeDay) {
range = self._getDayRange(y,m); nodeDay.innerHTML = '';
for(var i = range.min; i <= range.max; i++) {
option = new Option(self.bitExpand(i),self.bitExpand(i)); // 如果有默认的天数的话
if(i == d) {
option.selected = true;
d = i;
t = true;
}
// 兼容所有浏览器 插入到最后
nodeDay.add(option,undefined);
}
if(!t) {
d = range.min;
}
} $(nodeDay).attr('day',d);
return d;
},

5 最后用绑定change事件 调用_bindEnv方法。如:

/*
* 绑定所有事件
* @method _bindEnv
* private
*/
_bindEnv:function(){
var self = this,
_config = self.config,
_cache = self.cache;
//年份改变
$(_config.nodeYear).change(function(e){ var y = e.target.value,
m = self._renderMonth(y); self._renderDay(y,m);
$(_config.nodeYear).attr('year',y);
});
//月份改变
$(_config.nodeMonth).change(function(e){ var m = e.target.value,
y = $(_config.nodeYear).attr('year'); self._renderDay(y,m);
$(_config.nodeMonth).attr('month',m);
}); //日期改变
$(_config.nodeDay).change(function(e){
var d = e.target.value;
$(_config.nodeDay).attr('day',d);
});
},

HTML代码如下:

<label>出生日期: </label>
<select id="year"> </select>年
<select id="month"> </select>月
<select id="day"> </select>日 <ul>
<li><em>getDate</em> : <button id="testDate">日期</button><input id="textDate"/></li>
<li><em>getYear</em> : <button id="testYear">年</button><input id="textYear"/></li>
<li><em>getMonth</em> : <button id="testMonth">月</button><input id="textMonth"/></li>
<li><em>getDay</em> : <button id="testDay">日</button><input id="textDay"/></li>
</ul>

JS代码如下:

/**
* JS日期级联组件
* @constructor DateCascade
* @param {object} 可配置的对象
* @time 2014-1-13
* @author 879083421@qq.com
*/ function DateCascade(options) { this.config = {
nodeYear : '#year', // 年份下拉框dom
nodeMonth : '#month', // 月份下拉框dom
nodeDay : '#day', // 日期下拉框dom
dateStart : '', // 开始日期
dateEnd : '', // 结束日期(可选 默认为空就为当前时间)
dateDefault : '' // 默认日期
}; this.cache = {
_dayInMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] // 月份对应的天数
};
this.init(options);
} DateCascade.prototype = { constructor: DateCascade, init: function(options) { this.config = $.extend(this.config,options || {});
var self = this,
_config = self.config,
_cache = self.cache; var y,
m; /* 开始时间 和 截至时间 默认时间*/ // 开始时间可选 如果为空的话 那么默认开始时间是1900-01-01
if(_config.dateStart != '') { this.startDate = {
y: new Date(_config.dateStart).getFullYear(),
m: new Date(_config.dateStart).getMonth() + 1,
d: new Date(_config.dateStart).getDate()
};
}else {
var dateStart = '1900/01/01'; this.startDate = {
y: new Date(dateStart).getFullYear(),
m: new Date(dateStart).getMonth() + 1,
d: new Date(dateStart).getDate()
};
} // dateEnd 默认为空 如果没有传入的话 就取当前的时间
if(_config.dateEnd == '') {
this.endDate = {
y: new Date().getFullYear(),
m: new Date().getMonth() + 1,
d: new Date().getDate()
};
}else {
this.endDate = {
y: new Date(_config.dateEnd).getFullYear(),
m: new Date(_config.dateEnd).getMonth() + 1,
d: new Date(_config.dateEnd).getDate()
};
} // 默认时间可选 如果默认时间为空的话 那么就取当前的时间
if(_config.dateDefault != '') {
this.defaultDate = {
y: new Date(_config.dateDefault).getFullYear(),
m: new Date(_config.dateDefault).getMonth() + 1,
d: new Date(_config.dateDefault).getDate()
};
}else {
this.defaultDate = {
y: new Date().getFullYear(),
m: new Date().getMonth() + 1,
d: new Date().getDate()
};
}
// 判断时间是否合理
if((Date.parse(self._changeFormat(_config.dateStart)) > Date.parse(self._changeFormat(_config.dateEnd))) ||
(Date.parse(self._changeFormat(_config.dateDefault)) > Date.parse(self._changeFormat(_config.dateEnd)))){
return;
} // 渲染年份
y = self._renderYear(); // 渲染月份
m = self._renderMonth(y); // 渲染天
self._renderDay(y,m); // 所有绑定事件
self._bindEnv();
},
/*
* 渲染年份下拉框
* @method _renderYear
* private
*/
_renderYear: function(){
var self = this,
_config = self.config,
_cache = self.cache;
var nodeyear = $(_config.nodeYear)[0],
y = self.defaultDate.y,
range,
option; if(nodeyear) {
range = self._getYearRange();
for(var i = range.max; i >= range.min; i--) {
option = new Option(i,i); // 如果有默认年份的话
if(i == y) {
option.selected = true;
} // 兼容所有浏览器 插入到最后
nodeyear.add(option,undefined);
}
}
$(nodeyear).attr('year',y);
return y;
},
/*
* 根据年份 渲染所有的月份
* @method _renderMonth
* @param {y} 年份
*/
_renderMonth: function(y){
var self = this,
_config = self.config;
var nodeMonth = $(_config.nodeMonth)[0],
m = $(nodeMonth).attr('month') || self.defaultDate.m,
range,
option,
t = false;
if(nodeMonth) {
range = self._getMonthRange(y); nodeMonth.innerHTML = '';
for(var i = range.min; i <= range.max; i++) {
option = new Option(self.bitExpand(i),self.bitExpand(i)); // 如果有默认的月份的话
if(i == m) {
option.selected = true;
m = i;
t = true;
}
// 兼容所有浏览器 插入到最后
nodeMonth.add(option,undefined);
}
if(!t) {
m = range.min;
}
} $(nodeMonth).attr('month',m);
return m;
},
_renderDay: function(y,m) {
var self = this,
_config = self.config;
var nodeDay = $(_config.nodeDay)[0],
d = $(nodeDay).attr('day') || self.defaultDate.d,
range,
option,
t = false;
if(nodeDay) {
range = self._getDayRange(y,m); nodeDay.innerHTML = '';
for(var i = range.min; i <= range.max; i++) {
option = new Option(self.bitExpand(i),self.bitExpand(i)); // 如果有默认的天数的话
if(i == d) {
option.selected = true;
d = i;
t = true;
}
// 兼容所有浏览器 插入到最后
nodeDay.add(option,undefined);
}
if(!t) {
d = range.min;
}
} $(nodeDay).attr('day',d);
return d;
},
/*
* 绑定所有事件
* @method _bindEnv
* private
*/
_bindEnv:function(){
var self = this,
_config = self.config,
_cache = self.cache;
//年份改变
$(_config.nodeYear).change(function(e){ var y = e.target.value,
m = self._renderMonth(y); self._renderDay(y,m);
$(_config.nodeYear).attr('year',y);
});
//月份改变
$(_config.nodeMonth).change(function(e){ var m = e.target.value,
y = $(_config.nodeYear).attr('year'); self._renderDay(y,m);
$(_config.nodeMonth).attr('month',m);
}); //日期改变
$(_config.nodeDay).change(function(e){
var d = e.target.value;
$(_config.nodeDay).attr('day',d);
});
},
/*
* 获取年份的范围 最小-最大
* @method _getYearRange
* @return {min,max}
*/
_getYearRange: function(){
var self = this,
_config = self.config;
return {
min: self.startDate.y,
max: self.endDate.y
}
},
/*
* 获取月份的范围
* @method _getMonthRange
* @param {y} Number
*/
_getMonthRange: function(y){
var self = this,
_config = self.config;
var startDate = self.startDate,
endDate = self.endDate,
min = 1,
max = 12;
/*
* 如果默认年份等于开始年份的话 那么月份最小取得是开始的月份
* 因为如果开始是1900-05-01 如果默认的是 1900-03-02 那么最小月份肯定取得是5
* 因为默认时间不可能小于开始时间
*/
if(y == startDate.y) { // 开始年份
min = startDate.m;
} /*
* 同理 如果默认年份等于2014-04-01 那么取得是当前的年份(endDate未传的情况下)
* 那么最大的肯定取得是当前年份的 月份 不可能取的是4 因为只渲染出当前月份出来
* 后面的月份没有渲染出来
*/
if(y == endDate.y) {
max = endDate.m;
}
return {
min: min,
max: max
}
},
/*
* 获得天数的范围
* @method _getDayRange
* @param {y,m} {number,number}
*/
_getDayRange: function(y,m){
var self = this,
_config = self.config,
_cache = self.cache;
var startDate = self.startDate,
endDate = self.endDate,
min = 1,
max; if(m) {
if(m == 2) {
max = self._isLeapYear(y) ? 29 : 28;
}else {
max = _cache._dayInMonth[m-1];
}
// 如果年月份都等于开始日期的话 那么min也等于开始日
if(y == startDate.y && m == startDate.m) {
min = startDate.d;
}
// 如果年月份都等于结束日期的话 那么max也等于结束日
if(y == endDate.y && m == endDate.m) {
max = endDate.d;
}
}
return {
min: min,
max: max
}
},
/*
* 判断是否是闰年
*/
_isLeapYear: function(y){
return (y % 4 === 0 && y % 100 !== 0) || (y % 400 === 0);
},
/**
* 是否是Date格式
* @method _isDate
* @param {Date} d
* @private
* @return {Boolean}
*/
_isDate: function(d){
return Object.prototype.toString.call(d) === '[object Date]' && d.toString() !== 'Invalid Date' && !isNaN(d);
},
/*
* 小于10的数字加零
* @method bitExpand
*/
bitExpand: function(num) {
var num = num * 1;
if(/\d/.test(num)) {
if(num < 10) {
return '0' + num;
}else {
return num;
}
}
},
/*
* 判断开始日期 默认日期 结束日期的格式
*/
_changeFormat: function(date) {
return date.replace(/'-'/g,'/');
},
/*
* 获取日期
*/
getDate: function(){
var self = this,
_config = self.config;
var year = $(_config.nodeYear).attr('year'),
month = $(_config.nodeMonth).attr('month'),
day = $(_config.nodeDay).attr('day'); return (year + '-' + self.bitExpand(month) + '-' + self.bitExpand(day));
},
/*
* 获取年份
*/
getYear: function(){
var self = this,
_config = self.config;
var year = $(_config.nodeYear).attr('year');
return year;
},
/*
* 获取月份
*/
getMonth: function(){
var self = this,
_config = self.config;
var month = $(_config.nodeMonth).attr('month');
return month;
},
/*
* 获取天数
*/
getDay: function(){
var self = this,
_config = self.config; var day = $(_config.nodeDay).attr('day');
return day;
}
}

初始化方式如下:

// 初始化
$(function(){
var date = new DateCascade({});
$('#testDate').click(function(e){
$('#textDate').val(date.getDate());
}); $('#testYear').click(function(e){ $('#textYear').val(date.bitExpand(date.getYear()));
}); $('#testMonth').click(function(e){
$('#textMonth').val(date.bitExpand(date.getMonth()));
}); $('#testDay').click(function(e){
$('#textDay').val(date.bitExpand(date.getDay()));
});
});

DEMO下载

JS日期级联组件代码分析及demo的更多相关文章

  1. 纯js时钟特效详细代码分析实例教程

    电子时钟是网上常见的功能,在学习date对象和定时器功能时,来完成一个电子时钟的制作是不错的选择.学习本教程之前,读者需要具备html和css技能,同时需要有简单的javascript基础. 先准备一 ...

  2. tensorflow faster rcnn 代码分析一 demo.py

    os.environ["CUDA_VISIBLE_DEVICES"]=2 # 设置使用的GPU tfconfig=tf.ConfigProto(allow_soft_placeme ...

  3. arcgis api for js之echarts开源js库实现地图统计图分析

    前面写过一篇关于arcgis api for js实现地图统计图的,具体见:http://www.cnblogs.com/giserhome/p/6727593.html 那是基于dojo组件来实现图 ...

  4. arcgis api 3.x for js 之 echarts 开源 js 库实现地图统计图分析(附源码下载)

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...

  5. 微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js)

    微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js) 微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞 ...

  6. 微信小游戏 demo 飞机大战 代码分析 (三)(spirit.js, animation.js)

    微信小游戏 demo 飞机大战 代码分析(三)(spirit.js, animation.js) 微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞机大战 代码 ...

  7. 微信小游戏 demo 飞机大战 代码分析 (二)(databus.js)

    微信小游戏 demo 飞机大战 代码分析(二)(databus.js) 微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞机大战 代码分析(三)(spirit. ...

  8. 微信小游戏 demo 飞机大战 代码分析 (一)(game.js, main.js)

    微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞机大战 代码分析(二)(databus.js) 微信小游戏 demo 飞机大战 代码分析(三)(spirit. ...

  9. js日期控件demo

    最近在钻研前端,写了个日期控件,内涵代码注释,希望能帮助到大家~ 1.html代码 <!DOCTYPE html> <html xmlns="http://www.w3.o ...

随机推荐

  1. Java三大特性:封装,继承,多态

    java提高篇(一)-----理解java的三大特性之封装 概念: 封装从字面上来理解就是包装的意思,专业点就是信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独 ...

  2. 几个常用T_SQL语句比较

    UNION ALL VS UNION : union all 对两个结果进行并集操作,包括重复行,即所有的结果全部显示,不管是不是重复:union 对两个结果集进行并集操作,不包括重复行,相当于 di ...

  3. 解决ios10以上版本缩放问题

    <script type="text/javascript"> /*解决ios10以上版本缩放问题 20171102*/ window.onload=function ...

  4. Angular环境搭建

    Angular4 随笔(一)----环境搭建 1.下载node.js 第一步:在浏览器中搜索node.js官网(https://nodejs.org/zh-cn/),根据自己系统下载相应版本,下载完成 ...

  5. CentOS7安装tomcat9

    1.去官网下载tomcat9的tar.gz安装包 2.移到centos7中并解压 解压命令: tar -xzvf tomcat9.tar.gz 3.打开文件 /etc 目录下的 profile 文件: ...

  6. Js利用Canvas实现图片压缩

    最近做的APP项目涉及到手机拍照上传图片,因为手机拍照的图片通常都比较大,所以上传的时候就会很慢.为此,需要对图片进行压缩处理来优化上传功能.以下是具体实现: /* * 图片压缩 * img 原始图片 ...

  7. 微信小程序< 1 > ~ Hello 微信小程序

    简介 微信小程序,最近声音比较大,开始慢慢学习一下这个小东西,从安装开发工具开始吧,不会JS,学起来会不会很吃力呢? 注册账号 参考官方网站 开发工具 1.微信Web开发工具 2.官方下载地址,针对自 ...

  8. IIS8发布Asp.net MVC程序后出现404错误,处理程序staticFile

    新部署的虚拟机,运行Asp.net MVC程序,出现如下图错误: 解决方法: 添加功能和角色->添加角色->Web服务器IIS->应用程序开发->Asp.net3.5 /Asp ...

  9. 安卓基础之Activity的四种启动模式

    Activity的四种启动模式   Activity的启动模式在清单文件中配置: <activity ... activity:lauchMode:"..."; //有四种模 ...

  10. 常用的第三方模块 psutil url

    psutil 用Python来编写脚本简化日常的运维工作是Python的一个重要用途.在Linux下,有许多系统命令可以让我们时刻监控系统运行的状态,如ps,top,free等等.要获取这些系统信息, ...