前言
前端已经过了单兵作战的时代了,现在一个稍微复杂一点的项目都需要几个人协同开发,一个战略级别的APP的话分工会更细,比如携程:
携程app = 机票频道 + 酒店频道 + 旅游频道 + ......
每个频道有独立的团队去维护这些代码,具体到某一个频道的话有会由数十个不等的页面组成,在各个页面开发过程中,会产生很多重复的功能,比如弹出层提示框,像这种纯粹非业务的UI,便成了我们所谓的UI组件,最初的前端组件也就仅仅指的是UI组件。
而由于移动端的兴起,前端页面的逻辑已经变得很重了,一个页面的代码超过5000行的场景渐渐增多,这个时候页面的维护便会很有问题,牵一发而动全身的事情会经常发生,为了解决这个问题,便出现了前端组件化,这个组件化就不是UI组件了,而是包含具体业务的业务组件。
这种开发的思想其实也就是分而治之(最重要的架构思想),APP分成多个频道由各个团队维护,频道分为多个页面由几个开发维护,页面逻辑过于复杂,便将页面分为很多个业务组件模块分而治之,这样的话维护人员每次只需要改动对应的模块即可,以达到最大程度的降低开发难度与维护成本的效果,所以现在比较好的框架都会对组件化作一定程度的实现。
组件一般是与展示相关,视觉变更与交互优化是一个产品最容易产生的迭代,所以多数组件相关的框架核心都是View层的实现,比如Vue与React的就认为自己仅仅是“View”,虽然展示与交互不断的在改变,但是底层展示的数据却不常变化,而View是表象,数据是根本,所以如何能更好的将数据展示到View也是各个组件需要考虑的,从而衍生出了单向数据绑定与双向数据绑定等概念,组件与组件之间的通信往往也是数据为桥梁。
所以如果没有复杂的业务逻辑的话,根本不能体现出组件化编程解决的痛点,这个也是为什么todoMVC中的demo没有太大参考意义。
今天,我们就一起来研究一下前端组件化中View部分的实现,后面再看看做一个相同业务(有点复杂的业务),也简单对比下React与Vue实现相同业务的差异。
PS:文章只是个人观点,有问题请指正
导读
github
代码地址:https://github.com/yexiaochai/module/
演示地址:http://yexiaochai.github.io/module/me/index.html
如果对文中的一些代码比较疑惑,可以对比着看看这些文章:
【移动前端开发实践】从无到有(统计、请求、MVC、模块化)H5开发须知
预览
组件化的实现
之前我们已经说过,所谓组件化,很大程度上是在View上面做文章,要把一个View打散,做到分散,但是又总会有一个总体的控制器在控制所有的View,把他们合到一起,一般来说这个总的控制器是根组件,很多时候就是页面本身(View实例本身)。
根据之前的经验,组件化不一定是越细越好,组件嵌套也不推荐,一般是将一个页面分为多个组件,而子组件不再做过深嵌套(个人经验)
所以我们这里的第一步是实现一个通用的View,这里借鉴之前的代码(【组件化开发】前端进阶篇之如何编写可维护可升级的代码):
define([], function () {
'use strict'; return _.inherit({ showPageView: function (name, _viewdata, id) {
this.APP.curViewIns = this;
this.APP.showPageView(name, _viewdata, id)
}, propertys: function () {
//这里设置UI的根节点所处包裹层
this.wrapper = $('#main');
this.id = _.uniqueId('page-view-');
this.classname = ''; this.viewId = null;
this.refer = null; //模板字符串,各个组件不同,现在加入预编译机制
this.template = '';
//事件机制
this.events = {}; //自定义事件
//此处需要注意mask 绑定事件前后问题,考虑scroll.radio插件类型的mask应用,考虑组件通信
this.eventArr = {}; //初始状态为实例化
this.status = 'init';
}, //子类事件绑定若想保留父级的,应该使用该方法
addEvents: function (events) {
if (_.isObject(events)) _.extend(this.events, events);
}, on: function (type, fn, insert) {
if (!this.eventArr[type]) this.eventArr[type] = []; //头部插入
if (insert) {
this.eventArr[type].splice(0, 0, fn);
} else {
this.eventArr[type].push(fn);
}
}, off: function (type, fn) {
if (!this.eventArr[type]) return;
if (fn) {
this.eventArr[type] = _.without(this.eventArr[type], fn);
} else {
this.eventArr[type] = [];
}
}, trigger: function (type) {
var _slice = Array.prototype.slice;
var args = _slice.call(arguments, 1);
var events = this.eventArr;
var results = [], i, l; if (events[type]) {
for (i = 0, l = events[type].length; i < l; i++) {
results[results.length] = events[type][i].apply(this, args);
}
}
return results;
}, createRoot: function (html) { //如果存在style节点,并且style节点不存在的时候需要处理
if (this.style && !$('#page_' + this.viewId)[0]) {
$('head').append($('<style id="page_' + this.viewId + '" class="page-style">' + this.style + '</style>'))
} //如果具有fake节点,需要移除
$('#fake-page').remove(); //UI的根节点
this.$el = $('<div class="cm-view page-' + this.viewId + ' ' + this.classname + '" style="display: none; " id="' + this.id + '">' + html + '</div>');
if (this.wrapper.find('.cm-view')[0]) {
this.wrapper.append(this.$el);
} else {
this.wrapper.html('').append(this.$el);
} }, _isAddEvent: function (key) {
if (key == 'onCreate' || key == 'onPreShow' || key == 'onShow' || key == 'onRefresh' || key == 'onHide')
return true;
return false;
}, setOption: function (options) {
//这里可以写成switch,开始没有想到有这么多分支
for (var k in options) {
if (k == 'events') {
_.extend(this[k], options[k]);
continue;
} else if (this._isAddEvent(k)) {
this.on(k, options[k])
continue;
}
this[k] = options[k];
}
// _.extend(this, options);
}, initialize: function (opts) {
//这种默认属性
this.propertys();
//根据参数重置属性
this.setOption(opts);
//检测不合理属性,修正为正确数据
this.resetPropery(); this.addEvent();
this.create(); this.initElement(); window.sss = this; }, $: function (selector) {
return this.$el.find(selector);
}, //提供属性重置功能,对属性做检查
resetPropery: function () { }, //各事件注册点,用于被继承override
addEvent: function () {
}, create: function () {
this.trigger('onPreCreate');
//如果没有传入模板,说明html结构已经存在
this.createRoot(this.render()); this.status = 'create';
this.trigger('onCreate');
}, //实例化需要用到到dom元素
initElement: function () { }, render: function (callback) {
var html = this.template;
if (!this.template) return '';
//引入预编译机制
if (_.isFunction(this.template)) {
html = this.template(data);
} else {
html = _.template(this.template)({});
}
typeof callback == 'function' && callback.call(this);
return html;
}, refresh: function (needRecreate) {
this.resetPropery();
if (needRecreate) {
this.create();
} else {
this.$el.html(this.render());
}
this.initElement();
if (this.status != 'hide') this.show();
this.trigger('onRefresh');
}, /**
* @description 组件显示方法,首次显示会将ui对象实际由内存插入包裹层
* @method initialize
* @param {Object} opts
*/
show: function () {
this.trigger('onPreShow'); this.$el.show();
this.status = 'show'; this.bindEvents(); this.initHeader();
this.trigger('onShow');
}, initHeader: function () { }, hide: function () {
if (!this.$el || this.status !== 'show') return; this.trigger('onPreHide');
this.$el.hide(); this.status = 'hide';
this.unBindEvents();
this.trigger('onHide');
}, destroy: function () {
this.status = 'destroy';
this.unBindEvents();
this.$root.remove();
this.trigger('onDestroy');
delete this;
}, bindEvents: function () {
var events = this.events; if (!(events || (events = _.result(this, 'events')))) return this;
this.unBindEvents(); // 解析event参数的正则
var delegateEventSplitter = /^(\S+)\s*(.*)$/;
var key, method, match, eventName, selector; // 做简单的字符串数据解析
for (key in events) {
method = events[key];
if (!_.isFunction(method)) method = this[events[key]];
if (!method) continue; match = key.match(delegateEventSplitter);
eventName = match[1], selector = match[2];
method = _.bind(method, this);
eventName += '.delegateViewEvents' + this.id; if (selector === '') {
this.$el.on(eventName, method);
} else {
this.$el.on(eventName, selector, method);
}
} return this;
}, unBindEvents: function () {
this.$el.off('.delegateViewEvents' + this.id);
return this;
}, getParam: function (key) {
return _.getUrlParam(window.location.href, key)
}, renderTpl: function (tpl, data) {
if (!_.isFunction(tpl)) tpl = _.template(tpl);
return tpl(data);
} }); });
有了View的代码后便需要组件级别的代码,正如之前所说,这里的组件只有根元素与子组件两层的层级:
define([], function () {
'use strict'; return _.inherit({ propertys: function () {
//这里设置UI的根节点所处包裹层,必须设置
this.$el = null; //用于定位dom的选择器
this.selector = ''; //每个moduleView必须有一个父view,页面级容器
this.view = null; //模板字符串,各个组件不同,现在加入预编译机制
this.template = ''; //事件机制
this.events = {}; //实体model,跨模块通信的桥梁
this.entity = null;
}, setOption: function (options) {
//这里可以写成switch,开始没有想到有这么多分支
for (var k in options) {
if (k == 'events') {
_.extend(this[k], options[k]);
continue;
}
this[k] = options[k];
}
// _.extend(this, options);
}, //@override
initData: function () {
}, //如果传入了dom便
initWrapper: function (el) {
if (el && el[0]) {
this.$el = el;
return;
}
this.$el = this.view.$(this.selector);
}, initialize: function (opts) { //这种默认属性
this.propertys();
//根据参数重置属性
this.setOption(opts);
this.initData(); this.initWithoutRender(); }, //当父容器关闭后,其对应子容器也应该隐藏
bindViewEvent: function () {
if (!this.view) return;
var scope = this;
this.view.on('onHide', function () {
scope.onHide();
});
}, //处理dom已经存在,不需要渲染的情况
initWithoutRender: function () {
if (this.template) return;
var scope = this;
this.view.on('onShow', function () {
scope.initWrapper();
if (!scope.$el[0]) return;
//如果没有父view则不能继续
if (!scope.view) return;
scope.initElement();
scope.bindEvents();
});
}, $: function (selector) {
return this.$el.find(selector);
}, //实例化需要用到到dom元素
initElement: function () { }, //@override
//收集来自各方的实体组成view渲染需要的数据,需要重写
getViewModel: function () {
throw '必须重写';
}, _render: function (callback) {
var data = this.getViewModel() || {};
var html = this.template;
if (!this.template) return '';
//引入预编译机制
if (_.isFunction(this.template)) {
html = this.template(data);
} else {
html = _.template(this.template)(data);
}
typeof callback == 'function' && callback.call(this);
return html;
}, //渲染时必须传入dom映射
render: function () {
this.initWrapper();
if (!this.$el[0]) return; //如果没有父view则不能继续
if (!this.view) return; var html = this._render();
this.$el.html(html);
this.initElement();
this.bindEvents(); }, bindEvents: function () {
var events = this.events; if (!(events || (events = _.result(this, 'events')))) return this;
this.unBindEvents(); // 解析event参数的正则
var delegateEventSplitter = /^(\S+)\s*(.*)$/;
var key, method, match, eventName, selector; // 做简单的字符串数据解析
for (key in events) {
method = events[key];
if (!_.isFunction(method)) method = this[events[key]];
if (!method) continue; match = key.match(delegateEventSplitter);
eventName = match[1], selector = match[2];
method = _.bind(method, this);
eventName += '.delegateUIEvents' + this.id; if (selector === '') {
this.$el.on(eventName, method);
} else {
this.$el.on(eventName, selector, method);
}
} return this;
}, unBindEvents: function () {
this.$el.off('.delegateUIEvents' + this.id);
return this;
}
}); });
有了根View与View组件的实现,剩下的便是数据实体的实现,View与组件Module之间通信的桥梁就是数据Entity,事实上我们的View或者组件模块未必会需要数据实体Entity,只有在业务逻辑的复杂度达到一定阶段才需要分模块,如果dom操作过多的话就需要Entity了:
define([], function () {
/*
一些原则:
init方法时,不可引起其它字段update
*/
var Entity = _.inherit({
initialize: function (opts) {
this.propertys();
this.setOption(opts);
}, propertys: function () {
//只取页面展示需要数据
this.data = {}; //局部数据改变对应的响应程序,暂定为一个方法
//可以是一个类的实例,如果是实例必须有render方法
this.controllers = {}; this.scope = null; }, subscribed: function (namespace, callback, scope) {
this.subscribed();
}, unsubscribed: function (namespace) {
this.unsubscribe(namespace);
}, subscribe: function (namespace, callback, scope) {
if (typeof namespace === 'function') {
scope = callback;
callback = namespace;
namespace = 'update';
}
if (!namespace || !callback) return;
if (scope) callback = $.proxy(callback, scope);
if (!this.controllers[namespace]) this.controllers[namespace] = [];
this.controllers[namespace].push(callback);
}, unsubscribe: function (namespace) {
if (!namespace) this.controllers = {};
if (this.controllers[namespace]) this.controllers[namespace] = [];
}, publish: function (namespace, data) {
if (!namespace) return;
if (!this.controllers[namespace]) return;
var arr = this.controllers[namespace];
var i, len = arr.length;
for (i = 0; i < len; i++) {
arr[i](data);
}
}, setOption: function (opts) {
for (var k in opts) {
this[k] = opts[k];
}
}, //首次初始化时,需要矫正数据,比如做服务器适配
//@override
handleData: function () { }, //一般用于首次根据服务器数据源填充数据
initData: function (data) {
var k;
if (!data) return; //如果默认数据没有被覆盖可能有误
for (k in this.data) {
if (data[k]) this.data[k] = data[k];
} this.handleData();
this.publish('init', this.get());
}, //验证data的有效性,如果无效的话,不应该进行以下逻辑,并且应该报警
//@override
validateData: function () {
return true;
}, //获取数据前,可以进行格式化
//@override
formatData: function (data) {
return data;
}, //获取数据
get: function () {
if (!this.validateData()) {
//需要log
return {};
}
return this.formatData(this.data);
}, //数据跟新后需要做的动作,执行对应的controller改变dom
//@override
update: function (key) {
key = key || 'update';
var data = this.get();
this.publish(key, data);
} }); return Entity;
});
我们这里抽取一段火车票列表的筛选功能做实现,这个页面有一定复杂度又不是太难,大概页面是这个样子的:
因为,我们这里的关注点在View,这里就直接将网上上海到北京的数据给拿下来:
this.listData = [
{
"train_no": "87000K180502",
"train_number": "K1805",
"from_station": "上海南",
"to_station": "杭州",
"from_time": "04:22",
"to_time": "06:29",
"from_station_type": "途经",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "127",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "28.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 555
}, {
"seat_price": "74.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 551
}, {
"seat_price": "77.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 551
}, {
"seat_price": "84.5",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 551
}, {
"seat_price": "112.5",
"seat_name": "软卧上",
"seat_bookable": 1,
"seat_yupiao": 28
}, {
"seat_price": "127.0",
"seat_name": "软卧下",
"seat_bookable": 1,
"seat_yupiao": 28
}, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 334}]
}, {
"train_no": "47000K151100",
"train_number": "K1511",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "04:56",
"to_time": "06:45",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "109",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 8
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 8
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 8
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 8
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 1,
"seat_yupiao": 1
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 1,
"seat_yupiao": 1
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "1100000K7509",
"train_number": "K75",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "05:02",
"to_time": "06:58",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "116",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 9
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 9
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 9
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 240}]
}, {
"train_no": "48000K837105",
"train_number": "K8371",
"from_station": "上海南",
"to_station": "杭州",
"from_time": "05:43",
"to_time": "08:14",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "151",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "28.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "74.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 21
}, {
"seat_price": "77.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 21
}, {
"seat_price": "84.5",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 21
}, {
"seat_price": "112.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "127.0",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G754130",
"train_number": "G7541",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "06:14",
"to_time": "07:06",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "52",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 375
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 24
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 10
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G7331B0",
"train_number": "G7331",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "06:20",
"to_time": "07:12",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "52",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 339
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 10
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "470000T13501",
"train_number": "T135",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "06:21",
"to_time": "08:18",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "117",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 4
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 38
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 38
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 38
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G132120",
"train_number": "G1321",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "06:25",
"to_time": "07:17",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "52",
"sale_time": "13:30",
"control_day": 57,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 304
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 15
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 9}]
}, {
"train_no": "5l000G733380",
"train_number": "G7333",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "06:30",
"to_time": "07:22",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "52",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 702
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 51
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "0400000K5004",
"train_number": "K47",
"from_station": "上海南",
"to_station": "杭州",
"from_time": "06:34",
"to_time": "09:15",
"from_station_type": "途经",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "161",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "28.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "74.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 122
}, {
"seat_price": "77.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 122
}, {
"seat_price": "84.5",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 122
}, {
"seat_price": "112.5",
"seat_name": "软卧上",
"seat_bookable": 1,
"seat_yupiao": 1
}, {
"seat_price": "127.0",
"seat_name": "软卧下",
"seat_bookable": 1,
"seat_yupiao": 1
}, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G7301B1",
"train_number": "G7301",
"from_station": "上海虹桥",
"to_station": "杭州",
"from_time": "06:35",
"to_time": "07:40",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "65",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "77.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 490
}, {
"seat_price": "123.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {
"seat_price": "233.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 10
}, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000D314510",
"train_number": "D3145",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "06:40",
"to_time": "07:41",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 21
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 1
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G138330",
"train_number": "G1383",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "06:45",
"to_time": "07:46",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 384
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 16
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G750164",
"train_number": "G7501",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "06:50",
"to_time": "07:56",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "66",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 165
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 3
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "49000K11840B",
"train_number": "K1181",
"from_station": "上海南",
"to_station": "杭州",
"from_time": "06:51",
"to_time": "09:50",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "179",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "28.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 7
}, {
"seat_price": "74.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 15
}, {
"seat_price": "77.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 15
}, {
"seat_price": "84.5",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 15
}, {
"seat_price": "112.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "127.0",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 75}]
}, {
"train_no": "5l000G165120",
"train_number": "G1651",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "06:56",
"to_time": "08:00",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "64",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 190
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 5
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 1}]
}, {
"train_no": "5l0000D37960",
"train_number": "D379",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "07:02",
"to_time": "08:08",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "66",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 683
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 45
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G754304",
"train_number": "G7543",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "07:07",
"to_time": "08:04",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "57",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 128
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 54
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G236510",
"train_number": "G2365",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "07:12",
"to_time": "08:18",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "66",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 816
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 28
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 15}]
}, {
"train_no": "5l000G137120",
"train_number": "G1371",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "07:22",
"to_time": "08:23",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 210
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 16
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G739541",
"train_number": "G7395",
"from_station": "上海虹桥",
"to_station": "杭州",
"from_time": "07:27",
"to_time": "08:32",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "65",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "77.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 435
}, {
"seat_price": "123.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 22
}, {
"seat_price": "233.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 10
}, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K83392",
"train_number": "K833",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "07:31",
"to_time": "09:34",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "123",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 234
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 17
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 17
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 17
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G134140",
"train_number": "G1341",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "07:32",
"to_time": "08:31",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 704
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 18}]
}, {
"train_no": "5l000D313140",
"train_number": "D3131",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "07:37",
"to_time": "08:38",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 543
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 60
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 84}]
}, {
"train_no": "5l000D228730",
"train_number": "D2287",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "07:47",
"to_time": "09:07",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "80",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 114
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 12
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 84}]
}, {
"train_no": "5l000G163120",
"train_number": "G1631",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "07:54",
"to_time": "08:49",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "55",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 238
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 4
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 6}]
}, {
"train_no": "5l00000G8520",
"train_number": "G85",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "08:00",
"to_time": "08:45",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "45",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 97
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 25
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G750350",
"train_number": "G7503",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "08:05",
"to_time": "08:57",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "52",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 130
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 46
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "55000G735700",
"train_number": "G7357",
"from_station": "上海",
"to_station": "杭州",
"from_time": "08:05",
"to_time": "09:44",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "99",
"sale_time": "14:30",
"control_day": 59,
"from_telecode": "SHH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "92.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 995
}, {
"seat_price": "147.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 48
}, {
"seat_price": "278.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "92.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G754920",
"train_number": "G7549",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "08:10",
"to_time": "09:12",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "62",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 128
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 50
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G165330",
"train_number": "G1653",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "08:15",
"to_time": "09:01",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "46",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 752
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 29
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 15}]
}, {
"train_no": "5l000D320190",
"train_number": "D3201",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "08:20",
"to_time": "09:28",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "68",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 73
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 19
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 168}]
}, {
"train_no": "5l000G132320",
"train_number": "G1323",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "08:25",
"to_time": "09:24",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 272
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 12
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 9}]
}, {
"train_no": "5l000G150930",
"train_number": "G1509",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "08:31",
"to_time": "09:46",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "75",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 80
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 56
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 24}]
}, {
"train_no": "550000K14906",
"train_number": "K149",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "08:42",
"to_time": "10:38",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "116",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 66
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 38
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 38
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 38
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "55000K118532",
"train_number": "K1185",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "08:48",
"to_time": "11:01",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "133",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 234
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "54000D312510",
"train_number": "D3125",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "08:49",
"to_time": "10:09",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "80",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 355
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 55
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G134340",
"train_number": "G1343",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "08:55",
"to_time": "09:42",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "47",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 139
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 13
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 2}]
}, {
"train_no": "5l000G750550",
"train_number": "G7505",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "09:00",
"to_time": "09:54",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "54",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 190
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 113
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 13
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000D228520",
"train_number": "D2285",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "09:05",
"to_time": "10:18",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "73",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 3
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 10
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G734952",
"train_number": "G7349",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "09:10",
"to_time": "10:04",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "54",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 281
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 21
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 7
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "54000D5589A2",
"train_number": "D5589",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "09:16",
"to_time": "10:32",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "76",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 503
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 43
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 112}]
}, {
"train_no": "5l000G165510",
"train_number": "G1655",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "09:23",
"to_time": "10:23",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 208
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 78
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 20}]
}, {
"train_no": "5l000G132530",
"train_number": "G1325",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "09:28",
"to_time": "10:27",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 2
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 2}]
}, {
"train_no": "5l000D3205A0",
"train_number": "D3205",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "09:35",
"to_time": "10:36",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 154
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 1
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "55000G735102",
"train_number": "G7351",
"from_station": "上海",
"to_station": "杭州",
"from_time": "09:38",
"to_time": "11:07",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "89",
"sale_time": "14:30",
"control_day": 59,
"from_telecode": "SHH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "92.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 988
}, {
"seat_price": "147.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 53
}, {
"seat_price": "278.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "92.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000D3107A0",
"train_number": "D3107",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "09:40",
"to_time": "11:22",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "102",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 65
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 12
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "54000G735520",
"train_number": "G7355",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "09:46",
"to_time": "10:40",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "54",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 222
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G134740",
"train_number": "G1347",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "09:51",
"to_time": "10:50",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 547
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G758730",
"train_number": "G7587",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "09:58",
"to_time": "10:54",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "56",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 3
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 5
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G150120",
"train_number": "G1501",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "10:03",
"to_time": "10:58",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "55",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 394
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 14}]
}, {
"train_no": "5l000G750731",
"train_number": "G7507",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "10:13",
"to_time": "11:06",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "53",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 111
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 51
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G165720",
"train_number": "G1657",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "10:18",
"to_time": "11:18",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 63
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 1}]
}, {
"train_no": "330000Z28404",
"train_number": "Z281",
"from_station": "上海南",
"to_station": "杭州",
"from_time": "10:20",
"to_time": "12:23",
"from_station_type": "途经",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "123",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "28.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 86
}, {
"seat_price": "74.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 259
}, {
"seat_price": "77.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 259
}, {
"seat_price": "84.5",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 259
}, {
"seat_price": "112.5",
"seat_name": "软卧上",
"seat_bookable": 1,
"seat_yupiao": 7
}, {
"seat_price": "127.0",
"seat_name": "软卧下",
"seat_bookable": 1,
"seat_yupiao": 7
}, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 94}]
}, {
"train_no": "5l000G130140",
"train_number": "G1301",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "10:23",
"to_time": "11:26",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "63",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 64
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "54000D228130",
"train_number": "D2281",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "10:28",
"to_time": "11:31",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "63",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 533
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 76
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G758942",
"train_number": "G7589",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "10:34",
"to_time": "11:41",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "67",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 62
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 50
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "54000D313550",
"train_number": "D3135",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "10:42",
"to_time": "12:04",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "82",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 2
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 28
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K751A3",
"train_number": "K751",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "10:42",
"to_time": "13:26",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "164",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 17
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G134920",
"train_number": "G1349",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "10:48",
"to_time": "11:48",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 668
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 24
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 17}]
}, {
"train_no": "550000T16920",
"train_number": "T169",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "10:50",
"to_time": "12:40",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "110",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 355
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 337
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 337
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 337
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 1,
"seat_yupiao": 24
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 1,
"seat_yupiao": 24
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 124}]
}, {
"train_no": "55000K120961",
"train_number": "K1209",
"from_station": "上海南",
"to_station": "杭州",
"from_time": "10:59",
"to_time": "12:55",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "116",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "28.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 122
}, {
"seat_price": "74.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "77.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "84.5",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "112.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "127.0",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 62}]
}, {
"train_no": "5l000G750931",
"train_number": "G7509",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "10:59",
"to_time": "11:58",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 123
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 113
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "55000K125102",
"train_number": "K1251",
"from_station": "上海南",
"to_station": "杭州",
"from_time": "11:13",
"to_time": "13:30",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "137",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "28.5",
"seat_name": "硬座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "74.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "77.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "84.5",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "112.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "127.0",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000D543141",
"train_number": "D5431",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "11:19",
"to_time": "12:34",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "75",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 35
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 46
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 16}]
}, {
"train_no": "5l000D228350",
"train_number": "D2283",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "11:24",
"to_time": "12:26",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "62",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 31
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5500000T7780",
"train_number": "T77",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "11:27",
"to_time": "13:13",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "106",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 18
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 18
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 18
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "122.5", "seat_name": "软卧下", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G753724",
"train_number": "G7537",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "11:29",
"to_time": "12:30",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 25
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 67
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G7303A1",
"train_number": "G7303",
"from_station": "上海虹桥",
"to_station": "杭州",
"from_time": "11:34",
"to_time": "12:49",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "75",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "77.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 458
}, {
"seat_price": "123.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {
"seat_price": "233.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 10
}, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G132720",
"train_number": "G1327",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "11:39",
"to_time": "12:39",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 63
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000T21140",
"train_number": "T211",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "11:41",
"to_time": "13:39",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "118",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 309
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 407
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 407
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 407
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 1,
"seat_yupiao": 16
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 1,
"seat_yupiao": 16
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 168}]
}, {
"train_no": "5l000D3207A3",
"train_number": "D3207",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "11:48",
"to_time": "12:59",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "71",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 476
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G751107",
"train_number": "G7511",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "11:53",
"to_time": "12:54",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 815
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 49
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K75960",
"train_number": "K759",
"from_station": "上海南",
"to_station": "杭州",
"from_time": "12:01",
"to_time": "14:26",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "145",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HZH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "28.5",
"seat_name": "硬座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "74.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "77.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "84.5",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "112.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "127.0",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "59000G759400",
"train_number": "G7591",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "12:02",
"to_time": "13:03",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 120
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 52
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000D329100",
"train_number": "D3291",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "12:15",
"to_time": "13:16",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "15:00",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 63
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 9
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G138711",
"train_number": "G1387",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "12:20",
"to_time": "13:21",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 103
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 14
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 1}]
}, {
"train_no": "5l000G163320",
"train_number": "G1633",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "12:25",
"to_time": "13:25",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 113
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G733570",
"train_number": "G7335",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "12:30",
"to_time": "13:29",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 545
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 37
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 17
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G137331",
"train_number": "G1373",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "12:40",
"to_time": "13:40",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 72
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 21
}, {"seat_price": "132.0", "seat_name": "特等座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5i000G737813",
"train_number": "G7375",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "12:53",
"to_time": "13:53",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 287
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 49
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G165920",
"train_number": "G1659",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "12:58",
"to_time": "13:58",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 80
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 9
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 7}]
}, {
"train_no": "5i000G759807",
"train_number": "G7595",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "13:05",
"to_time": "14:08",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "63",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 3
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 2
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G138920",
"train_number": "G1389",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "13:11",
"to_time": "14:03",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "52",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 16
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 1}]
}, {
"train_no": "5l000D314110",
"train_number": "D3141",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "13:19",
"to_time": "14:21",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "62",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 93
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 18
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000T10140",
"train_number": "T101",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "13:20",
"to_time": "15:11",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "111",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 116
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 204
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 204
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 204
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G135320",
"train_number": "G1353",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "13:25",
"to_time": "14:26",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 211
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G730583",
"train_number": "G7305",
"from_station": "上海虹桥",
"to_station": "杭州",
"from_time": "13:30",
"to_time": "14:36",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "66",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "77.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 11
}, {
"seat_price": "123.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 52
}, {
"seat_price": "233.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 17
}, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K123C0",
"train_number": "K123",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "13:40",
"to_time": "15:54",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "134",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 917
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 5
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 5
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 5
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 1,
"seat_yupiao": 4
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 1,
"seat_yupiao": 4
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 488}]
}, {
"train_no": "5l000G132930",
"train_number": "G1329",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "13:43",
"to_time": "14:42",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 417
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 9}]
}, {
"train_no": "5l000G135511",
"train_number": "G1355",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "13:48",
"to_time": "14:47",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 198
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 27
}, {"seat_price": "132.0", "seat_name": "特等座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G751550",
"train_number": "G7515",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "14:00",
"to_time": "14:59",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 837
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 112
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "54000G735300",
"train_number": "G7353",
"from_station": "上海虹桥",
"to_station": "杭州",
"from_time": "14:05",
"to_time": "15:14",
"from_station_type": "途经",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "69",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "77.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 82
}, {
"seat_price": "123.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {
"seat_price": "233.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 5
}, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G130340",
"train_number": "G1303",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "14:10",
"to_time": "15:04",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "54",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 333
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 18
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 9}]
}, {
"train_no": "5l000G753960",
"train_number": "G7539",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "14:15",
"to_time": "15:14",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 186
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 3
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 13
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K57570",
"train_number": "K575",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "14:18",
"to_time": "16:26",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "128",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "53000G757820",
"train_number": "G7575",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "14:19",
"to_time": "15:19",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 3
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 1
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G730771",
"train_number": "G7307",
"from_station": "上海虹桥",
"to_station": "杭州",
"from_time": "14:30",
"to_time": "15:37",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "67",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "77.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 471
}, {
"seat_price": "123.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 24
}, {
"seat_price": "233.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 10
}, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5500000K7120",
"train_number": "K71",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "14:34",
"to_time": "16:40",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "126",
"sale_time": "15:00",
"control_day": 57,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 234
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 12
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 12
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 12
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 1,
"seat_yupiao": 4
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 1,
"seat_yupiao": 4
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 310}]
}, {
"train_no": "5l000G163510",
"train_number": "G1635",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "14:35",
"to_time": "15:36",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 68
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "54000G758350",
"train_number": "G7583",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "14:40",
"to_time": "15:40",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 62
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 39
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l0000D38170",
"train_number": "D381",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "14:45",
"to_time": "16:16",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "91",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 1
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 83}]
}, {
"train_no": "2400000G410B",
"train_number": "G41",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "14:51",
"to_time": "15:44",
"from_station_type": "途经",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "53",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 224
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 67
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 1}]
}, {
"train_no": "5l000G751721",
"train_number": "G7517",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "14:56",
"to_time": "15:49",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "53",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 18
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 52
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "55000K112700",
"train_number": "K1127",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "14:59",
"to_time": "17:11",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "132",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 414
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 9
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 9
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 9
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G135711",
"train_number": "G1357",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "15:01",
"to_time": "15:54",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "53",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 11
}, {"seat_price": "132.0", "seat_name": "特等座", "seat_bookable": 1, "seat_yupiao": 7}]
}, {
"train_no": "550000K111D0",
"train_number": "K111",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "15:05",
"to_time": "17:24",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "139",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 34
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 11
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 11
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 11
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000D310160",
"train_number": "D3101",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "15:09",
"to_time": "16:28",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "79",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 579
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 35
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G163720",
"train_number": "G1637",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "15:17",
"to_time": "16:21",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "64",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 684
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 13
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 17}]
}, {
"train_no": "5l000G130503",
"train_number": "G1305",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "15:25",
"to_time": "16:12",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "47",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 109
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 113
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 17}]
}, {
"train_no": "5l000G733780",
"train_number": "G7337",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "15:32",
"to_time": "16:32",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 638
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 43
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 18
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G135921",
"train_number": "G1359",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "15:37",
"to_time": "16:36",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 148
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 24
}, {"seat_price": "132.0", "seat_name": "特等座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K253A0",
"train_number": "K253",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "15:37",
"to_time": "17:45",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "128",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 170
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 16
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 16
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 16
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G751951",
"train_number": "G7519",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "15:47",
"to_time": "16:47",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 844
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 116
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G754532",
"train_number": "G7545",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "15:52",
"to_time": "16:51",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 15
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 54
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000D310350",
"train_number": "D3103",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "15:57",
"to_time": "16:58",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 154
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 2
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K351B2",
"train_number": "K351",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "15:58",
"to_time": "18:11",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "133",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 326
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5500000T8150",
"train_number": "T81",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "16:06",
"to_time": "17:58",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "112",
"sale_time": "15:00",
"control_day": 57,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 113
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 7
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 7
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 7
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G736510",
"train_number": "G7365",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "16:07",
"to_time": "17:03",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "56",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 128
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 54
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "53000G757120",
"train_number": "G7571",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "16:12",
"to_time": "17:12",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 396
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 45
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G139121",
"train_number": "G1391",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "16:27",
"to_time": "17:28",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 261
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 14
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 9}]
}, {
"train_no": "5l000G7309B0",
"train_number": "G7309",
"from_station": "上海虹桥",
"to_station": "杭州",
"from_time": "16:32",
"to_time": "17:44",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "72",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "77.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 450
}, {
"seat_price": "123.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {
"seat_price": "233.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 10
}, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G136120",
"train_number": "G1361",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "16:42",
"to_time": "17:41",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 255
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 16
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 2}]
}, {
"train_no": "5l000G752174",
"train_number": "G7521",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "16:47",
"to_time": "17:53",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "66",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 125
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 5
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 25
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "55000K137303",
"train_number": "K1373",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "16:49",
"to_time": "19:10",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "141",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 317
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 5
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 5
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 5
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000T38120",
"train_number": "T381",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "16:55",
"to_time": "18:44",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "109",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 68
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K27172",
"train_number": "K271",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "17:01",
"to_time": "19:23",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "142",
"sale_time": "15:00",
"control_day": 57,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 293
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5i000G756611",
"train_number": "G7563",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "17:05",
"to_time": "18:06",
"from_station_type": "途经",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 1
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 2
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000Z25701",
"train_number": "Z257",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "17:14",
"to_time": "18:57",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "103",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 17
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 17
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 17
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 1,
"seat_yupiao": 6
}, {"seat_price": "122.5", "seat_name": "软卧下", "seat_bookable": 1, "seat_yupiao": 6}]
}, {
"train_no": "54000G758560",
"train_number": "G7585",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "17:14",
"to_time": "18:14",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 504
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 29
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G136331",
"train_number": "G1363",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "17:19",
"to_time": "18:19",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 57,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "N",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 191
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 24
}, {"seat_price": "132.0", "seat_name": "特等座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K46951",
"train_number": "K469",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "17:20",
"to_time": "19:42",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "142",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 1
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G163911",
"train_number": "G1639",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "17:24",
"to_time": "18:23",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 58
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 2}]
}, {
"train_no": "5l000G7311B1",
"train_number": "G7311",
"from_station": "上海虹桥",
"to_station": "杭州",
"from_time": "17:29",
"to_time": "18:41",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "72",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "77.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 463
}, {
"seat_price": "123.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {
"seat_price": "233.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 10
}, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5i000G738810",
"train_number": "G7385",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "17:34",
"to_time": "18:54",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "80",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 207
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 133
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G136521",
"train_number": "G1365",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "17:40",
"to_time": "18:40",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 135
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 24
}, {"seat_price": "132.0", "seat_name": "特等座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G139330",
"train_number": "G1393",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "17:47",
"to_time": "18:49",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "62",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 222
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 16
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 5}]
}, {
"train_no": "5500000T2520",
"train_number": "T25",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "17:49",
"to_time": "19:55",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "126",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 23
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 23
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 23
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "55000G735900",
"train_number": "G7359",
"from_station": "上海",
"to_station": "杭州",
"from_time": "17:54",
"to_time": "19:32",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "98",
"sale_time": "14:30",
"control_day": 59,
"from_telecode": "SHH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "92.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 987
}, {
"seat_price": "147.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 54
}, {
"seat_price": "278.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "92.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G75250B",
"train_number": "G7525",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "17:59",
"to_time": "19:00",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 632
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 50
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G7391A1",
"train_number": "G7391",
"from_station": "上海虹桥",
"to_station": "杭州",
"from_time": "18:04",
"to_time": "19:17",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "73",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "77.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 860
}, {
"seat_price": "123.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 114
}, {
"seat_price": "233.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5i000G737421",
"train_number": "G7371",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "18:14",
"to_time": "19:15",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 81
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 137
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "540000K52520",
"train_number": "K525",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "18:20",
"to_time": "20:19",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "119",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 30}]
}, {
"train_no": "550000K18162",
"train_number": "K181",
"from_station": "上海南",
"to_station": "杭州",
"from_time": "18:26",
"to_time": "20:32",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "126",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "28.5",
"seat_name": "硬座",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "74.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "77.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "84.5",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "112.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "127.0",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G139510",
"train_number": "G1395",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "18:30",
"to_time": "19:29",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 750
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 27
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 10}]
}, {
"train_no": "5500000K79B3",
"train_number": "K79",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "18:34",
"to_time": "20:46",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "132",
"sale_time": "15:00",
"control_day": 57,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 209
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G733950",
"train_number": "G7339",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "18:36",
"to_time": "19:42",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "66",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 312
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 8
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 7
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000D332100",
"train_number": "D3321",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "18:46",
"to_time": "19:47",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 441
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 87
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 84}]
}, {
"train_no": "5l000G753542",
"train_number": "G7535",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "18:59",
"to_time": "19:58",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 482
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 51
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K49530",
"train_number": "K495",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "19:02",
"to_time": "21:00",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "118",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 146
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 31
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 31
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 31
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G734350",
"train_number": "G7343",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "19:04",
"to_time": "20:03",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "59",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 980
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 47
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K807C2",
"train_number": "K807",
"from_station": "上海南",
"to_station": "杭州",
"from_time": "19:08",
"to_time": "21:09",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "121",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "28.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 39
}, {
"seat_price": "74.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 14
}, {
"seat_price": "77.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 14
}, {
"seat_price": "84.5",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 14
}, {
"seat_price": "112.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "127.0",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000D332300",
"train_number": "D3323",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "19:09",
"to_time": "20:10",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "49.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 545
}, {
"seat_price": "59.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 89
}, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 84}]
}, {
"train_no": "540000K527A0",
"train_number": "K527",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "19:14",
"to_time": "21:13",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "119",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 41
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 15
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 15
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 15
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G738112",
"train_number": "G7381",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "19:15",
"to_time": "20:24",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "69",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 857
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 116
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K51160",
"train_number": "K511",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "19:20",
"to_time": "21:41",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "141",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 155
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 100}]
}, {
"train_no": "5l000G756140",
"train_number": "G7561",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "19:21",
"to_time": "20:14",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "53",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 244
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 12
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 10
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G755720",
"train_number": "G7557",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "19:32",
"to_time": "20:32",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "60",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 749
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 179
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 25
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K73905",
"train_number": "K739",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "19:36",
"to_time": "21:54",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "138",
"sale_time": "15:00",
"control_day": 57,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 89
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 14
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 14
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 14
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "2400000G4305",
"train_number": "G43",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "19:40",
"to_time": "20:47",
"from_station_type": "途经",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "67",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 171
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 151
}, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 20}]
}, {
"train_no": "5l000G752751",
"train_number": "G7527",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "20:21",
"to_time": "21:22",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "61",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 998
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 53
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000K28702",
"train_number": "K287",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "20:25",
"to_time": "22:37",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "132",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 40
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 6
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G738310",
"train_number": "G7383",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "20:33",
"to_time": "21:50",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "77",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 767
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 184
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 28
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G752911",
"train_number": "G7529",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "20:40",
"to_time": "21:46",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "66",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 101
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 26
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 10
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "550000Z24700",
"train_number": "Z247",
"from_station": "上海南",
"to_station": "杭州东",
"from_time": "20:42",
"to_time": "22:23",
"from_station_type": "起点",
"to_station_type": "途经",
"day_diff": "0",
"use_time": "101",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "24.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 186
}, {
"seat_price": "70.5",
"seat_name": "硬卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "73.5",
"seat_name": "硬卧中",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "80.0",
"seat_name": "硬卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "108.5",
"seat_name": "软卧上",
"seat_bookable": 0,
"seat_yupiao": 0
}, {
"seat_price": "122.5",
"seat_name": "软卧下",
"seat_bookable": 0,
"seat_yupiao": 0
}, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G755510",
"train_number": "G7555",
"from_station": "上海虹桥",
"to_station": "杭州东",
"from_time": "20:52",
"to_time": "22:07",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "75",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HGH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "73.0",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 129
}, {
"seat_price": "117.0",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 54
}, {
"seat_price": "219.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "5l000G7315A1",
"train_number": "G7315",
"from_station": "上海虹桥",
"to_station": "杭州",
"from_time": "21:30",
"to_time": "22:28",
"from_station_type": "起点",
"to_station_type": "终点",
"day_diff": "0",
"use_time": "58",
"sale_time": "13:30",
"control_day": 59,
"from_telecode": "AOH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "77.5",
"seat_name": "二等座",
"seat_bookable": 1,
"seat_yupiao": 962
}, {
"seat_price": "123.5",
"seat_name": "一等座",
"seat_bookable": 1,
"seat_yupiao": 54
}, {
"seat_price": "233.5",
"seat_name": "商务座",
"seat_bookable": 1,
"seat_yupiao": 20
}, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
}, {
"train_no": "53000K8401A0",
"train_number": "K8401",
"from_station": "上海南",
"to_station": "杭州",
"from_time": "23:30",
"to_time": "02:57",
"from_station_type": "途经",
"to_station_type": "途经",
"day_diff": "1",
"use_time": "207",
"sale_time": "15:30",
"control_day": 59,
"from_telecode": "SNH",
"to_telecode": "HZH",
"can_web_buy": "Y",
"note": "",
"seats": [{
"seat_price": "28.5",
"seat_name": "硬座",
"seat_bookable": 1,
"seat_yupiao": 812
}, {
"seat_price": "74.5",
"seat_name": "硬卧上",
"seat_bookable": 1,
"seat_yupiao": 247
}, {
"seat_price": "77.5",
"seat_name": "硬卧中",
"seat_bookable": 1,
"seat_yupiao": 247
}, {
"seat_price": "84.5",
"seat_name": "硬卧下",
"seat_bookable": 1,
"seat_yupiao": 247
}, {
"seat_price": "112.5",
"seat_name": "软卧上",
"seat_bookable": 1,
"seat_yupiao": 29
}, {
"seat_price": "127.0",
"seat_name": "软卧下",
"seat_bookable": 1,
"seat_yupiao": 29
}, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 430}]
}];
我们这里做的第一个事情是将数据全部展示出来,在具体渲染前,原始数据需要做一些处理:
define([
'AbstractView',
'pages/list.data',
'text!pages/tpl.layout.html',
'text!pages/tpl.list.html' ], function (
AbstractView,
listData, layoutHtml,
listTpl ) {
return _.inherit(AbstractView, { propertys: function ($super) {
$super(); this.viewId = 'list'; this.template = layoutHtml; this.events = {}; //班车信息
this.listData = listData; }, initElement: function () {
this.d_list_wrapper = this.$('.js_list_wrapper');
this.d_none_data = this.$('.js_none_data'); this.d_js_show_setoutdate = this.$('.js_show_setoutdate');
this.d_js_show_setstation = this.$('.js_show_setstation');
this.d_js_show_arrivalstation = this.$('.js_show_arrivalstation');
this.d_js_list_loading = this.$('.js_list_loading');
this.d_js_tabs = this.$('.js_tabs'); this.d_js_day_sec = this.$('.js_day_sec');
this.d_js_start_sec = this.$('.js_start_sec');
this.d_js_arrival_sec = this.$('.js_arrival_sec'); }, //复杂的业务数据处理,为了达到产品的需求,这段代码逻辑与业务相关
//这段数据处理的代码过长(超过50行就过长),应该重构掉
formatData: function (data) {
var item, seat;
var typeMap = {
'g': 'g',
'd': 'd',
't': 't',
'c': 'g'
}; //出发时间对应的分钟数
var fromMinute = 0; //获取当前班车日期当前的时间戳,这个数据是动态的,这里写死了
var d = 1464192000000;
var date = new Date();
var now = parseInt(date.getTime() / 1000);
date.setTime(d);
var year = date.getFullYear();
var month = date.getMonth();
var day = date.getDate();
var toBegin;
var seatName, seatIndex, iii; //处理坐席问题,仅显示二等座,一等座,特等座 无座
// 二等座 一等座 商务座 无座 动卧 特等座
var my_seats = {};
var seatSort = ['二等座', '一等座', '硬座', '硬卧', '软卧', '商务座', '无座', '动卧', '特等座', '软座']; for (var i = 0, len = data.length; i < len; i++) {
fromMinute = data[i].from_time.split(':');
fromMinute[0] = fromMinute[0] + '';
fromMinute[1] = fromMinute[1] + '';
if ((fromMinute[0].charAt(0) == '0')) fromMinute[0] = fromMinute[0].charAt(1);
if ((fromMinute[1].charAt(0) == '0')) fromMinute[1] = fromMinute[1].charAt(1);
date = new Date(year, month, day, fromMinute[0], fromMinute[1], 0);
fromMinute = parseInt(date.getTime() / 1000)
toBegin = parseInt((fromMinute - now) / 60); data[i].toBegin = toBegin; //处理车次类型问题
data[i].my_train_number = typeMap[data[i].train_number.charAt(0).toLowerCase()] || 'other'; seat = data[i].seats;
//所有余票
data[i].sum_ticket = 0;
//最低价
data[i].min_price = null; for (var j = 0, len1 = seat.length; j < len1; j++) {
if (!data[i].min_price || data[i].min_price > seat[j].seat_price) data[i].min_price = parseFloat(seat[j].seat_price);
data[i].sum_ticket += parseInt(seat[j].seat_yupiao); //坐席问题如果坐席不包括上中下则去掉
seatName = seat[j].seat_name;
//去掉上中下
seatName = seatName.replace(/上|中|下/g, '');
if (!my_seats[seatName]) {
my_seats[seatName] = parseInt(seat[j].seat_yupiao);
} else {
my_seats[seatName] = my_seats[seatName] + parseInt(seat[j].seat_yupiao);
}
}
//这里myseat为对象,需要转换为数组
//将定制坐席转为排序后的数组
data[i].my_seats = [];
for (iii = 0; iii < seatSort.length; iii++) {
if (typeof my_seats[seatSort[iii]] == 'number') data[i].my_seats.push({ name: seatSort[iii], yupiao: my_seats[seatSort[iii]] });
} my_seats = {};
} return data;
}, //渲染列表
renderList: function() {
var data = this.formatData(this.listData); var html = '';
window.scrollTo(0, 0); if (data.length === 0) {
this.d_none_data.show();
this.d_list_wrapper.hide();
return;
} this.d_none_data.hide();
this.d_list_wrapper.show();
html = this.renderTpl(listTpl, { data: data });
this.d_list_wrapper.html(html);
}, addEvent: function () {
this.on('onShow', function () { this.renderList(); });
} }); });
然后第一步的效果出来了,后面只需要处理数据筛选即可:
这里开始实现第一个业务组件,顶部的搜索栏,这个搜索栏有以下需求:
① 默认以时间升序排列
② 三个tab彼此互斥,点击时候仍然使用升序,再次点击为倒序
顶部导航组件
这里的交互就有一定复杂性了,这种场景是有数据实体出现的必要了,所以我们先实现数据实体:
define(['AbstractEntity'], function (AbstractEntity) { var Entity = _.inherit(AbstractEntity, {
propertys: function ($super) {
$super(); //三个对象,时间,耗时,价格,升序降序,三个数据互斥
//默认down up null
this.data = {
time: 'up',
sumTime: '',
price: ''
};
}, _resetData: function () {
this.data = {
time: '',
sumTime: '',
price: ''
};
}, setTime: function () {
this._setData('time');
}, setSumTime: function () {
this._setData('sumTime');
}, setPrice: function () {
this._setData('price');
}, _setData: function (key) { //如果设置当前key存在,则反置,否则清空筛选,设置默认值
if (this.data[key] != '') {
if (this.data[key] == 'up') this.data[key] = 'down';
else this.data[key] = 'up';
} else {
this._resetData();
this.data[key] = 'down';
}
this.update();
} }); return Entity;
});
对应视觉展示比较简单:
<ul class="bus-tabs sort-bar js_sort_item">
<li class="tabs-item " data-sort="Time" style="-webkit-flex: 1.5; flex: 1.5;">出发时间<i class="icon-sort <%=time %>"></i></li>
<li class="tabs-item " data-sort="SumTime" >耗时<i class="icon-sort <%=sumTime %>"></i></li>
<li class="tabs-item " data-sort="Price" >价格<i class="icon-sort <%=price %>"></i></li>
</ul>
事实上这个数据实体是完全独立的,这个视觉模板也仅仅负责了展示,而在哪展示,数据怎么与模板产生关联其中就是我们的组件控制器了:
define(['ModuleView', 'text!pages/tpl.sort.bar.html'], function (ModuleView, tpl) {
return _.inherit(ModuleView, { //此处若是要使用model,处实例化时候一定要保证entity的存在,如果不存在便是业务BUG
initData: function () { this.template = tpl;
this.events = {
'click .js_sort_item li ': function (e) {
var el = $(e.currentTarget);
var sort = el.attr('data-sort');
_hmt.push(['_trackEvent', 'train.list.sort.' + sort, 'click']); this.sortEntity['set' + sort]();
}
}; this.sortEntity.subscribe('init', this.render, this);
this.sortEntity.subscribe(this.render, this); }, getViewModel: function () {
return this.sortEntity.get();
} }); });
至此,可以看到,一个组件就已经完成了,组件的功能很简单:
PS:注意组件是不能脱离根组件View而存在,一个组件一定会有一个this.view对象
① 组件控制器获取了模板
② 组件控制器获取了根组件View给予(实例化时注入)的数据实体sortEntiy
于是我们在主控制器中实例化我们的数据实体与组件:
initEntity: function() { //实例化排序的导航栏的实体
this.sortEntity = new SortEntity(); }, initModule: function() { //view为注入给组件的根元素
//selector为组件将要显示的容器
//sortEntity为注入给组件的数据实体,做通信用
//这个module在数据显示后会自动展示
this.sortModule = new SortModule({
view: this,
selector: '.js_sort_wrapper',
sortEntity: this.sortEntity
}); },
这里简单说明下代码,首先这里说明一个错误的实践,一个笔误:
this.sortEntity.subscribe(this.render, this);
在mod.sort中有这么一段代码,事实上这段代码是有问题的,因为数据实体是作为被观察者实现的,所以subscribe应该为subscribed!!!
但是因为最初一个笔误导致所有团队所有业务团队都这样用了下去,产生了很大的误导作用,这里继续写错,提醒大家做框架层的东西要慎重。
这里事实上观察者为View或者Module,按道理说该是:
view.subscribe(entity, callback)
但是当时考虑到一个view未必会有数据实体,而view实现后module还要做实现,于是这块代码就写到了entity上,现在看来写到View上更合理,这里不多说,回到我们的业务代码。
这里对entity的变化绑定了一个回调,“数据变化的话重新渲染本身”,于是我们每次点击的话:
var el = $(e.currentTarget);
var sort = el.attr('data-sort');
this.sortEntity['set' + sort]();
由标签获取了当前设置的key,完了引起数据更新,便导致了组件本身的重新渲染,于是功能完成。
但是我们知道这个数据变化除了组件本身变化以外还应该引起列表的变化,所以我们在View中也应该观察这个数据实体的变化,以便重新渲染数据:
//实例化排序的导航栏的实体
this.sortEntity = new SortEntity();
this.sortEntity.subscribe(this.renderList, this);
这个时候由于排序的产生,我们需要重写renderList的实现:
_timeSort: function (data, sort) {
data = _.sortBy(data, function (item) {
item = item.from_time.split(':');
item = item[0] + '.' + item[1];
item = parseFloat(item);
return item;
});
if (sort == 'down') data.reverse();
return data;
}, _sumTimeSort: function (data, sort) {
data = _.sortBy(data, function (item) {
return parseInt(item.use_time);
});
if (sort == 'down') data.reverse();
return data;
}, _priceSort: function (data, sort) {
data = _.sortBy(data, function (item) {
return item.min_price;
});
if (sort == 'down') data.reverse();
return data;
}, //获取导航栏排序后的数据
getSortData: function (data) {
var tmp = [];
var sort = this.sortEntity.get(); for (var k in sort) {
if (sort[k].length > 0) {
tmp = this['_' + k + 'Sort'](data, sort[k])
return tmp;
}
}
}, //完成所有的筛选条件,逻辑比较重
getFilteData: function () {
var data = this.formatData(this.listData);
data = this.getSortData(data); return data;
}, //渲染列表
renderList: function() {
var data = this.getFilteData();
var html = '';
window.scrollTo(0, 0); if (data.length === 0) {
this.d_none_data.show();
this.d_list_wrapper.hide();
return;
} this.d_none_data.hide();
this.d_list_wrapper.show();
html = this.renderTpl(listTpl, { data: data });
this.d_list_wrapper.html(html);
},
可以看到,这里复杂操作被分解为了一个个小小的方法,配合underscore释放的一些数组操作,便可以简单的完成列表渲染功能,因为这里最终的渲染逻辑没有改变,改变的仅仅是排序后的数组。
此处对班次数据的处理篇幅已经超过了50行,如果再增加,可以实例化一个班次Entity用于格式化的数据输出,因为我们这里没有实现这个实体,放到根View中又过于臃肿,所以将之放到Module中,这样关于筛选的所有API全部下放到了排序模块,而主View代码就会清晰的多:
//完成所有的筛选条件,逻辑比较重
getFilteData: function () {
var data = this.formatData(this.listData);
data = this.sortModule.getSortData(data);
return data;
},
define(['ModuleView', 'text!pages/tpl.sort.bar.html'], function (ModuleView, tpl) {
return _.inherit(ModuleView, { //此处若是要使用model,处实例化时候一定要保证entity的存在,如果不存在便是业务BUG
initData: function () { this.template = tpl;
this.events = {
'click .js_sort_item li ': function (e) {
var el = $(e.currentTarget);
var sort = el.attr('data-sort');
this.sortEntity['set' + sort]();
}
}; this.sortEntity.subscribe(this.render, this); }, _timeSort: function (data, sort) {
data = _.sortBy(data, function (item) {
item = item.from_time.split(':');
item = item[0] + '.' + item[1];
item = parseFloat(item);
return item;
});
if (sort == 'down') data.reverse();
return data;
}, _sumTimeSort: function (data, sort) {
data = _.sortBy(data, function (item) {
return parseInt(item.use_time);
});
if (sort == 'down') data.reverse();
return data;
}, _priceSort: function (data, sort) {
data = _.sortBy(data, function (item) {
return item.min_price;
});
if (sort == 'down') data.reverse();
return data;
}, //获取导航栏排序后的数据
getSortData: function (data) {
var tmp = [];
var sort = this.sortEntity.get(); for (var k in sort) {
if (sort[k].length > 0) {
tmp = this['_' + k + 'Sort'](data, sort[k])
return tmp;
}
}
}, getViewModel: function () {
return this.sortEntity.get();
} }); });
sort模块
至此,一个完整的业务组件便实现结束了,那么这里也实现了一个简单的View组件系统,那么这么做有什么不足呢?
不足
首先,我们这里的实现扔是以js为基础,这种做法似乎不太“组件化”,更好的实现似乎是直接以一个标签的方式使用。
然后,可以看出,我们每次点击先是改变数据,然后数据触发更新,刷新了整个列表,也改变了组件本身的展示,这里的方案简单粗暴,完全是重新渲染,这种重新渲染的做法,在数据列表达到一定数量的话是一种资源浪费。
但是楼主基本无力解决以上问题,这种问题我们看看Vue与React后面是如何解决的,这里暂时搁置。
底部菜单
我们这里做的第二个业务组件是底部菜单栏:
观察这个视觉,我们这里出现了一个纯粹的UI组件,于是我们做的第一步是实现这个UI组件。
UI组件的实现
所谓UI组件是不包含任何业务代码,因为UI组件不是今天的重点,我这里贴出实现不做过多讲解:
define([
'AbstractView'
], function (
AbstractView
) { //实现一个方法产生最大的zindex
var getBiggerzIndex = (function () {
var index = 2000;
return function (level) {
return level + (++index);
};
})(); return _.inherit(AbstractView, { propertys: function ($super) {
$super(); //这里设置UI的根节点所处包裹层
this.wrapper = $('body');
this.viewId = _.uniqueId('ui-view-'); }, //为当前View设置最大的zindex,这里用于弹出层情况
setzIndexTop: function (el, level) {
if (!el) el = this.$el;
if (!level || level > 10) level = 0;
level = level * 1000;
el.css('z-index', getBiggerzIndex(level)); }, _getDefaultViewModel: function (arr) {
var k, i, len, obj = {};
for (i = 0, len = arr.length; i < len; i++) {
k = arr[i];
if (!_.isUndefined(this[k]) && !_.isNull(this[k])) obj[k] = this[k];
}
return obj;
} }); });
UIView
define([
'ui/ui.view'
], function (
UIView
) { return _.inherit(UIView, { setRootStyle: function () {
this.$el.addClass('cm-overlay');
}, addEvent: function ($super) {
$super(); this.on('onShow', function () {
this.setRootStyle();
this.setzIndexTop();
}); } }); });
UIMask
define([
'ui/ui.view',
'ui/ui.mask',
'text!ui/ui.list.html' ], function (
UIView,
UIMask,
template ) {
return _.inherit(UIView, { propertys: function ($super) {
$super(); this.mask = new UIMask(); this.viewId = 'uilist'; this.template = template;
this.classname = 'cm-layer-list';
this.list = [];
this.cancelText = '取消';
this.index = -1;
this.displayNum = 5;
this.curClass = 'active'; this.addEvents({
'click .js_cancel': 'cancelAction',
'click .js_item': 'itemAction'
}); this.onItemAction = function (data, index, e) {
}; }, getViewModel: function () {
return this._getDefaultViewModel(['list', 'cancelText', 'index', 'curClass', 'itemFn', 'title']);
}, setIndex: function (i, position) {
if (i < 0 || i > this.list.length) return;
this.index = i;
this.$('li').removeClass(this.curClass);
this.$('li[data-index="' + i + '"]').addClass(this.curClass); }, cancelAction: function (e) {
this.hide();
}, //弹出层类垂直居中使用
reposition: function () {
this.$el.css({
'position': 'fixed',
'-webkit-box-sizing': 'border-box',
'box-sizing': 'border-box',
'width': '100%',
'left': '0',
'background-color': '#fff',
'bottom': '36px'
});
}, itemAction: function (e) {
var el = $(e.currentTarget);
if (el.hasClass('disabled')) return; var index = el.attr('data-index');
var data = this.list[index];
this.setIndex(index);
this.onItemAction.call(this, data, index, e); }, addEvent: function () { this.on('onPreShow', function () {
this.mask.show();
}); this.on('onShow', function () { this.setzIndexTop();
this.reposition(); }); this.on('onHide', function () {
this.mask.hide();
}); } }); });
UIList
<section class="cm-modal cm-modal--action">
<%if(typeof title == 'string' && title.length > 0){ %>
<header class="cm-modal-hd">
<h3 class="cm-modal-title js_title"><%=title %></h3>
</header>
<%} %>
<div class="cm-modal-bd js_wrapper">
<ul class="cm-actions cm-actions--full js_scroller" >
<%for(var i = 0, len = list.length; i < len; i++) {%>
<li data-index="<%=i%>" class="cm-actions-btn js_item <%if(list[i].disabled){ %>disabled<%} %> <%if(i == index) { %>active<% } %>"><%=((typeof itemFn == "function" && itemFn(list[i])) || list[i].name)%></li>
<%}%>
</ul>
</div>
<div class="cm-modal-ft cm-actions">
<span class="cm-actions-btn js_cancel"><%=cancelText%></span>
</div>
</section>
UIList模板
这里的UIView是继承至基础View的代码,做了简单改造,让他更适合做UI的基类
UIMask就是我们常用的蒙版
UIList是我们真实使用的组件,继承至UIView,其中会实例化一个Mask的实例
有兴趣的朋友这里自己看看,我们将关注点放到底部的业务组件。
底部业务组件
细心的朋友应该看到了,事实上在布局之初的时候(list还未渲染),底部菜单栏DOM结构便已经存在,我们这里做的一件事情就是当组件已经存在如何和组件交互逻辑关联起来,这里做的第一步也是最重要一部依旧是数据实体的抽象。
他这个数据是一个多选框类型的数据,数组的第一项是全选功能,根据需求我们抽象出了这种数据实体:
define(['AbstractEntity'], function (AbstractEntity) { var Entity = _.inherit(AbstractEntity, {
propertys: function ($super) {
$super();
this.data = [];
}, getLength: function () {
return this.data.length;
}, unCheckAll: function () {
for (var i = 0, len = this.getLength(); i < len; i++) {
this.data[i].checked = false;
}
}, checkAll: function (noEvent) {
if (this.getLength() == 0) return;
this.unCheckAll();
this.data[0].checked = true;
if (!noEvent) this.update();
}, setIndex: function (i, noEvent) {
if (typeof i === 'string') i = parseInt(i);
if (i < 0 || i > this.getLength()) return;
if (i === 0) {
this.checkAll(noEvent);
return;
}
this.data[0].checked = false;
if (this.data[i].checked) this.data[i].checked = false;
else this.data[i].checked = true; //如果除了第一个都被选了的话,那么就是全选,如果全部没被选也得全选
if (this.getCheckedIndex().length == this.getLength() - 1 || this.getCheckedIndex().length == 0) {
this.checkAll(noEvent);
} if (!noEvent) this.update();
}, getCheckedIndex: function (index) {
var indexArr = [];
for (var i = 0, len = this.getLength(); i < len; i++) {
if (index === i && this.data[i].checked) continue;
if (this.data[i].checked) indexArr.push(i);
}
return indexArr;
}, getCheckedKey: function () {
if (this.data[0].checked) return null;
var checed = [], index = this.getCheckedIndex();
for (var i = 0, len = index.length; i < len; i++) {
checed.push(this.data[index[i]].id);
}
return checed;
}, initData: function (data) {
this.data = data;
} }); return Entity;
});
多选数据实体
有了数据实体,我们这里便需要实现使用数据实体的组件(非最终代码):
define(['ModuleView', 'ui/ui.list'], function (ModuleView, UILayerList) {
return _.inherit(ModuleView, { //此处若是要使用model,处实例化时候一定要保证entity的存在,如果不存在便是业务BUG
initData: function () { this.events = {
'click': 'showLayer'
}; this.entity.checkAll(true); }, onHide: function () {
if (this.layer) {
this.layer.destroy();
}
}, showLayer: function () { var scope = this;
var data = this.entity.get();
if (data.length == 0) return; if (!this.layer) { //这里注释了车站地图需求
this.layer = new UILayerList({
list: data,
events: {
'click .js_ok': function () {
scope.entity.update();
this.hide();
}
},
onHide: function () {
scope.searchBarEntity.resetData(true);
},
title: '<span class="fl js_cancel" style="font-weight: 500;">取消</span><span class="fr js_ok" style="color: #00b358; font-weight: 500;">确定</span>',
itemFn: function (item) {
return '<div style="text-align: left; padding-left: 10px; ">' + item.name + '</div>';
},
setIndex: function (i) {
scope.entity.setIndex(i, true);
this.setIndexArr();
},
setIndexArr: function () {
var indexArr = scope.entity.getCheckedIndex();
if (typeof indexArr == 'number') indexArr = [indexArr];
this.$('li').removeClass(this.curClass);
for (var i = 0, len = indexArr.length; i < len; i++) this._setIndex(indexArr[i])
},
_setIndex: function (i) {
if (i < 0 || i > this.list.length) return;
this.index = i;
this.$('li[data-index="' + i + '"]').addClass(this.curClass);
}
});
} else {
this.layer.list = data;
this.layer.refresh();
} this.layer.show();
this.layer.setIndexArr();
}, getViewModel: function () {
return this.entity.get();
} }); });
组件代码
然后在根View中将View与组件关联起来:
//车次类型数据实体
this.trainTypeEntity = new CheckBoxEntity({
data: [
{ name: '全部车次', id: 'all', checked: true },
{ name: '高铁城际(G/C)', id: 'g' },
{ name: '动车(D)', id: 'd' },
{ name: '特快(T)', id: 't' },
{ name: '其它类型', id: 'other' }
]
}); //车次类型模块
this.trainTypeModule = new CheckBoxModule({
view: this,
selector: '.js_type',
tagname: 'Type',
entity: this.trainTypeEntity
});
这样,我们点击车次类型便能很好的运行了,但是tab并没有被选中:
这里思考一个问题:底部有三个组件交互,依次是车次类型、出发站、更多,事实上组件之间并不知道当前是哪个tab被点击了,应该展示选中状态,知道的是根View,所以我们这里需要在View中实现一个数据实体,注入给三个业务组件,告诉他们现在该谁被选中了,下面三个tab只有一个能选中,并且选择了一个tab,另一个tab的菜单如果是展示状态需要将其隐藏,所以我们实现了单选的实体:
define(['AbstractEntity'], function (AbstractEntity) { var Entity = _.inherit(AbstractEntity, {
propertys: function ($super) {
$super();
this.data = [];
}, getLength: function () {
return this.data.length;
}, unCheckAll: function () {
for (var i = 0, len = this.getLength(); i < len; i++) {
this.data[i].checked = false;
}
this.update();
}, setIndex: function (i, noEvent) {
if (typeof i === 'string') i = parseInt(i);
if (i < 0 || i > this.getLength()) return;
this.unCheckAll();
this.data[i].checked = true;
if (!noEvent) this.update();
}, setId: function(id) { for(var i = 0, len = this.getLength(); i < len; i++) {
if(this.data[i].id == id) {
this.setIndex(i);
return;
}
}
}, getCheckedIndex: function () {
for (var i = 0, len = this.getLength(); i < len; i++) {
if (this.data[i].checked) return i;
}
return null;
}, getCheckedKey: function () {
var index = this.getCheckedIndex();
if (index !== null) return this.data[index].id;
return null;
}, getCheckedName: function () {
var index = this.getCheckedIndex();
if (index !== null) return this.data[index].name;
return null;
} }); return Entity;
});
单选数据实体
然后是出发站的实现,这里出发站有一点特殊,首先这个数据需要列表加载结束,我们去筛选数据获得出发站,所以实体有一个初始化的过程(而且这里数据更新是不需要触发事件的);其次他的交互与车次类型完全一致,唯一不同的只是数据实体,所以这里出发站的组件我们可以复用,只需要实例化一个数据出来即可:
//由初始数据筛选出所有出发站
initSetout: function () {
var data = this.listData;
var stations = [];
var stationMap = {};
var tmp = [{ id: 'all', name: '全部出发站'}]; for (var i = 0, len = data.length; i < len; i++) {
stationMap[data[i].from_telecode] = data[i].from_station;
if (data[i].from_station_type == '起点' && _.indexOf(stations, data[i].from_telecode) == -1) {
stations.push(data[i].from_telecode);
}
} for (i = 0, len = stations.length; i < len; i++) {
var key = stations[i];
var value = stationMap[key];
stations[i] = {
id: key,
name: value
};
} tmp = tmp.concat(stations); this.setoutEntity.initData(tmp);
this.setoutEntity.checkAll(true); },
view
如此底部菜单栏中的车次类型与出发站,我们便基本实现了,这里每次数据变化还需要更新列表数据,这里在主View中绑定相关变化即可,然后再重写下renderList,便结束了主要功能,这里有个不同的地方,是列表数据的筛选却不能放在Module中,因为车次类型与出发站的数据筛选可能不一样,所以这样看来最初的班次数据操作就应该封装为一个ListEntity做数据筛选,我们这里暂时放到主View中:
//根据车次类型做筛选
getTypeData: function (data) {
var typeKeys = this.trainTypeEntity.getCheckedKey();
if (!typeKeys) return data;
var tmp = _.filter(data, function (item) {
var no = item.my_train_number;
if (_.indexOf(typeKeys, no) != -1) {
return true;
}
return false;
}); return tmp;
}, //根据出发站做筛选
//事实上这个方法与getTypeData不是完全不能重构到一起,但是可读性可能会变得晦涩
getSetoutData: function (data) {
var keys = this.setoutEntity.getCheckedKey();
if (!keys) return data; var tmp = _.filter(data, function (item) {
var no = item.from_telecode;
if (_.indexOf(keys, no) != -1)
return true;
return false;
}); return tmp;
}, //完成所有的筛选条件,逻辑比较重
getFilteData: function () {
var data = this.formatData(this.listData);
data = this.getTypeData(data);
data = this.getSetoutData(data);
data = this.sortModule.getSortData(data);
return data;
},
更多功能
这里更多功能也是比较复杂的,但是就算一个更多,里面又会分为几个小组件,几个数据实体,所以真实实现功能后代码反而简单,这里我便不做实现,感兴趣的同学作为家庭作业做吧。
总结
我们这里使用js实现了一个简单的组件化View的实现,中间形成了纯粹的UI组件,也将View拆分实现了一些业务组件,最终形成的目录为:
有兴趣的朋友在git上面去看吧,这里是入口js:
(function () { require.config({
paths: {
'text': 'libs/require.text', 'AbstractView': 'js/view',
'AbstractEntity': 'js/entity',
'ModuleView': 'js/module' }
}); require(['pages/list'], function (List) { var list = new List();
list.show(); }); })();
好了,经过上面的实现,如果看过之前我的博客的话,应该对组件化开发有一定了解了,我们也可以进入今日的正题了,首先我们以Vue实现上述功能。
Vue的实现
首先,这里说Vue的实现,不是底层源码分析,而是实现上述功能,这里大家不要误会,因为我也是昨天才开始看Vue,暂时无能了解他的实现。
我虽然没有使用过Vue做业务开发,但是在很久之前就听我一个好基友夸耀Vue,到今日实现,Vue已经火的不行了。。。。。。
今天,就让我们来试试Vue的威力,因为是初次使用,如果有不对的地方,大家可以指正,也不要喷了
组件拆分
根据之前的实现,我们发现我们的组件可以轻易的分为三个部分:
① 顶部导航
② 列表
③ 底部菜单
我们之前做的第一件事情是将列表展示做掉,这种开发流程是不会改变的,所以使用Vue将列表展示实现。
组件定义
在使用组件化之前,我们先看看Vue如何实现一个组件(这里不得不说Vue的作者文档确实写的好):
// 定义
var MyComponent = Vue.extend({
template: '<div>A custom component!</div>'
}) // 注册
Vue.component('my-component', MyComponent) // 创建根实例
new Vue({
el: '#example'
})
<!--正确做法-->
<article class="cm-page" id="main">
<my-component></my-component>
</article> <!--错误做法,组件一定要依赖根部View-->
<my-component></my-component>
作者这个实现很不错,也考虑了不是所有组件都需要全局释放的,一般来说可以全局释放的组件都与业务无关或者是公共业务,这里是局部组件的实现:
var Child = Vue.extend({ /* ... */ }) var Parent = Vue.extend({
template: '...',
components: {
// <my-component> 只能用在父组件模板内
'my-component': Child
}
})
上面的例子比较简单,我们马上使用一个复杂一点的例子试试水:
// 定义
var MyList = Vue.extend({ data: function () {
return {
data: /**/[
{name: '出发时间', id: 'time'},
{name: '耗时', id: 'sumTime'},
{name: '价格', id: 'price'}
]
};
},
template: [
'<ul>',
'<li v-for="item in data" >',
'{{item.name}}',
'</li>',
'</ul>'
].join('')
}) // 注册
Vue.component('my-list', MyList) // 创建根实例
new Vue({
el: '#main'
})
<article class="cm-page" id="main">
<my-component></my-component>
<my-list></my-list>
</article>
这段代码会输出:
<article class="cm-page" id="main">
<div>A custom component!</div>
<ul><li>出发时间</li><li>耗时</li><li>价格</li></ul>
</article>
这里了解到这里便暂时结束,我们来实现我们的列表组件。
根实例
我们在第一节的实现中很多初始化的动作与数据全部放到了根View中,同样,我们这里需要实现一个根View:
define([
'Vue'
], function (Vue) {
return new Vue({
data: {
a: 1
},
el: '#main',
template: '<div>test</div>'
});
});
现在我们在入口index.html将写入list组件,然后在写实现(不得不说,我也认为这种做法很直观):
<article class="cm-page" id="main">
<my-list></my-list>
</article>
根据上面的知识我们实现这个业务组件,这里也碰到了第一个问题,根View如何将数据传递给他的组件也就是,组件与View之间如何通信。
组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。可以使用 props 把数据传给子组件。
“prop” 是组件数据的一个字段,期望从父组件传下来。子组件需要显式地用 props 选项 声明 props:
根据文档,我这里写个demo试试:
var MyList = Vue.extend({
props: ['data'],
template: [
'<ul>',
'<li v-for="item in data" >',
'{{item.name}}',
'</li>',
'</ul>'
].join('')
}) // 注册
Vue.component('my-list', MyList) // 创建根实例
new Vue({
data: {
name: 'test',
data: /**/[
{name: '出发时间', id: 'time'},
{name: '耗时', id: 'sumTime'},
{name: '价格', id: 'price'}
]
},
el: '#main'
})
<article class="cm-page" id="main">
{{name}}
<my-component></my-component>
<my-list v-bind:data="data"></my-list>
</article>
代码虽然和传统习惯不一样,但是确实完成了我们要的实现。
PS:楼主这里慢慢开始感觉有点怪了,可能使用方法不太对
list组件
因为Vue的模板里面不能写表达式,所以所有的数据相关逻辑需要写到组件代码部分,因为之前的处理,我们只需要简单的改下模板便能正确的运行:
<article class="cm-page page-list" id="main">
<my-list :data="data"></my-list>
</article> <script type="text/javascript" src="./libs/underscore.js"></script>
<script type="text/javascript" src="./libs/require.js"></script>
<script type="text/javascript" src="main.js"></script>
define([
'Vue',
'pages/list.data',
'pages/mod.list' ], function (
Vue,
listData,
ListModule ) { return new Vue({
components: {
'my-list': ListModule
},
data: {
data: formatData(listData)
},
el: '#main'
}); //该方法放到这里是否合适?
function formatData(data) {
var item, seat;
var typeMap = {
'g': 'g',
'd': 'd',
't': 't',
'c': 'g'
}; //出发时间对应的分钟数
var fromMinute = 0; //获取当前班车日期当前的时间戳,这个数据是动态的,这里写死了
var d = 1464192000000;
var date = new Date();
var now = parseInt(date.getTime() / 1000);
date.setTime(d);
var year = date.getFullYear();
var month = date.getMonth();
var day = date.getDate();
var toBegin;
var seatName, seatIndex, iii; //处理坐席问题,仅显示二等座,一等座,特等座 无座
// 二等座 一等座 商务座 无座 动卧 特等座
var my_seats = {};
var seatSort = ['二等座', '一等座', '硬座', '硬卧', '软卧', '商务座', '无座', '动卧', '特等座', '软座']; for (var i = 0, len = data.length; i < len; i++) {
fromMinute = data[i].from_time.split(':');
fromMinute[0] = fromMinute[0] + '';
fromMinute[1] = fromMinute[1] + '';
if ((fromMinute[0].charAt(0) == '0')) fromMinute[0] = fromMinute[0].charAt(1);
if ((fromMinute[1].charAt(0) == '0')) fromMinute[1] = fromMinute[1].charAt(1);
date = new Date(year, month, day, fromMinute[0], fromMinute[1], 0);
fromMinute = parseInt(date.getTime() / 1000)
toBegin = parseInt((fromMinute - now) / 60); data[i].toBegin = toBegin; //处理车次类型问题
data[i].my_train_number = typeMap[data[i].train_number.charAt(0).toLowerCase()] || 'other'; seat = data[i].seats;
//所有余票
data[i].sum_ticket = 0;
//最低价
data[i].min_price = null; for (var j = 0, len1 = seat.length; j < len1; j++) {
if (!data[i].min_price || data[i].min_price > seat[j].seat_price) data[i].min_price = parseFloat(seat[j].seat_price);
data[i].sum_ticket += parseInt(seat[j].seat_yupiao); //坐席问题如果坐席不包括上中下则去掉
seatName = seat[j].seat_name;
//去掉上中下
seatName = seatName.replace(/上|中|下/g, '');
if (!my_seats[seatName]) {
my_seats[seatName] = parseInt(seat[j].seat_yupiao);
} else {
my_seats[seatName] = my_seats[seatName] + parseInt(seat[j].seat_yupiao);
}
}
//这里myseat为对象,需要转换为数组
//将定制坐席转为排序后的数组
data[i].my_seats = [];
for (iii = 0; iii < seatSort.length; iii++) {
if (typeof my_seats[seatSort[iii]] == 'number') data[i].my_seats.push({
name: seatSort[iii],
yupiao: my_seats[seatSort[iii]]
});
} my_seats = {};
} return data;
} });
根View
组件js代码部分:
define([
'Vue',
'text!pages/tpl.list.html'
], function (Vue,
template) { return Vue.extend({
props: ['data'],
data: function() {
return {
mapping: {
'g': '高速',
't': '特快',
'd': '高速动车',
'c': '城际高铁',
'z': '直达'
}
};
},
template: template }); });
组件模板部分:
<ul class="bus-list js_bus_list ">
<li v-for="item in data" class="bus-list-item ">
<div class="bus-seat">
<span class=" fl">{{item.train_number }} | {{mapping[item.my_train_number] || '其它'}} </span>
<span class=" fr">{{parseInt(item.use_time / 60) + '小时' + item.use_time % 60 + '分'}}</span>
</div>
<div class="detail">
<div class="sub-list set-out">
<span class="bus-go-off">{{item.from_time}}</span> <span class="start"><span class="icon-circle s-icon1">
</span>{{item.from_station }}</span> <span class="fr price">¥{{item.min_price}}起</span>
</div>
<div class="sub-list">
<span class="bus-arrival-time">{{item.to_time}}</span> <span class="end"><span class="icon-circle s-icon2">
</span>{{item.to_station}}</span> <span class="fr ">{{item.sum_ticket}}张</span>
</div>
</div>
<div class="bus-seats-info" >
<span v-for="seat in item.my_seats">{{seat.name}}({{seat.yupiao }}) </span>
</div>
</li>
</ul>
从代码上看,其实主要的处理逻辑仍旧是最初数据的处理,我这里其实有一个疑问?
这里数据是写死的,如果真实业务中数据由ajax返回,那么这个业务代码该在View哪个位置进行?这个问题留待下次完整阅读Vue文档后分析
顶部导航组件
这里回到顶部导航的实现,这个与列表不同的是他会多出很多交互了,首先嵌入一个新标签:
<article class="cm-page page-list" id="main">
<my-sort-bar></my-sort-bar>
<my-list :data="data"></my-list>
</article>
这个标签的模板可以直接将之前的模板拷贝过来改成Vue的语法即可:
<ul class="bus-tabs sort-bar js_sort_item">
<li class="tabs-item " v-on:click="setTime" style="-webkit-flex: 1.5; flex: 1.5;">
出发时间<i class="icon-sort {{time }}"></i></li>
<li class="tabs-item " v-on:click="setSumTime">耗时<i class="icon-sort {{sumTime }}"></i></li>
<li class="tabs-item " v-on:click="setPrice">价格<i class="icon-sort {{price }}"></i></li>
</ul>
完了完成最主要的组件模块代码实现,在这里小钗发现之前的导航相关的Entity实体中的操作就是所有需要的处理,所以这里可以稍微改造下便使用:
define([
'Vue',
'text!pages/tpl.sort.html'
], function (Vue,
template) { return Vue.extend({
props: ['data'],
data: function () {
return {
time: 'up',
sumTime: '',
price: ''
};
},
methods: {
_resetData: function () {
this.time = '';
this.sumTime = '';
this.price = '';
}, setTime: function () {
this._setData('time');
}, setSumTime: function () {
this._setData('sumTime');
}, setPrice: function () {
this._setData('price');
}, _setData: function (key) {
//如果设置当前key存在,则反置,否则清空筛选,设置默认值
if (this[key] != '') {
if (this[key] == 'up') this[key] = 'down';
else this[key] = 'up';
} else {
this._resetData();
this[key] = 'down';
}
}
},
template: template }); });
对比下之前数据实体的代码,以及组件控制器的实现:
define(['ModuleView', 'text!pages/tpl.sort.bar.html'], function (ModuleView, tpl) {
return _.inherit(ModuleView, { //此处若是要使用model,处实例化时候一定要保证entity的存在,如果不存在便是业务BUG
initData: function () { this.template = tpl;
this.events = {
'click .js_sort_item li ': function (e) {
var el = $(e.currentTarget);
var sort = el.attr('data-sort');
this.sortEntity['set' + sort]();
}
}; this.sortEntity.subscribe(this.render, this); }, getViewModel: function () {
return this.sortEntity.get();
} }); });
define(['AbstractEntity'], function (AbstractEntity) { var Entity = _.inherit(AbstractEntity, {
propertys: function ($super) {
$super(); //三个对象,时间,耗时,架构,升序降序,三个数据互斥
//默认down up null
this.data = {
time: 'up',
sumTime: '',
price: ''
};
}, _resetData: function () {
this.data = {
time: '',
sumTime: '',
price: ''
};
}, setTime: function () {
this._setData('time');
}, setSumTime: function () {
this._setData('sumTime');
}, setPrice: function () {
this._setData('price');
}, _setData: function (key) { //如果设置当前key存在,则反置,否则清空筛选,设置默认值
if (this.data[key] != '') {
if (this.data[key] == 'up') this.data[key] = 'down';
else this.data[key] = 'up';
} else {
this._resetData();
this.data[key] = 'down';
}
this.update();
} }); return Entity;
});
数据实体
这里是Vue根仅仅使用AMD方式引入该模块即可,View的实现:
components: {
'my-list': ListModule,
'my-sort-bar': SortModule
},
现在第二个问题来了,这里每次操作事实上应该影响列表组件的排序,之前我们这块是通过数据实体entity做通信,不出意外的话Vue也该如此,但是小钗在这里却产生了疑惑,该怎么做,怎么实现?
因为根据之前经验,主View与组件之间以数据实体的方式通信是比较常见的操作,组件之间也可以通过数据实体沟通,因为数据实体是实例化在根View中的,但是组件与组件之间不应该产生直接的关联,一般来说主View或者组件使用数据以外的方式都是不可取的。
这里顶部导航组件是独立的,并没有释放对外的接口,根View也没有注入数据对象给他,那么他的变化该如何通知到列表组件,让他重新排序呢?这个时候又开始查文档ing。
小钗这里没有想到很好的办法,于是将顶部导航的组件的数据上升到了主View中,主View以pros的方式传递了给两个组件,所以上述代码要有变动,首先是根节点:
data: {
data: formatData(listData),
sort: {
time: 'up',
sumTime: '',
price: ''
}
},
html中的调用:
<article class="cm-page page-list" id="main">
<div class="js_sort_wrapper sort-bar-wrapper">
<my-sort-bar :sort="sort"></my-sort-bar>
</div>
<my-list :data="data"></my-list>
</article>
最后是组件js与具体模板的实现:
define([
'Vue',
'text!pages/tpl.sort.html'
], function (Vue,
template) { return Vue.extend({
props: ['sort'],
methods: {
_resetData: function () {
this.sort.time = '';
this.sort.sumTime = '';
this.sort.price = '';
}, setTime: function () {
this._setData('time');
}, setSumTime: function () {
this._setData('sumTime');
}, setPrice: function () {
this._setData('price');
}, _setData: function (key) {
//如果设置当前key存在,则反置,否则清空筛选,设置默认值
if (this.sort[key] != '') {
if (this.sort[key] == 'up') this.sort[key] = 'down';
else this.sort[key] = 'up';
} else {
this._resetData();
this.sort[key] = 'down';
}
}
},
template: template }); });
<ul class="bus-tabs sort-bar js_sort_item">
<li class="tabs-item " v-on:click="setTime" data-sort="Time" style="-webkit-flex: 1.5; flex: 1.5;">出发时间<i class="icon-sort {{sort.time }}"></i></li>
<li class="tabs-item " v-on:click="setSumTime" data-sort="SumTime" >耗时<i class="icon-sort {{sort.sumTime }}"></i></li>
<li class="tabs-item " v-on:click="setPrice" data-sort="Price" >价格<i class="icon-sort {{sort.price }}"></i></li>
</ul>
这样一来主View又可以使用pros的方式将sort字段释放给列表组件了,这里代码再做变更。
PS:小钗注意观察过每次数据变化,不是重新渲染的方式,替代的是局部更新,这个功能要实现好很难
PS:这里代码改来改去,一是希望大家看到思考过程,二是楼主帮助自己思考,最终大家还是对着git看吧
然后这里将原来写在导航模块的数据处理移到列表组件中,从这里也可以看出,我们最开始的代码实现中事实上也可以将列表实现成一个组件,这应该是“强组件”思维与“弱组件”思维带来的规则差异,强的框架会用规则限制你的代码,让你绕过错误,减少你思考的概率,弱的框架会更灵活些,也意味着能力不足易出错,但是因为根View的data是共享的,要把所有的筛选数据透传给列表组件又比较烦,所以我们便直接在根View中操作数据了。
这里数据变化了虽然会引起自身的的渲染,但是并不能执行对应的回调,所以我们应该给他注册回调,Vue使用watch为数据绑定观察回调,我们这里试试。
define([
'Vue',
'pages/list.data',
'pages/mod.list',
'pages/mod.sort' ], function (Vue,
listData,
ListModule,
SortModule) { return new Vue({
components: {
'my-list': ListModule,
'my-sort-bar': SortModule
},
data: {
data: formatData(listData),
sort: {
time: 'up',
sumTime: '',
price: ''
}
}, methods: {
_timeSort: function (data, sort) {
data = _.sortBy(data, function (item) {
item = item.from_time.split(':');
item = item[0] + '.' + item[1];
item = parseFloat(item);
return item;
});
if (sort == 'down') data.reverse();
return data;
}, _sumTimeSort: function (data, sort) {
data = _.sortBy(data, function (item) {
return parseInt(item.use_time);
});
if (sort == 'down') data.reverse();
return data;
}, _priceSort: function (data, sort) {
data = _.sortBy(data, function (item) {
return item.min_price;
});
if (sort == 'down') data.reverse();
return data;
}, //获取导航栏排序后的数据
getSortData: function (data) {
var tmp = [];
var sort = this.sort; for (var k in sort) {
if (sort[k].length > 0) {
tmp = this['_' + k + 'Sort'](data, sort[k])
return tmp;
}
}
}
}, watch: {
sort: {
deep: true,
handler: function() {
this.data = this.getSortData(this.data);
}
}
},
el: '#main'
}); //该方法放到这里是否合适?
function formatData(data) {
var item, seat;
var typeMap = {
'g': 'g',
'd': 'd',
't': 't',
'c': 'g'
}; //出发时间对应的分钟数
var fromMinute = 0; //获取当前班车日期当前的时间戳,这个数据是动态的,这里写死了
var d = 1464192000000;
var date = new Date();
var now = parseInt(date.getTime() / 1000);
date.setTime(d);
var year = date.getFullYear();
var month = date.getMonth();
var day = date.getDate();
var toBegin;
var seatName, seatIndex, iii; //处理坐席问题,仅显示二等座,一等座,特等座 无座
// 二等座 一等座 商务座 无座 动卧 特等座
var my_seats = {};
var seatSort = ['二等座', '一等座', '硬座', '硬卧', '软卧', '商务座', '无座', '动卧', '特等座', '软座']; for (var i = 0, len = data.length; i < len; i++) {
fromMinute = data[i].from_time.split(':');
fromMinute[0] = fromMinute[0] + '';
fromMinute[1] = fromMinute[1] + '';
if ((fromMinute[0].charAt(0) == '0')) fromMinute[0] = fromMinute[0].charAt(1);
if ((fromMinute[1].charAt(0) == '0')) fromMinute[1] = fromMinute[1].charAt(1);
date = new Date(year, month, day, fromMinute[0], fromMinute[1], 0);
fromMinute = parseInt(date.getTime() / 1000)
toBegin = parseInt((fromMinute - now) / 60); data[i].toBegin = toBegin; //处理车次类型问题
data[i].my_train_number = typeMap[data[i].train_number.charAt(0).toLowerCase()] || 'other'; seat = data[i].seats;
//所有余票
data[i].sum_ticket = 0;
//最低价
data[i].min_price = null; for (var j = 0, len1 = seat.length; j < len1; j++) {
if (!data[i].min_price || data[i].min_price > seat[j].seat_price) data[i].min_price = parseFloat(seat[j].seat_price);
data[i].sum_ticket += parseInt(seat[j].seat_yupiao); //坐席问题如果坐席不包括上中下则去掉
seatName = seat[j].seat_name;
//去掉上中下
seatName = seatName.replace(/上|中|下/g, '');
if (!my_seats[seatName]) {
my_seats[seatName] = parseInt(seat[j].seat_yupiao);
} else {
my_seats[seatName] = my_seats[seatName] + parseInt(seat[j].seat_yupiao);
}
}
//这里myseat为对象,需要转换为数组
//将定制坐席转为排序后的数组
data[i].my_seats = [];
for (iii = 0; iii < seatSort.length; iii++) {
if (typeof my_seats[seatSort[iii]] == 'number') data[i].my_seats.push({
name: seatSort[iii],
yupiao: my_seats[seatSort[iii]]
});
} my_seats = {};
} return data;
} });
顶部导航组件实现
至此,我们完成了顶部导航组件,现在来完成底部菜单栏。
底部菜单栏
根据之前的开发模式,我们依旧是形成一个组件,放到html里面:
<article class="cm-page page-list" id="main">
<div class="js_sort_wrapper sort-bar-wrapper">
<my-sort-bar :sort="sort"></my-sort-bar>
</div>
<my-list :data="data" :sort="sort"></my-list>
<my-tabs></my-tabs>
</article>
PS:这里会用到的UI组件我不愿意再重新写了,所以就直接将原来的拿来用,反正与业务无关。
PS:请注意,Vue本身是不包含任何dom操作的,因为使用UI组件加入了一些第三方库,大家可以无视掉
根据之前的经验,这里无论是车次类型还是出发站,是几个组件共享的,所以我们仍然将之实现在根View中,然后传递给子组件,因为出发城市和车次类型事实上是一个组件,所以上面的结构有所变化:
<article class="cm-page page-list" id="main">
<div class="js_sort_wrapper sort-bar-wrapper">
<my-sort-bar :sort="sort"></my-sort-bar>
</div>
<my-list :data="data" :sort="sort"></my-list>
<ul class="bus-tabs list-filter js_tabs " style="z-index: 3000;">
<my-tab-item :data="type" name="车次类型"></my-tab-item>
<my-tab-item :data="setout" name="出发站"></my-tab-item>
<li class="tabs-item js_more">更多<i class="icon-sec"></i>
</li>
</ul>
</article>
对应的组件第一步也结束了:
data: {
data: formatData(listData),
sort: {
time: 'up',
sumTime: '',
price: ''
},
//车次类型
type: [
{name: '全部车次', id: 'all', checked: true},
{name: '高铁城际(G/C)', id: 'g'},
{name: '动车(D)', id: 'd'},
{name: '特快(T)', id: 't'},
{name: '其它类型', id: 'other'}
],
setout: [
{name: '全部出发站', id: 'all', checked: true}
]
},
define([
'Vue',
'text!pages/tpl.tabs.html',
'ui/ui.list'
], function (Vue,
template,
UILayerList) { return Vue.extend({
props: ['data', 'name'],
methods: { getLength: function () {
return this.data.length;
}, unCheckAll: function () {
for (var i = 0, len = this.getLength(); i < len; i++) {
this.data[i].checked = false;
}
}, checkAll: function (noEvent) {
if (this.getLength() == 0) return;
this.unCheckAll();
this.data[0].checked = true;
//if (!noEvent) this.update();
}, setIndex: function (i, noEvent) {
if (typeof i === 'string') i = parseInt(i);
if (i < 0 || i > this.getLength()) return;
if (i === 0) {
this.checkAll(noEvent);
return;
}
this.data[0].checked = false;
if (this.data[i].checked) this.data[i].checked = false;
else this.data[i].checked = true; //如果除了第一个都被选了的话,那么就是全选,如果全部没被选也得全选
if (this.getCheckedIndex().length == this.getLength() - 1 || this.getCheckedIndex().length == 0) {
this.checkAll(noEvent);
} //if (!noEvent) this.update();
}, getCheckedIndex: function (index) {
var indexArr = [];
for (var i = 0, len = this.getLength(); i < len; i++) {
if (index === i && this.data[i].checked) continue;
if (this.data[i].checked) indexArr.push(i);
}
return indexArr;
}, getCheckedKey: function () {
if (this.data[0].checked) return null;
var checed = [], index = this.getCheckedIndex();
for (var i = 0, len = index.length; i < len; i++) {
checed.push(this.data[index[i]].id);
}
return checed;
}, showLayer: function() {
var data = this.data;
var scope = this; if(!data) return; if (this.layer && this.layer.status == 'show') return; if (!this.layer) { //这里注释了车站地图需求
this.layer = new UILayerList({
list: data,
events: {
'click .js_ok': function () {
this.hide();
}
},
onHide: function () { },
title: '<span class="fl js_cancel" style="font-weight: 500;">取消</span><span class="fr js_ok" style="color: #00b358; font-weight: 500;">确定</span>',
itemFn: function (item) {
return '<div style="text-align: left; padding-left: 10px; ">' + item.name + '</div>';
},
setIndex: function (i) {
scope.setIndex(i, true);
this.setIndexArr();
},
setIndexArr: function () {
var indexArr = scope.getCheckedIndex(); if (typeof indexArr == 'number') indexArr = [indexArr];
this.$('li').removeClass(this.curClass);
for (var i = 0, len = indexArr.length; i < len; i++) this._setIndex(indexArr[i])
},
_setIndex: function (i) {
if (i < 0 || i > this.list.length) return;
this.index = i;
this.$('li[data-index="' + i + '"]').addClass(this.curClass);
}
});
} else {
this.layer.list = data;
this.layer.refresh();
} this.layer.show();
this.layer.setIndexArr(); }, hideLayer: function() { }
},
template: template }); });
从代码可以看出,组件中包含了原来的entity的操作,与module操作,事实上这两块东西应该也可以按原来那种方式划分,让组件中的代码更加纯粹不去操作过多数据,第一步结束,第二步是当数据变化时候更新到列表,这里又需要观察数据变化了,最后形成了这个代码:
define([
'Vue',
'pages/list.data',
'pages/mod.list',
'pages/mod.sort',
'pages/mod.tabs' ], function (Vue,
listData,
ListModule,
SortModule,
TabModule) { return new Vue({
components: {
'my-list': ListModule,
'my-sort-bar': SortModule,
'my-tab-item': TabModule
},
data: {
data: formatData(listData),
dataTmp: formatData(listData),
sort: {
time: 'up',
sumTime: '',
price: ''
},
//车次类型
type: [
{name: '全部车次', id: 'all', checked: true},
{name: '高铁城际(G/C)', id: 'g', checked: false},
{name: '动车(D)', id: 'd', checked: false},
{name: '特快(T)', id: 't', checked: false},
{name: '其它类型', id: 'other', checked: false}
],
setout: getSetout()
}, methods: { _timeSort: function (data, sort) {
data = _.sortBy(data, function (item) {
item = item.from_time.split(':');
item = item[0] + '.' + item[1];
item = parseFloat(item);
return item;
});
if (sort == 'down') data.reverse();
return data;
}, _sumTimeSort: function (data, sort) {
data = _.sortBy(data, function (item) {
return parseInt(item.use_time);
});
if (sort == 'down') data.reverse();
return data;
}, _priceSort: function (data, sort) {
data = _.sortBy(data, function (item) {
return item.min_price;
});
if (sort == 'down') data.reverse();
return data;
}, //获取导航栏排序后的数据
getSortData: function (data) {
var tmp = [];
var sort = this.sort; for (var k in sort) {
if (sort[k].length > 0) {
tmp = this['_' + k + 'Sort'](data, sort[k])
return tmp;
}
}
}, //根据车次类型做筛选
getTypeData: function (data) {
var typeKeys = [];
var _data = this.type; for(var i = 0, len = _data.length; i < len; i++) {
if (_data[0].checked) return data;
if(_data[i].checked) typeKeys.push(_data[i].id);
} if (!typeKeys) return data; var tmp = _.filter(data, function (item) {
var no = item.my_train_number;
if (_.indexOf(typeKeys, no) != -1) {
return true;
}
return false;
}); return tmp;
}, onCreate: function() { console.log(1) }, //根据出发站做筛选
//事实上这个方法与getTypeData不是完全不能重构到一起,但是可读性可能会变得晦涩
getSetoutData: function (data) {
var typeKeys = [];
var _data = this.setout; for(var i = 0, len = _data.length; i < len; i++) {
if (_data[0].checked) return data;
if(_data[i].checked) typeKeys.push(_data[i].id);
} if (!typeKeys) return data; var tmp = _.filter(data, function (item) {
var no = item.from_telecode;
if (_.indexOf(typeKeys, no) != -1) {
return true;
}
return false;
}); return tmp;
}, renderList: function() {
//这样实现似乎不好
var data = this.dataTmp;
data= this.getTypeData(data);
data= this.getSetoutData(data);
data= this.getSortData(data); this.data = data; } }, watch: {
sort: {
deep: true,
handler: function () {
this.renderList();
}
},
type: {
deep: true,
handler: function() {
this.renderList();
}
},
setout: {
deep: true,
handler: function() {
this.renderList();
}
}
},
el: '#main'
}); //该方法放到这里是否合适?
function formatData(data) {
var item, seat;
var typeMap = {
'g': 'g',
'd': 'd',
't': 't',
'c': 'g'
}; //出发时间对应的分钟数
var fromMinute = 0; //获取当前班车日期当前的时间戳,这个数据是动态的,这里写死了
var d = 1464192000000;
var date = new Date();
var now = parseInt(date.getTime() / 1000);
date.setTime(d);
var year = date.getFullYear();
var month = date.getMonth();
var day = date.getDate();
var toBegin;
var seatName, seatIndex, iii; //处理坐席问题,仅显示二等座,一等座,特等座 无座
// 二等座 一等座 商务座 无座 动卧 特等座
var my_seats = {};
var seatSort = ['二等座', '一等座', '硬座', '硬卧', '软卧', '商务座', '无座', '动卧', '特等座', '软座']; for (var i = 0, len = data.length; i < len; i++) {
fromMinute = data[i].from_time.split(':');
fromMinute[0] = fromMinute[0] + '';
fromMinute[1] = fromMinute[1] + '';
if ((fromMinute[0].charAt(0) == '0')) fromMinute[0] = fromMinute[0].charAt(1);
if ((fromMinute[1].charAt(0) == '0')) fromMinute[1] = fromMinute[1].charAt(1);
date = new Date(year, month, day, fromMinute[0], fromMinute[1], 0);
fromMinute = parseInt(date.getTime() / 1000)
toBegin = parseInt((fromMinute - now) / 60); data[i].toBegin = toBegin; //处理车次类型问题
data[i].my_train_number = typeMap[data[i].train_number.charAt(0).toLowerCase()] || 'other'; seat = data[i].seats;
//所有余票
data[i].sum_ticket = 0;
//最低价
data[i].min_price = null; for (var j = 0, len1 = seat.length; j < len1; j++) {
if (!data[i].min_price || data[i].min_price > seat[j].seat_price) data[i].min_price = parseFloat(seat[j].seat_price);
data[i].sum_ticket += parseInt(seat[j].seat_yupiao); //坐席问题如果坐席不包括上中下则去掉
seatName = seat[j].seat_name;
//去掉上中下
seatName = seatName.replace(/上|中|下/g, '');
if (!my_seats[seatName]) {
my_seats[seatName] = parseInt(seat[j].seat_yupiao);
} else {
my_seats[seatName] = my_seats[seatName] + parseInt(seat[j].seat_yupiao);
}
}
//这里myseat为对象,需要转换为数组
//将定制坐席转为排序后的数组
data[i].my_seats = [];
for (iii = 0; iii < seatSort.length; iii++) {
if (typeof my_seats[seatSort[iii]] == 'number') data[i].my_seats.push({
name: seatSort[iii],
yupiao: my_seats[seatSort[iii]]
});
} my_seats = {};
} return data;
} //根据列表筛选出发站
function getSetout() {
var data = listData;
var stations = [];
var stationMap = {};
var tmp = [{id: 'all', name: '全部出发站', checked: true}]; for (var i = 0, len = data.length; i < len; i++) {
stationMap[data[i].from_telecode] = data[i].from_station;
if (data[i].from_station_type == '起点' && _.indexOf(stations, data[i].from_telecode) == -1) {
stations.push(data[i].from_telecode);
}
} for (i = 0, len = stations.length; i < len; i++) {
var key = stations[i];
var value = stationMap[key];
stations[i] = {
id: key,
name: value,
checked: false
};
} tmp = tmp.concat(stations); return tmp;
} });
这个应该不是最优的做法,后续值得深入研究,最后我们实现下tab的选择效果便结束Vue的代码。
PS:这个写的有点累了,具体代码大家去git上看吧,这里不多写了。
总结
Vue带来了最大一个好处是:
摆脱DOM操作
你真的在页面中就没有看到任何DOM操作了!这个是很牛的一个事情,另外Vue的文档写的很完备,后面点有时间应该做更深入全面的学习!
结语
介于篇幅过长,楼主体力虚脱,关于React的实现,下次再补齐吧,文中不足希望您的指出。