前言
前端已经过了单兵作战的时代了,现在一个稍微复杂一点的项目都需要几个人协同开发,一个战略级别的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
如果对文中的一些代码比较疑惑,可以对比着看看这些文章:
【一次面试】再谈javascript中的继承
【移动前端开发实践】从无到有(统计、请求、MVC、模块化)H5开发须知
【组件化开发】前端进阶篇之如何编写可维护可升级的代码
预览
组件化的实现
之前我们已经说过,所谓组件化,很大程度上是在View上面做文章,要把一个View打散,做到分散,但是又总会有一个总体的控制器在控制所有的View,把他们合到一起,一般来说这个总的控制器是根组件,很多时候就是页面本身(View实例本身)。
根据之前的经验,组件化不一定是越细越好,组件嵌套也不推荐,一般是将一个页面分为多个组件,而子组件不再做过深嵌套(个人经验)
所以我们这里的第一步是实现一个通用的View,这里借鉴之前的代码(【组件化开发】前端进阶篇之如何编写可维护可升级的代码):
1 define([], function () {
2 'use strict';
3
4 return _.inherit({
5
6 showPageView: function (name, _viewdata, id) {
7 this.APP.curViewIns = this;
8 this.APP.showPageView(name, _viewdata, id)
9 },
10
11 propertys: function () {
12 //这里设置UI的根节点所处包裹层
13 this.wrapper = $('#main');
14 this.id = _.uniqueId('page-view-');
15 this.classname = '';
16
17 this.viewId = null;
18 this.refer = null;
19
20 //模板字符串,各个组件不同,现在加入预编译机制
21 this.template = '';
22 //事件机制
23 this.events = {};
24
25 //自定义事件
26 //此处需要注意mask 绑定事件前后问题,考虑scroll.radio插件类型的mask应用,考虑组件通信
27 this.eventArr = {};
28
29 //初始状态为实例化
30 this.status = 'init';
31 },
32
33 //子类事件绑定若想保留父级的,应该使用该方法
34 addEvents: function (events) {
35 if (_.isObject(events)) _.extend(this.events, events);
36 },
37
38 on: function (type, fn, insert) {
39 if (!this.eventArr[type]) this.eventArr[type] = [];
40
41 //头部插入
42 if (insert) {
43 this.eventArr[type].splice(0, 0, fn);
44 } else {
45 this.eventArr[type].push(fn);
46 }
47 },
48
49 off: function (type, fn) {
50 if (!this.eventArr[type]) return;
51 if (fn) {
52 this.eventArr[type] = _.without(this.eventArr[type], fn);
53 } else {
54 this.eventArr[type] = [];
55 }
56 },
57
58 trigger: function (type) {
59 var _slice = Array.prototype.slice;
60 var args = _slice.call(arguments, 1);
61 var events = this.eventArr;
62 var results = [], i, l;
63
64 if (events[type]) {
65 for (i = 0, l = events[type].length; i < l; i++) {
66 results[results.length] = events[type][i].apply(this, args);
67 }
68 }
69 return results;
70 },
71
72 createRoot: function (html) {
73
74 //如果存在style节点,并且style节点不存在的时候需要处理
75 if (this.style && !$('#page_' + this.viewId)[0]) {
76 $('head').append($('<style id="page_' + this.viewId + '" class="page-style">' + this.style + '</style>'))
77 }
78
79 //如果具有fake节点,需要移除
80 $('#fake-page').remove();
81
82 //UI的根节点
83 this.$el = $('<div class="cm-view page-' + this.viewId + ' ' + this.classname + '" style="display: none; " id="' + this.id + '">' + html + '</div>');
84 if (this.wrapper.find('.cm-view')[0]) {
85 this.wrapper.append(this.$el);
86 } else {
87 this.wrapper.html('').append(this.$el);
88 }
89
90 },
91
92 _isAddEvent: function (key) {
93 if (key == 'onCreate' || key == 'onPreShow' || key == 'onShow' || key == 'onRefresh' || key == 'onHide')
94 return true;
95 return false;
96 },
97
98 setOption: function (options) {
99 //这里可以写成switch,开始没有想到有这么多分支
100 for (var k in options) {
101 if (k == 'events') {
102 _.extend(this[k], options[k]);
103 continue;
104 } else if (this._isAddEvent(k)) {
105 this.on(k, options[k])
106 continue;
107 }
108 this[k] = options[k];
109 }
110 // _.extend(this, options);
111 },
112
113 initialize: function (opts) {
114 //这种默认属性
115 this.propertys();
116 //根据参数重置属性
117 this.setOption(opts);
118 //检测不合理属性,修正为正确数据
119 this.resetPropery();
120
121 this.addEvent();
122 this.create();
123
124 this.initElement();
125
126 window.sss = this;
127
128 },
129
130 $: function (selector) {
131 return this.$el.find(selector);
132 },
133
134 //提供属性重置功能,对属性做检查
135 resetPropery: function () { },
136
137 //各事件注册点,用于被继承override
138 addEvent: function () {
139 },
140
141 create: function () {
142 this.trigger('onPreCreate');
143 //如果没有传入模板,说明html结构已经存在
144 this.createRoot(this.render());
145
146 this.status = 'create';
147 this.trigger('onCreate');
148 },
149
150 //实例化需要用到到dom元素
151 initElement: function () { },
152
153 render: function (callback) {
154 var html = this.template;
155 if (!this.template) return '';
156 //引入预编译机制
157 if (_.isFunction(this.template)) {
158 html = this.template(data);
159 } else {
160 html = _.template(this.template)({});
161 }
162 typeof callback == 'function' && callback.call(this);
163 return html;
164 },
165
166 refresh: function (needRecreate) {
167 this.resetPropery();
168 if (needRecreate) {
169 this.create();
170 } else {
171 this.$el.html(this.render());
172 }
173 this.initElement();
174 if (this.status != 'hide') this.show();
175 this.trigger('onRefresh');
176 },
177
178 /**
179 * @description 组件显示方法,首次显示会将ui对象实际由内存插入包裹层
180 * @method initialize
181 * @param {Object} opts
182 */
183 show: function () {
184 this.trigger('onPreShow');
185
186 this.$el.show();
187 this.status = 'show';
188
189 this.bindEvents();
190
191 this.initHeader();
192 this.trigger('onShow');
193 },
194
195 initHeader: function () { },
196
197 hide: function () {
198 if (!this.$el || this.status !== 'show') return;
199
200 this.trigger('onPreHide');
201 this.$el.hide();
202
203 this.status = 'hide';
204 this.unBindEvents();
205 this.trigger('onHide');
206 },
207
208 destroy: function () {
209 this.status = 'destroy';
210 this.unBindEvents();
211 this.$root.remove();
212 this.trigger('onDestroy');
213 delete this;
214 },
215
216 bindEvents: function () {
217 var events = this.events;
218
219 if (!(events || (events = _.result(this, 'events')))) return this;
220 this.unBindEvents();
221
222 // 解析event参数的正则
223 var delegateEventSplitter = /^(\S+)\s*(.*)$/;
224 var key, method, match, eventName, selector;
225
226 // 做简单的字符串数据解析
227 for (key in events) {
228 method = events[key];
229 if (!_.isFunction(method)) method = this[events[key]];
230 if (!method) continue;
231
232 match = key.match(delegateEventSplitter);
233 eventName = match[1], selector = match[2];
234 method = _.bind(method, this);
235 eventName += '.delegateViewEvents' + this.id;
236
237 if (selector === '') {
238 this.$el.on(eventName, method);
239 } else {
240 this.$el.on(eventName, selector, method);
241 }
242 }
243
244 return this;
245 },
246
247 unBindEvents: function () {
248 this.$el.off('.delegateViewEvents' + this.id);
249 return this;
250 },
251
252 getParam: function (key) {
253 return _.getUrlParam(window.location.href, key)
254 },
255
256 renderTpl: function (tpl, data) {
257 if (!_.isFunction(tpl)) tpl = _.template(tpl);
258 return tpl(data);
259 }
260
261
262 });
263
264 });
View Code
有了View的代码后便需要组件级别的代码,正如之前所说,这里的组件只有根元素与子组件两层的层级:
1 define([], function () {
2 'use strict';
3
4 return _.inherit({
5
6 propertys: function () {
7 //这里设置UI的根节点所处包裹层,必须设置
8 this.$el = null;
9
10 //用于定位dom的选择器
11 this.selector = '';
12
13 //每个moduleView必须有一个父view,页面级容器
14 this.view = null;
15
16 //模板字符串,各个组件不同,现在加入预编译机制
17 this.template = '';
18
19 //事件机制
20 this.events = {};
21
22 //实体model,跨模块通信的桥梁
23 this.entity = null;
24 },
25
26 setOption: function (options) {
27 //这里可以写成switch,开始没有想到有这么多分支
28 for (var k in options) {
29 if (k == 'events') {
30 _.extend(this[k], options[k]);
31 continue;
32 }
33 this[k] = options[k];
34 }
35 // _.extend(this, options);
36 },
37
38 //@override
39 initData: function () {
40 },
41
42 //如果传入了dom便
43 initWrapper: function (el) {
44 if (el && el[0]) {
45 this.$el = el;
46 return;
47 }
48 this.$el = this.view.$(this.selector);
49 },
50
51 initialize: function (opts) {
52
53 //这种默认属性
54 this.propertys();
55 //根据参数重置属性
56 this.setOption(opts);
57 this.initData();
58
59 this.initWithoutRender();
60
61 },
62
63 //当父容器关闭后,其对应子容器也应该隐藏
64 bindViewEvent: function () {
65 if (!this.view) return;
66 var scope = this;
67 this.view.on('onHide', function () {
68 scope.onHide();
69 });
70 },
71
72 //处理dom已经存在,不需要渲染的情况
73 initWithoutRender: function () {
74 if (this.template) return;
75 var scope = this;
76 this.view.on('onShow', function () {
77 scope.initWrapper();
78 if (!scope.$el[0]) return;
79 //如果没有父view则不能继续
80 if (!scope.view) return;
81 scope.initElement();
82 scope.bindEvents();
83 });
84 },
85
86 $: function (selector) {
87 return this.$el.find(selector);
88 },
89
90 //实例化需要用到到dom元素
91 initElement: function () { },
92
93 //@override
94 //收集来自各方的实体组成view渲染需要的数据,需要重写
95 getViewModel: function () {
96 throw '必须重写';
97 },
98
99 _render: function (callback) {
100 var data = this.getViewModel() || {};
101 var html = this.template;
102 if (!this.template) return '';
103 //引入预编译机制
104 if (_.isFunction(this.template)) {
105 html = this.template(data);
106 } else {
107 html = _.template(this.template)(data);
108 }
109 typeof callback == 'function' && callback.call(this);
110 return html;
111 },
112
113 //渲染时必须传入dom映射
114 render: function () {
115 this.initWrapper();
116 if (!this.$el[0]) return;
117
118 //如果没有父view则不能继续
119 if (!this.view) return;
120
121 var html = this._render();
122 this.$el.html(html);
123 this.initElement();
124 this.bindEvents();
125
126 },
127
128 bindEvents: function () {
129 var events = this.events;
130
131 if (!(events || (events = _.result(this, 'events')))) return this;
132 this.unBindEvents();
133
134 // 解析event参数的正则
135 var delegateEventSplitter = /^(\S+)\s*(.*)$/;
136 var key, method, match, eventName, selector;
137
138 // 做简单的字符串数据解析
139 for (key in events) {
140 method = events[key];
141 if (!_.isFunction(method)) method = this[events[key]];
142 if (!method) continue;
143
144 match = key.match(delegateEventSplitter);
145 eventName = match[1], selector = match[2];
146 method = _.bind(method, this);
147 eventName += '.delegateUIEvents' + this.id;
148
149 if (selector === '') {
150 this.$el.on(eventName, method);
151 } else {
152 this.$el.on(eventName, selector, method);
153 }
154 }
155
156 return this;
157 },
158
159 unBindEvents: function () {
160 this.$el.off('.delegateUIEvents' + this.id);
161 return this;
162 }
163 });
164
165 });
View Code
有了根View与View组件的实现,剩下的便是数据实体的实现,View与组件Module之间通信的桥梁就是数据Entity,事实上我们的View或者组件模块未必会需要数据实体Entity,只有在业务逻辑的复杂度达到一定阶段才需要分模块,如果dom操作过多的话就需要Entity了:
1 define([], function () {
2 /*
3 一些原则:
4 init方法时,不可引起其它字段update
5 */
6 var Entity = _.inherit({
7 initialize: function (opts) {
8 this.propertys();
9 this.setOption(opts);
10 },
11
12 propertys: function () {
13 //只取页面展示需要数据
14 this.data = {};
15
16 //局部数据改变对应的响应程序,暂定为一个方法
17 //可以是一个类的实例,如果是实例必须有render方法
18 this.controllers = {};
19
20 this.scope = null;
21
22 },
23
24 subscribed: function (namespace, callback, scope) {
25 this.subscribed();
26 },
27
28 unsubscribed: function (namespace) {
29 this.unsubscribe(namespace);
30 },
31
32 subscribe: function (namespace, callback, scope) {
33 if (typeof namespace === 'function') {
34 scope = callback;
35 callback = namespace;
36 namespace = 'update';
37 }
38 if (!namespace || !callback) return;
39 if (scope) callback = $.proxy(callback, scope);
40 if (!this.controllers[namespace]) this.controllers[namespace] = [];
41 this.controllers[namespace].push(callback);
42 },
43
44 unsubscribe: function (namespace) {
45 if (!namespace) this.controllers = {};
46 if (this.controllers[namespace]) this.controllers[namespace] = [];
47 },
48
49 publish: function (namespace, data) {
50 if (!namespace) return;
51 if (!this.controllers[namespace]) return;
52 var arr = this.controllers[namespace];
53 var i, len = arr.length;
54 for (i = 0; i < len; i++) {
55 arr[i](data);
56 }
57 },
58
59 setOption: function (opts) {
60 for (var k in opts) {
61 this[k] = opts[k];
62 }
63 },
64
65 //首次初始化时,需要矫正数据,比如做服务器适配
66 //@override
67 handleData: function () { },
68
69 //一般用于首次根据服务器数据源填充数据
70 initData: function (data) {
71 var k;
72 if (!data) return;
73
74 //如果默认数据没有被覆盖可能有误
75 for (k in this.data) {
76 if (data[k]) this.data[k] = data[k];
77 }
78
79 this.handleData();
80 this.publish('init', this.get());
81 },
82
83 //验证data的有效性,如果无效的话,不应该进行以下逻辑,并且应该报警
84 //@override
85 validateData: function () {
86 return true;
87 },
88
89 //获取数据前,可以进行格式化
90 //@override
91 formatData: function (data) {
92 return data;
93 },
94
95 //获取数据
96 get: function () {
97 if (!this.validateData()) {
98 //需要log
99 return {};
100 }
101 return this.formatData(this.data);
102 },
103
104 //数据跟新后需要做的动作,执行对应的controller改变dom
105 //@override
106 update: function (key) {
107 key = key || 'update';
108 var data = this.get();
109 this.publish(key, data);
110 }
111
112 });
113
114 return Entity;
115 });
View Code
我们这里抽取一段火车票列表的筛选功能做实现,这个页面有一定复杂度又不是太难,大概页面是这个样子的:
因为,我们这里的关注点在View,这里就直接将网上上海到北京的数据给拿下来:
1 this.listData = [
2 {
3 "train_no": "87000K180502",
4 "train_number": "K1805",
5 "from_station": "上海南",
6 "to_station": "杭州",
7 "from_time": "04:22",
8 "to_time": "06:29",
9 "from_station_type": "途经",
10 "to_station_type": "终点",
11 "day_diff": "0",
12 "use_time": "127",
13 "sale_time": "15:30",
14 "control_day": 59,
15 "from_telecode": "SNH",
16 "to_telecode": "HZH",
17 "can_web_buy": "Y",
18 "note": "",
19 "seats": [{
20 "seat_price": "28.5",
21 "seat_name": "硬座",
22 "seat_bookable": 1,
23 "seat_yupiao": 555
24 }, {
25 "seat_price": "74.5",
26 "seat_name": "硬卧上",
27 "seat_bookable": 1,
28 "seat_yupiao": 551
29 }, {
30 "seat_price": "77.5",
31 "seat_name": "硬卧中",
32 "seat_bookable": 1,
33 "seat_yupiao": 551
34 }, {
35 "seat_price": "84.5",
36 "seat_name": "硬卧下",
37 "seat_bookable": 1,
38 "seat_yupiao": 551
39 }, {
40 "seat_price": "112.5",
41 "seat_name": "软卧上",
42 "seat_bookable": 1,
43 "seat_yupiao": 28
44 }, {
45 "seat_price": "127.0",
46 "seat_name": "软卧下",
47 "seat_bookable": 1,
48 "seat_yupiao": 28
49 }, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 334}]
50 }, {
51 "train_no": "47000K151100",
52 "train_number": "K1511",
53 "from_station": "上海南",
54 "to_station": "杭州东",
55 "from_time": "04:56",
56 "to_time": "06:45",
57 "from_station_type": "途经",
58 "to_station_type": "途经",
59 "day_diff": "0",
60 "use_time": "109",
61 "sale_time": "15:30",
62 "control_day": 59,
63 "from_telecode": "SNH",
64 "to_telecode": "HGH",
65 "can_web_buy": "Y",
66 "note": "",
67 "seats": [{
68 "seat_price": "24.5",
69 "seat_name": "硬座",
70 "seat_bookable": 1,
71 "seat_yupiao": 8
72 }, {
73 "seat_price": "70.5",
74 "seat_name": "硬卧上",
75 "seat_bookable": 1,
76 "seat_yupiao": 8
77 }, {
78 "seat_price": "73.5",
79 "seat_name": "硬卧中",
80 "seat_bookable": 1,
81 "seat_yupiao": 8
82 }, {
83 "seat_price": "80.0",
84 "seat_name": "硬卧下",
85 "seat_bookable": 1,
86 "seat_yupiao": 8
87 }, {
88 "seat_price": "108.5",
89 "seat_name": "软卧上",
90 "seat_bookable": 1,
91 "seat_yupiao": 1
92 }, {
93 "seat_price": "122.5",
94 "seat_name": "软卧下",
95 "seat_bookable": 1,
96 "seat_yupiao": 1
97 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
98 }, {
99 "train_no": "1100000K7509",
100 "train_number": "K75",
101 "from_station": "上海南",
102 "to_station": "杭州东",
103 "from_time": "05:02",
104 "to_time": "06:58",
105 "from_station_type": "途经",
106 "to_station_type": "途经",
107 "day_diff": "0",
108 "use_time": "116",
109 "sale_time": "15:30",
110 "control_day": 59,
111 "from_telecode": "SNH",
112 "to_telecode": "HGH",
113 "can_web_buy": "Y",
114 "note": "",
115 "seats": [{
116 "seat_price": "24.5",
117 "seat_name": "硬座",
118 "seat_bookable": 0,
119 "seat_yupiao": 0
120 }, {
121 "seat_price": "70.5",
122 "seat_name": "硬卧上",
123 "seat_bookable": 1,
124 "seat_yupiao": 9
125 }, {
126 "seat_price": "73.5",
127 "seat_name": "硬卧中",
128 "seat_bookable": 1,
129 "seat_yupiao": 9
130 }, {
131 "seat_price": "80.0",
132 "seat_name": "硬卧下",
133 "seat_bookable": 1,
134 "seat_yupiao": 9
135 }, {
136 "seat_price": "108.5",
137 "seat_name": "软卧上",
138 "seat_bookable": 0,
139 "seat_yupiao": 0
140 }, {
141 "seat_price": "122.5",
142 "seat_name": "软卧下",
143 "seat_bookable": 0,
144 "seat_yupiao": 0
145 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 240}]
146 }, {
147 "train_no": "48000K837105",
148 "train_number": "K8371",
149 "from_station": "上海南",
150 "to_station": "杭州",
151 "from_time": "05:43",
152 "to_time": "08:14",
153 "from_station_type": "途经",
154 "to_station_type": "途经",
155 "day_diff": "0",
156 "use_time": "151",
157 "sale_time": "15:30",
158 "control_day": 59,
159 "from_telecode": "SNH",
160 "to_telecode": "HZH",
161 "can_web_buy": "Y",
162 "note": "",
163 "seats": [{
164 "seat_price": "28.5",
165 "seat_name": "硬座",
166 "seat_bookable": 1,
167 "seat_yupiao": 6
168 }, {
169 "seat_price": "74.5",
170 "seat_name": "硬卧上",
171 "seat_bookable": 1,
172 "seat_yupiao": 21
173 }, {
174 "seat_price": "77.5",
175 "seat_name": "硬卧中",
176 "seat_bookable": 1,
177 "seat_yupiao": 21
178 }, {
179 "seat_price": "84.5",
180 "seat_name": "硬卧下",
181 "seat_bookable": 1,
182 "seat_yupiao": 21
183 }, {
184 "seat_price": "112.5",
185 "seat_name": "软卧上",
186 "seat_bookable": 0,
187 "seat_yupiao": 0
188 }, {
189 "seat_price": "127.0",
190 "seat_name": "软卧下",
191 "seat_bookable": 0,
192 "seat_yupiao": 0
193 }, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
194 }, {
195 "train_no": "5l000G754130",
196 "train_number": "G7541",
197 "from_station": "上海虹桥",
198 "to_station": "杭州东",
199 "from_time": "06:14",
200 "to_time": "07:06",
201 "from_station_type": "起点",
202 "to_station_type": "途经",
203 "day_diff": "0",
204 "use_time": "52",
205 "sale_time": "13:30",
206 "control_day": 59,
207 "from_telecode": "AOH",
208 "to_telecode": "HGH",
209 "can_web_buy": "Y",
210 "note": "",
211 "seats": [{
212 "seat_price": "73.0",
213 "seat_name": "二等座",
214 "seat_bookable": 1,
215 "seat_yupiao": 375
216 }, {
217 "seat_price": "117.0",
218 "seat_name": "一等座",
219 "seat_bookable": 1,
220 "seat_yupiao": 24
221 }, {
222 "seat_price": "219.5",
223 "seat_name": "商务座",
224 "seat_bookable": 1,
225 "seat_yupiao": 10
226 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
227 }, {
228 "train_no": "5l000G7331B0",
229 "train_number": "G7331",
230 "from_station": "上海虹桥",
231 "to_station": "杭州东",
232 "from_time": "06:20",
233 "to_time": "07:12",
234 "from_station_type": "起点",
235 "to_station_type": "途经",
236 "day_diff": "0",
237 "use_time": "52",
238 "sale_time": "13:30",
239 "control_day": 59,
240 "from_telecode": "AOH",
241 "to_telecode": "HGH",
242 "can_web_buy": "Y",
243 "note": "",
244 "seats": [{
245 "seat_price": "73.0",
246 "seat_name": "二等座",
247 "seat_bookable": 1,
248 "seat_yupiao": 339
249 }, {
250 "seat_price": "117.0",
251 "seat_name": "一等座",
252 "seat_bookable": 1,
253 "seat_yupiao": 26
254 }, {
255 "seat_price": "219.5",
256 "seat_name": "商务座",
257 "seat_bookable": 1,
258 "seat_yupiao": 10
259 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
260 }, {
261 "train_no": "470000T13501",
262 "train_number": "T135",
263 "from_station": "上海南",
264 "to_station": "杭州东",
265 "from_time": "06:21",
266 "to_time": "08:18",
267 "from_station_type": "途经",
268 "to_station_type": "途经",
269 "day_diff": "0",
270 "use_time": "117",
271 "sale_time": "15:30",
272 "control_day": 59,
273 "from_telecode": "SNH",
274 "to_telecode": "HGH",
275 "can_web_buy": "Y",
276 "note": "",
277 "seats": [{
278 "seat_price": "24.5",
279 "seat_name": "硬座",
280 "seat_bookable": 1,
281 "seat_yupiao": 4
282 }, {
283 "seat_price": "70.5",
284 "seat_name": "硬卧上",
285 "seat_bookable": 1,
286 "seat_yupiao": 38
287 }, {
288 "seat_price": "73.5",
289 "seat_name": "硬卧中",
290 "seat_bookable": 1,
291 "seat_yupiao": 38
292 }, {
293 "seat_price": "80.0",
294 "seat_name": "硬卧下",
295 "seat_bookable": 1,
296 "seat_yupiao": 38
297 }, {
298 "seat_price": "108.5",
299 "seat_name": "软卧上",
300 "seat_bookable": 0,
301 "seat_yupiao": 0
302 }, {
303 "seat_price": "122.5",
304 "seat_name": "软卧下",
305 "seat_bookable": 0,
306 "seat_yupiao": 0
307 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
308 }, {
309 "train_no": "5l000G132120",
310 "train_number": "G1321",
311 "from_station": "上海虹桥",
312 "to_station": "杭州东",
313 "from_time": "06:25",
314 "to_time": "07:17",
315 "from_station_type": "起点",
316 "to_station_type": "途经",
317 "day_diff": "0",
318 "use_time": "52",
319 "sale_time": "13:30",
320 "control_day": 57,
321 "from_telecode": "AOH",
322 "to_telecode": "HGH",
323 "can_web_buy": "Y",
324 "note": "",
325 "seats": [{
326 "seat_price": "73.0",
327 "seat_name": "二等座",
328 "seat_bookable": 1,
329 "seat_yupiao": 304
330 }, {
331 "seat_price": "117.0",
332 "seat_name": "一等座",
333 "seat_bookable": 1,
334 "seat_yupiao": 15
335 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 9}]
336 }, {
337 "train_no": "5l000G733380",
338 "train_number": "G7333",
339 "from_station": "上海虹桥",
340 "to_station": "杭州东",
341 "from_time": "06:30",
342 "to_time": "07:22",
343 "from_station_type": "起点",
344 "to_station_type": "途经",
345 "day_diff": "0",
346 "use_time": "52",
347 "sale_time": "13:30",
348 "control_day": 59,
349 "from_telecode": "AOH",
350 "to_telecode": "HGH",
351 "can_web_buy": "Y",
352 "note": "",
353 "seats": [{
354 "seat_price": "73.0",
355 "seat_name": "二等座",
356 "seat_bookable": 1,
357 "seat_yupiao": 702
358 }, {
359 "seat_price": "117.0",
360 "seat_name": "一等座",
361 "seat_bookable": 1,
362 "seat_yupiao": 51
363 }, {
364 "seat_price": "219.5",
365 "seat_name": "商务座",
366 "seat_bookable": 1,
367 "seat_yupiao": 20
368 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
369 }, {
370 "train_no": "0400000K5004",
371 "train_number": "K47",
372 "from_station": "上海南",
373 "to_station": "杭州",
374 "from_time": "06:34",
375 "to_time": "09:15",
376 "from_station_type": "途经",
377 "to_station_type": "终点",
378 "day_diff": "0",
379 "use_time": "161",
380 "sale_time": "15:30",
381 "control_day": 59,
382 "from_telecode": "SNH",
383 "to_telecode": "HZH",
384 "can_web_buy": "Y",
385 "note": "",
386 "seats": [{
387 "seat_price": "28.5",
388 "seat_name": "硬座",
389 "seat_bookable": 1,
390 "seat_yupiao": 6
391 }, {
392 "seat_price": "74.5",
393 "seat_name": "硬卧上",
394 "seat_bookable": 1,
395 "seat_yupiao": 122
396 }, {
397 "seat_price": "77.5",
398 "seat_name": "硬卧中",
399 "seat_bookable": 1,
400 "seat_yupiao": 122
401 }, {
402 "seat_price": "84.5",
403 "seat_name": "硬卧下",
404 "seat_bookable": 1,
405 "seat_yupiao": 122
406 }, {
407 "seat_price": "112.5",
408 "seat_name": "软卧上",
409 "seat_bookable": 1,
410 "seat_yupiao": 1
411 }, {
412 "seat_price": "127.0",
413 "seat_name": "软卧下",
414 "seat_bookable": 1,
415 "seat_yupiao": 1
416 }, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
417 }, {
418 "train_no": "5l000G7301B1",
419 "train_number": "G7301",
420 "from_station": "上海虹桥",
421 "to_station": "杭州",
422 "from_time": "06:35",
423 "to_time": "07:40",
424 "from_station_type": "起点",
425 "to_station_type": "终点",
426 "day_diff": "0",
427 "use_time": "65",
428 "sale_time": "13:30",
429 "control_day": 59,
430 "from_telecode": "AOH",
431 "to_telecode": "HZH",
432 "can_web_buy": "Y",
433 "note": "",
434 "seats": [{
435 "seat_price": "77.5",
436 "seat_name": "二等座",
437 "seat_bookable": 1,
438 "seat_yupiao": 490
439 }, {
440 "seat_price": "123.5",
441 "seat_name": "一等座",
442 "seat_bookable": 1,
443 "seat_yupiao": 26
444 }, {
445 "seat_price": "233.5",
446 "seat_name": "商务座",
447 "seat_bookable": 1,
448 "seat_yupiao": 10
449 }, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
450 }, {
451 "train_no": "5l000D314510",
452 "train_number": "D3145",
453 "from_station": "上海虹桥",
454 "to_station": "杭州东",
455 "from_time": "06:40",
456 "to_time": "07:41",
457 "from_station_type": "起点",
458 "to_station_type": "途经",
459 "day_diff": "0",
460 "use_time": "61",
461 "sale_time": "13:30",
462 "control_day": 59,
463 "from_telecode": "AOH",
464 "to_telecode": "HGH",
465 "can_web_buy": "Y",
466 "note": "",
467 "seats": [{
468 "seat_price": "49.0",
469 "seat_name": "二等座",
470 "seat_bookable": 1,
471 "seat_yupiao": 21
472 }, {
473 "seat_price": "59.0",
474 "seat_name": "一等座",
475 "seat_bookable": 1,
476 "seat_yupiao": 1
477 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
478 }, {
479 "train_no": "5l000G138330",
480 "train_number": "G1383",
481 "from_station": "上海虹桥",
482 "to_station": "杭州东",
483 "from_time": "06:45",
484 "to_time": "07:46",
485 "from_station_type": "起点",
486 "to_station_type": "途经",
487 "day_diff": "0",
488 "use_time": "61",
489 "sale_time": "13:30",
490 "control_day": 59,
491 "from_telecode": "AOH",
492 "to_telecode": "HGH",
493 "can_web_buy": "Y",
494 "note": "",
495 "seats": [{
496 "seat_price": "73.0",
497 "seat_name": "二等座",
498 "seat_bookable": 1,
499 "seat_yupiao": 384
500 }, {
501 "seat_price": "117.0",
502 "seat_name": "一等座",
503 "seat_bookable": 1,
504 "seat_yupiao": 16
505 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
506 }, {
507 "train_no": "5l000G750164",
508 "train_number": "G7501",
509 "from_station": "上海虹桥",
510 "to_station": "杭州东",
511 "from_time": "06:50",
512 "to_time": "07:56",
513 "from_station_type": "起点",
514 "to_station_type": "途经",
515 "day_diff": "0",
516 "use_time": "66",
517 "sale_time": "13:30",
518 "control_day": 59,
519 "from_telecode": "AOH",
520 "to_telecode": "HGH",
521 "can_web_buy": "Y",
522 "note": "",
523 "seats": [{
524 "seat_price": "73.0",
525 "seat_name": "二等座",
526 "seat_bookable": 1,
527 "seat_yupiao": 165
528 }, {
529 "seat_price": "117.0",
530 "seat_name": "一等座",
531 "seat_bookable": 0,
532 "seat_yupiao": 0
533 }, {
534 "seat_price": "219.5",
535 "seat_name": "商务座",
536 "seat_bookable": 1,
537 "seat_yupiao": 3
538 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
539 }, {
540 "train_no": "49000K11840B",
541 "train_number": "K1181",
542 "from_station": "上海南",
543 "to_station": "杭州",
544 "from_time": "06:51",
545 "to_time": "09:50",
546 "from_station_type": "途经",
547 "to_station_type": "途经",
548 "day_diff": "0",
549 "use_time": "179",
550 "sale_time": "15:30",
551 "control_day": 59,
552 "from_telecode": "SNH",
553 "to_telecode": "HZH",
554 "can_web_buy": "Y",
555 "note": "",
556 "seats": [{
557 "seat_price": "28.5",
558 "seat_name": "硬座",
559 "seat_bookable": 1,
560 "seat_yupiao": 7
561 }, {
562 "seat_price": "74.5",
563 "seat_name": "硬卧上",
564 "seat_bookable": 1,
565 "seat_yupiao": 15
566 }, {
567 "seat_price": "77.5",
568 "seat_name": "硬卧中",
569 "seat_bookable": 1,
570 "seat_yupiao": 15
571 }, {
572 "seat_price": "84.5",
573 "seat_name": "硬卧下",
574 "seat_bookable": 1,
575 "seat_yupiao": 15
576 }, {
577 "seat_price": "112.5",
578 "seat_name": "软卧上",
579 "seat_bookable": 0,
580 "seat_yupiao": 0
581 }, {
582 "seat_price": "127.0",
583 "seat_name": "软卧下",
584 "seat_bookable": 0,
585 "seat_yupiao": 0
586 }, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 75}]
587 }, {
588 "train_no": "5l000G165120",
589 "train_number": "G1651",
590 "from_station": "上海虹桥",
591 "to_station": "杭州东",
592 "from_time": "06:56",
593 "to_time": "08:00",
594 "from_station_type": "起点",
595 "to_station_type": "途经",
596 "day_diff": "0",
597 "use_time": "64",
598 "sale_time": "13:30",
599 "control_day": 59,
600 "from_telecode": "AOH",
601 "to_telecode": "HGH",
602 "can_web_buy": "Y",
603 "note": "",
604 "seats": [{
605 "seat_price": "73.0",
606 "seat_name": "二等座",
607 "seat_bookable": 1,
608 "seat_yupiao": 190
609 }, {
610 "seat_price": "117.0",
611 "seat_name": "一等座",
612 "seat_bookable": 1,
613 "seat_yupiao": 5
614 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 1}]
615 }, {
616 "train_no": "5l0000D37960",
617 "train_number": "D379",
618 "from_station": "上海虹桥",
619 "to_station": "杭州东",
620 "from_time": "07:02",
621 "to_time": "08:08",
622 "from_station_type": "起点",
623 "to_station_type": "途经",
624 "day_diff": "0",
625 "use_time": "66",
626 "sale_time": "13:30",
627 "control_day": 59,
628 "from_telecode": "AOH",
629 "to_telecode": "HGH",
630 "can_web_buy": "Y",
631 "note": "",
632 "seats": [{
633 "seat_price": "49.0",
634 "seat_name": "二等座",
635 "seat_bookable": 1,
636 "seat_yupiao": 683
637 }, {
638 "seat_price": "59.0",
639 "seat_name": "一等座",
640 "seat_bookable": 1,
641 "seat_yupiao": 45
642 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
643 }, {
644 "train_no": "5l000G754304",
645 "train_number": "G7543",
646 "from_station": "上海虹桥",
647 "to_station": "杭州东",
648 "from_time": "07:07",
649 "to_time": "08:04",
650 "from_station_type": "起点",
651 "to_station_type": "途经",
652 "day_diff": "0",
653 "use_time": "57",
654 "sale_time": "13:30",
655 "control_day": 59,
656 "from_telecode": "AOH",
657 "to_telecode": "HGH",
658 "can_web_buy": "Y",
659 "note": "",
660 "seats": [{
661 "seat_price": "73.0",
662 "seat_name": "二等座",
663 "seat_bookable": 1,
664 "seat_yupiao": 128
665 }, {
666 "seat_price": "117.0",
667 "seat_name": "一等座",
668 "seat_bookable": 1,
669 "seat_yupiao": 54
670 }, {
671 "seat_price": "219.5",
672 "seat_name": "商务座",
673 "seat_bookable": 1,
674 "seat_yupiao": 20
675 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
676 }, {
677 "train_no": "5l000G236510",
678 "train_number": "G2365",
679 "from_station": "上海虹桥",
680 "to_station": "杭州东",
681 "from_time": "07:12",
682 "to_time": "08:18",
683 "from_station_type": "起点",
684 "to_station_type": "途经",
685 "day_diff": "0",
686 "use_time": "66",
687 "sale_time": "13:30",
688 "control_day": 59,
689 "from_telecode": "AOH",
690 "to_telecode": "HGH",
691 "can_web_buy": "Y",
692 "note": "",
693 "seats": [{
694 "seat_price": "73.0",
695 "seat_name": "二等座",
696 "seat_bookable": 1,
697 "seat_yupiao": 816
698 }, {
699 "seat_price": "117.0",
700 "seat_name": "一等座",
701 "seat_bookable": 1,
702 "seat_yupiao": 28
703 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 15}]
704 }, {
705 "train_no": "5l000G137120",
706 "train_number": "G1371",
707 "from_station": "上海虹桥",
708 "to_station": "杭州东",
709 "from_time": "07:22",
710 "to_time": "08:23",
711 "from_station_type": "起点",
712 "to_station_type": "途经",
713 "day_diff": "0",
714 "use_time": "61",
715 "sale_time": "13:30",
716 "control_day": 59,
717 "from_telecode": "AOH",
718 "to_telecode": "HGH",
719 "can_web_buy": "Y",
720 "note": "",
721 "seats": [{
722 "seat_price": "73.0",
723 "seat_name": "二等座",
724 "seat_bookable": 1,
725 "seat_yupiao": 210
726 }, {
727 "seat_price": "117.0",
728 "seat_name": "一等座",
729 "seat_bookable": 1,
730 "seat_yupiao": 16
731 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
732 }, {
733 "train_no": "5l000G739541",
734 "train_number": "G7395",
735 "from_station": "上海虹桥",
736 "to_station": "杭州",
737 "from_time": "07:27",
738 "to_time": "08:32",
739 "from_station_type": "起点",
740 "to_station_type": "途经",
741 "day_diff": "0",
742 "use_time": "65",
743 "sale_time": "13:30",
744 "control_day": 59,
745 "from_telecode": "AOH",
746 "to_telecode": "HZH",
747 "can_web_buy": "Y",
748 "note": "",
749 "seats": [{
750 "seat_price": "77.5",
751 "seat_name": "二等座",
752 "seat_bookable": 1,
753 "seat_yupiao": 435
754 }, {
755 "seat_price": "123.5",
756 "seat_name": "一等座",
757 "seat_bookable": 1,
758 "seat_yupiao": 22
759 }, {
760 "seat_price": "233.5",
761 "seat_name": "商务座",
762 "seat_bookable": 1,
763 "seat_yupiao": 10
764 }, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
765 }, {
766 "train_no": "550000K83392",
767 "train_number": "K833",
768 "from_station": "上海南",
769 "to_station": "杭州东",
770 "from_time": "07:31",
771 "to_time": "09:34",
772 "from_station_type": "起点",
773 "to_station_type": "途经",
774 "day_diff": "0",
775 "use_time": "123",
776 "sale_time": "15:30",
777 "control_day": 59,
778 "from_telecode": "SNH",
779 "to_telecode": "HGH",
780 "can_web_buy": "Y",
781 "note": "",
782 "seats": [{
783 "seat_price": "24.5",
784 "seat_name": "硬座",
785 "seat_bookable": 1,
786 "seat_yupiao": 234
787 }, {
788 "seat_price": "70.5",
789 "seat_name": "硬卧上",
790 "seat_bookable": 1,
791 "seat_yupiao": 17
792 }, {
793 "seat_price": "73.5",
794 "seat_name": "硬卧中",
795 "seat_bookable": 1,
796 "seat_yupiao": 17
797 }, {
798 "seat_price": "80.0",
799 "seat_name": "硬卧下",
800 "seat_bookable": 1,
801 "seat_yupiao": 17
802 }, {
803 "seat_price": "108.5",
804 "seat_name": "软卧上",
805 "seat_bookable": 0,
806 "seat_yupiao": 0
807 }, {
808 "seat_price": "122.5",
809 "seat_name": "软卧下",
810 "seat_bookable": 0,
811 "seat_yupiao": 0
812 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
813 }, {
814 "train_no": "5l000G134140",
815 "train_number": "G1341",
816 "from_station": "上海虹桥",
817 "to_station": "杭州东",
818 "from_time": "07:32",
819 "to_time": "08:31",
820 "from_station_type": "起点",
821 "to_station_type": "途经",
822 "day_diff": "0",
823 "use_time": "59",
824 "sale_time": "13:30",
825 "control_day": 59,
826 "from_telecode": "AOH",
827 "to_telecode": "HGH",
828 "can_web_buy": "Y",
829 "note": "",
830 "seats": [{
831 "seat_price": "73.0",
832 "seat_name": "二等座",
833 "seat_bookable": 1,
834 "seat_yupiao": 704
835 }, {
836 "seat_price": "117.0",
837 "seat_name": "一等座",
838 "seat_bookable": 1,
839 "seat_yupiao": 20
840 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 18}]
841 }, {
842 "train_no": "5l000D313140",
843 "train_number": "D3131",
844 "from_station": "上海虹桥",
845 "to_station": "杭州东",
846 "from_time": "07:37",
847 "to_time": "08:38",
848 "from_station_type": "起点",
849 "to_station_type": "途经",
850 "day_diff": "0",
851 "use_time": "61",
852 "sale_time": "13:30",
853 "control_day": 59,
854 "from_telecode": "AOH",
855 "to_telecode": "HGH",
856 "can_web_buy": "Y",
857 "note": "",
858 "seats": [{
859 "seat_price": "49.0",
860 "seat_name": "二等座",
861 "seat_bookable": 1,
862 "seat_yupiao": 543
863 }, {
864 "seat_price": "59.0",
865 "seat_name": "一等座",
866 "seat_bookable": 1,
867 "seat_yupiao": 60
868 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 84}]
869 }, {
870 "train_no": "5l000D228730",
871 "train_number": "D2287",
872 "from_station": "上海虹桥",
873 "to_station": "杭州东",
874 "from_time": "07:47",
875 "to_time": "09:07",
876 "from_station_type": "起点",
877 "to_station_type": "途经",
878 "day_diff": "0",
879 "use_time": "80",
880 "sale_time": "13:30",
881 "control_day": 59,
882 "from_telecode": "AOH",
883 "to_telecode": "HGH",
884 "can_web_buy": "Y",
885 "note": "",
886 "seats": [{
887 "seat_price": "49.0",
888 "seat_name": "二等座",
889 "seat_bookable": 1,
890 "seat_yupiao": 114
891 }, {
892 "seat_price": "59.0",
893 "seat_name": "一等座",
894 "seat_bookable": 1,
895 "seat_yupiao": 12
896 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 84}]
897 }, {
898 "train_no": "5l000G163120",
899 "train_number": "G1631",
900 "from_station": "上海虹桥",
901 "to_station": "杭州东",
902 "from_time": "07:54",
903 "to_time": "08:49",
904 "from_station_type": "起点",
905 "to_station_type": "途经",
906 "day_diff": "0",
907 "use_time": "55",
908 "sale_time": "13:30",
909 "control_day": 59,
910 "from_telecode": "AOH",
911 "to_telecode": "HGH",
912 "can_web_buy": "Y",
913 "note": "",
914 "seats": [{
915 "seat_price": "73.0",
916 "seat_name": "二等座",
917 "seat_bookable": 1,
918 "seat_yupiao": 238
919 }, {
920 "seat_price": "117.0",
921 "seat_name": "一等座",
922 "seat_bookable": 1,
923 "seat_yupiao": 4
924 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 6}]
925 }, {
926 "train_no": "5l00000G8520",
927 "train_number": "G85",
928 "from_station": "上海虹桥",
929 "to_station": "杭州东",
930 "from_time": "08:00",
931 "to_time": "08:45",
932 "from_station_type": "起点",
933 "to_station_type": "途经",
934 "day_diff": "0",
935 "use_time": "45",
936 "sale_time": "13:30",
937 "control_day": 59,
938 "from_telecode": "AOH",
939 "to_telecode": "HGH",
940 "can_web_buy": "N",
941 "note": "",
942 "seats": [{
943 "seat_price": "73.0",
944 "seat_name": "二等座",
945 "seat_bookable": 1,
946 "seat_yupiao": 97
947 }, {
948 "seat_price": "117.0",
949 "seat_name": "一等座",
950 "seat_bookable": 1,
951 "seat_yupiao": 25
952 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
953 }, {
954 "train_no": "5l000G750350",
955 "train_number": "G7503",
956 "from_station": "上海虹桥",
957 "to_station": "杭州东",
958 "from_time": "08:05",
959 "to_time": "08:57",
960 "from_station_type": "起点",
961 "to_station_type": "途经",
962 "day_diff": "0",
963 "use_time": "52",
964 "sale_time": "13:30",
965 "control_day": 59,
966 "from_telecode": "AOH",
967 "to_telecode": "HGH",
968 "can_web_buy": "Y",
969 "note": "",
970 "seats": [{
971 "seat_price": "73.0",
972 "seat_name": "二等座",
973 "seat_bookable": 1,
974 "seat_yupiao": 130
975 }, {
976 "seat_price": "117.0",
977 "seat_name": "一等座",
978 "seat_bookable": 1,
979 "seat_yupiao": 46
980 }, {
981 "seat_price": "219.5",
982 "seat_name": "商务座",
983 "seat_bookable": 1,
984 "seat_yupiao": 20
985 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
986 }, {
987 "train_no": "55000G735700",
988 "train_number": "G7357",
989 "from_station": "上海",
990 "to_station": "杭州",
991 "from_time": "08:05",
992 "to_time": "09:44",
993 "from_station_type": "起点",
994 "to_station_type": "终点",
995 "day_diff": "0",
996 "use_time": "99",
997 "sale_time": "14:30",
998 "control_day": 59,
999 "from_telecode": "SHH",
1000 "to_telecode": "HZH",
1001 "can_web_buy": "Y",
1002 "note": "",
1003 "seats": [{
1004 "seat_price": "92.5",
1005 "seat_name": "二等座",
1006 "seat_bookable": 1,
1007 "seat_yupiao": 995
1008 }, {
1009 "seat_price": "147.5",
1010 "seat_name": "一等座",
1011 "seat_bookable": 1,
1012 "seat_yupiao": 48
1013 }, {
1014 "seat_price": "278.5",
1015 "seat_name": "商务座",
1016 "seat_bookable": 1,
1017 "seat_yupiao": 20
1018 }, {"seat_price": "92.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1019 }, {
1020 "train_no": "5l000G754920",
1021 "train_number": "G7549",
1022 "from_station": "上海虹桥",
1023 "to_station": "杭州东",
1024 "from_time": "08:10",
1025 "to_time": "09:12",
1026 "from_station_type": "起点",
1027 "to_station_type": "途经",
1028 "day_diff": "0",
1029 "use_time": "62",
1030 "sale_time": "13:30",
1031 "control_day": 59,
1032 "from_telecode": "AOH",
1033 "to_telecode": "HGH",
1034 "can_web_buy": "Y",
1035 "note": "",
1036 "seats": [{
1037 "seat_price": "73.0",
1038 "seat_name": "二等座",
1039 "seat_bookable": 1,
1040 "seat_yupiao": 128
1041 }, {
1042 "seat_price": "117.0",
1043 "seat_name": "一等座",
1044 "seat_bookable": 1,
1045 "seat_yupiao": 50
1046 }, {
1047 "seat_price": "219.5",
1048 "seat_name": "商务座",
1049 "seat_bookable": 1,
1050 "seat_yupiao": 20
1051 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1052 }, {
1053 "train_no": "5l000G165330",
1054 "train_number": "G1653",
1055 "from_station": "上海虹桥",
1056 "to_station": "杭州东",
1057 "from_time": "08:15",
1058 "to_time": "09:01",
1059 "from_station_type": "起点",
1060 "to_station_type": "途经",
1061 "day_diff": "0",
1062 "use_time": "46",
1063 "sale_time": "13:30",
1064 "control_day": 59,
1065 "from_telecode": "AOH",
1066 "to_telecode": "HGH",
1067 "can_web_buy": "Y",
1068 "note": "",
1069 "seats": [{
1070 "seat_price": "73.0",
1071 "seat_name": "二等座",
1072 "seat_bookable": 1,
1073 "seat_yupiao": 752
1074 }, {
1075 "seat_price": "117.0",
1076 "seat_name": "一等座",
1077 "seat_bookable": 1,
1078 "seat_yupiao": 29
1079 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 15}]
1080 }, {
1081 "train_no": "5l000D320190",
1082 "train_number": "D3201",
1083 "from_station": "上海虹桥",
1084 "to_station": "杭州东",
1085 "from_time": "08:20",
1086 "to_time": "09:28",
1087 "from_station_type": "起点",
1088 "to_station_type": "途经",
1089 "day_diff": "0",
1090 "use_time": "68",
1091 "sale_time": "13:30",
1092 "control_day": 59,
1093 "from_telecode": "AOH",
1094 "to_telecode": "HGH",
1095 "can_web_buy": "Y",
1096 "note": "",
1097 "seats": [{
1098 "seat_price": "49.0",
1099 "seat_name": "二等座",
1100 "seat_bookable": 1,
1101 "seat_yupiao": 73
1102 }, {
1103 "seat_price": "59.0",
1104 "seat_name": "一等座",
1105 "seat_bookable": 1,
1106 "seat_yupiao": 19
1107 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 168}]
1108 }, {
1109 "train_no": "5l000G132320",
1110 "train_number": "G1323",
1111 "from_station": "上海虹桥",
1112 "to_station": "杭州东",
1113 "from_time": "08:25",
1114 "to_time": "09:24",
1115 "from_station_type": "起点",
1116 "to_station_type": "途经",
1117 "day_diff": "0",
1118 "use_time": "59",
1119 "sale_time": "13:30",
1120 "control_day": 59,
1121 "from_telecode": "AOH",
1122 "to_telecode": "HGH",
1123 "can_web_buy": "Y",
1124 "note": "",
1125 "seats": [{
1126 "seat_price": "73.0",
1127 "seat_name": "二等座",
1128 "seat_bookable": 1,
1129 "seat_yupiao": 272
1130 }, {
1131 "seat_price": "117.0",
1132 "seat_name": "一等座",
1133 "seat_bookable": 1,
1134 "seat_yupiao": 12
1135 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 9}]
1136 }, {
1137 "train_no": "5l000G150930",
1138 "train_number": "G1509",
1139 "from_station": "上海虹桥",
1140 "to_station": "杭州东",
1141 "from_time": "08:31",
1142 "to_time": "09:46",
1143 "from_station_type": "起点",
1144 "to_station_type": "途经",
1145 "day_diff": "0",
1146 "use_time": "75",
1147 "sale_time": "13:30",
1148 "control_day": 59,
1149 "from_telecode": "AOH",
1150 "to_telecode": "HGH",
1151 "can_web_buy": "Y",
1152 "note": "",
1153 "seats": [{
1154 "seat_price": "73.0",
1155 "seat_name": "二等座",
1156 "seat_bookable": 1,
1157 "seat_yupiao": 80
1158 }, {
1159 "seat_price": "117.0",
1160 "seat_name": "一等座",
1161 "seat_bookable": 1,
1162 "seat_yupiao": 56
1163 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 24}]
1164 }, {
1165 "train_no": "550000K14906",
1166 "train_number": "K149",
1167 "from_station": "上海南",
1168 "to_station": "杭州东",
1169 "from_time": "08:42",
1170 "to_time": "10:38",
1171 "from_station_type": "起点",
1172 "to_station_type": "途经",
1173 "day_diff": "0",
1174 "use_time": "116",
1175 "sale_time": "15:30",
1176 "control_day": 59,
1177 "from_telecode": "SNH",
1178 "to_telecode": "HGH",
1179 "can_web_buy": "Y",
1180 "note": "",
1181 "seats": [{
1182 "seat_price": "24.5",
1183 "seat_name": "硬座",
1184 "seat_bookable": 1,
1185 "seat_yupiao": 66
1186 }, {
1187 "seat_price": "70.5",
1188 "seat_name": "硬卧上",
1189 "seat_bookable": 1,
1190 "seat_yupiao": 38
1191 }, {
1192 "seat_price": "73.5",
1193 "seat_name": "硬卧中",
1194 "seat_bookable": 1,
1195 "seat_yupiao": 38
1196 }, {
1197 "seat_price": "80.0",
1198 "seat_name": "硬卧下",
1199 "seat_bookable": 1,
1200 "seat_yupiao": 38
1201 }, {
1202 "seat_price": "108.5",
1203 "seat_name": "软卧上",
1204 "seat_bookable": 0,
1205 "seat_yupiao": 0
1206 }, {
1207 "seat_price": "122.5",
1208 "seat_name": "软卧下",
1209 "seat_bookable": 0,
1210 "seat_yupiao": 0
1211 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1212 }, {
1213 "train_no": "55000K118532",
1214 "train_number": "K1185",
1215 "from_station": "上海南",
1216 "to_station": "杭州东",
1217 "from_time": "08:48",
1218 "to_time": "11:01",
1219 "from_station_type": "起点",
1220 "to_station_type": "途经",
1221 "day_diff": "0",
1222 "use_time": "133",
1223 "sale_time": "15:30",
1224 "control_day": 59,
1225 "from_telecode": "SNH",
1226 "to_telecode": "HGH",
1227 "can_web_buy": "Y",
1228 "note": "",
1229 "seats": [{
1230 "seat_price": "24.5",
1231 "seat_name": "硬座",
1232 "seat_bookable": 1,
1233 "seat_yupiao": 234
1234 }, {
1235 "seat_price": "70.5",
1236 "seat_name": "硬卧上",
1237 "seat_bookable": 0,
1238 "seat_yupiao": 0
1239 }, {
1240 "seat_price": "73.5",
1241 "seat_name": "硬卧中",
1242 "seat_bookable": 0,
1243 "seat_yupiao": 0
1244 }, {
1245 "seat_price": "80.0",
1246 "seat_name": "硬卧下",
1247 "seat_bookable": 0,
1248 "seat_yupiao": 0
1249 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1250 }, {
1251 "train_no": "54000D312510",
1252 "train_number": "D3125",
1253 "from_station": "上海虹桥",
1254 "to_station": "杭州东",
1255 "from_time": "08:49",
1256 "to_time": "10:09",
1257 "from_station_type": "途经",
1258 "to_station_type": "途经",
1259 "day_diff": "0",
1260 "use_time": "80",
1261 "sale_time": "13:30",
1262 "control_day": 59,
1263 "from_telecode": "AOH",
1264 "to_telecode": "HGH",
1265 "can_web_buy": "Y",
1266 "note": "",
1267 "seats": [{
1268 "seat_price": "49.0",
1269 "seat_name": "二等座",
1270 "seat_bookable": 1,
1271 "seat_yupiao": 355
1272 }, {
1273 "seat_price": "59.0",
1274 "seat_name": "一等座",
1275 "seat_bookable": 1,
1276 "seat_yupiao": 55
1277 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1278 }, {
1279 "train_no": "5l000G134340",
1280 "train_number": "G1343",
1281 "from_station": "上海虹桥",
1282 "to_station": "杭州东",
1283 "from_time": "08:55",
1284 "to_time": "09:42",
1285 "from_station_type": "起点",
1286 "to_station_type": "途经",
1287 "day_diff": "0",
1288 "use_time": "47",
1289 "sale_time": "13:30",
1290 "control_day": 59,
1291 "from_telecode": "AOH",
1292 "to_telecode": "HGH",
1293 "can_web_buy": "N",
1294 "note": "",
1295 "seats": [{
1296 "seat_price": "73.0",
1297 "seat_name": "二等座",
1298 "seat_bookable": 1,
1299 "seat_yupiao": 139
1300 }, {
1301 "seat_price": "117.0",
1302 "seat_name": "一等座",
1303 "seat_bookable": 1,
1304 "seat_yupiao": 13
1305 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 2}]
1306 }, {
1307 "train_no": "5l000G750550",
1308 "train_number": "G7505",
1309 "from_station": "上海虹桥",
1310 "to_station": "杭州东",
1311 "from_time": "09:00",
1312 "to_time": "09:54",
1313 "from_station_type": "途经",
1314 "to_station_type": "途经",
1315 "day_diff": "0",
1316 "use_time": "54",
1317 "sale_time": "13:30",
1318 "control_day": 59,
1319 "from_telecode": "AOH",
1320 "to_telecode": "HGH",
1321 "can_web_buy": "Y",
1322 "note": "",
1323 "seats": [{
1324 "seat_price": "73.0",
1325 "seat_name": "二等座",
1326 "seat_bookable": 1,
1327 "seat_yupiao": 190
1328 }, {
1329 "seat_price": "117.0",
1330 "seat_name": "一等座",
1331 "seat_bookable": 1,
1332 "seat_yupiao": 113
1333 }, {
1334 "seat_price": "219.5",
1335 "seat_name": "商务座",
1336 "seat_bookable": 1,
1337 "seat_yupiao": 13
1338 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1339 }, {
1340 "train_no": "5l000D228520",
1341 "train_number": "D2285",
1342 "from_station": "上海虹桥",
1343 "to_station": "杭州东",
1344 "from_time": "09:05",
1345 "to_time": "10:18",
1346 "from_station_type": "起点",
1347 "to_station_type": "途经",
1348 "day_diff": "0",
1349 "use_time": "73",
1350 "sale_time": "13:30",
1351 "control_day": 59,
1352 "from_telecode": "AOH",
1353 "to_telecode": "HGH",
1354 "can_web_buy": "N",
1355 "note": "",
1356 "seats": [{
1357 "seat_price": "49.0",
1358 "seat_name": "二等座",
1359 "seat_bookable": 1,
1360 "seat_yupiao": 3
1361 }, {
1362 "seat_price": "59.0",
1363 "seat_name": "一等座",
1364 "seat_bookable": 1,
1365 "seat_yupiao": 10
1366 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1367 }, {
1368 "train_no": "5l000G734952",
1369 "train_number": "G7349",
1370 "from_station": "上海虹桥",
1371 "to_station": "杭州东",
1372 "from_time": "09:10",
1373 "to_time": "10:04",
1374 "from_station_type": "途经",
1375 "to_station_type": "途经",
1376 "day_diff": "0",
1377 "use_time": "54",
1378 "sale_time": "13:30",
1379 "control_day": 59,
1380 "from_telecode": "AOH",
1381 "to_telecode": "HGH",
1382 "can_web_buy": "Y",
1383 "note": "",
1384 "seats": [{
1385 "seat_price": "73.0",
1386 "seat_name": "二等座",
1387 "seat_bookable": 1,
1388 "seat_yupiao": 281
1389 }, {
1390 "seat_price": "117.0",
1391 "seat_name": "一等座",
1392 "seat_bookable": 1,
1393 "seat_yupiao": 21
1394 }, {
1395 "seat_price": "219.5",
1396 "seat_name": "商务座",
1397 "seat_bookable": 1,
1398 "seat_yupiao": 7
1399 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1400 }, {
1401 "train_no": "54000D5589A2",
1402 "train_number": "D5589",
1403 "from_station": "上海虹桥",
1404 "to_station": "杭州东",
1405 "from_time": "09:16",
1406 "to_time": "10:32",
1407 "from_station_type": "途经",
1408 "to_station_type": "途经",
1409 "day_diff": "0",
1410 "use_time": "76",
1411 "sale_time": "13:30",
1412 "control_day": 59,
1413 "from_telecode": "AOH",
1414 "to_telecode": "HGH",
1415 "can_web_buy": "Y",
1416 "note": "",
1417 "seats": [{
1418 "seat_price": "49.0",
1419 "seat_name": "二等座",
1420 "seat_bookable": 1,
1421 "seat_yupiao": 503
1422 }, {
1423 "seat_price": "59.0",
1424 "seat_name": "一等座",
1425 "seat_bookable": 1,
1426 "seat_yupiao": 43
1427 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 112}]
1428 }, {
1429 "train_no": "5l000G165510",
1430 "train_number": "G1655",
1431 "from_station": "上海虹桥",
1432 "to_station": "杭州东",
1433 "from_time": "09:23",
1434 "to_time": "10:23",
1435 "from_station_type": "起点",
1436 "to_station_type": "途经",
1437 "day_diff": "0",
1438 "use_time": "60",
1439 "sale_time": "13:30",
1440 "control_day": 59,
1441 "from_telecode": "AOH",
1442 "to_telecode": "HGH",
1443 "can_web_buy": "Y",
1444 "note": "",
1445 "seats": [{
1446 "seat_price": "73.0",
1447 "seat_name": "二等座",
1448 "seat_bookable": 1,
1449 "seat_yupiao": 208
1450 }, {
1451 "seat_price": "117.0",
1452 "seat_name": "一等座",
1453 "seat_bookable": 1,
1454 "seat_yupiao": 78
1455 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 20}]
1456 }, {
1457 "train_no": "5l000G132530",
1458 "train_number": "G1325",
1459 "from_station": "上海虹桥",
1460 "to_station": "杭州东",
1461 "from_time": "09:28",
1462 "to_time": "10:27",
1463 "from_station_type": "途经",
1464 "to_station_type": "途经",
1465 "day_diff": "0",
1466 "use_time": "59",
1467 "sale_time": "13:30",
1468 "control_day": 59,
1469 "from_telecode": "AOH",
1470 "to_telecode": "HGH",
1471 "can_web_buy": "N",
1472 "note": "",
1473 "seats": [{
1474 "seat_price": "73.0",
1475 "seat_name": "二等座",
1476 "seat_bookable": 1,
1477 "seat_yupiao": 2
1478 }, {
1479 "seat_price": "117.0",
1480 "seat_name": "一等座",
1481 "seat_bookable": 0,
1482 "seat_yupiao": 0
1483 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 2}]
1484 }, {
1485 "train_no": "5l000D3205A0",
1486 "train_number": "D3205",
1487 "from_station": "上海虹桥",
1488 "to_station": "杭州东",
1489 "from_time": "09:35",
1490 "to_time": "10:36",
1491 "from_station_type": "起点",
1492 "to_station_type": "途经",
1493 "day_diff": "0",
1494 "use_time": "61",
1495 "sale_time": "13:30",
1496 "control_day": 59,
1497 "from_telecode": "AOH",
1498 "to_telecode": "HGH",
1499 "can_web_buy": "Y",
1500 "note": "",
1501 "seats": [{
1502 "seat_price": "49.0",
1503 "seat_name": "二等座",
1504 "seat_bookable": 1,
1505 "seat_yupiao": 154
1506 }, {
1507 "seat_price": "59.0",
1508 "seat_name": "一等座",
1509 "seat_bookable": 1,
1510 "seat_yupiao": 1
1511 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1512 }, {
1513 "train_no": "55000G735102",
1514 "train_number": "G7351",
1515 "from_station": "上海",
1516 "to_station": "杭州",
1517 "from_time": "09:38",
1518 "to_time": "11:07",
1519 "from_station_type": "起点",
1520 "to_station_type": "终点",
1521 "day_diff": "0",
1522 "use_time": "89",
1523 "sale_time": "14:30",
1524 "control_day": 59,
1525 "from_telecode": "SHH",
1526 "to_telecode": "HZH",
1527 "can_web_buy": "Y",
1528 "note": "",
1529 "seats": [{
1530 "seat_price": "92.5",
1531 "seat_name": "二等座",
1532 "seat_bookable": 1,
1533 "seat_yupiao": 988
1534 }, {
1535 "seat_price": "147.5",
1536 "seat_name": "一等座",
1537 "seat_bookable": 1,
1538 "seat_yupiao": 53
1539 }, {
1540 "seat_price": "278.5",
1541 "seat_name": "商务座",
1542 "seat_bookable": 1,
1543 "seat_yupiao": 20
1544 }, {"seat_price": "92.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1545 }, {
1546 "train_no": "5l000D3107A0",
1547 "train_number": "D3107",
1548 "from_station": "上海虹桥",
1549 "to_station": "杭州东",
1550 "from_time": "09:40",
1551 "to_time": "11:22",
1552 "from_station_type": "起点",
1553 "to_station_type": "途经",
1554 "day_diff": "0",
1555 "use_time": "102",
1556 "sale_time": "13:30",
1557 "control_day": 59,
1558 "from_telecode": "AOH",
1559 "to_telecode": "HGH",
1560 "can_web_buy": "Y",
1561 "note": "",
1562 "seats": [{
1563 "seat_price": "49.0",
1564 "seat_name": "二等座",
1565 "seat_bookable": 1,
1566 "seat_yupiao": 65
1567 }, {
1568 "seat_price": "59.0",
1569 "seat_name": "一等座",
1570 "seat_bookable": 1,
1571 "seat_yupiao": 12
1572 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1573 }, {
1574 "train_no": "54000G735520",
1575 "train_number": "G7355",
1576 "from_station": "上海虹桥",
1577 "to_station": "杭州东",
1578 "from_time": "09:46",
1579 "to_time": "10:40",
1580 "from_station_type": "途经",
1581 "to_station_type": "途经",
1582 "day_diff": "0",
1583 "use_time": "54",
1584 "sale_time": "13:30",
1585 "control_day": 59,
1586 "from_telecode": "AOH",
1587 "to_telecode": "HGH",
1588 "can_web_buy": "Y",
1589 "note": "",
1590 "seats": [{
1591 "seat_price": "73.0",
1592 "seat_name": "二等座",
1593 "seat_bookable": 1,
1594 "seat_yupiao": 222
1595 }, {
1596 "seat_price": "117.0",
1597 "seat_name": "一等座",
1598 "seat_bookable": 0,
1599 "seat_yupiao": 0
1600 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1601 }, {
1602 "train_no": "5l000G134740",
1603 "train_number": "G1347",
1604 "from_station": "上海虹桥",
1605 "to_station": "杭州东",
1606 "from_time": "09:51",
1607 "to_time": "10:50",
1608 "from_station_type": "起点",
1609 "to_station_type": "途经",
1610 "day_diff": "0",
1611 "use_time": "59",
1612 "sale_time": "13:30",
1613 "control_day": 59,
1614 "from_telecode": "AOH",
1615 "to_telecode": "HGH",
1616 "can_web_buy": "Y",
1617 "note": "",
1618 "seats": [{
1619 "seat_price": "73.0",
1620 "seat_name": "二等座",
1621 "seat_bookable": 1,
1622 "seat_yupiao": 547
1623 }, {
1624 "seat_price": "117.0",
1625 "seat_name": "一等座",
1626 "seat_bookable": 1,
1627 "seat_yupiao": 20
1628 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
1629 }, {
1630 "train_no": "5l000G758730",
1631 "train_number": "G7587",
1632 "from_station": "上海虹桥",
1633 "to_station": "杭州东",
1634 "from_time": "09:58",
1635 "to_time": "10:54",
1636 "from_station_type": "途经",
1637 "to_station_type": "途经",
1638 "day_diff": "0",
1639 "use_time": "56",
1640 "sale_time": "13:30",
1641 "control_day": 59,
1642 "from_telecode": "AOH",
1643 "to_telecode": "HGH",
1644 "can_web_buy": "Y",
1645 "note": "",
1646 "seats": [{
1647 "seat_price": "73.0",
1648 "seat_name": "二等座",
1649 "seat_bookable": 0,
1650 "seat_yupiao": 0
1651 }, {
1652 "seat_price": "117.0",
1653 "seat_name": "一等座",
1654 "seat_bookable": 1,
1655 "seat_yupiao": 3
1656 }, {
1657 "seat_price": "219.5",
1658 "seat_name": "商务座",
1659 "seat_bookable": 1,
1660 "seat_yupiao": 5
1661 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1662 }, {
1663 "train_no": "5l000G150120",
1664 "train_number": "G1501",
1665 "from_station": "上海虹桥",
1666 "to_station": "杭州东",
1667 "from_time": "10:03",
1668 "to_time": "10:58",
1669 "from_station_type": "起点",
1670 "to_station_type": "途经",
1671 "day_diff": "0",
1672 "use_time": "55",
1673 "sale_time": "13:30",
1674 "control_day": 59,
1675 "from_telecode": "AOH",
1676 "to_telecode": "HGH",
1677 "can_web_buy": "Y",
1678 "note": "",
1679 "seats": [{
1680 "seat_price": "73.0",
1681 "seat_name": "二等座",
1682 "seat_bookable": 1,
1683 "seat_yupiao": 394
1684 }, {
1685 "seat_price": "117.0",
1686 "seat_name": "一等座",
1687 "seat_bookable": 1,
1688 "seat_yupiao": 20
1689 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 14}]
1690 }, {
1691 "train_no": "5l000G750731",
1692 "train_number": "G7507",
1693 "from_station": "上海虹桥",
1694 "to_station": "杭州东",
1695 "from_time": "10:13",
1696 "to_time": "11:06",
1697 "from_station_type": "起点",
1698 "to_station_type": "途经",
1699 "day_diff": "0",
1700 "use_time": "53",
1701 "sale_time": "13:30",
1702 "control_day": 59,
1703 "from_telecode": "AOH",
1704 "to_telecode": "HGH",
1705 "can_web_buy": "Y",
1706 "note": "",
1707 "seats": [{
1708 "seat_price": "73.0",
1709 "seat_name": "二等座",
1710 "seat_bookable": 1,
1711 "seat_yupiao": 111
1712 }, {
1713 "seat_price": "117.0",
1714 "seat_name": "一等座",
1715 "seat_bookable": 1,
1716 "seat_yupiao": 51
1717 }, {
1718 "seat_price": "219.5",
1719 "seat_name": "商务座",
1720 "seat_bookable": 1,
1721 "seat_yupiao": 20
1722 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1723 }, {
1724 "train_no": "5l000G165720",
1725 "train_number": "G1657",
1726 "from_station": "上海虹桥",
1727 "to_station": "杭州东",
1728 "from_time": "10:18",
1729 "to_time": "11:18",
1730 "from_station_type": "起点",
1731 "to_station_type": "途经",
1732 "day_diff": "0",
1733 "use_time": "60",
1734 "sale_time": "13:30",
1735 "control_day": 59,
1736 "from_telecode": "AOH",
1737 "to_telecode": "HGH",
1738 "can_web_buy": "Y",
1739 "note": "",
1740 "seats": [{
1741 "seat_price": "73.0",
1742 "seat_name": "二等座",
1743 "seat_bookable": 1,
1744 "seat_yupiao": 63
1745 }, {
1746 "seat_price": "117.0",
1747 "seat_name": "一等座",
1748 "seat_bookable": 0,
1749 "seat_yupiao": 0
1750 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 1}]
1751 }, {
1752 "train_no": "330000Z28404",
1753 "train_number": "Z281",
1754 "from_station": "上海南",
1755 "to_station": "杭州",
1756 "from_time": "10:20",
1757 "to_time": "12:23",
1758 "from_station_type": "途经",
1759 "to_station_type": "终点",
1760 "day_diff": "0",
1761 "use_time": "123",
1762 "sale_time": "15:30",
1763 "control_day": 59,
1764 "from_telecode": "SNH",
1765 "to_telecode": "HZH",
1766 "can_web_buy": "Y",
1767 "note": "",
1768 "seats": [{
1769 "seat_price": "28.5",
1770 "seat_name": "硬座",
1771 "seat_bookable": 1,
1772 "seat_yupiao": 86
1773 }, {
1774 "seat_price": "74.5",
1775 "seat_name": "硬卧上",
1776 "seat_bookable": 1,
1777 "seat_yupiao": 259
1778 }, {
1779 "seat_price": "77.5",
1780 "seat_name": "硬卧中",
1781 "seat_bookable": 1,
1782 "seat_yupiao": 259
1783 }, {
1784 "seat_price": "84.5",
1785 "seat_name": "硬卧下",
1786 "seat_bookable": 1,
1787 "seat_yupiao": 259
1788 }, {
1789 "seat_price": "112.5",
1790 "seat_name": "软卧上",
1791 "seat_bookable": 1,
1792 "seat_yupiao": 7
1793 }, {
1794 "seat_price": "127.0",
1795 "seat_name": "软卧下",
1796 "seat_bookable": 1,
1797 "seat_yupiao": 7
1798 }, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 94}]
1799 }, {
1800 "train_no": "5l000G130140",
1801 "train_number": "G1301",
1802 "from_station": "上海虹桥",
1803 "to_station": "杭州东",
1804 "from_time": "10:23",
1805 "to_time": "11:26",
1806 "from_station_type": "起点",
1807 "to_station_type": "途经",
1808 "day_diff": "0",
1809 "use_time": "63",
1810 "sale_time": "13:30",
1811 "control_day": 59,
1812 "from_telecode": "AOH",
1813 "to_telecode": "HGH",
1814 "can_web_buy": "Y",
1815 "note": "",
1816 "seats": [{
1817 "seat_price": "73.0",
1818 "seat_name": "二等座",
1819 "seat_bookable": 1,
1820 "seat_yupiao": 64
1821 }, {
1822 "seat_price": "117.0",
1823 "seat_name": "一等座",
1824 "seat_bookable": 0,
1825 "seat_yupiao": 0
1826 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
1827 }, {
1828 "train_no": "54000D228130",
1829 "train_number": "D2281",
1830 "from_station": "上海虹桥",
1831 "to_station": "杭州东",
1832 "from_time": "10:28",
1833 "to_time": "11:31",
1834 "from_station_type": "途经",
1835 "to_station_type": "途经",
1836 "day_diff": "0",
1837 "use_time": "63",
1838 "sale_time": "13:30",
1839 "control_day": 59,
1840 "from_telecode": "AOH",
1841 "to_telecode": "HGH",
1842 "can_web_buy": "Y",
1843 "note": "",
1844 "seats": [{
1845 "seat_price": "49.0",
1846 "seat_name": "二等座",
1847 "seat_bookable": 1,
1848 "seat_yupiao": 533
1849 }, {
1850 "seat_price": "59.0",
1851 "seat_name": "一等座",
1852 "seat_bookable": 1,
1853 "seat_yupiao": 76
1854 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1855 }, {
1856 "train_no": "5l000G758942",
1857 "train_number": "G7589",
1858 "from_station": "上海虹桥",
1859 "to_station": "杭州东",
1860 "from_time": "10:34",
1861 "to_time": "11:41",
1862 "from_station_type": "途经",
1863 "to_station_type": "途经",
1864 "day_diff": "0",
1865 "use_time": "67",
1866 "sale_time": "13:30",
1867 "control_day": 59,
1868 "from_telecode": "AOH",
1869 "to_telecode": "HGH",
1870 "can_web_buy": "Y",
1871 "note": "",
1872 "seats": [{
1873 "seat_price": "73.0",
1874 "seat_name": "二等座",
1875 "seat_bookable": 1,
1876 "seat_yupiao": 62
1877 }, {
1878 "seat_price": "117.0",
1879 "seat_name": "一等座",
1880 "seat_bookable": 1,
1881 "seat_yupiao": 50
1882 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1883 }, {
1884 "train_no": "54000D313550",
1885 "train_number": "D3135",
1886 "from_station": "上海虹桥",
1887 "to_station": "杭州东",
1888 "from_time": "10:42",
1889 "to_time": "12:04",
1890 "from_station_type": "途经",
1891 "to_station_type": "途经",
1892 "day_diff": "0",
1893 "use_time": "82",
1894 "sale_time": "13:30",
1895 "control_day": 59,
1896 "from_telecode": "AOH",
1897 "to_telecode": "HGH",
1898 "can_web_buy": "Y",
1899 "note": "",
1900 "seats": [{
1901 "seat_price": "49.0",
1902 "seat_name": "二等座",
1903 "seat_bookable": 1,
1904 "seat_yupiao": 2
1905 }, {
1906 "seat_price": "59.0",
1907 "seat_name": "一等座",
1908 "seat_bookable": 1,
1909 "seat_yupiao": 28
1910 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1911 }, {
1912 "train_no": "550000K751A3",
1913 "train_number": "K751",
1914 "from_station": "上海南",
1915 "to_station": "杭州东",
1916 "from_time": "10:42",
1917 "to_time": "13:26",
1918 "from_station_type": "起点",
1919 "to_station_type": "途经",
1920 "day_diff": "0",
1921 "use_time": "164",
1922 "sale_time": "15:30",
1923 "control_day": 59,
1924 "from_telecode": "SNH",
1925 "to_telecode": "HGH",
1926 "can_web_buy": "Y",
1927 "note": "",
1928 "seats": [{
1929 "seat_price": "24.5",
1930 "seat_name": "硬座",
1931 "seat_bookable": 1,
1932 "seat_yupiao": 17
1933 }, {
1934 "seat_price": "70.5",
1935 "seat_name": "硬卧上",
1936 "seat_bookable": 0,
1937 "seat_yupiao": 0
1938 }, {
1939 "seat_price": "73.5",
1940 "seat_name": "硬卧中",
1941 "seat_bookable": 0,
1942 "seat_yupiao": 0
1943 }, {
1944 "seat_price": "80.0",
1945 "seat_name": "硬卧下",
1946 "seat_bookable": 0,
1947 "seat_yupiao": 0
1948 }, {
1949 "seat_price": "108.5",
1950 "seat_name": "软卧上",
1951 "seat_bookable": 0,
1952 "seat_yupiao": 0
1953 }, {
1954 "seat_price": "122.5",
1955 "seat_name": "软卧下",
1956 "seat_bookable": 0,
1957 "seat_yupiao": 0
1958 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
1959 }, {
1960 "train_no": "5l000G134920",
1961 "train_number": "G1349",
1962 "from_station": "上海虹桥",
1963 "to_station": "杭州东",
1964 "from_time": "10:48",
1965 "to_time": "11:48",
1966 "from_station_type": "起点",
1967 "to_station_type": "途经",
1968 "day_diff": "0",
1969 "use_time": "60",
1970 "sale_time": "13:30",
1971 "control_day": 59,
1972 "from_telecode": "AOH",
1973 "to_telecode": "HGH",
1974 "can_web_buy": "Y",
1975 "note": "",
1976 "seats": [{
1977 "seat_price": "73.0",
1978 "seat_name": "二等座",
1979 "seat_bookable": 1,
1980 "seat_yupiao": 668
1981 }, {
1982 "seat_price": "117.0",
1983 "seat_name": "一等座",
1984 "seat_bookable": 1,
1985 "seat_yupiao": 24
1986 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 17}]
1987 }, {
1988 "train_no": "550000T16920",
1989 "train_number": "T169",
1990 "from_station": "上海南",
1991 "to_station": "杭州东",
1992 "from_time": "10:50",
1993 "to_time": "12:40",
1994 "from_station_type": "起点",
1995 "to_station_type": "途经",
1996 "day_diff": "0",
1997 "use_time": "110",
1998 "sale_time": "15:30",
1999 "control_day": 59,
2000 "from_telecode": "SNH",
2001 "to_telecode": "HGH",
2002 "can_web_buy": "Y",
2003 "note": "",
2004 "seats": [{
2005 "seat_price": "24.5",
2006 "seat_name": "硬座",
2007 "seat_bookable": 1,
2008 "seat_yupiao": 355
2009 }, {
2010 "seat_price": "70.5",
2011 "seat_name": "硬卧上",
2012 "seat_bookable": 1,
2013 "seat_yupiao": 337
2014 }, {
2015 "seat_price": "73.5",
2016 "seat_name": "硬卧中",
2017 "seat_bookable": 1,
2018 "seat_yupiao": 337
2019 }, {
2020 "seat_price": "80.0",
2021 "seat_name": "硬卧下",
2022 "seat_bookable": 1,
2023 "seat_yupiao": 337
2024 }, {
2025 "seat_price": "108.5",
2026 "seat_name": "软卧上",
2027 "seat_bookable": 1,
2028 "seat_yupiao": 24
2029 }, {
2030 "seat_price": "122.5",
2031 "seat_name": "软卧下",
2032 "seat_bookable": 1,
2033 "seat_yupiao": 24
2034 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 124}]
2035 }, {
2036 "train_no": "55000K120961",
2037 "train_number": "K1209",
2038 "from_station": "上海南",
2039 "to_station": "杭州",
2040 "from_time": "10:59",
2041 "to_time": "12:55",
2042 "from_station_type": "起点",
2043 "to_station_type": "途经",
2044 "day_diff": "0",
2045 "use_time": "116",
2046 "sale_time": "15:30",
2047 "control_day": 59,
2048 "from_telecode": "SNH",
2049 "to_telecode": "HZH",
2050 "can_web_buy": "Y",
2051 "note": "",
2052 "seats": [{
2053 "seat_price": "28.5",
2054 "seat_name": "硬座",
2055 "seat_bookable": 1,
2056 "seat_yupiao": 122
2057 }, {
2058 "seat_price": "74.5",
2059 "seat_name": "硬卧上",
2060 "seat_bookable": 0,
2061 "seat_yupiao": 0
2062 }, {
2063 "seat_price": "77.5",
2064 "seat_name": "硬卧中",
2065 "seat_bookable": 0,
2066 "seat_yupiao": 0
2067 }, {
2068 "seat_price": "84.5",
2069 "seat_name": "硬卧下",
2070 "seat_bookable": 0,
2071 "seat_yupiao": 0
2072 }, {
2073 "seat_price": "112.5",
2074 "seat_name": "软卧上",
2075 "seat_bookable": 0,
2076 "seat_yupiao": 0
2077 }, {
2078 "seat_price": "127.0",
2079 "seat_name": "软卧下",
2080 "seat_bookable": 0,
2081 "seat_yupiao": 0
2082 }, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 62}]
2083 }, {
2084 "train_no": "5l000G750931",
2085 "train_number": "G7509",
2086 "from_station": "上海虹桥",
2087 "to_station": "杭州东",
2088 "from_time": "10:59",
2089 "to_time": "11:58",
2090 "from_station_type": "起点",
2091 "to_station_type": "途经",
2092 "day_diff": "0",
2093 "use_time": "59",
2094 "sale_time": "13:30",
2095 "control_day": 59,
2096 "from_telecode": "AOH",
2097 "to_telecode": "HGH",
2098 "can_web_buy": "Y",
2099 "note": "",
2100 "seats": [{
2101 "seat_price": "73.0",
2102 "seat_name": "二等座",
2103 "seat_bookable": 1,
2104 "seat_yupiao": 123
2105 }, {
2106 "seat_price": "117.0",
2107 "seat_name": "一等座",
2108 "seat_bookable": 1,
2109 "seat_yupiao": 113
2110 }, {
2111 "seat_price": "219.5",
2112 "seat_name": "商务座",
2113 "seat_bookable": 1,
2114 "seat_yupiao": 26
2115 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2116 }, {
2117 "train_no": "55000K125102",
2118 "train_number": "K1251",
2119 "from_station": "上海南",
2120 "to_station": "杭州",
2121 "from_time": "11:13",
2122 "to_time": "13:30",
2123 "from_station_type": "起点",
2124 "to_station_type": "途经",
2125 "day_diff": "0",
2126 "use_time": "137",
2127 "sale_time": "15:30",
2128 "control_day": 59,
2129 "from_telecode": "SNH",
2130 "to_telecode": "HZH",
2131 "can_web_buy": "Y",
2132 "note": "",
2133 "seats": [{
2134 "seat_price": "28.5",
2135 "seat_name": "硬座",
2136 "seat_bookable": 0,
2137 "seat_yupiao": 0
2138 }, {
2139 "seat_price": "74.5",
2140 "seat_name": "硬卧上",
2141 "seat_bookable": 1,
2142 "seat_yupiao": 6
2143 }, {
2144 "seat_price": "77.5",
2145 "seat_name": "硬卧中",
2146 "seat_bookable": 1,
2147 "seat_yupiao": 6
2148 }, {
2149 "seat_price": "84.5",
2150 "seat_name": "硬卧下",
2151 "seat_bookable": 1,
2152 "seat_yupiao": 6
2153 }, {
2154 "seat_price": "112.5",
2155 "seat_name": "软卧上",
2156 "seat_bookable": 0,
2157 "seat_yupiao": 0
2158 }, {
2159 "seat_price": "127.0",
2160 "seat_name": "软卧下",
2161 "seat_bookable": 0,
2162 "seat_yupiao": 0
2163 }, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2164 }, {
2165 "train_no": "5l000D543141",
2166 "train_number": "D5431",
2167 "from_station": "上海虹桥",
2168 "to_station": "杭州东",
2169 "from_time": "11:19",
2170 "to_time": "12:34",
2171 "from_station_type": "途经",
2172 "to_station_type": "途经",
2173 "day_diff": "0",
2174 "use_time": "75",
2175 "sale_time": "13:30",
2176 "control_day": 59,
2177 "from_telecode": "AOH",
2178 "to_telecode": "HGH",
2179 "can_web_buy": "Y",
2180 "note": "",
2181 "seats": [{
2182 "seat_price": "49.0",
2183 "seat_name": "二等座",
2184 "seat_bookable": 1,
2185 "seat_yupiao": 35
2186 }, {
2187 "seat_price": "59.0",
2188 "seat_name": "一等座",
2189 "seat_bookable": 1,
2190 "seat_yupiao": 46
2191 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 16}]
2192 }, {
2193 "train_no": "5l000D228350",
2194 "train_number": "D2283",
2195 "from_station": "上海虹桥",
2196 "to_station": "杭州东",
2197 "from_time": "11:24",
2198 "to_time": "12:26",
2199 "from_station_type": "起点",
2200 "to_station_type": "途经",
2201 "day_diff": "0",
2202 "use_time": "62",
2203 "sale_time": "13:30",
2204 "control_day": 59,
2205 "from_telecode": "AOH",
2206 "to_telecode": "HGH",
2207 "can_web_buy": "N",
2208 "note": "",
2209 "seats": [{
2210 "seat_price": "49.0",
2211 "seat_name": "二等座",
2212 "seat_bookable": 1,
2213 "seat_yupiao": 31
2214 }, {
2215 "seat_price": "59.0",
2216 "seat_name": "一等座",
2217 "seat_bookable": 0,
2218 "seat_yupiao": 0
2219 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2220 }, {
2221 "train_no": "5500000T7780",
2222 "train_number": "T77",
2223 "from_station": "上海南",
2224 "to_station": "杭州东",
2225 "from_time": "11:27",
2226 "to_time": "13:13",
2227 "from_station_type": "起点",
2228 "to_station_type": "途经",
2229 "day_diff": "0",
2230 "use_time": "106",
2231 "sale_time": "15:30",
2232 "control_day": 59,
2233 "from_telecode": "SNH",
2234 "to_telecode": "HGH",
2235 "can_web_buy": "Y",
2236 "note": "",
2237 "seats": [{
2238 "seat_price": "70.5",
2239 "seat_name": "硬卧上",
2240 "seat_bookable": 1,
2241 "seat_yupiao": 18
2242 }, {
2243 "seat_price": "73.5",
2244 "seat_name": "硬卧中",
2245 "seat_bookable": 1,
2246 "seat_yupiao": 18
2247 }, {
2248 "seat_price": "80.0",
2249 "seat_name": "硬卧下",
2250 "seat_bookable": 1,
2251 "seat_yupiao": 18
2252 }, {
2253 "seat_price": "108.5",
2254 "seat_name": "软卧上",
2255 "seat_bookable": 0,
2256 "seat_yupiao": 0
2257 }, {"seat_price": "122.5", "seat_name": "软卧下", "seat_bookable": 0, "seat_yupiao": 0}]
2258 }, {
2259 "train_no": "5l000G753724",
2260 "train_number": "G7537",
2261 "from_station": "上海虹桥",
2262 "to_station": "杭州东",
2263 "from_time": "11:29",
2264 "to_time": "12:30",
2265 "from_station_type": "起点",
2266 "to_station_type": "途经",
2267 "day_diff": "0",
2268 "use_time": "61",
2269 "sale_time": "13:30",
2270 "control_day": 59,
2271 "from_telecode": "AOH",
2272 "to_telecode": "HGH",
2273 "can_web_buy": "Y",
2274 "note": "",
2275 "seats": [{
2276 "seat_price": "73.0",
2277 "seat_name": "二等座",
2278 "seat_bookable": 1,
2279 "seat_yupiao": 25
2280 }, {
2281 "seat_price": "117.0",
2282 "seat_name": "一等座",
2283 "seat_bookable": 1,
2284 "seat_yupiao": 67
2285 }, {
2286 "seat_price": "219.5",
2287 "seat_name": "商务座",
2288 "seat_bookable": 0,
2289 "seat_yupiao": 0
2290 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2291 }, {
2292 "train_no": "5l000G7303A1",
2293 "train_number": "G7303",
2294 "from_station": "上海虹桥",
2295 "to_station": "杭州",
2296 "from_time": "11:34",
2297 "to_time": "12:49",
2298 "from_station_type": "起点",
2299 "to_station_type": "终点",
2300 "day_diff": "0",
2301 "use_time": "75",
2302 "sale_time": "13:30",
2303 "control_day": 59,
2304 "from_telecode": "AOH",
2305 "to_telecode": "HZH",
2306 "can_web_buy": "Y",
2307 "note": "",
2308 "seats": [{
2309 "seat_price": "77.5",
2310 "seat_name": "二等座",
2311 "seat_bookable": 1,
2312 "seat_yupiao": 458
2313 }, {
2314 "seat_price": "123.5",
2315 "seat_name": "一等座",
2316 "seat_bookable": 1,
2317 "seat_yupiao": 26
2318 }, {
2319 "seat_price": "233.5",
2320 "seat_name": "商务座",
2321 "seat_bookable": 1,
2322 "seat_yupiao": 10
2323 }, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2324 }, {
2325 "train_no": "5l000G132720",
2326 "train_number": "G1327",
2327 "from_station": "上海虹桥",
2328 "to_station": "杭州东",
2329 "from_time": "11:39",
2330 "to_time": "12:39",
2331 "from_station_type": "起点",
2332 "to_station_type": "途经",
2333 "day_diff": "0",
2334 "use_time": "60",
2335 "sale_time": "13:30",
2336 "control_day": 59,
2337 "from_telecode": "AOH",
2338 "to_telecode": "HGH",
2339 "can_web_buy": "Y",
2340 "note": "",
2341 "seats": [{
2342 "seat_price": "73.0",
2343 "seat_name": "二等座",
2344 "seat_bookable": 1,
2345 "seat_yupiao": 63
2346 }, {
2347 "seat_price": "117.0",
2348 "seat_name": "一等座",
2349 "seat_bookable": 0,
2350 "seat_yupiao": 0
2351 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
2352 }, {
2353 "train_no": "550000T21140",
2354 "train_number": "T211",
2355 "from_station": "上海南",
2356 "to_station": "杭州东",
2357 "from_time": "11:41",
2358 "to_time": "13:39",
2359 "from_station_type": "起点",
2360 "to_station_type": "途经",
2361 "day_diff": "0",
2362 "use_time": "118",
2363 "sale_time": "15:30",
2364 "control_day": 59,
2365 "from_telecode": "SNH",
2366 "to_telecode": "HGH",
2367 "can_web_buy": "Y",
2368 "note": "",
2369 "seats": [{
2370 "seat_price": "24.5",
2371 "seat_name": "硬座",
2372 "seat_bookable": 1,
2373 "seat_yupiao": 309
2374 }, {
2375 "seat_price": "70.5",
2376 "seat_name": "硬卧上",
2377 "seat_bookable": 1,
2378 "seat_yupiao": 407
2379 }, {
2380 "seat_price": "73.5",
2381 "seat_name": "硬卧中",
2382 "seat_bookable": 1,
2383 "seat_yupiao": 407
2384 }, {
2385 "seat_price": "80.0",
2386 "seat_name": "硬卧下",
2387 "seat_bookable": 1,
2388 "seat_yupiao": 407
2389 }, {
2390 "seat_price": "108.5",
2391 "seat_name": "软卧上",
2392 "seat_bookable": 1,
2393 "seat_yupiao": 16
2394 }, {
2395 "seat_price": "122.5",
2396 "seat_name": "软卧下",
2397 "seat_bookable": 1,
2398 "seat_yupiao": 16
2399 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 168}]
2400 }, {
2401 "train_no": "5l000D3207A3",
2402 "train_number": "D3207",
2403 "from_station": "上海虹桥",
2404 "to_station": "杭州东",
2405 "from_time": "11:48",
2406 "to_time": "12:59",
2407 "from_station_type": "起点",
2408 "to_station_type": "途经",
2409 "day_diff": "0",
2410 "use_time": "71",
2411 "sale_time": "13:30",
2412 "control_day": 59,
2413 "from_telecode": "AOH",
2414 "to_telecode": "HGH",
2415 "can_web_buy": "Y",
2416 "note": "",
2417 "seats": [{
2418 "seat_price": "49.0",
2419 "seat_name": "二等座",
2420 "seat_bookable": 1,
2421 "seat_yupiao": 476
2422 }, {
2423 "seat_price": "59.0",
2424 "seat_name": "一等座",
2425 "seat_bookable": 1,
2426 "seat_yupiao": 20
2427 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2428 }, {
2429 "train_no": "5l000G751107",
2430 "train_number": "G7511",
2431 "from_station": "上海虹桥",
2432 "to_station": "杭州东",
2433 "from_time": "11:53",
2434 "to_time": "12:54",
2435 "from_station_type": "起点",
2436 "to_station_type": "途经",
2437 "day_diff": "0",
2438 "use_time": "61",
2439 "sale_time": "13:30",
2440 "control_day": 59,
2441 "from_telecode": "AOH",
2442 "to_telecode": "HGH",
2443 "can_web_buy": "Y",
2444 "note": "",
2445 "seats": [{
2446 "seat_price": "73.0",
2447 "seat_name": "二等座",
2448 "seat_bookable": 1,
2449 "seat_yupiao": 815
2450 }, {
2451 "seat_price": "117.0",
2452 "seat_name": "一等座",
2453 "seat_bookable": 1,
2454 "seat_yupiao": 49
2455 }, {
2456 "seat_price": "219.5",
2457 "seat_name": "商务座",
2458 "seat_bookable": 1,
2459 "seat_yupiao": 20
2460 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2461 }, {
2462 "train_no": "550000K75960",
2463 "train_number": "K759",
2464 "from_station": "上海南",
2465 "to_station": "杭州",
2466 "from_time": "12:01",
2467 "to_time": "14:26",
2468 "from_station_type": "起点",
2469 "to_station_type": "途经",
2470 "day_diff": "0",
2471 "use_time": "145",
2472 "sale_time": "15:30",
2473 "control_day": 59,
2474 "from_telecode": "SNH",
2475 "to_telecode": "HZH",
2476 "can_web_buy": "N",
2477 "note": "",
2478 "seats": [{
2479 "seat_price": "28.5",
2480 "seat_name": "硬座",
2481 "seat_bookable": 0,
2482 "seat_yupiao": 0
2483 }, {
2484 "seat_price": "74.5",
2485 "seat_name": "硬卧上",
2486 "seat_bookable": 0,
2487 "seat_yupiao": 0
2488 }, {
2489 "seat_price": "77.5",
2490 "seat_name": "硬卧中",
2491 "seat_bookable": 0,
2492 "seat_yupiao": 0
2493 }, {
2494 "seat_price": "84.5",
2495 "seat_name": "硬卧下",
2496 "seat_bookable": 0,
2497 "seat_yupiao": 0
2498 }, {
2499 "seat_price": "112.5",
2500 "seat_name": "软卧上",
2501 "seat_bookable": 0,
2502 "seat_yupiao": 0
2503 }, {
2504 "seat_price": "127.0",
2505 "seat_name": "软卧下",
2506 "seat_bookable": 0,
2507 "seat_yupiao": 0
2508 }, {"seat_price": "28.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2509 }, {
2510 "train_no": "59000G759400",
2511 "train_number": "G7591",
2512 "from_station": "上海虹桥",
2513 "to_station": "杭州东",
2514 "from_time": "12:02",
2515 "to_time": "13:03",
2516 "from_station_type": "途经",
2517 "to_station_type": "途经",
2518 "day_diff": "0",
2519 "use_time": "61",
2520 "sale_time": "13:30",
2521 "control_day": 59,
2522 "from_telecode": "AOH",
2523 "to_telecode": "HGH",
2524 "can_web_buy": "Y",
2525 "note": "",
2526 "seats": [{
2527 "seat_price": "73.0",
2528 "seat_name": "二等座",
2529 "seat_bookable": 1,
2530 "seat_yupiao": 120
2531 }, {
2532 "seat_price": "117.0",
2533 "seat_name": "一等座",
2534 "seat_bookable": 1,
2535 "seat_yupiao": 52
2536 }, {
2537 "seat_price": "219.5",
2538 "seat_name": "商务座",
2539 "seat_bookable": 1,
2540 "seat_yupiao": 20
2541 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2542 }, {
2543 "train_no": "5l000D329100",
2544 "train_number": "D3291",
2545 "from_station": "上海虹桥",
2546 "to_station": "杭州东",
2547 "from_time": "12:15",
2548 "to_time": "13:16",
2549 "from_station_type": "起点",
2550 "to_station_type": "途经",
2551 "day_diff": "0",
2552 "use_time": "61",
2553 "sale_time": "15:00",
2554 "control_day": 59,
2555 "from_telecode": "AOH",
2556 "to_telecode": "HGH",
2557 "can_web_buy": "Y",
2558 "note": "",
2559 "seats": [{
2560 "seat_price": "49.0",
2561 "seat_name": "二等座",
2562 "seat_bookable": 1,
2563 "seat_yupiao": 63
2564 }, {
2565 "seat_price": "59.0",
2566 "seat_name": "一等座",
2567 "seat_bookable": 1,
2568 "seat_yupiao": 9
2569 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2570 }, {
2571 "train_no": "5l000G138711",
2572 "train_number": "G1387",
2573 "from_station": "上海虹桥",
2574 "to_station": "杭州东",
2575 "from_time": "12:20",
2576 "to_time": "13:21",
2577 "from_station_type": "起点",
2578 "to_station_type": "途经",
2579 "day_diff": "0",
2580 "use_time": "61",
2581 "sale_time": "13:30",
2582 "control_day": 59,
2583 "from_telecode": "AOH",
2584 "to_telecode": "HGH",
2585 "can_web_buy": "Y",
2586 "note": "",
2587 "seats": [{
2588 "seat_price": "73.0",
2589 "seat_name": "二等座",
2590 "seat_bookable": 1,
2591 "seat_yupiao": 103
2592 }, {
2593 "seat_price": "117.0",
2594 "seat_name": "一等座",
2595 "seat_bookable": 1,
2596 "seat_yupiao": 14
2597 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 1}]
2598 }, {
2599 "train_no": "5l000G163320",
2600 "train_number": "G1633",
2601 "from_station": "上海虹桥",
2602 "to_station": "杭州东",
2603 "from_time": "12:25",
2604 "to_time": "13:25",
2605 "from_station_type": "起点",
2606 "to_station_type": "途经",
2607 "day_diff": "0",
2608 "use_time": "60",
2609 "sale_time": "13:30",
2610 "control_day": 59,
2611 "from_telecode": "AOH",
2612 "to_telecode": "HGH",
2613 "can_web_buy": "Y",
2614 "note": "",
2615 "seats": [{
2616 "seat_price": "73.0",
2617 "seat_name": "二等座",
2618 "seat_bookable": 1,
2619 "seat_yupiao": 113
2620 }, {
2621 "seat_price": "117.0",
2622 "seat_name": "一等座",
2623 "seat_bookable": 0,
2624 "seat_yupiao": 0
2625 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
2626 }, {
2627 "train_no": "5l000G733570",
2628 "train_number": "G7335",
2629 "from_station": "上海虹桥",
2630 "to_station": "杭州东",
2631 "from_time": "12:30",
2632 "to_time": "13:29",
2633 "from_station_type": "起点",
2634 "to_station_type": "途经",
2635 "day_diff": "0",
2636 "use_time": "59",
2637 "sale_time": "13:30",
2638 "control_day": 59,
2639 "from_telecode": "AOH",
2640 "to_telecode": "HGH",
2641 "can_web_buy": "Y",
2642 "note": "",
2643 "seats": [{
2644 "seat_price": "73.0",
2645 "seat_name": "二等座",
2646 "seat_bookable": 1,
2647 "seat_yupiao": 545
2648 }, {
2649 "seat_price": "117.0",
2650 "seat_name": "一等座",
2651 "seat_bookable": 1,
2652 "seat_yupiao": 37
2653 }, {
2654 "seat_price": "219.5",
2655 "seat_name": "商务座",
2656 "seat_bookable": 1,
2657 "seat_yupiao": 17
2658 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2659 }, {
2660 "train_no": "5l000G137331",
2661 "train_number": "G1373",
2662 "from_station": "上海虹桥",
2663 "to_station": "杭州东",
2664 "from_time": "12:40",
2665 "to_time": "13:40",
2666 "from_station_type": "起点",
2667 "to_station_type": "途经",
2668 "day_diff": "0",
2669 "use_time": "60",
2670 "sale_time": "13:30",
2671 "control_day": 59,
2672 "from_telecode": "AOH",
2673 "to_telecode": "HGH",
2674 "can_web_buy": "N",
2675 "note": "",
2676 "seats": [{
2677 "seat_price": "73.0",
2678 "seat_name": "二等座",
2679 "seat_bookable": 1,
2680 "seat_yupiao": 72
2681 }, {
2682 "seat_price": "117.0",
2683 "seat_name": "一等座",
2684 "seat_bookable": 1,
2685 "seat_yupiao": 21
2686 }, {"seat_price": "132.0", "seat_name": "特等座", "seat_bookable": 0, "seat_yupiao": 0}]
2687 }, {
2688 "train_no": "5i000G737813",
2689 "train_number": "G7375",
2690 "from_station": "上海虹桥",
2691 "to_station": "杭州东",
2692 "from_time": "12:53",
2693 "to_time": "13:53",
2694 "from_station_type": "途经",
2695 "to_station_type": "途经",
2696 "day_diff": "0",
2697 "use_time": "60",
2698 "sale_time": "13:30",
2699 "control_day": 59,
2700 "from_telecode": "AOH",
2701 "to_telecode": "HGH",
2702 "can_web_buy": "Y",
2703 "note": "",
2704 "seats": [{
2705 "seat_price": "73.0",
2706 "seat_name": "二等座",
2707 "seat_bookable": 1,
2708 "seat_yupiao": 287
2709 }, {
2710 "seat_price": "117.0",
2711 "seat_name": "一等座",
2712 "seat_bookable": 1,
2713 "seat_yupiao": 49
2714 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2715 }, {
2716 "train_no": "5l000G165920",
2717 "train_number": "G1659",
2718 "from_station": "上海虹桥",
2719 "to_station": "杭州东",
2720 "from_time": "12:58",
2721 "to_time": "13:58",
2722 "from_station_type": "起点",
2723 "to_station_type": "途经",
2724 "day_diff": "0",
2725 "use_time": "60",
2726 "sale_time": "13:30",
2727 "control_day": 59,
2728 "from_telecode": "AOH",
2729 "to_telecode": "HGH",
2730 "can_web_buy": "Y",
2731 "note": "",
2732 "seats": [{
2733 "seat_price": "73.0",
2734 "seat_name": "二等座",
2735 "seat_bookable": 1,
2736 "seat_yupiao": 80
2737 }, {
2738 "seat_price": "117.0",
2739 "seat_name": "一等座",
2740 "seat_bookable": 1,
2741 "seat_yupiao": 9
2742 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 7}]
2743 }, {
2744 "train_no": "5i000G759807",
2745 "train_number": "G7595",
2746 "from_station": "上海虹桥",
2747 "to_station": "杭州东",
2748 "from_time": "13:05",
2749 "to_time": "14:08",
2750 "from_station_type": "途经",
2751 "to_station_type": "途经",
2752 "day_diff": "0",
2753 "use_time": "63",
2754 "sale_time": "13:30",
2755 "control_day": 59,
2756 "from_telecode": "AOH",
2757 "to_telecode": "HGH",
2758 "can_web_buy": "Y",
2759 "note": "",
2760 "seats": [{
2761 "seat_price": "73.0",
2762 "seat_name": "二等座",
2763 "seat_bookable": 1,
2764 "seat_yupiao": 3
2765 }, {
2766 "seat_price": "117.0",
2767 "seat_name": "一等座",
2768 "seat_bookable": 1,
2769 "seat_yupiao": 2
2770 }, {
2771 "seat_price": "219.5",
2772 "seat_name": "商务座",
2773 "seat_bookable": 0,
2774 "seat_yupiao": 0
2775 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2776 }, {
2777 "train_no": "5l000G138920",
2778 "train_number": "G1389",
2779 "from_station": "上海虹桥",
2780 "to_station": "杭州东",
2781 "from_time": "13:11",
2782 "to_time": "14:03",
2783 "from_station_type": "起点",
2784 "to_station_type": "途经",
2785 "day_diff": "0",
2786 "use_time": "52",
2787 "sale_time": "13:30",
2788 "control_day": 59,
2789 "from_telecode": "AOH",
2790 "to_telecode": "HGH",
2791 "can_web_buy": "Y",
2792 "note": "",
2793 "seats": [{
2794 "seat_price": "73.0",
2795 "seat_name": "二等座",
2796 "seat_bookable": 1,
2797 "seat_yupiao": 16
2798 }, {
2799 "seat_price": "117.0",
2800 "seat_name": "一等座",
2801 "seat_bookable": 0,
2802 "seat_yupiao": 0
2803 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 1}]
2804 }, {
2805 "train_no": "5l000D314110",
2806 "train_number": "D3141",
2807 "from_station": "上海虹桥",
2808 "to_station": "杭州东",
2809 "from_time": "13:19",
2810 "to_time": "14:21",
2811 "from_station_type": "起点",
2812 "to_station_type": "途经",
2813 "day_diff": "0",
2814 "use_time": "62",
2815 "sale_time": "13:30",
2816 "control_day": 59,
2817 "from_telecode": "AOH",
2818 "to_telecode": "HGH",
2819 "can_web_buy": "N",
2820 "note": "",
2821 "seats": [{
2822 "seat_price": "49.0",
2823 "seat_name": "二等座",
2824 "seat_bookable": 1,
2825 "seat_yupiao": 93
2826 }, {
2827 "seat_price": "59.0",
2828 "seat_name": "一等座",
2829 "seat_bookable": 1,
2830 "seat_yupiao": 18
2831 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2832 }, {
2833 "train_no": "550000T10140",
2834 "train_number": "T101",
2835 "from_station": "上海南",
2836 "to_station": "杭州东",
2837 "from_time": "13:20",
2838 "to_time": "15:11",
2839 "from_station_type": "起点",
2840 "to_station_type": "途经",
2841 "day_diff": "0",
2842 "use_time": "111",
2843 "sale_time": "15:30",
2844 "control_day": 59,
2845 "from_telecode": "SNH",
2846 "to_telecode": "HGH",
2847 "can_web_buy": "Y",
2848 "note": "",
2849 "seats": [{
2850 "seat_price": "24.5",
2851 "seat_name": "硬座",
2852 "seat_bookable": 1,
2853 "seat_yupiao": 116
2854 }, {
2855 "seat_price": "70.5",
2856 "seat_name": "硬卧上",
2857 "seat_bookable": 1,
2858 "seat_yupiao": 204
2859 }, {
2860 "seat_price": "73.5",
2861 "seat_name": "硬卧中",
2862 "seat_bookable": 1,
2863 "seat_yupiao": 204
2864 }, {
2865 "seat_price": "80.0",
2866 "seat_name": "硬卧下",
2867 "seat_bookable": 1,
2868 "seat_yupiao": 204
2869 }, {
2870 "seat_price": "108.5",
2871 "seat_name": "软卧上",
2872 "seat_bookable": 0,
2873 "seat_yupiao": 0
2874 }, {
2875 "seat_price": "122.5",
2876 "seat_name": "软卧下",
2877 "seat_bookable": 0,
2878 "seat_yupiao": 0
2879 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2880 }, {
2881 "train_no": "5l000G135320",
2882 "train_number": "G1353",
2883 "from_station": "上海虹桥",
2884 "to_station": "杭州东",
2885 "from_time": "13:25",
2886 "to_time": "14:26",
2887 "from_station_type": "起点",
2888 "to_station_type": "途经",
2889 "day_diff": "0",
2890 "use_time": "61",
2891 "sale_time": "13:30",
2892 "control_day": 59,
2893 "from_telecode": "AOH",
2894 "to_telecode": "HGH",
2895 "can_web_buy": "Y",
2896 "note": "",
2897 "seats": [{
2898 "seat_price": "73.0",
2899 "seat_name": "二等座",
2900 "seat_bookable": 1,
2901 "seat_yupiao": 211
2902 }, {
2903 "seat_price": "117.0",
2904 "seat_name": "一等座",
2905 "seat_bookable": 0,
2906 "seat_yupiao": 0
2907 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
2908 }, {
2909 "train_no": "5l000G730583",
2910 "train_number": "G7305",
2911 "from_station": "上海虹桥",
2912 "to_station": "杭州",
2913 "from_time": "13:30",
2914 "to_time": "14:36",
2915 "from_station_type": "起点",
2916 "to_station_type": "终点",
2917 "day_diff": "0",
2918 "use_time": "66",
2919 "sale_time": "13:30",
2920 "control_day": 59,
2921 "from_telecode": "AOH",
2922 "to_telecode": "HZH",
2923 "can_web_buy": "Y",
2924 "note": "",
2925 "seats": [{
2926 "seat_price": "77.5",
2927 "seat_name": "二等座",
2928 "seat_bookable": 1,
2929 "seat_yupiao": 11
2930 }, {
2931 "seat_price": "123.5",
2932 "seat_name": "一等座",
2933 "seat_bookable": 1,
2934 "seat_yupiao": 52
2935 }, {
2936 "seat_price": "233.5",
2937 "seat_name": "商务座",
2938 "seat_bookable": 1,
2939 "seat_yupiao": 17
2940 }, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
2941 }, {
2942 "train_no": "550000K123C0",
2943 "train_number": "K123",
2944 "from_station": "上海南",
2945 "to_station": "杭州东",
2946 "from_time": "13:40",
2947 "to_time": "15:54",
2948 "from_station_type": "起点",
2949 "to_station_type": "途经",
2950 "day_diff": "0",
2951 "use_time": "134",
2952 "sale_time": "15:30",
2953 "control_day": 59,
2954 "from_telecode": "SNH",
2955 "to_telecode": "HGH",
2956 "can_web_buy": "Y",
2957 "note": "",
2958 "seats": [{
2959 "seat_price": "24.5",
2960 "seat_name": "硬座",
2961 "seat_bookable": 1,
2962 "seat_yupiao": 917
2963 }, {
2964 "seat_price": "70.5",
2965 "seat_name": "硬卧上",
2966 "seat_bookable": 1,
2967 "seat_yupiao": 5
2968 }, {
2969 "seat_price": "73.5",
2970 "seat_name": "硬卧中",
2971 "seat_bookable": 1,
2972 "seat_yupiao": 5
2973 }, {
2974 "seat_price": "80.0",
2975 "seat_name": "硬卧下",
2976 "seat_bookable": 1,
2977 "seat_yupiao": 5
2978 }, {
2979 "seat_price": "108.5",
2980 "seat_name": "软卧上",
2981 "seat_bookable": 1,
2982 "seat_yupiao": 4
2983 }, {
2984 "seat_price": "122.5",
2985 "seat_name": "软卧下",
2986 "seat_bookable": 1,
2987 "seat_yupiao": 4
2988 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 488}]
2989 }, {
2990 "train_no": "5l000G132930",
2991 "train_number": "G1329",
2992 "from_station": "上海虹桥",
2993 "to_station": "杭州东",
2994 "from_time": "13:43",
2995 "to_time": "14:42",
2996 "from_station_type": "起点",
2997 "to_station_type": "途经",
2998 "day_diff": "0",
2999 "use_time": "59",
3000 "sale_time": "13:30",
3001 "control_day": 59,
3002 "from_telecode": "AOH",
3003 "to_telecode": "HGH",
3004 "can_web_buy": "Y",
3005 "note": "",
3006 "seats": [{
3007 "seat_price": "73.0",
3008 "seat_name": "二等座",
3009 "seat_bookable": 1,
3010 "seat_yupiao": 417
3011 }, {
3012 "seat_price": "117.0",
3013 "seat_name": "一等座",
3014 "seat_bookable": 1,
3015 "seat_yupiao": 20
3016 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 9}]
3017 }, {
3018 "train_no": "5l000G135511",
3019 "train_number": "G1355",
3020 "from_station": "上海虹桥",
3021 "to_station": "杭州东",
3022 "from_time": "13:48",
3023 "to_time": "14:47",
3024 "from_station_type": "起点",
3025 "to_station_type": "途经",
3026 "day_diff": "0",
3027 "use_time": "59",
3028 "sale_time": "13:30",
3029 "control_day": 59,
3030 "from_telecode": "AOH",
3031 "to_telecode": "HGH",
3032 "can_web_buy": "N",
3033 "note": "",
3034 "seats": [{
3035 "seat_price": "73.0",
3036 "seat_name": "二等座",
3037 "seat_bookable": 1,
3038 "seat_yupiao": 198
3039 }, {
3040 "seat_price": "117.0",
3041 "seat_name": "一等座",
3042 "seat_bookable": 1,
3043 "seat_yupiao": 27
3044 }, {"seat_price": "132.0", "seat_name": "特等座", "seat_bookable": 0, "seat_yupiao": 0}]
3045 }, {
3046 "train_no": "5l000G751550",
3047 "train_number": "G7515",
3048 "from_station": "上海虹桥",
3049 "to_station": "杭州东",
3050 "from_time": "14:00",
3051 "to_time": "14:59",
3052 "from_station_type": "起点",
3053 "to_station_type": "途经",
3054 "day_diff": "0",
3055 "use_time": "59",
3056 "sale_time": "13:30",
3057 "control_day": 59,
3058 "from_telecode": "AOH",
3059 "to_telecode": "HGH",
3060 "can_web_buy": "Y",
3061 "note": "",
3062 "seats": [{
3063 "seat_price": "73.0",
3064 "seat_name": "二等座",
3065 "seat_bookable": 1,
3066 "seat_yupiao": 837
3067 }, {
3068 "seat_price": "117.0",
3069 "seat_name": "一等座",
3070 "seat_bookable": 1,
3071 "seat_yupiao": 112
3072 }, {
3073 "seat_price": "219.5",
3074 "seat_name": "商务座",
3075 "seat_bookable": 1,
3076 "seat_yupiao": 26
3077 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3078 }, {
3079 "train_no": "54000G735300",
3080 "train_number": "G7353",
3081 "from_station": "上海虹桥",
3082 "to_station": "杭州",
3083 "from_time": "14:05",
3084 "to_time": "15:14",
3085 "from_station_type": "途经",
3086 "to_station_type": "终点",
3087 "day_diff": "0",
3088 "use_time": "69",
3089 "sale_time": "13:30",
3090 "control_day": 59,
3091 "from_telecode": "AOH",
3092 "to_telecode": "HZH",
3093 "can_web_buy": "Y",
3094 "note": "",
3095 "seats": [{
3096 "seat_price": "77.5",
3097 "seat_name": "二等座",
3098 "seat_bookable": 1,
3099 "seat_yupiao": 82
3100 }, {
3101 "seat_price": "123.5",
3102 "seat_name": "一等座",
3103 "seat_bookable": 1,
3104 "seat_yupiao": 26
3105 }, {
3106 "seat_price": "233.5",
3107 "seat_name": "商务座",
3108 "seat_bookable": 1,
3109 "seat_yupiao": 5
3110 }, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3111 }, {
3112 "train_no": "5l000G130340",
3113 "train_number": "G1303",
3114 "from_station": "上海虹桥",
3115 "to_station": "杭州东",
3116 "from_time": "14:10",
3117 "to_time": "15:04",
3118 "from_station_type": "起点",
3119 "to_station_type": "途经",
3120 "day_diff": "0",
3121 "use_time": "54",
3122 "sale_time": "13:30",
3123 "control_day": 59,
3124 "from_telecode": "AOH",
3125 "to_telecode": "HGH",
3126 "can_web_buy": "Y",
3127 "note": "",
3128 "seats": [{
3129 "seat_price": "73.0",
3130 "seat_name": "二等座",
3131 "seat_bookable": 1,
3132 "seat_yupiao": 333
3133 }, {
3134 "seat_price": "117.0",
3135 "seat_name": "一等座",
3136 "seat_bookable": 1,
3137 "seat_yupiao": 18
3138 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 9}]
3139 }, {
3140 "train_no": "5l000G753960",
3141 "train_number": "G7539",
3142 "from_station": "上海虹桥",
3143 "to_station": "杭州东",
3144 "from_time": "14:15",
3145 "to_time": "15:14",
3146 "from_station_type": "起点",
3147 "to_station_type": "途经",
3148 "day_diff": "0",
3149 "use_time": "59",
3150 "sale_time": "13:30",
3151 "control_day": 59,
3152 "from_telecode": "AOH",
3153 "to_telecode": "HGH",
3154 "can_web_buy": "Y",
3155 "note": "",
3156 "seats": [{
3157 "seat_price": "73.0",
3158 "seat_name": "二等座",
3159 "seat_bookable": 1,
3160 "seat_yupiao": 186
3161 }, {
3162 "seat_price": "117.0",
3163 "seat_name": "一等座",
3164 "seat_bookable": 1,
3165 "seat_yupiao": 3
3166 }, {
3167 "seat_price": "219.5",
3168 "seat_name": "商务座",
3169 "seat_bookable": 1,
3170 "seat_yupiao": 13
3171 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3172 }, {
3173 "train_no": "550000K57570",
3174 "train_number": "K575",
3175 "from_station": "上海南",
3176 "to_station": "杭州东",
3177 "from_time": "14:18",
3178 "to_time": "16:26",
3179 "from_station_type": "起点",
3180 "to_station_type": "途经",
3181 "day_diff": "0",
3182 "use_time": "128",
3183 "sale_time": "15:30",
3184 "control_day": 59,
3185 "from_telecode": "SNH",
3186 "to_telecode": "HGH",
3187 "can_web_buy": "Y",
3188 "note": "",
3189 "seats": [{
3190 "seat_price": "24.5",
3191 "seat_name": "硬座",
3192 "seat_bookable": 0,
3193 "seat_yupiao": 0
3194 }, {
3195 "seat_price": "70.5",
3196 "seat_name": "硬卧上",
3197 "seat_bookable": 0,
3198 "seat_yupiao": 0
3199 }, {
3200 "seat_price": "73.5",
3201 "seat_name": "硬卧中",
3202 "seat_bookable": 0,
3203 "seat_yupiao": 0
3204 }, {
3205 "seat_price": "80.0",
3206 "seat_name": "硬卧下",
3207 "seat_bookable": 0,
3208 "seat_yupiao": 0
3209 }, {
3210 "seat_price": "108.5",
3211 "seat_name": "软卧上",
3212 "seat_bookable": 0,
3213 "seat_yupiao": 0
3214 }, {
3215 "seat_price": "122.5",
3216 "seat_name": "软卧下",
3217 "seat_bookable": 0,
3218 "seat_yupiao": 0
3219 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3220 }, {
3221 "train_no": "53000G757820",
3222 "train_number": "G7575",
3223 "from_station": "上海虹桥",
3224 "to_station": "杭州东",
3225 "from_time": "14:19",
3226 "to_time": "15:19",
3227 "from_station_type": "途经",
3228 "to_station_type": "途经",
3229 "day_diff": "0",
3230 "use_time": "60",
3231 "sale_time": "13:30",
3232 "control_day": 59,
3233 "from_telecode": "AOH",
3234 "to_telecode": "HGH",
3235 "can_web_buy": "Y",
3236 "note": "",
3237 "seats": [{
3238 "seat_price": "73.0",
3239 "seat_name": "二等座",
3240 "seat_bookable": 1,
3241 "seat_yupiao": 3
3242 }, {
3243 "seat_price": "117.0",
3244 "seat_name": "一等座",
3245 "seat_bookable": 1,
3246 "seat_yupiao": 1
3247 }, {
3248 "seat_price": "219.5",
3249 "seat_name": "商务座",
3250 "seat_bookable": 0,
3251 "seat_yupiao": 0
3252 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3253 }, {
3254 "train_no": "5l000G730771",
3255 "train_number": "G7307",
3256 "from_station": "上海虹桥",
3257 "to_station": "杭州",
3258 "from_time": "14:30",
3259 "to_time": "15:37",
3260 "from_station_type": "起点",
3261 "to_station_type": "终点",
3262 "day_diff": "0",
3263 "use_time": "67",
3264 "sale_time": "13:30",
3265 "control_day": 59,
3266 "from_telecode": "AOH",
3267 "to_telecode": "HZH",
3268 "can_web_buy": "Y",
3269 "note": "",
3270 "seats": [{
3271 "seat_price": "77.5",
3272 "seat_name": "二等座",
3273 "seat_bookable": 1,
3274 "seat_yupiao": 471
3275 }, {
3276 "seat_price": "123.5",
3277 "seat_name": "一等座",
3278 "seat_bookable": 1,
3279 "seat_yupiao": 24
3280 }, {
3281 "seat_price": "233.5",
3282 "seat_name": "商务座",
3283 "seat_bookable": 1,
3284 "seat_yupiao": 10
3285 }, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3286 }, {
3287 "train_no": "5500000K7120",
3288 "train_number": "K71",
3289 "from_station": "上海南",
3290 "to_station": "杭州东",
3291 "from_time": "14:34",
3292 "to_time": "16:40",
3293 "from_station_type": "起点",
3294 "to_station_type": "途经",
3295 "day_diff": "0",
3296 "use_time": "126",
3297 "sale_time": "15:00",
3298 "control_day": 57,
3299 "from_telecode": "SNH",
3300 "to_telecode": "HGH",
3301 "can_web_buy": "Y",
3302 "note": "",
3303 "seats": [{
3304 "seat_price": "24.5",
3305 "seat_name": "硬座",
3306 "seat_bookable": 1,
3307 "seat_yupiao": 234
3308 }, {
3309 "seat_price": "70.5",
3310 "seat_name": "硬卧上",
3311 "seat_bookable": 1,
3312 "seat_yupiao": 12
3313 }, {
3314 "seat_price": "73.5",
3315 "seat_name": "硬卧中",
3316 "seat_bookable": 1,
3317 "seat_yupiao": 12
3318 }, {
3319 "seat_price": "80.0",
3320 "seat_name": "硬卧下",
3321 "seat_bookable": 1,
3322 "seat_yupiao": 12
3323 }, {
3324 "seat_price": "108.5",
3325 "seat_name": "软卧上",
3326 "seat_bookable": 1,
3327 "seat_yupiao": 4
3328 }, {
3329 "seat_price": "122.5",
3330 "seat_name": "软卧下",
3331 "seat_bookable": 1,
3332 "seat_yupiao": 4
3333 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 310}]
3334 }, {
3335 "train_no": "5l000G163510",
3336 "train_number": "G1635",
3337 "from_station": "上海虹桥",
3338 "to_station": "杭州东",
3339 "from_time": "14:35",
3340 "to_time": "15:36",
3341 "from_station_type": "起点",
3342 "to_station_type": "途经",
3343 "day_diff": "0",
3344 "use_time": "61",
3345 "sale_time": "13:30",
3346 "control_day": 59,
3347 "from_telecode": "AOH",
3348 "to_telecode": "HGH",
3349 "can_web_buy": "Y",
3350 "note": "",
3351 "seats": [{
3352 "seat_price": "73.0",
3353 "seat_name": "二等座",
3354 "seat_bookable": 1,
3355 "seat_yupiao": 68
3356 }, {
3357 "seat_price": "117.0",
3358 "seat_name": "一等座",
3359 "seat_bookable": 0,
3360 "seat_yupiao": 0
3361 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 0, "seat_yupiao": 0}]
3362 }, {
3363 "train_no": "54000G758350",
3364 "train_number": "G7583",
3365 "from_station": "上海虹桥",
3366 "to_station": "杭州东",
3367 "from_time": "14:40",
3368 "to_time": "15:40",
3369 "from_station_type": "途经",
3370 "to_station_type": "途经",
3371 "day_diff": "0",
3372 "use_time": "60",
3373 "sale_time": "13:30",
3374 "control_day": 59,
3375 "from_telecode": "AOH",
3376 "to_telecode": "HGH",
3377 "can_web_buy": "Y",
3378 "note": "",
3379 "seats": [{
3380 "seat_price": "73.0",
3381 "seat_name": "二等座",
3382 "seat_bookable": 1,
3383 "seat_yupiao": 62
3384 }, {
3385 "seat_price": "117.0",
3386 "seat_name": "一等座",
3387 "seat_bookable": 1,
3388 "seat_yupiao": 39
3389 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3390 }, {
3391 "train_no": "5l0000D38170",
3392 "train_number": "D381",
3393 "from_station": "上海虹桥",
3394 "to_station": "杭州东",
3395 "from_time": "14:45",
3396 "to_time": "16:16",
3397 "from_station_type": "起点",
3398 "to_station_type": "途经",
3399 "day_diff": "0",
3400 "use_time": "91",
3401 "sale_time": "13:30",
3402 "control_day": 59,
3403 "from_telecode": "AOH",
3404 "to_telecode": "HGH",
3405 "can_web_buy": "Y",
3406 "note": "",
3407 "seats": [{
3408 "seat_price": "49.0",
3409 "seat_name": "二等座",
3410 "seat_bookable": 0,
3411 "seat_yupiao": 0
3412 }, {
3413 "seat_price": "59.0",
3414 "seat_name": "一等座",
3415 "seat_bookable": 1,
3416 "seat_yupiao": 1
3417 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 1, "seat_yupiao": 83}]
3418 }, {
3419 "train_no": "2400000G410B",
3420 "train_number": "G41",
3421 "from_station": "上海虹桥",
3422 "to_station": "杭州东",
3423 "from_time": "14:51",
3424 "to_time": "15:44",
3425 "from_station_type": "途经",
3426 "to_station_type": "终点",
3427 "day_diff": "0",
3428 "use_time": "53",
3429 "sale_time": "13:30",
3430 "control_day": 59,
3431 "from_telecode": "AOH",
3432 "to_telecode": "HGH",
3433 "can_web_buy": "Y",
3434 "note": "",
3435 "seats": [{
3436 "seat_price": "73.0",
3437 "seat_name": "二等座",
3438 "seat_bookable": 1,
3439 "seat_yupiao": 224
3440 }, {
3441 "seat_price": "117.0",
3442 "seat_name": "一等座",
3443 "seat_bookable": 1,
3444 "seat_yupiao": 67
3445 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 1}]
3446 }, {
3447 "train_no": "5l000G751721",
3448 "train_number": "G7517",
3449 "from_station": "上海虹桥",
3450 "to_station": "杭州东",
3451 "from_time": "14:56",
3452 "to_time": "15:49",
3453 "from_station_type": "起点",
3454 "to_station_type": "途经",
3455 "day_diff": "0",
3456 "use_time": "53",
3457 "sale_time": "13:30",
3458 "control_day": 59,
3459 "from_telecode": "AOH",
3460 "to_telecode": "HGH",
3461 "can_web_buy": "Y",
3462 "note": "",
3463 "seats": [{
3464 "seat_price": "73.0",
3465 "seat_name": "二等座",
3466 "seat_bookable": 1,
3467 "seat_yupiao": 18
3468 }, {
3469 "seat_price": "117.0",
3470 "seat_name": "一等座",
3471 "seat_bookable": 1,
3472 "seat_yupiao": 52
3473 }, {
3474 "seat_price": "219.5",
3475 "seat_name": "商务座",
3476 "seat_bookable": 1,
3477 "seat_yupiao": 20
3478 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3479 }, {
3480 "train_no": "55000K112700",
3481 "train_number": "K1127",
3482 "from_station": "上海南",
3483 "to_station": "杭州东",
3484 "from_time": "14:59",
3485 "to_time": "17:11",
3486 "from_station_type": "起点",
3487 "to_station_type": "途经",
3488 "day_diff": "0",
3489 "use_time": "132",
3490 "sale_time": "15:30",
3491 "control_day": 59,
3492 "from_telecode": "SNH",
3493 "to_telecode": "HGH",
3494 "can_web_buy": "Y",
3495 "note": "",
3496 "seats": [{
3497 "seat_price": "24.5",
3498 "seat_name": "硬座",
3499 "seat_bookable": 1,
3500 "seat_yupiao": 414
3501 }, {
3502 "seat_price": "70.5",
3503 "seat_name": "硬卧上",
3504 "seat_bookable": 1,
3505 "seat_yupiao": 9
3506 }, {
3507 "seat_price": "73.5",
3508 "seat_name": "硬卧中",
3509 "seat_bookable": 1,
3510 "seat_yupiao": 9
3511 }, {
3512 "seat_price": "80.0",
3513 "seat_name": "硬卧下",
3514 "seat_bookable": 1,
3515 "seat_yupiao": 9
3516 }, {
3517 "seat_price": "108.5",
3518 "seat_name": "软卧上",
3519 "seat_bookable": 0,
3520 "seat_yupiao": 0
3521 }, {
3522 "seat_price": "122.5",
3523 "seat_name": "软卧下",
3524 "seat_bookable": 0,
3525 "seat_yupiao": 0
3526 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3527 }, {
3528 "train_no": "5l000G135711",
3529 "train_number": "G1357",
3530 "from_station": "上海虹桥",
3531 "to_station": "杭州东",
3532 "from_time": "15:01",
3533 "to_time": "15:54",
3534 "from_station_type": "起点",
3535 "to_station_type": "途经",
3536 "day_diff": "0",
3537 "use_time": "53",
3538 "sale_time": "13:30",
3539 "control_day": 59,
3540 "from_telecode": "AOH",
3541 "to_telecode": "HGH",
3542 "can_web_buy": "Y",
3543 "note": "",
3544 "seats": [{
3545 "seat_price": "73.0",
3546 "seat_name": "二等座",
3547 "seat_bookable": 0,
3548 "seat_yupiao": 0
3549 }, {
3550 "seat_price": "117.0",
3551 "seat_name": "一等座",
3552 "seat_bookable": 1,
3553 "seat_yupiao": 11
3554 }, {"seat_price": "132.0", "seat_name": "特等座", "seat_bookable": 1, "seat_yupiao": 7}]
3555 }, {
3556 "train_no": "550000K111D0",
3557 "train_number": "K111",
3558 "from_station": "上海南",
3559 "to_station": "杭州东",
3560 "from_time": "15:05",
3561 "to_time": "17:24",
3562 "from_station_type": "起点",
3563 "to_station_type": "途经",
3564 "day_diff": "0",
3565 "use_time": "139",
3566 "sale_time": "15:30",
3567 "control_day": 59,
3568 "from_telecode": "SNH",
3569 "to_telecode": "HGH",
3570 "can_web_buy": "Y",
3571 "note": "",
3572 "seats": [{
3573 "seat_price": "24.5",
3574 "seat_name": "硬座",
3575 "seat_bookable": 1,
3576 "seat_yupiao": 34
3577 }, {
3578 "seat_price": "70.5",
3579 "seat_name": "硬卧上",
3580 "seat_bookable": 1,
3581 "seat_yupiao": 11
3582 }, {
3583 "seat_price": "73.5",
3584 "seat_name": "硬卧中",
3585 "seat_bookable": 1,
3586 "seat_yupiao": 11
3587 }, {
3588 "seat_price": "80.0",
3589 "seat_name": "硬卧下",
3590 "seat_bookable": 1,
3591 "seat_yupiao": 11
3592 }, {
3593 "seat_price": "108.5",
3594 "seat_name": "软卧上",
3595 "seat_bookable": 0,
3596 "seat_yupiao": 0
3597 }, {
3598 "seat_price": "122.5",
3599 "seat_name": "软卧下",
3600 "seat_bookable": 0,
3601 "seat_yupiao": 0
3602 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3603 }, {
3604 "train_no": "5l000D310160",
3605 "train_number": "D3101",
3606 "from_station": "上海虹桥",
3607 "to_station": "杭州东",
3608 "from_time": "15:09",
3609 "to_time": "16:28",
3610 "from_station_type": "起点",
3611 "to_station_type": "途经",
3612 "day_diff": "0",
3613 "use_time": "79",
3614 "sale_time": "13:30",
3615 "control_day": 59,
3616 "from_telecode": "AOH",
3617 "to_telecode": "HGH",
3618 "can_web_buy": "Y",
3619 "note": "",
3620 "seats": [{
3621 "seat_price": "49.0",
3622 "seat_name": "二等座",
3623 "seat_bookable": 1,
3624 "seat_yupiao": 579
3625 }, {
3626 "seat_price": "59.0",
3627 "seat_name": "一等座",
3628 "seat_bookable": 1,
3629 "seat_yupiao": 35
3630 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3631 }, {
3632 "train_no": "5l000G163720",
3633 "train_number": "G1637",
3634 "from_station": "上海虹桥",
3635 "to_station": "杭州东",
3636 "from_time": "15:17",
3637 "to_time": "16:21",
3638 "from_station_type": "起点",
3639 "to_station_type": "途经",
3640 "day_diff": "0",
3641 "use_time": "64",
3642 "sale_time": "13:30",
3643 "control_day": 59,
3644 "from_telecode": "AOH",
3645 "to_telecode": "HGH",
3646 "can_web_buy": "Y",
3647 "note": "",
3648 "seats": [{
3649 "seat_price": "73.0",
3650 "seat_name": "二等座",
3651 "seat_bookable": 1,
3652 "seat_yupiao": 684
3653 }, {
3654 "seat_price": "117.0",
3655 "seat_name": "一等座",
3656 "seat_bookable": 1,
3657 "seat_yupiao": 13
3658 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 17}]
3659 }, {
3660 "train_no": "5l000G130503",
3661 "train_number": "G1305",
3662 "from_station": "上海虹桥",
3663 "to_station": "杭州东",
3664 "from_time": "15:25",
3665 "to_time": "16:12",
3666 "from_station_type": "起点",
3667 "to_station_type": "途经",
3668 "day_diff": "0",
3669 "use_time": "47",
3670 "sale_time": "13:30",
3671 "control_day": 59,
3672 "from_telecode": "AOH",
3673 "to_telecode": "HGH",
3674 "can_web_buy": "Y",
3675 "note": "",
3676 "seats": [{
3677 "seat_price": "73.0",
3678 "seat_name": "二等座",
3679 "seat_bookable": 1,
3680 "seat_yupiao": 109
3681 }, {
3682 "seat_price": "117.0",
3683 "seat_name": "一等座",
3684 "seat_bookable": 1,
3685 "seat_yupiao": 113
3686 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 17}]
3687 }, {
3688 "train_no": "5l000G733780",
3689 "train_number": "G7337",
3690 "from_station": "上海虹桥",
3691 "to_station": "杭州东",
3692 "from_time": "15:32",
3693 "to_time": "16:32",
3694 "from_station_type": "起点",
3695 "to_station_type": "途经",
3696 "day_diff": "0",
3697 "use_time": "60",
3698 "sale_time": "13:30",
3699 "control_day": 59,
3700 "from_telecode": "AOH",
3701 "to_telecode": "HGH",
3702 "can_web_buy": "Y",
3703 "note": "",
3704 "seats": [{
3705 "seat_price": "73.0",
3706 "seat_name": "二等座",
3707 "seat_bookable": 1,
3708 "seat_yupiao": 638
3709 }, {
3710 "seat_price": "117.0",
3711 "seat_name": "一等座",
3712 "seat_bookable": 1,
3713 "seat_yupiao": 43
3714 }, {
3715 "seat_price": "219.5",
3716 "seat_name": "商务座",
3717 "seat_bookable": 1,
3718 "seat_yupiao": 18
3719 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3720 }, {
3721 "train_no": "5l000G135921",
3722 "train_number": "G1359",
3723 "from_station": "上海虹桥",
3724 "to_station": "杭州东",
3725 "from_time": "15:37",
3726 "to_time": "16:36",
3727 "from_station_type": "起点",
3728 "to_station_type": "途经",
3729 "day_diff": "0",
3730 "use_time": "59",
3731 "sale_time": "13:30",
3732 "control_day": 59,
3733 "from_telecode": "AOH",
3734 "to_telecode": "HGH",
3735 "can_web_buy": "N",
3736 "note": "",
3737 "seats": [{
3738 "seat_price": "73.0",
3739 "seat_name": "二等座",
3740 "seat_bookable": 1,
3741 "seat_yupiao": 148
3742 }, {
3743 "seat_price": "117.0",
3744 "seat_name": "一等座",
3745 "seat_bookable": 1,
3746 "seat_yupiao": 24
3747 }, {"seat_price": "132.0", "seat_name": "特等座", "seat_bookable": 0, "seat_yupiao": 0}]
3748 }, {
3749 "train_no": "550000K253A0",
3750 "train_number": "K253",
3751 "from_station": "上海南",
3752 "to_station": "杭州东",
3753 "from_time": "15:37",
3754 "to_time": "17:45",
3755 "from_station_type": "起点",
3756 "to_station_type": "途经",
3757 "day_diff": "0",
3758 "use_time": "128",
3759 "sale_time": "15:30",
3760 "control_day": 59,
3761 "from_telecode": "SNH",
3762 "to_telecode": "HGH",
3763 "can_web_buy": "Y",
3764 "note": "",
3765 "seats": [{
3766 "seat_price": "24.5",
3767 "seat_name": "硬座",
3768 "seat_bookable": 1,
3769 "seat_yupiao": 170
3770 }, {
3771 "seat_price": "70.5",
3772 "seat_name": "硬卧上",
3773 "seat_bookable": 1,
3774 "seat_yupiao": 16
3775 }, {
3776 "seat_price": "73.5",
3777 "seat_name": "硬卧中",
3778 "seat_bookable": 1,
3779 "seat_yupiao": 16
3780 }, {
3781 "seat_price": "80.0",
3782 "seat_name": "硬卧下",
3783 "seat_bookable": 1,
3784 "seat_yupiao": 16
3785 }, {
3786 "seat_price": "108.5",
3787 "seat_name": "软卧上",
3788 "seat_bookable": 0,
3789 "seat_yupiao": 0
3790 }, {
3791 "seat_price": "122.5",
3792 "seat_name": "软卧下",
3793 "seat_bookable": 0,
3794 "seat_yupiao": 0
3795 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3796 }, {
3797 "train_no": "5l000G751951",
3798 "train_number": "G7519",
3799 "from_station": "上海虹桥",
3800 "to_station": "杭州东",
3801 "from_time": "15:47",
3802 "to_time": "16:47",
3803 "from_station_type": "起点",
3804 "to_station_type": "途经",
3805 "day_diff": "0",
3806 "use_time": "60",
3807 "sale_time": "13:30",
3808 "control_day": 59,
3809 "from_telecode": "AOH",
3810 "to_telecode": "HGH",
3811 "can_web_buy": "Y",
3812 "note": "",
3813 "seats": [{
3814 "seat_price": "73.0",
3815 "seat_name": "二等座",
3816 "seat_bookable": 1,
3817 "seat_yupiao": 844
3818 }, {
3819 "seat_price": "117.0",
3820 "seat_name": "一等座",
3821 "seat_bookable": 1,
3822 "seat_yupiao": 116
3823 }, {
3824 "seat_price": "219.5",
3825 "seat_name": "商务座",
3826 "seat_bookable": 1,
3827 "seat_yupiao": 26
3828 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3829 }, {
3830 "train_no": "5l000G754532",
3831 "train_number": "G7545",
3832 "from_station": "上海虹桥",
3833 "to_station": "杭州东",
3834 "from_time": "15:52",
3835 "to_time": "16:51",
3836 "from_station_type": "起点",
3837 "to_station_type": "途经",
3838 "day_diff": "0",
3839 "use_time": "59",
3840 "sale_time": "13:30",
3841 "control_day": 59,
3842 "from_telecode": "AOH",
3843 "to_telecode": "HGH",
3844 "can_web_buy": "Y",
3845 "note": "",
3846 "seats": [{
3847 "seat_price": "73.0",
3848 "seat_name": "二等座",
3849 "seat_bookable": 1,
3850 "seat_yupiao": 15
3851 }, {
3852 "seat_price": "117.0",
3853 "seat_name": "一等座",
3854 "seat_bookable": 1,
3855 "seat_yupiao": 54
3856 }, {
3857 "seat_price": "219.5",
3858 "seat_name": "商务座",
3859 "seat_bookable": 1,
3860 "seat_yupiao": 20
3861 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3862 }, {
3863 "train_no": "5l000D310350",
3864 "train_number": "D3103",
3865 "from_station": "上海虹桥",
3866 "to_station": "杭州东",
3867 "from_time": "15:57",
3868 "to_time": "16:58",
3869 "from_station_type": "起点",
3870 "to_station_type": "途经",
3871 "day_diff": "0",
3872 "use_time": "61",
3873 "sale_time": "13:30",
3874 "control_day": 59,
3875 "from_telecode": "AOH",
3876 "to_telecode": "HGH",
3877 "can_web_buy": "Y",
3878 "note": "",
3879 "seats": [{
3880 "seat_price": "49.0",
3881 "seat_name": "二等座",
3882 "seat_bookable": 1,
3883 "seat_yupiao": 154
3884 }, {
3885 "seat_price": "59.0",
3886 "seat_name": "一等座",
3887 "seat_bookable": 1,
3888 "seat_yupiao": 2
3889 }, {"seat_price": "49.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3890 }, {
3891 "train_no": "550000K351B2",
3892 "train_number": "K351",
3893 "from_station": "上海南",
3894 "to_station": "杭州东",
3895 "from_time": "15:58",
3896 "to_time": "18:11",
3897 "from_station_type": "起点",
3898 "to_station_type": "途经",
3899 "day_diff": "0",
3900 "use_time": "133",
3901 "sale_time": "15:30",
3902 "control_day": 59,
3903 "from_telecode": "SNH",
3904 "to_telecode": "HGH",
3905 "can_web_buy": "Y",
3906 "note": "",
3907 "seats": [{
3908 "seat_price": "24.5",
3909 "seat_name": "硬座",
3910 "seat_bookable": 1,
3911 "seat_yupiao": 326
3912 }, {
3913 "seat_price": "70.5",
3914 "seat_name": "硬卧上",
3915 "seat_bookable": 1,
3916 "seat_yupiao": 6
3917 }, {
3918 "seat_price": "73.5",
3919 "seat_name": "硬卧中",
3920 "seat_bookable": 1,
3921 "seat_yupiao": 6
3922 }, {
3923 "seat_price": "80.0",
3924 "seat_name": "硬卧下",
3925 "seat_bookable": 1,
3926 "seat_yupiao": 6
3927 }, {
3928 "seat_price": "108.5",
3929 "seat_name": "软卧上",
3930 "seat_bookable": 0,
3931 "seat_yupiao": 0
3932 }, {
3933 "seat_price": "122.5",
3934 "seat_name": "软卧下",
3935 "seat_bookable": 0,
3936 "seat_yupiao": 0
3937 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3938 }, {
3939 "train_no": "5500000T8150",
3940 "train_number": "T81",
3941 "from_station": "上海南",
3942 "to_station": "杭州东",
3943 "from_time": "16:06",
3944 "to_time": "17:58",
3945 "from_station_type": "起点",
3946 "to_station_type": "途经",
3947 "day_diff": "0",
3948 "use_time": "112",
3949 "sale_time": "15:00",
3950 "control_day": 57,
3951 "from_telecode": "SNH",
3952 "to_telecode": "HGH",
3953 "can_web_buy": "Y",
3954 "note": "",
3955 "seats": [{
3956 "seat_price": "24.5",
3957 "seat_name": "硬座",
3958 "seat_bookable": 1,
3959 "seat_yupiao": 113
3960 }, {
3961 "seat_price": "70.5",
3962 "seat_name": "硬卧上",
3963 "seat_bookable": 1,
3964 "seat_yupiao": 7
3965 }, {
3966 "seat_price": "73.5",
3967 "seat_name": "硬卧中",
3968 "seat_bookable": 1,
3969 "seat_yupiao": 7
3970 }, {
3971 "seat_price": "80.0",
3972 "seat_name": "硬卧下",
3973 "seat_bookable": 1,
3974 "seat_yupiao": 7
3975 }, {
3976 "seat_price": "108.5",
3977 "seat_name": "软卧上",
3978 "seat_bookable": 0,
3979 "seat_yupiao": 0
3980 }, {
3981 "seat_price": "122.5",
3982 "seat_name": "软卧下",
3983 "seat_bookable": 0,
3984 "seat_yupiao": 0
3985 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
3986 }, {
3987 "train_no": "5l000G736510",
3988 "train_number": "G7365",
3989 "from_station": "上海虹桥",
3990 "to_station": "杭州东",
3991 "from_time": "16:07",
3992 "to_time": "17:03",
3993 "from_station_type": "起点",
3994 "to_station_type": "途经",
3995 "day_diff": "0",
3996 "use_time": "56",
3997 "sale_time": "13:30",
3998 "control_day": 59,
3999 "from_telecode": "AOH",
4000 "to_telecode": "HGH",
4001 "can_web_buy": "Y",
4002 "note": "",
4003 "seats": [{
4004 "seat_price": "73.0",
4005 "seat_name": "二等座",
4006 "seat_bookable": 1,
4007 "seat_yupiao": 128
4008 }, {
4009 "seat_price": "117.0",
4010 "seat_name": "一等座",
4011 "seat_bookable": 1,
4012 "seat_yupiao": 54
4013 }, {
4014 "seat_price": "219.5",
4015 "seat_name": "商务座",
4016 "seat_bookable": 1,
4017 "seat_yupiao": 20
4018 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
4019 }, {
4020 "train_no": "53000G757120",
4021 "train_number": "G7571",
4022 "from_station": "上海虹桥",
4023 "to_station": "杭州东",
4024 "from_time": "16:12",
4025 "to_time": "17:12",
4026 "from_station_type": "途经",
4027 "to_station_type": "途经",
4028 "day_diff": "0",
4029 "use_time": "60",
4030 "sale_time": "13:30",
4031 "control_day": 59,
4032 "from_telecode": "AOH",
4033 "to_telecode": "HGH",
4034 "can_web_buy": "Y",
4035 "note": "",
4036 "seats": [{
4037 "seat_price": "73.0",
4038 "seat_name": "二等座",
4039 "seat_bookable": 1,
4040 "seat_yupiao": 396
4041 }, {
4042 "seat_price": "117.0",
4043 "seat_name": "一等座",
4044 "seat_bookable": 1,
4045 "seat_yupiao": 45
4046 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
4047 }, {
4048 "train_no": "5l000G139121",
4049 "train_number": "G1391",
4050 "from_station": "上海虹桥",
4051 "to_station": "杭州东",
4052 "from_time": "16:27",
4053 "to_time": "17:28",
4054 "from_station_type": "起点",
4055 "to_station_type": "途经",
4056 "day_diff": "0",
4057 "use_time": "61",
4058 "sale_time": "13:30",
4059 "control_day": 59,
4060 "from_telecode": "AOH",
4061 "to_telecode": "HGH",
4062 "can_web_buy": "Y",
4063 "note": "",
4064 "seats": [{
4065 "seat_price": "73.0",
4066 "seat_name": "二等座",
4067 "seat_bookable": 1,
4068 "seat_yupiao": 261
4069 }, {
4070 "seat_price": "117.0",
4071 "seat_name": "一等座",
4072 "seat_bookable": 1,
4073 "seat_yupiao": 14
4074 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 9}]
4075 }, {
4076 "train_no": "5l000G7309B0",
4077 "train_number": "G7309",
4078 "from_station": "上海虹桥",
4079 "to_station": "杭州",
4080 "from_time": "16:32",
4081 "to_time": "17:44",
4082 "from_station_type": "起点",
4083 "to_station_type": "终点",
4084 "day_diff": "0",
4085 "use_time": "72",
4086 "sale_time": "13:30",
4087 "control_day": 59,
4088 "from_telecode": "AOH",
4089 "to_telecode": "HZH",
4090 "can_web_buy": "Y",
4091 "note": "",
4092 "seats": [{
4093 "seat_price": "77.5",
4094 "seat_name": "二等座",
4095 "seat_bookable": 1,
4096 "seat_yupiao": 450
4097 }, {
4098 "seat_price": "123.5",
4099 "seat_name": "一等座",
4100 "seat_bookable": 1,
4101 "seat_yupiao": 20
4102 }, {
4103 "seat_price": "233.5",
4104 "seat_name": "商务座",
4105 "seat_bookable": 1,
4106 "seat_yupiao": 10
4107 }, {"seat_price": "77.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
4108 }, {
4109 "train_no": "5l000G136120",
4110 "train_number": "G1361",
4111 "from_station": "上海虹桥",
4112 "to_station": "杭州东",
4113 "from_time": "16:42",
4114 "to_time": "17:41",
4115 "from_station_type": "起点",
4116 "to_station_type": "途经",
4117 "day_diff": "0",
4118 "use_time": "59",
4119 "sale_time": "13:30",
4120 "control_day": 59,
4121 "from_telecode": "AOH",
4122 "to_telecode": "HGH",
4123 "can_web_buy": "Y",
4124 "note": "",
4125 "seats": [{
4126 "seat_price": "73.0",
4127 "seat_name": "二等座",
4128 "seat_bookable": 1,
4129 "seat_yupiao": 255
4130 }, {
4131 "seat_price": "117.0",
4132 "seat_name": "一等座",
4133 "seat_bookable": 1,
4134 "seat_yupiao": 16
4135 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 2}]
4136 }, {
4137 "train_no": "5l000G752174",
4138 "train_number": "G7521",
4139 "from_station": "上海虹桥",
4140 "to_station": "杭州东",
4141 "from_time": "16:47",
4142 "to_time": "17:53",
4143 "from_station_type": "起点",
4144 "to_station_type": "途经",
4145 "day_diff": "0",
4146 "use_time": "66",
4147 "sale_time": "13:30",
4148 "control_day": 59,
4149 "from_telecode": "AOH",
4150 "to_telecode": "HGH",
4151 "can_web_buy": "Y",
4152 "note": "",
4153 "seats": [{
4154 "seat_price": "73.0",
4155 "seat_name": "二等座",
4156 "seat_bookable": 1,
4157 "seat_yupiao": 125
4158 }, {
4159 "seat_price": "117.0",
4160 "seat_name": "一等座",
4161 "seat_bookable": 1,
4162 "seat_yupiao": 5
4163 }, {
4164 "seat_price": "219.5",
4165 "seat_name": "商务座",
4166 "seat_bookable": 1,
4167 "seat_yupiao": 25
4168 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
4169 }, {
4170 "train_no": "55000K137303",
4171 "train_number": "K1373",
4172 "from_station": "上海南",
4173 "to_station": "杭州东",
4174 "from_time": "16:49",
4175 "to_time": "19:10",
4176 "from_station_type": "起点",
4177 "to_station_type": "途经",
4178 "day_diff": "0",
4179 "use_time": "141",
4180 "sale_time": "15:30",
4181 "control_day": 59,
4182 "from_telecode": "SNH",
4183 "to_telecode": "HGH",
4184 "can_web_buy": "Y",
4185 "note": "",
4186 "seats": [{
4187 "seat_price": "24.5",
4188 "seat_name": "硬座",
4189 "seat_bookable": 1,
4190 "seat_yupiao": 317
4191 }, {
4192 "seat_price": "70.5",
4193 "seat_name": "硬卧上",
4194 "seat_bookable": 1,
4195 "seat_yupiao": 5
4196 }, {
4197 "seat_price": "73.5",
4198 "seat_name": "硬卧中",
4199 "seat_bookable": 1,
4200 "seat_yupiao": 5
4201 }, {
4202 "seat_price": "80.0",
4203 "seat_name": "硬卧下",
4204 "seat_bookable": 1,
4205 "seat_yupiao": 5
4206 }, {
4207 "seat_price": "108.5",
4208 "seat_name": "软卧上",
4209 "seat_bookable": 0,
4210 "seat_yupiao": 0
4211 }, {
4212 "seat_price": "122.5",
4213 "seat_name": "软卧下",
4214 "seat_bookable": 0,
4215 "seat_yupiao": 0
4216 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
4217 }, {
4218 "train_no": "550000T38120",
4219 "train_number": "T381",
4220 "from_station": "上海南",
4221 "to_station": "杭州东",
4222 "from_time": "16:55",
4223 "to_time": "18:44",
4224 "from_station_type": "起点",
4225 "to_station_type": "途经",
4226 "day_diff": "0",
4227 "use_time": "109",
4228 "sale_time": "15:30",
4229 "control_day": 59,
4230 "from_telecode": "SNH",
4231 "to_telecode": "HGH",
4232 "can_web_buy": "Y",
4233 "note": "",
4234 "seats": [{
4235 "seat_price": "24.5",
4236 "seat_name": "硬座",
4237 "seat_bookable": 1,
4238 "seat_yupiao": 68
4239 }, {
4240 "seat_price": "70.5",
4241 "seat_name": "硬卧上",
4242 "seat_bookable": 0,
4243 "seat_yupiao": 0
4244 }, {
4245 "seat_price": "73.5",
4246 "seat_name": "硬卧中",
4247 "seat_bookable": 0,
4248 "seat_yupiao": 0
4249 }, {
4250 "seat_price": "80.0",
4251 "seat_name": "硬卧下",
4252 "seat_bookable": 0,
4253 "seat_yupiao": 0
4254 }, {
4255 "seat_price": "108.5",
4256 "seat_name": "软卧上",
4257 "seat_bookable": 0,
4258 "seat_yupiao": 0
4259 }, {
4260 "seat_price": "122.5",
4261 "seat_name": "软卧下",
4262 "seat_bookable": 0,
4263 "seat_yupiao": 0
4264 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
4265 }, {
4266 "train_no": "550000K27172",
4267 "train_number": "K271",
4268 "from_station": "上海南",
4269 "to_station": "杭州东",
4270 "from_time": "17:01",
4271 "to_time": "19:23",
4272 "from_station_type": "起点",
4273 "to_station_type": "途经",
4274 "day_diff": "0",
4275 "use_time": "142",
4276 "sale_time": "15:00",
4277 "control_day": 57,
4278 "from_telecode": "SNH",
4279 "to_telecode": "HGH",
4280 "can_web_buy": "Y",
4281 "note": "",
4282 "seats": [{
4283 "seat_price": "24.5",
4284 "seat_name": "硬座",
4285 "seat_bookable": 1,
4286 "seat_yupiao": 293
4287 }, {
4288 "seat_price": "70.5",
4289 "seat_name": "硬卧上",
4290 "seat_bookable": 0,
4291 "seat_yupiao": 0
4292 }, {
4293 "seat_price": "73.5",
4294 "seat_name": "硬卧中",
4295 "seat_bookable": 0,
4296 "seat_yupiao": 0
4297 }, {
4298 "seat_price": "80.0",
4299 "seat_name": "硬卧下",
4300 "seat_bookable": 0,
4301 "seat_yupiao": 0
4302 }, {
4303 "seat_price": "108.5",
4304 "seat_name": "软卧上",
4305 "seat_bookable": 0,
4306 "seat_yupiao": 0
4307 }, {
4308 "seat_price": "122.5",
4309 "seat_name": "软卧下",
4310 "seat_bookable": 0,
4311 "seat_yupiao": 0
4312 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
4313 }, {
4314 "train_no": "5i000G756611",
4315 "train_number": "G7563",
4316 "from_station": "上海虹桥",
4317 "to_station": "杭州东",
4318 "from_time": "17:05",
4319 "to_time": "18:06",
4320 "from_station_type": "途经",
4321 "to_station_type": "终点",
4322 "day_diff": "0",
4323 "use_time": "61",
4324 "sale_time": "13:30",
4325 "control_day": 59,
4326 "from_telecode": "AOH",
4327 "to_telecode": "HGH",
4328 "can_web_buy": "Y",
4329 "note": "",
4330 "seats": [{
4331 "seat_price": "73.0",
4332 "seat_name": "二等座",
4333 "seat_bookable": 1,
4334 "seat_yupiao": 1
4335 }, {
4336 "seat_price": "117.0",
4337 "seat_name": "一等座",
4338 "seat_bookable": 1,
4339 "seat_yupiao": 2
4340 }, {
4341 "seat_price": "219.5",
4342 "seat_name": "商务座",
4343 "seat_bookable": 0,
4344 "seat_yupiao": 0
4345 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
4346 }, {
4347 "train_no": "550000Z25701",
4348 "train_number": "Z257",
4349 "from_station": "上海南",
4350 "to_station": "杭州东",
4351 "from_time": "17:14",
4352 "to_time": "18:57",
4353 "from_station_type": "起点",
4354 "to_station_type": "途经",
4355 "day_diff": "0",
4356 "use_time": "103",
4357 "sale_time": "15:30",
4358 "control_day": 59,
4359 "from_telecode": "SNH",
4360 "to_telecode": "HGH",
4361 "can_web_buy": "N",
4362 "note": "",
4363 "seats": [{
4364 "seat_price": "70.5",
4365 "seat_name": "硬卧上",
4366 "seat_bookable": 1,
4367 "seat_yupiao": 17
4368 }, {
4369 "seat_price": "73.5",
4370 "seat_name": "硬卧中",
4371 "seat_bookable": 1,
4372 "seat_yupiao": 17
4373 }, {
4374 "seat_price": "80.0",
4375 "seat_name": "硬卧下",
4376 "seat_bookable": 1,
4377 "seat_yupiao": 17
4378 }, {
4379 "seat_price": "108.5",
4380 "seat_name": "软卧上",
4381 "seat_bookable": 1,
4382 "seat_yupiao": 6
4383 }, {"seat_price": "122.5", "seat_name": "软卧下", "seat_bookable": 1, "seat_yupiao": 6}]
4384 }, {
4385 "train_no": "54000G758560",
4386 "train_number": "G7585",
4387 "from_station": "上海虹桥",
4388 "to_station": "杭州东",
4389 "from_time": "17:14",
4390 "to_time": "18:14",
4391 "from_station_type": "途经",
4392 "to_station_type": "途经",
4393 "day_diff": "0",
4394 "use_time": "60",
4395 "sale_time": "13:30",
4396 "control_day": 59,
4397 "from_telecode": "AOH",
4398 "to_telecode": "HGH",
4399 "can_web_buy": "Y",
4400 "note": "",
4401 "seats": [{
4402 "seat_price": "73.0",
4403 "seat_name": "二等座",
4404 "seat_bookable": 1,
4405 "seat_yupiao": 504
4406 }, {
4407 "seat_price": "117.0",
4408 "seat_name": "一等座",
4409 "seat_bookable": 1,
4410 "seat_yupiao": 29
4411 }, {"seat_price": "73.0", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
4412 }, {
4413 "train_no": "5l000G136331",
4414 "train_number": "G1363",
4415 "from_station": "上海虹桥",
4416 "to_station": "杭州东",
4417 "from_time": "17:19",
4418 "to_time": "18:19",
4419 "from_station_type": "起点",
4420 "to_station_type": "途经",
4421 "day_diff": "0",
4422 "use_time": "60",
4423 "sale_time": "13:30",
4424 "control_day": 57,
4425 "from_telecode": "AOH",
4426 "to_telecode": "HGH",
4427 "can_web_buy": "N",
4428 "note": "",
4429 "seats": [{
4430 "seat_price": "73.0",
4431 "seat_name": "二等座",
4432 "seat_bookable": 1,
4433 "seat_yupiao": 191
4434 }, {
4435 "seat_price": "117.0",
4436 "seat_name": "一等座",
4437 "seat_bookable": 1,
4438 "seat_yupiao": 24
4439 }, {"seat_price": "132.0", "seat_name": "特等座", "seat_bookable": 0, "seat_yupiao": 0}]
4440 }, {
4441 "train_no": "550000K46951",
4442 "train_number": "K469",
4443 "from_station": "上海南",
4444 "to_station": "杭州东",
4445 "from_time": "17:20",
4446 "to_time": "19:42",
4447 "from_station_type": "途经",
4448 "to_station_type": "途经",
4449 "day_diff": "0",
4450 "use_time": "142",
4451 "sale_time": "15:30",
4452 "control_day": 59,
4453 "from_telecode": "SNH",
4454 "to_telecode": "HGH",
4455 "can_web_buy": "Y",
4456 "note": "",
4457 "seats": [{
4458 "seat_price": "24.5",
4459 "seat_name": "硬座",
4460 "seat_bookable": 1,
4461 "seat_yupiao": 1
4462 }, {
4463 "seat_price": "70.5",
4464 "seat_name": "硬卧上",
4465 "seat_bookable": 0,
4466 "seat_yupiao": 0
4467 }, {
4468 "seat_price": "73.5",
4469 "seat_name": "硬卧中",
4470 "seat_bookable": 0,
4471 "seat_yupiao": 0
4472 }, {
4473 "seat_price": "80.0",
4474 "seat_name": "硬卧下",
4475 "seat_bookable": 0,
4476 "seat_yupiao": 0
4477 }, {
4478 "seat_price": "108.5",
4479 "seat_name": "软卧上",
4480 "seat_bookable": 0,
4481 "seat_yupiao": 0
4482 }, {
4483 "seat_price": "122.5",
4484 "seat_name": "软卧下",
4485 "seat_bookable": 0,
4486 "seat_yupiao": 0
4487 }, {"seat_price": "24.5", "seat_name": "无座", "seat_bookable": 0, "seat_yupiao": 0}]
4488 }, {
4489 "train_no": "5l000G163911",
4490 "train_number": "G1639",
4491 "from_station": "上海虹桥",
4492 "to_station": "杭州东",
4493 "from_time": "17:24",
4494 "to_time": "18:23",
4495 "from_station_type": "起点",
4496 "to_station_type": "途经",
4497 "day_diff": "0",
4498 "use_time": "59",
4499 "sale_time": "13:30",
4500 "control_day": 59,
4501 "from_telecode": "AOH",
4502 "to_telecode": "HGH",
4503 "can_web_buy": "Y",
4504 "note": "",
4505 "seats": [{
4506 "seat_price": "73.0",
4507 "seat_name": "二等座",
4508 "seat_bookable": 1,
4509 "seat_yupiao": 58
4510 }, {
4511 "seat_price": "117.0",
4512 "seat_name": "一等座",
4513 "seat_bookable": 0,
4514 "seat_yupiao": 0
4515 }, {"seat_price": "219.5", "seat_name": "商务座", "seat_bookable": 1, "seat_yupiao": 2}]
4516 }, {
4517 "train_no": "5l000G7311B1",
4518 "train_number": "G7311",
4519 "from_station": "上海虹桥",
4520 "to_station": "杭州",
4521 "from_time": "17:29",
4522 "to_time": "18:41",
4523 "from_station_type": "起点",
4524 "to_station_type": "终点",
4525 "day_diff": "0",
4526 "use_time": "72",
4527 "sale_time": "13:30",
4528 "control_day": 59,
4529
View Code
我们这里做的第一个事情是将数据全部展示出来,在具体渲染前,原始数据需要做一些处理:
1 define([
2 'AbstractView',
3 'pages/list.data',
4 'text!pages/tpl.layout.html',
5 'text!pages/tpl.list.html'
6
7
8 ], function (
9 AbstractView,
10 listData,
11
12 layoutHtml,
13 listTpl
14
15 ) {
16 return _.inherit(AbstractView, {
17
18 propertys: function ($super) {
19 $super();
20
21 this.viewId = 'list';
22
23 this.template = layoutHtml;
24
25 this.events = {};
26
27 //班车信息
28 this.listData = listData;
29
30 },
31
32 initElement: function () {
33 this.d_list_wrapper = this.$('.js_list_wrapper');
34 this.d_none_data = this.$('.js_none_data');
35
36 this.d_js_show_setoutdate = this.$('.js_show_setoutdate');
37 this.d_js_show_setstation = this.$('.js_show_setstation');
38 this.d_js_show_arrivalstation = this.$('.js_show_arrivalstation');
39 this.d_js_list_loading = this.$('.js_list_loading');
40 this.d_js_tabs = this.$('.js_tabs');
41
42 this.d_js_day_sec = this.$('.js_day_sec');
43 this.d_js_start_sec = this.$('.js_start_sec');
44 this.d_js_arrival_sec = this.$('.js_arrival_sec');
45
46 },
47
48 //复杂的业务数据处理,为了达到产品的需求,这段代码逻辑与业务相关
49 //这段数据处理的代码过长(超过50行就过长),应该重构掉
50 formatData: function (data) {
51 var item, seat;
52 var typeMap = {
53 'g': 'g',
54 'd': 'd',
55 't': 't',
56 'c': 'g'
57 };
58
59 //出发时间对应的分钟数
60 var fromMinute = 0;
61
62 //获取当前班车日期当前的时间戳,这个数据是动态的,这里写死了
63 var d = 1464192000000;
64 var date = new Date();
65 var now = parseInt(date.getTime() / 1000);
66 date.setTime(d);
67 var year = date.getFullYear();
68 var month = date.getMonth();
69 var day = date.getDate();
70 var toBegin;
71 var seatName, seatIndex, iii;
72
73 //处理坐席问题,仅显示二等座,一等座,特等座 无座
74 // 二等座 一等座 商务座 无座 动卧 特等座
75 var my_seats = {};
76 var seatSort = ['二等座', '一等座', '硬座', '硬卧', '软卧', '商务座', '无座', '动卧', '特等座', '软座'];
77
78 for (var i = 0, len = data.length; i < len; i++) {
79 fromMinute = data[i].from_time.split(':');
80 fromMinute[0] = fromMinute[0] + '';
81 fromMinute[1] = fromMinute[1] + '';
82 if ((fromMinute[0].charAt(0) == '0')) fromMinute[0] = fromMinute[0].charAt(1);
83 if ((fromMinute[1].charAt(0) == '0')) fromMinute[1] = fromMinute[1].charAt(1);
84 date = new Date(year, month, day, fromMinute[0], fromMinute[1], 0);
85 fromMinute = parseInt(date.getTime() / 1000)
86 toBegin = parseInt((fromMinute - now) / 60);
87
88 data[i].toBegin = toBegin;
89
90 //处理车次类型问题
91 data[i].my_train_number = typeMap[data[i].train_number.charAt(0).toLowerCase()] || 'other';
92
93 seat = data[i].seats;
94 //所有余票
95 data[i].sum_ticket = 0;
96 //最低价
97 data[i].min_price = null;
98
99 for (var j = 0, len1 = seat.length; j < len1; j++) {
100 if (!data[i].min_price || data[i].min_price > seat[j].seat_price) data[i].min_price = parseFloat(seat[j].seat_price);
101 data[i].sum_ticket += parseInt(seat[j].seat_yupiao);
102
103 //坐席问题如果坐席不包括上中下则去掉
104 seatName = seat[j].seat_name;
105 //去掉上中下
106 seatName = seatName.replace(/上|中|下/g, '');
107 if (!my_seats[seatName]) {
108 my_seats[seatName] = parseInt(seat[j].seat_yupiao);
109 } else {
110 my_seats[seatName] = my_seats[seatName] + parseInt(seat[j].seat_yupiao);
111 }
112 }
113 //这里myseat为对象,需要转换为数组
114 //将定制坐席转为排序后的数组
115 data[i].my_seats = [];
116 for (iii = 0; iii < seatSort.length; iii++) {
117 if (typeof my_seats[seatSort[iii]] == 'number') data[i].my_seats.push({ name: seatSort[iii], yupiao: my_seats[seatSort[iii]] });
118 }
119
120 my_seats = {};
121 }
122
123 return data;
124 },
125
126 //渲染列表
127 renderList: function() {
128 var data = this.formatData(this.listData);
129
130 var html = '';
131 window.scrollTo(0, 0);
132
133 if (data.length === 0) {
134 this.d_none_data.show();
135 this.d_list_wrapper.hide();
136 return;
137 }
138
139 this.d_none_data.hide();
140 this.d_list_wrapper.show();
141 html = this.renderTpl(listTpl, { data: data });
142 this.d_list_wrapper.html(html);
143 },
144
145 addEvent: function () {
146 this.on('onShow', function () {
147
148 this.renderList();
149
150 });
151 }
152
153 });
154
155 });
View Code
然后第一步的效果出来了,后面只需要处理数据筛选即可:
这里开始实现第一个业务组件,顶部的搜索栏,这个搜索栏有以下需求:
① 默认以时间升序排列
② 三个tab彼此互斥,点击时候仍然使用升序,再次点击为倒序
顶部导航组件
这里的交互就有一定复杂性了,这种场景是有数据实体出现的必要了,所以我们先实现数据实体:
1 define(['AbstractEntity'], function (AbstractEntity) {
2
3 var Entity = _.inherit(AbstractEntity, {
4 propertys: function ($super) {
5 $super();
6
7 //三个对象,时间,耗时,价格,升序降序,三个数据互斥
8 //默认down up null
9 this.data = {
10 time: 'up',
11 sumTime: '',
12 price: ''
13 };
14 },
15
16 _resetData: function () {
17 this.data = {
18 time: '',
19 sumTime: '',
20 price: ''
21 };
22 },
23
24 setTime: function () {
25 this._setData('time');
26 },
27
28 setSumTime: function () {
29 this._setData('sumTime');
30 },
31
32 setPrice: function () {
33 this._setData('price');
34 },
35
36 _setData: function (key) {
37
38 //如果设置当前key存在,则反置,否则清空筛选,设置默认值
39 if (this.data[key] != '') {
40 if (this.data[key] == 'up') this.data[key] = 'down';
41 else this.data[key] = 'up';
42 } else {
43 this._resetData();
44 this.data[key] = 'down';
45 }
46 this.update();
47 }
48
49 });
50
51 return Entity;
52 });
对应视觉展示比较简单:
1 <ul class="bus-tabs sort-bar js_sort_item">
2 <li class="tabs-item " data-sort="Time" style="-webkit-flex: 1.5; flex: 1.5;">出发时间<i class="icon-sort <%=time %>"></i></li>
3 <li class="tabs-item " data-sort="SumTime" >耗时<i class="icon-sort <%=sumTime %>"></i></li>
4 <li class="tabs-item " data-sort="Price" >价格<i class="icon-sort <%=price %>"></i></li>
5 </ul>
事实上这个数据实体是完全独立的,这个视觉模板也仅仅负责了展示,而在哪展示,数据怎么与模板产生关联其中就是我们的组件控制器了:
1 define(['ModuleView', 'text!pages/tpl.sort.bar.html'], function (ModuleView, tpl) {
2 return _.inherit(ModuleView, {
3
4 //此处若是要使用model,处实例化时候一定要保证entity的存在,如果不存在便是业务BUG
5 initData: function () {
6
7 this.template = tpl;
8 this.events = {
9 'click .js_sort_item li ': function (e) {
10 var el = $(e.currentTarget);
11 var sort = el.attr('data-sort');
12 _hmt.push(['_trackEvent', 'train.list.sort.' + sort, 'click']);
13
14 this.sortEntity['set' + sort]();
15 }
16 };
17
18 this.sortEntity.subscribe('init', this.render, this);
19 this.sortEntity.subscribe(this.render, this);
20
21 },
22
23 getViewModel: function () {
24 return this.sortEntity.get();
25 }
26
27 });
28
29 });
至此,可以看到,一个组件就已经完成了,组件的功能很简单:
PS:注意组件是不能脱离根组件View而存在,一个组件一定会有一个this.view对象
① 组件控制器获取了模板
② 组件控制器获取了根组件View给予(实例化时注入)的数据实体sortEntiy
于是我们在主控制器中实例化我们的数据实体与组件:
1 initEntity: function() {
2
3 //实例化排序的导航栏的实体
4 this.sortEntity = new SortEntity();
5
6 },
7
8 initModule: function() {
9
10 //view为注入给组件的根元素
11 //selector为组件将要显示的容器
12 //sortEntity为注入给组件的数据实体,做通信用
13 //这个module在数据显示后会自动展示
14 this.sortModule = new SortModule({
15 view: this,
16 selector: '.js_sort_wrapper',
17 sortEntity: this.sortEntity
18 });
19
20 },
这里简单说明下代码,首先这里说明一个错误的实践,一个笔误:
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的实现:
1 _timeSort: function (data, sort) {
2 data = _.sortBy(data, function (item) {
3 item = item.from_time.split(':');
4 item = item[0] + '.' + item[1];
5 item = parseFloat(item);
6 return item;
7 });
8 if (sort == 'down') data.reverse();
9 return data;
10 },
11
12 _sumTimeSort: function (data, sort) {
13 data = _.sortBy(data, function (item) {
14 return parseInt(item.use_time);
15 });
16 if (sort == 'down') data.reverse();
17 return data;
18 },
19
20 _priceSort: function (data, sort) {
21 data = _.sortBy(data, function (item) {
22 return item.min_price;
23 });
24 if (sort == 'down') data.reverse();
25 return data;
26 },
27
28 //获取导航栏排序后的数据
29 getSortData: function (data) {
30 var tmp = [];
31 var sort = this.sortEntity.get();
32
33 for (var k in sort) {
34 if (sort[k].length > 0) {
35 tmp = this['_' + k + 'Sort'](data, sort[k])
36 return tmp;
37 }
38 }
39 },
40
41 //完成所有的筛选条件,逻辑比较重
42 getFilteData: function () {
43 var data = this.formatData(this.listData);
44 data = this.getSortData(data);
45
46 return data;
47 },
48
49 //渲染列表
50 renderList: function() {
51 var data = this.getFilteData();
52
53 var html = '';
54 window.scrollTo(0, 0);
55
56 if (data.length === 0) {
57 this.d_none_data.show();
58 this.d_list_wrapper.hide();
59 return;
60 }
61
62 this.d_none_data.hide();
63 this.d_list_wrapper.show();
64 html = this.renderTpl(listTpl, { data: data });
65 this.d_list_wrapper.html(html);
66 },
可以看到,这里复杂操作被分解为了一个个小小的方法,配合underscore释放的一些数组操作,便可以简单的完成列表渲染功能,因为这里最终的渲染逻辑没有改变,改变的仅仅是排序后的数组。
此处对班次数据的处理篇幅已经超过了50行,如果再增加,可以实例化一个班次Entity用于格式化的数据输出,因为我们这里没有实现这个实体,放到根View中又过于臃肿,所以将之放到Module中,这样关于筛选的所有API全部下放到了排序模块,而主View代码就会清晰的多:
1 //完成所有的筛选条件,逻辑比较重
2 getFilteData: function () {
3 var data = this.formatData(this.listData);
4 data = this.sortModule.getSortData(data);
5 return data;
6 },
1 define(['ModuleView', 'text!pages/tpl.sort.bar.html'], function (ModuleView, tpl) {
2 return _.inherit(ModuleView, {
3
4 //此处若是要使用model,处实例化时候一定要保证entity的存在,如果不存在便是业务BUG
5 initData: function () {
6
7 this.template = tpl;
8 this.events = {
9 'click .js_sort_item li ': function (e) {
10 var el = $(e.currentTarget);
11 var sort = el.attr('data-sort');
12 this.sortEntity['set' + sort]();
13 }
14 };
15
16 this.sortEntity.subscribe(this.render, this);
17
18 },
19
20 _timeSort: function (data, sort) {
21 data = _.sortBy(data, function (item) {
22 item = item.from_time.split(':');
23 item = item[0] + '.' + item[1];
24 item = parseFloat(item);
25 return item;
26 });
27 if (sort == 'down') data.reverse();
28 return data;
29 },
30
31 _sumTimeSort: function (data, sort) {
32 data = _.sortBy(data, function (item) {
33 return parseInt(item.use_time);
34 });
35 if (sort == 'down') data.reverse();
36 return data;
37 },
38
39 _priceSort: function (data, sort) {
40 data = _.sortBy(data, function (item) {
41 return item.min_price;
42 });
43 if (sort == 'down') data.reverse();
44 return data;
45 },
46
47 //获取导航栏排序后的数据
48 getSortData: function (data) {
49 var tmp = [];
50 var sort = this.sortEntity.get();
51
52 for (var k in sort) {
53 if (sort[k].length > 0) {
54 tmp = this['_' + k + 'Sort'](data, sort[k])
55 return tmp;
56 }
57 }
58 },
59
60 getViewModel: function () {
61 return this.sortEntity.get();
62 }
63
64 });
65
66 });
sort模块
至此,一个完整的业务组件便实现结束了,那么这里也实现了一个简单的View组件系统,那么这么做有什么不足呢?
不足
首先,我们这里的实现扔是以js为基础,这种做法似乎不太“组件化”,更好的实现似乎是直接以一个标签的方式使用。
然后,可以看出,我们每次点击先是改变数据,然后数据触发更新,刷新了整个列表,也改变了组件本身的展示,这里的方案简单粗暴,完全是重新渲染,这种重新渲染的做法,在数据列表达到一定数量的话是一种资源浪费。
但是楼主基本无力解决以上问题,这种问题我们看看Vue与React后面是如何解决的,这里暂时搁置。
底部菜单
我们这里做的第二个业务组件是底部菜单栏:
观察这个视觉,我们这里出现了一个纯粹的UI组件,于是我们做的第一步是实现这个UI组件。
UI组件的实现
所谓UI组件是不包含任何业务代码,因为UI组件不是今天的重点,我这里贴出实现不做过多讲解:
1 define([
2 'AbstractView'
3 ], function (
4 AbstractView
5 ) {
6
7 //实现一个方法产生最大的zindex
8 var getBiggerzIndex = (function () {
9 var index = 2000;
10 return function (level) {
11 return level + (++index);
12 };
13 })();
14
15 return _.inherit(AbstractView, {
16
17 propertys: function ($super) {
18 $super();
19
20 //这里设置UI的根节点所处包裹层
21 this.wrapper = $('body');
22 this.viewId = _.uniqueId('ui-view-');
23
24 },
25
26 //为当前View设置最大的zindex,这里用于弹出层情况
27 setzIndexTop: function (el, level) {
28 if (!el) el = this.$el;
29 if (!level || level > 10) level = 0;
30 level = level * 1000;
31 el.css('z-index', getBiggerzIndex(level));
32
33 },
34
35 _getDefaultViewModel: function (arr) {
36 var k, i, len, obj = {};
37 for (i = 0, len = arr.length; i < len; i++) {
38 k = arr[i];
39 if (!_.isUndefined(this[k]) && !_.isNull(this[k])) obj[k] = this[k];
40 }
41 return obj;
42 }
43
44 });
45
46 });
UIView
1 define([
2 'ui/ui.view'
3 ], function (
4 UIView
5 ) {
6
7 return _.inherit(UIView, {
8
9 setRootStyle: function () {
10 this.$el.addClass('cm-overlay');
11 },
12
13 addEvent: function ($super) {
14 $super();
15
16 this.on('onShow', function () {
17 this.setRootStyle();
18 this.setzIndexTop();
19 });
20
21 }
22
23 });
24
25 });
UIMask
1 define([
2 'ui/ui.view',
3 'ui/ui.mask',
4 'text!ui/ui.list.html'
5
6 ], function (
7 UIView,
8 UIMask,
9 template
10
11 ) {
12 return _.inherit(UIView, {
13
14 propertys: function ($super) {
15 $super();
16
17 this.mask = new UIMask();
18
19 this.viewId = 'uilist';
20
21 this.template = template;
22 this.classname = 'cm-layer-list';
23 this.list = [];
24 this.cancelText = '取消';
25 this.index = -1;
26 this.displayNum = 5;
27 this.curClass = 'active';
28
29
30 this.addEvents({
31 'click .js_cancel': 'cancelAction',
32 'click .js_item': 'itemAction'
33 });
34
35 this.onItemAction = function (data, index, e) {
36 };
37
38 },
39
40 getViewModel: function () {
41 return this._getDefaultViewModel(['list', 'cancelText', 'index', 'curClass', 'itemFn', 'title']);
42 },
43
44 setIndex: function (i, position) {
45 if (i < 0 || i > this.list.length) return;
46 this.index = i;
47 this.$('li').removeClass(this.curClass);
48 this.$('li[data-index="' + i + '"]').addClass(this.curClass);
49
50 },
51
52 cancelAction: function (e) {
53 this.hide();
54 },
55
56 //弹出层类垂直居中使用
57 reposition: function () {
58 this.$el.css({
59 'position': 'fixed',
60 '-webkit-box-sizing': 'border-box',
61 'box-sizing': 'border-box',
62 'width': '100%',
63 'left': '0',
64 'background-color': '#fff',
65 'bottom': '36px'
66 });
67 },
68
69 itemAction: function (e) {
70 var el = $(e.currentTarget);
71 if (el.hasClass('disabled')) return;
72
73 var index = el.attr('data-index');
74 var data = this.list[index];
75 this.setIndex(index);
76 this.onItemAction.call(this, data, index, e);
77
78 },
79
80 addEvent: function () {
81
82 this.on('onPreShow', function () {
83 this.mask.show();
84 });
85
86 this.on('onShow', function () {
87
88 this.setzIndexTop();
89 this.reposition();
90
91 });
92
93 this.on('onHide', function () {
94 this.mask.hide();
95 });
96
97 }
98
99 });
100
101 });
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结构便已经存在,我们这里做的一件事情就是当组件已经存在如何和组件交互逻辑关联起来,这里做的第一步也是最重要一部依旧是数据实体的抽象。
他这个数据是一个多选框类型的数据,数组的第一项是全选功能,根据需求我们抽象出了这种数据实体:
1 define(['AbstractEntity'], function (AbstractEntity) {
2
3 var Entity = _.inherit(AbstractEntity, {
4 propertys: function ($super) {
5 $super();
6 this.data = [];
7 },
8
9 getLength: function () {
10 return this.data.length;
11 },
12
13 unCheckAll: function () {
14 for (var i = 0, len = this.getLength(); i < len; i++) {
15 this.data[i].checked = false;
16 }
17 },
18
19 checkAll: function (noEvent) {
20 if (this.getLength() == 0) return;
21 this.unCheckAll();
22 this.data[0].checked = true;
23 if (!noEvent) this.update();
24 },
25
26 setIndex: function (i, noEvent) {
27 if (typeof i === 'string') i = parseInt(i);
28 if (i < 0 || i > this.getLength()) return;
29 if (i === 0) {
30 this.checkAll(noEvent);
31 return;
32 }
33 this.data[0].checked = false;
34 if (this.data[i].checked) this.data[i].checked = false;
35 else this.data[i].checked = true;
36
37 //如果除了第一个都被选了的话,那么就是全选,如果全部没被选也得全选
38 if (this.getCheckedIndex().length == this.getLength() - 1 || this.getCheckedIndex().length == 0) {
39 this.checkAll(noEvent);
40 }
41
42 if (!noEvent) this.update();
43 },
44
45 getCheckedIndex: function (index) {
46 var indexArr = [];
47 for (var i = 0, len = this.getLength(); i < len; i++) {
48 if (index === i && this.data[i].checked) continue;
49 if (this.data[i].checked) indexArr.push(i);
50 }
51 return indexArr;
52 },
53
54 getCheckedKey: function () {
55 if (this.data[0].checked) return null;
56 var checed = [], index = this.getCheckedIndex();
57 for (var i = 0, len = index.length; i < len; i++) {
58 checed.push(this.data[index[i]].id);
59 }
60 return checed;
61 },
62
63 initData: function (data) {
64 this.data = data;
65 }
66
67 });
68
69 return Entity;
70 });
多选数据实体
有了数据实体,我们这里便需要实现使用数据实体的组件(非最终代码):
1 define(['ModuleView', 'ui/ui.list'], function (ModuleView, UILayerList) {
2 return _.inherit(ModuleView, {
3
4 //此处若是要使用model,处实例化时候一定要保证entity的存在,如果不存在便是业务BUG
5 initData: function () {
6
7 this.events = {
8 'click': 'showLayer'
9 };
10
11 this.entity.checkAll(true);
12
13 },
14
15 onHide: function () {
16 if (this.layer) {
17 this.layer.destroy();
18 }
19 },
20
21 showLayer: function () {
22
23 var scope = this;
24 var data = this.entity.get();
25 if (data.length == 0) return;
26
27 if (!this.layer) {
28
29 //这里注释了车站地图需求
30 this.layer = new UILayerList({
31 list: data,
32 events: {
33 'click .js_ok': function () {
34 scope.entity.update();
35 this.hide();
36 }
37 },
38 onHide: function () {
39 scope.searchBarEntity.resetData(true);
40 },
41 title: '<span class="fl js_cancel" style="font-weight: 500;">取消</span><span class="fr js_ok" style="color: #00b358; font-weight: 500;">确定</span>',
42 itemFn: function (item) {
43 return '<div style="text-align: left; padding-left: 10px; ">' + item.name + '</div>';
44 },
45 setIndex: function (i) {
46 scope.entity.setIndex(i, true);
47 this.setIndexArr();
48 },
49 setIndexArr: function () {
50 var indexArr = scope.entity.getCheckedIndex();
51 if (typeof indexArr == 'number') indexArr = [indexArr];
52 this.$('li').removeClass(this.curClass);
53 for (var i = 0, len = indexArr.length; i < len; i++) this._setIndex(indexArr[i])
54 },
55 _setIndex: function (i) {
56 if (i < 0 || i > this.list.length) return;
57 this.index = i;
58 this.$('li[data-index="' + i + '"]').addClass(this.curClass);
59 }
60 });
61 } else {
62 this.layer.list = data;
63 this.layer.refresh();
64 }
65
66 this.layer.show();
67 this.layer.setIndexArr();
68 },
69
70 getViewModel: function () {
71 return this.entity.get();
72 }
73
74 });
75
76 });
组件代码
然后在根View中将View与组件关联起来:
1 //车次类型数据实体
2 this.trainTypeEntity = new CheckBoxEntity({
3 data: [
4 { name: '全部车次', id: 'all', checked: true },
5 { name: '高铁城际(G/C)', id: 'g' },
6 { name: '动车(D)', id: 'd' },
7 { name: '特快(T)', id: 't' },
8 { name: '其它类型', id: 'other' }
9 ]
10 });
11
12 //车次类型模块
13 this.trainTypeModule = new CheckBoxModule({
14 view: this,
15 selector: '.js_type',
16 tagname: 'Type',
17 entity: this.trainTypeEntity
18 });
这样,我们点击车次类型便能很好的运行了,但是tab并没有被选中:
这里思考一个问题:底部有三个组件交互,依次是车次类型、出发站、更多,事实上组件之间并不知道当前是哪个tab被点击了,应该展示选中状态,知道的是根View,所以我们这里需要在View中实现一个数据实体,注入给三个业务组件,告诉他们现在该谁被选中了,下面三个tab只有一个能选中,并且选择了一个tab,另一个tab的菜单如果是展示状态需要将其隐藏,所以我们实现了单选的实体:
1 define(['AbstractEntity'], function (AbstractEntity) {
2
3 var Entity = _.inherit(AbstractEntity, {
4 propertys: function ($super) {
5 $super();
6 this.data = [];
7 },
8
9 getLength: function () {
10 return this.data.length;
11 },
12
13 unCheckAll: function () {
14 for (var i = 0, len = this.getLength(); i < len; i++) {
15 this.data[i].checked = false;
16 }
17 this.update();
18 },
19
20 setIndex: function (i, noEvent) {
21 if (typeof i === 'string') i = parseInt(i);
22 if (i < 0 || i > this.getLength()) return;
23 this.unCheckAll();
24 this.data[i].checked = true;
25 if (!noEvent) this.update();
26 },
27
28 setId: function(id) {
29
30 for(var i = 0, len = this.getLength(); i < len; i++) {
31 if(this.data[i].id == id) {
32 this.setIndex(i);
33 return;
34 }
35 }
36 },
37
38 getCheckedIndex: function () {
39 for (var i = 0, len = this.getLength(); i < len; i++) {
40 if (this.data[i].checked) return i;
41 }
42 return null;
43 },
44
45 getCheckedKey: function () {
46 var index = this.getCheckedIndex();
47 if (index !== null) return this.data[index].id;
48 return null;
49 },
50
51 getCheckedName: function () {
52 var index = this.getCheckedIndex();
53 if (index !== null) return this.data[index].name;
54 return null;
55 }
56
57 });
58
59 return Entity;
60 });
单选数据实体
然后是出发站的实现,这里出发站有一点特殊,首先这个数据需要列表加载结束,我们去筛选数据获得出发站,所以实体有一个初始化的过程(而且这里数据更新是不需要触发事件的);其次他的交互与车次类型完全一致,唯一不同的只是数据实体,所以这里出发站的组件我们可以复用,只需要实例化一个数据出来即可:
1 //由初始数据筛选出所有出发站
2 initSetout: function () {
3 var data = this.listData;
4 var stations = [];
5 var stationMap = {};
6 var tmp = [{ id: 'all', name: '全部出发站'}];
7
8 for (var i = 0, len = data.length; i < len; i++) {
9 stationMap[data[i].from_telecode] = data[i].from_station;
10 if (data[i].from_station_type == '起点' && _.indexOf(stations, data[i].from_telecode) == -1) {
11 stations.push(data[i].from_telecode);
12 }
13 }
14
15 for (i = 0, len = stations.length; i < len; i++) {
16 var key = stations[i];
17 var value = stationMap[key];
18 stations[i] = {
19 id: key,
20 name: value
21 };
22 }
23
24 tmp = tmp.concat(stations);
25
26 this.setoutEntity.initData(tmp);
27 this.setoutEntity.checkAll(true);
28
29 },
view
如此底部菜单栏中的车次类型与出发站,我们便基本实现了,这里每次数据变化还需要更新列表数据,这里在主View中绑定相关变化即可,然后再重写下renderList,便结束了主要功能,这里有个不同的地方,是列表数据的筛选却不能放在Module中,因为车次类型与出发站的数据筛选可能不一样,所以这样看来最初的班次数据操作就应该封装为一个ListEntity做数据筛选,我们这里暂时放到主View中:
1 //根据车次类型做筛选
2 getTypeData: function (data) {
3 var typeKeys = this.trainTypeEntity.getCheckedKey();
4 if (!typeKeys) return data;
5 var tmp = _.filter(data, function (item) {
6 var no = item.my_train_number;
7 if (_.indexOf(typeKeys, no) != -1) {
8 return true;
9 }
10 return false;
11 });
12
13 return tmp;
14 },
15
16 //根据出发站做筛选
17 //事实上这个方法与getTypeData不是完全不能重构到一起,但是可读性可能会变得晦涩
18 getSetoutData: function (data) {
19 var keys = this.setoutEntity.getCheckedKey();
20 if (!keys) return data;
21
22 var tmp = _.filter(data, function (item) {
23 var no = item.from_telecode;
24 if (_.indexOf(keys, no) != -1)
25 return true;
26 return false;
27 });
28
29 return tmp;
30 },
31
32 //完成所有的筛选条件,逻辑比较重
33 getFilteData: function () {
34 var data = this.formatData(this.listData);
35 data = this.getTypeData(data);
36 data = this.getSetoutData(data);
37 data = this.sortModule.getSortData(data);
38 return data;
39 },
更多功能
这里更多功能也是比较复杂的,但是就算一个更多,里面又会分为几个小组件,几个数据实体,所以真实实现功能后代码反而简单,这里我便不做实现,感兴趣的同学作为家庭作业做吧。
总结
我们这里使用js实现了一个简单的组件化View的实现,中间形成了纯粹的UI组件,也将View拆分实现了一些业务组件,最终形成的目录为:
有兴趣的朋友在git上面去看吧,这里是入口js:
1 (function () {
2
3 require.config({
4 paths: {
5 'text': 'libs/require.text',
6
7 'AbstractView': 'js/view',
8 'AbstractEntity': 'js/entity',
9 'ModuleView': 'js/module'
10
11
12 }
13 });
14
15 require(['pages/list'], function (List) {
16
17 var list = new List();
18 list.show();
19
20 });
21
22 })();
好了,经过上面的实现,如果看过之前我的博客的话,应该对组件化开发有一定了解了,我们也可以进入今日的正题了,首先我们以Vue实现上述功能。
Vue的实现
首先,这里说Vue的实现,不是底层源码分析,而是实现上述功能,这里大家不要误会,因为我也是昨天才开始看Vue,暂时无能了解他的实现。
我虽然没有使用过Vue做业务开发,但是在很久之前就听我一个好基友夸耀Vue,到今日实现,Vue已经火的不行了。。。。。。
今天,就让我们来试试Vue的威力,因为是初次使用,如果有不对的地方,大家可以指正,也不要喷了
组件拆分
根据之前的实现,我们发现我们的组件可以轻易的分为三个部分:
① 顶部导航
② 列表
③ 底部菜单
我们之前做的第一件事情是将列表展示做掉,这种开发流程是不会改变的,所以使用Vue将列表展示实现。
组件定义
在使用组件化之前,我们先看看Vue如何实现一个组件(这里不得不说Vue的作者文档确实写的好):
1 // 定义
2 var MyComponent = Vue.extend({
3 template: '<div>A custom component!</div>'
4 })
5
6 // 注册
7 Vue.component('my-component', MyComponent)
8
9 // 创建根实例
10 new Vue({
11 el: '#example'
12 })
1 <!--正确做法-->
2 <article class="cm-page" id="main">
3 <my-component></my-component>
4 </article>
5
6 <!--错误做法,组件一定要依赖根部View-->
7 <my-component></my-component>
作者这个实现很不错,也考虑了不是所有组件都需要全局释放的,一般来说可以全局释放的组件都与业务无关或者是公共业务,这里是局部组件的实现:
1 var Child = Vue.extend({ /* ... */ })
2
3 var Parent = Vue.extend({
4 template: '...',
5 components: {
6 // <my-component> 只能用在父组件模板内
7 'my-component': Child
8 }
9 })
上面的例子比较简单,我们马上使用一个复杂一点的例子试试水:
1 // 定义
2 var MyList = Vue.extend({
3
4 data: function () {
5 return {
6 data: /**/[
7 {name: '出发时间', id: 'time'},
8 {name: '耗时', id: 'sumTime'},
9 {name: '价格', id: 'price'}
10 ]
11 };
12 },
13 template: [
14 '<ul>',
15 '<li v-for="item in data" >',
16 '{{item.name}}',
17 '</li>',
18 '</ul>'
19 ].join('')
20 })
21
22 // 注册
23 Vue.component('my-list', MyList)
24
25 // 创建根实例
26 new Vue({
27 el: '#main'
28 })
<article class="cm-page" id="main">
<my-component></my-component>
<my-list></my-list>
</article>
这段代码会输出:
1 <article class="cm-page" id="main">
2 <div>A custom component!</div>
3 <ul><li>出发时间</li><li>耗时</li><li>价格</li></ul>
4 </article>
这里了解到这里便暂时结束,我们来实现我们的列表组件。
根实例
我们在第一节的实现中很多初始化的动作与数据全部放到了根View中,同样,我们这里需要实现一个根View:
1 define([
2 'Vue'
3 ], function (Vue) {
4 return new Vue({
5 data: {
6 a: 1
7 },
8 el: '#main',
9 template: '<div>test</div>'
10 });
11 });
现在我们在入口index.html将写入list组件,然后在写实现(不得不说,我也认为这种做法很直观):
<article class="cm-page" id="main">
<my-list></my-list>
</article>
根据上面的知识我们实现这个业务组件,这里也碰到了第一个问题,根View如何将数据传递给他的组件也就是,组件与View之间如何通信。
组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。可以使用 props 把数据传给子组件。
“prop” 是组件数据的一个字段,期望从父组件传下来。子组件需要显式地用 props 选项 声明 props:
根据文档,我这里写个demo试试:
1 var MyList = Vue.extend({
2 props: ['data'],
3 template: [
4 '<ul>',
5 '<li v-for="item in data" >',
6 '{{item.name}}',
7 '</li>',
8 '</ul>'
9 ].join('')
10 })
11
12 // 注册
13 Vue.component('my-list', MyList)
14
15 // 创建根实例
16 new Vue({
17 data: {
18 name: 'test',
19 data: /**/[
20 {name: '出发时间', id: 'time'},
21 {name: '耗时', id: 'sumTime'},
22 {name: '价格', id: 'price'}
23 ]
24 },
25 el: '#main'
26 })
<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>
1 define([
2 'Vue',
3 'pages/list.data',
4 'pages/mod.list'
5
6 ], function (
7 Vue,
8 listData,
9 ListModule
10
11 ) {
12
13 return new Vue({
14 components: {
15 'my-list': ListModule
16 },
17 data: {
18 data: formatData(listData)
19 },
20 el: '#main'
21 });
22
23 //该方法放到这里是否合适?
24 function formatData(data) {
25 var item, seat;
26 var typeMap = {
27 'g': 'g',
28 'd': 'd',
29 't': 't',
30 'c': 'g'
31 };
32
33 //出发时间对应的分钟数
34 var fromMinute = 0;
35
36 //获取当前班车日期当前的时间戳,这个数据是动态的,这里写死了
37 var d = 1464192000000;
38 var date = new Date();
39 var now = parseInt(date.getTime() / 1000);
40 date.setTime(d);
41 var year = date.getFullYear();
42 var month = date.getMonth();
43 var day = date.getDate();
44 var toBegin;
45 var seatName, seatIndex, iii;
46
47 //处理坐席问题,仅显示二等座,一等座,特等座 无座
48 // 二等座 一等座 商务座 无座 动卧 特等座
49 var my_seats = {};
50 var seatSort = ['二等座', '一等座', '硬座', '硬卧', '软卧', '商务座', '无座', '动卧', '特等座', '软座'];
51
52 for (var i = 0, len = data.length; i < len; i++) {
53 fromMinute = data[i].from_time.split(':');
54 fromMinute[0] = fromMinute[0] + '';
55 fromMinute[1] = fromMinute[1] + '';
56 if ((fromMinute[0].charAt(0) == '0')) fromMinute[0] = fromMinute[0].charAt(1);
57 if ((fromMinute[1].charAt(0) == '0')) fromMinute[1] = fromMinute[1].charAt(1);
58 date = new Date(year, month, day, fromMinute[0], fromMinute[1], 0);
59 fromMinute = parseInt(date.getTime() / 1000)
60 toBegin = parseInt((fromMinute - now) / 60);
61
62 data[i].toBegin = toBegin;
63
64 //处理车次类型问题
65 data[i].my_train_number = typeMap[data[i].train_number.charAt(0).toLowerCase()] || 'other';
66
67 seat = data[i].seats;
68 //所有余票
69 data[i].sum_ticket = 0;
70 //最低价
71 data[i].min_price = null;
72
73 for (var j = 0, len1 = seat.length; j < len1; j++) {
74 if (!data[i].min_price || data[i].min_price > seat[j].seat_price) data[i].min_price = parseFloat(seat[j].seat_price);
75 data[i].sum_ticket += parseInt(seat[j].seat_yupiao);
76
77 //坐席问题如果坐席不包括上中下则去掉
78 seatName = seat[j].seat_name;
79 //去掉上中下
80 seatName = seatName.replace(/上|中|下/g, '');
81 if (!my_seats[seatName]) {
82 my_seats[seatName] = parseInt(seat[j].seat_yupiao);
83 } else {
84 my_seats[seatName] = my_seats[seatName] + parseInt(seat[j].seat_yupiao);
85 }
86 }
87 //这里myseat为对象,需要转换为数组
88 //将定制坐席转为排序后的数组
89 data[i].my_seats = [];
90 for (iii = 0; iii < seatSort.length; iii++) {
91 if (typeof my_seats[seatSort[iii]] == 'number') data[i].my_seats.push({
92 name: seatSort[iii],
93 yupiao: my_seats[seatSort[iii]]
94 });
95 }
96
97 my_seats = {};
98 }
99
100 return data;
101 }
102
103 });
根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
});
});
组件模板部分:
1 <ul class="bus-list js_bus_list ">
2 <li v-for="item in data" class="bus-list-item ">
3 <div class="bus-seat">
4 <span class=" fl">{{item.train_number }} | {{mapping[item.my_train_number] || '其它'}} </span>
5 <span class=" fr">{{parseInt(item.use_time / 60) + '小时' + item.use_time % 60 + '分'}}</span>
6 </div>
7 <div class="detail">
8 <div class="sub-list set-out">
9 <span class="bus-go-off">{{item.from_time}}</span> <span class="start"><span class="icon-circle s-icon1">
10 </span>{{item.from_station }}</span> <span class="fr price">¥{{item.min_price}}起</span>
11 </div>
12 <div class="sub-list">
13 <span class="bus-arrival-time">{{item.to_time}}</span> <span class="end"><span class="icon-circle s-icon2">
14 </span>{{item.to_station}}</span> <span class="fr ">{{item.sum_ticket}}张</span>
15 </div>
16 </div>
17 <div class="bus-seats-info" >
18 <span v-for="seat in item.my_seats">{{seat.name}}({{seat.yupiao }}) </span>
19 </div>
20 </li>
21 </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的语法即可:
1 <ul class="bus-tabs sort-bar js_sort_item">
2 <li class="tabs-item " v-on:click="setTime" style="-webkit-flex: 1.5; flex: 1.5;">
出发时间<i class="icon-sort {{time }}"></i></li>
3 <li class="tabs-item " v-on:click="setSumTime">耗时<i class="icon-sort {{sumTime }}"></i></li>
4 <li class="tabs-item " v-on:click="setPrice">价格<i class="icon-sort {{price }}"></i></li>
5 </ul>
完了完成最主要的组件模块代码实现,在这里小钗发现之前的导航相关的Entity实体中的操作就是所有需要的处理,所以这里可以稍微改造下便使用:
1 define([
2 'Vue',
3 'text!pages/tpl.sort.html'
4 ], function (Vue,
5 template) {
6
7 return Vue.extend({
8 props: ['data'],
9 data: function () {
10 return {
11 time: 'up',
12 sumTime: '',
13 price: ''
14 };
15 },
16 methods: {
17 _resetData: function () {
18 this.time = '';
19 this.sumTime = '';
20 this.price = '';
21 },
22
23 setTime: function () {
24 this._setData('time');
25 },
26
27 setSumTime: function () {
28 this._setData('sumTime');
29 },
30
31 setPrice: function () {
32 this._setData('price');
33 },
34
35 _setData: function (key) {
36 //如果设置当前key存在,则反置,否则清空筛选,设置默认值
37 if (this[key] != '') {
38 if (this[key] == 'up') this[key] = 'down';
39 else this[key] = 'up';
40 } else {
41 this._resetData();
42 this[key] = 'down';
43 }
44 }
45 },
46 template: template
47
48 });
49
50 });
对比下之前数据实体的代码,以及组件控制器的实现:
1 define(['ModuleView', 'text!pages/tpl.sort.bar.html'], function (ModuleView, tpl) {
2 return _.inherit(ModuleView, {
3
4 //此处若是要使用model,处实例化时候一定要保证entity的存在,如果不存在便是业务BUG
5 initData: function () {
6
7 this.template = tpl;
8 this.events = {
9 'click .js_sort_item li ': function (e) {
10 var el = $(e.currentTarget);
11 var sort = el.attr('data-sort');
12 this.sortEntity['set' + sort]();
13 }
14 };
15
16 this.sortEntity.subscribe(this.render, this);
17
18 },
19
20 getViewModel: function () {
21 return this.sortEntity.get();
22 }
23
24 });
25
26 });
1 define(['AbstractEntity'], function (AbstractEntity) {
2
3 var Entity = _.inherit(AbstractEntity, {
4 propertys: function ($super) {
5 $super();
6
7 //三个对象,时间,耗时,架构,升序降序,三个数据互斥
8 //默认down up null
9 this.data = {
10 time: 'up',
11 sumTime: '',
12 price: ''
13 };
14 },
15
16 _resetData: function () {
17 this.data = {
18 time: '',
19 sumTime: '',
20 price: ''
21 };
22 },
23
24 setTime: function () {
25 this._setData('time');
26 },
27
28 setSumTime: function () {
29 this._setData('sumTime');
30 },
31
32 setPrice: function () {
33 this._setData('price');
34 },
35
36 _setData: function (key) {
37
38 //如果设置当前key存在,则反置,否则清空筛选,设置默认值
39 if (this.data[key] != '') {
40 if (this.data[key] == 'up') this.data[key] = 'down';
41 else this.data[key] = 'up';
42 } else {
43 this._resetData();
44 this.data[key] = 'down';
45 }
46 this.update();
47 }
48
49 });
50
51 return Entity;
52 });
数据实体
这里是Vue根仅仅使用AMD方式引入该模块即可,View的实现:
components: {
'my-list': ListModule,
'my-sort-bar': SortModule
},
现在第二个问题来了,这里每次操作事实上应该影响列表组件的排序,之前我们这块是通过数据实体entity做通信,不出意外的话Vue也该如此,但是小钗在这里却产生了疑惑,该怎么做,怎么实现?
因为根据之前经验,主View与组件之间以数据实体的方式通信是比较常见的操作,组件之间也可以通过数据实体沟通,因为数据实体是实例化在根View中的,但是组件与组件之间不应该产生直接的关联,一般来说主View或者组件使用数据以外的方式都是不可取的。
这里顶部导航组件是独立的,并没有释放对外的接口,根View也没有注入数据对象给他,那么他的变化该如何通知到列表组件,让他重新排序呢?这个时候又开始查文档ing。
小钗这里没有想到很好的办法,于是将顶部导航的组件的数据上升到了主View中,主View以pros的方式传递了给两个组件,所以上述代码要有变动,首先是根节点:
1 data: {
2 data: formatData(listData),
3 sort: {
4 time: 'up',
5 sumTime: '',
6 price: ''
7 }
8 },
html中的调用:
1 <article class="cm-page page-list" id="main">
2 <div class="js_sort_wrapper sort-bar-wrapper">
3 <my-sort-bar :sort="sort"></my-sort-bar>
4 </div>
5 <my-list :data="data"></my-list>
6 </article>
最后是组件js与具体模板的实现:
1 define([
2 'Vue',
3 'text!pages/tpl.sort.html'
4 ], function (Vue,
5 template) {
6
7 return Vue.extend({
8 props: ['sort'],
9 methods: {
10 _resetData: function () {
11 this.sort.time = '';
12 this.sort.sumTime = '';
13 this.sort.price = '';
14 },
15
16 setTime: function () {
17 this._setData('time');
18 },
19
20 setSumTime: function () {
21 this._setData('sumTime');
22 },
23
24 setPrice: function () {
25 this._setData('price');
26 },
27
28 _setData: function (key) {
29 //如果设置当前key存在,则反置,否则清空筛选,设置默认值
30 if (this.sort[key] != '') {
31 if (this.sort[key] == 'up') this.sort[key] = 'down';
32 else this.sort[key] = 'up';
33 } else {
34 this._resetData();
35 this.sort[key] = 'down';
36 }
37 }
38 },
39 template: template
40
41 });
42
43 });
View Code
1 <ul class="bus-tabs sort-bar js_sort_item">
2 <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>
3 <li class="tabs-item " v-on:click="setSumTime" data-sort="SumTime" >耗时<i class="icon-sort {{sort.sumTime }}"></i></li>
4 <li class="tabs-item " v-on:click="setPrice" data-sort="Price" >价格<i class="icon-sort {{sort.price }}"></i></li>
5 </ul>
View Code
这样一来主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中,然后传递给子组件,因为出发城市和车次类型事实上是一个组件,所以上面的结构有所变化:
1 <article class="cm-page page-list" id="main">
2 <div class="js_sort_wrapper sort-bar-wrapper">
3 <my-sort-bar :sort="sort"></my-sort-bar>
4 </div>
5 <my-list :data="data" :sort="sort"></my-list>
6 <ul class="bus-tabs list-filter js_tabs " style="z-index: 3000;">
7 <my-tab-item :data="type" name="车次类型"></my-tab-item>
8 <my-tab-item :data="setout" name="出发站"></my-tab-item>
9 <li class="tabs-item js_more">更多<i class="icon-sec"></i>
10 </li>
11 </ul>
12 </article>
对应的组件第一步也结束了:
1 data: {
2 data: formatData(listData),
3 sort: {
4 time: 'up',
5 sumTime: '',
6 price: ''
7 },
8 //车次类型
9 type: [
10 {name: '全部车次', id: 'all', checked: true},
11 {name: '高铁城际(G/C)', id: 'g'},
12 {name: '动车(D)', id: 'd'},
13 {name: '特快(T)', id: 't'},
14 {name: '其它类型', id: 'other'}
15 ],
16 setout: [
17 {name: '全部出发站', id: 'all', checked: true}
18 ]
19 },
1 define([
2 'Vue',
3 'text!pages/tpl.tabs.html',
4 'ui/ui.list'
5 ], function (Vue,
6 template,
7 UILayerList) {
8
9 return Vue.extend({
10 props: ['data', 'name'],
11 methods: {
12
13 getLength: function () {
14 return this.data.length;
15 },
16
17 unCheckAll: function () {
18 for (var i = 0, len = this.getLength(); i < len; i++) {
19 this.data[i].checked = false;
20 }
21 },
22
23 checkAll: function (noEvent) {
24 if (this.getLength() == 0) return;
25 this.unCheckAll();
26 this.data[0].checked = true;
27 //if (!noEvent) this.update();
28 },
29
30 setIndex: function (i, noEvent) {
31 if (typeof i === 'string') i = parseInt(i);
32 if (i < 0 || i > this.getLength()) return;
33 if (i === 0) {
34 this.checkAll(noEvent);
35 return;
36 }
37 this.data[0].checked = false;
38 if (this.data[i].checked) this.data[i].checked = false;
39 else this.data[i].checked = true;
40
41 //如果除了第一个都被选了的话,那么就是全选,如果全部没被选也得全选
42 if (this.getCheckedIndex().length == this.getLength() - 1 || this.getCheckedIndex().length == 0) {
43 this.checkAll(noEvent);
44 }
45
46 //if (!noEvent) this.update();
47 },
48
49 getCheckedIndex: function (index) {
50 var indexArr = [];
51 for (var i = 0, len = this.getLength(); i < len; i++) {
52 if (index === i && this.data[i].checked) continue;
53 if (this.data[i].checked) indexArr.push(i);
54 }
55 return indexArr;
56 },
57
58 getCheckedKey: function () {
59 if (this.data[0].checked) return null;
60 var checed = [], index = this.getCheckedIndex();
61 for (var i = 0, len = index.length; i < len; i++) {
62 checed.push(this.data[index[i]].id);
63 }
64 return checed;
65 },
66
67
68 showLayer: function() {
69 var data = this.data;
70 var scope = this;
71
72 if(!data) return;
73
74 if (this.layer && this.layer.status == 'show') return;
75
76 if (!this.layer) {
77
78 //这里注释了车站地图需求
79 this.layer = new UILayerList({
80 list: data,
81 events: {
82 'click .js_ok': function () {
83 this.hide();
84 }
85 },
86 onHide: function () {
87
88 },
89 title: '<span class="fl js_cancel" style="font-weight: 500;">取消</span><span class="fr js_ok" style="color: #00b358; font-weight: 500;">确定</span>',
90 itemFn: function (item) {
91 return '<div style="text-align: left; padding-left: 10px; ">' + item.name + '</div>';
92 },
93 setIndex: function (i) {
94 scope.setIndex(i, true);
95 this.setIndexArr();
96 },
97 setIndexArr: function () {
98 var indexArr = scope.getCheckedIndex();
99
100 if (typeof indexArr == 'number') indexArr = [indexArr];
101 this.$('li').removeClass(this.curClass);
102 for (var i = 0, len = indexArr.length; i < len; i++) this._setIndex(indexArr[i])
103 },
104 _setIndex: function (i) {
105 if (i < 0 || i > this.list.length) return;
106 this.index = i;
107 this.$('li[data-index="' + i + '"]').addClass(this.curClass);
108 }
109 });
110 } else {
111 this.layer.list = data;
112 this.layer.refresh();
113 }
114
115 this.layer.show();
116 this.layer.setIndexArr();
117
118 },
119
120 hideLayer: function() {
121
122 }
123 },
124 template: template
125
126 });
127
128 });
View Code
从代码可以看出,组件中包含了原来的entity的操作,与module操作,事实上这两块东西应该也可以按原来那种方式划分,让组件中的代码更加纯粹不去操作过多数据,第一步结束,第二步是当数据变化时候更新到列表,这里又需要观察数据变化了,最后形成了这个代码:
1 define([
2 'Vue',
3 'pages/list.data',
4 'pages/mod.list',
5 'pages/mod.sort',
6 'pages/mod.tabs'
7
8 ], function (Vue,
9 listData,
10 ListModule,
11 SortModule,
12 TabModule) {
13
14 return new Vue({
15 components: {
16 'my-list': ListModule,
17 'my-sort-bar': SortModule,
18 'my-tab-item': TabModule
19 },
20 data: {
21 data: formatData(listData),
22 dataTmp: formatData(listData),
23 sort: {
24 time: 'up',
25 sumTime: '',
26 price: ''
27 },
28 //车次类型
29 type: [
30 {name: '全部车次', id: 'all', checked: true},
31 {name: '高铁城际(G/C)', id: 'g', checked: false},
32 {name: '动车(D)', id: 'd', checked: false},
33 {name: '特快(T)', id: 't', checked: false},
34 {name: '其它类型', id: 'other', checked: false}
35 ],
36 setout: getSetout()
37 },
38
39 methods: {
40
41 _timeSort: function (data, sort) {
42 data = _.sortBy(data, function (item) {
43 item = item.from_time.split(':');
44 item = item[0] + '.' + item[1];
45 item = parseFloat(item);
46 return item;
47 });
48 if (sort == 'down') data.reverse();
49 return data;
50 },
51
52 _sumTimeSort: function (data, sort) {
53 data = _.sortBy(data, function (item) {
54 return parseInt(item.use_time);
55 });
56 if (sort == 'down') data.reverse();
57 return data;
58 },
59
60 _priceSort: function (data, sort) {
61 data = _.sortBy(data, function (item) {
62 return item.min_price;
63 });
64 if (sort == 'down') data.reverse();
65 return data;
66 },
67
68 //获取导航栏排序后的数据
69 getSortData: function (data) {
70 var tmp = [];
71 var sort = this.sort;
72
73 for (var k in sort) {
74 if (sort[k].length > 0) {
75 tmp = this['_' + k + 'Sort'](data, sort[k])
76 return tmp;
77 }
78 }
79 },
80
81 //根据车次类型做筛选
82 getTypeData: function (data) {
83 var typeKeys = [];
84 var _data = this.type;
85
86 for(var i = 0, len = _data.length; i < len; i++) {
87 if (_data[0].checked) return data;
88 if(_data[i].checked) typeKeys.push(_data[i].id);
89 }
90
91 if (!typeKeys) return data;
92
93 var tmp = _.filter(data, function (item) {
94 var no = item.my_train_number;
95 if (_.indexOf(typeKeys, no) != -1) {
96 return true;
97 }
98 return false;
99 });
100
101 return tmp;
102 },
103
104 onCreate: function() {
105
106 console.log(1)
107
108 },
109
110 //根据出发站做筛选
111 //事实上这个方法与getTypeData不是完全不能重构到一起,但是可读性可能会变得晦涩
112 getSetoutData: function (data) {
113 var typeKeys = [];
114 var _data = this.setout;
115
116 for(var i = 0, len = _data.length; i < len; i++) {
117 if (_data[0].checked) return data;
118 if(_data[i].checked) typeKeys.push(_data[i].id);
119 }
120
121 if (!typeKeys) return data;
122
123 var tmp = _.filter(data, function (item) {
124 var no = item.from_telecode;
125 if (_.indexOf(typeKeys, no) != -1) {
126 return true;
127 }
128 return false;
129 });
130
131 return tmp;
132 },
133
134 renderList: function() {
135 //这样实现似乎不好
136 var data = this.dataTmp;
137 data= this.getTypeData(data);
138 data= this.getSetoutData(data);
139 data= this.getSortData(data);
140
141 this.data = data;
142
143 }
144
145 },
146
147 watch: {
148 sort: {
149 deep: true,
150 handler: function () {
151 this.renderList();
152 }
153 },
154 type: {
155 deep: true,
156 handler: function() {
157 this.renderList();
158 }
159 },
160 setout: {
161 deep: true,
162 handler: function() {
163 this.renderList();
164 }
165 }
166 },
167 el: '#main'
168 });
169
170 //该方法放到这里是否合适?
171 function formatData(data) {
172 var item, seat;
173 var typeMap = {
174 'g': 'g',
175 'd': 'd',
176 't': 't',
177 'c': 'g'
178 };
179
180 //出发时间对应的分钟数
181 var fromMinute = 0;
182
183 //获取当前班车日期当前的时间戳,这个数据是动态的,这里写死了
184 var d = 1464192000000;
185 var date = new Date();
186 var now = parseInt(date.getTime() / 1000);
187 date.setTime(d);
188 var year = date.getFullYear();
189 var month = date.getMonth();
190 var day = date.getDate();
191 var toBegin;
192 var seatName, seatIndex, iii;
193
194 //处理坐席问题,仅显示二等座,一等座,特等座 无座
195 // 二等座 一等座 商务座 无座 动卧 特等座
196 var my_seats = {};
197 var seatSort = ['二等座', '一等座', '硬座', '硬卧', '软卧', '商务座', '无座', '动卧', '特等座', '软座'];
198
199 for (var i = 0, len = data.length; i < len; i++) {
200 fromMinute = data[i].from_time.split(':');
201 fromMinute[0] = fromMinute[0] + '';
202 fromMinute[1] = fromMinute[1] + '';
203 if ((fromMinute[0].charAt(0) == '0')) fromMinute[0] = fromMinute[0].charAt(1);
204 if ((fromMinute[1].charAt(0) == '0')) fromMinute[1] = fromMinute[1].charAt(1);
205 date = new Date(year, month, day, fromMinute[0], fromMinute[1], 0);
206 fromMinute = parseInt(date.getTime() / 1000)
207 toBegin = parseInt((fromMinute - now) / 60);
208
209 data[i].toBegin = toBegin;
210
211 //处理车次类型问题
212 data[i].my_train_number = typeMap[data[i].train_number.charAt(0).toLowerCase()] || 'other';
213
214 seat = data[i].seats;
215 //所有余票
216 data[i].sum_ticket = 0;
217 //最低价
218 data[i].min_price = null;
219
220 for (var j = 0, len1 = seat.length; j < len1; j++) {
221 if (!data[i].min_price || data[i].min_price > seat[j].seat_price) data[i].min_price = parseFloat(seat[j].seat_price);
222 data[i].sum_ticket += parseInt(seat[j].seat_yupiao);
223
224 //坐席问题如果坐席不包括上中下则去掉
225 seatName = seat[j].seat_name;
226 //去掉上中下
227 seatName = seatName.replace(/上|中|下/g, '');
228 if (!my_seats[seatName]) {
229 my_seats[seatName] = parseInt(seat[j].seat_yupiao);
230 } else {
231 my_seats[seatName] = my_seats[seatName] + parseInt(seat[j].seat_yupiao);
232 }
233 }
234 //这里myseat为对象,需要转换为数组
235 //将定制坐席转为排序后的数组
236 data[i].my_seats = [];
237 for (iii = 0; iii < seatSort.length; iii++) {
238 if (typeof my_seats[seatSort[iii]] == 'number') data[i].my_seats.push({
239 name: seatSort[iii],
240 yupiao: my_seats[seatSort[iii]]
241 });
242 }
243
244 my_seats = {};
245 }
246
247 return data;
248 }
249
250
251 //根据列表筛选出发站
252 function getSetout() {
253 var data = listData;
254 var stations = [];
255 var stationMap = {};
256 var tmp = [{id: 'all', name: '全部出发站', checked: true}];
257
258 for (var i = 0, len = data.length; i < len; i++) {
259 stationMap[data[i].from_telecode] = data[i].from_station;
260 if (data[i].from_station_type == '起点' && _.indexOf(stations, data[i].from_telecode) == -1) {
261 stations.push(data[i].from_telecode);
262 }
263 }
264
265 for (i = 0, len = stations.length; i < len; i++) {
266 var key = stations[i];
267 var value = stationMap[key];
268 stations[i] = {
269 id: key,
270 name: value,
271 checked: false
272 };
273 }
274
275 tmp = tmp.concat(stations);
276
277 return tmp;
278 }
279
280
281 });
View Code
这个应该不是最优的做法,后续值得深入研究,最后我们实现下tab的选择效果便结束Vue的代码。
PS:这个写的有点累了,具体代码大家去git上看吧,这里不多写了。
总结
Vue带来了最大一个好处是:
摆脱DOM操作
你真的在页面中就没有看到任何DOM操作了!这个是很牛的一个事情,另外Vue的文档写的很完备,后面点有时间应该做更深入全面的学习!
结语
介于篇幅过长,楼主体力虚脱,关于React的实现,下次再补齐吧,文中不足希望您的指出。