Commit 6b8d3df635c2287d3b7bb12e647f0a9ebe3e44c1

Authored by 徐烜
1 parent 6622cf87

电子站牌项目

1、重新定义一个线路模拟图滚动列表组件(bsth-line-chart-scrollList)代替原来的bsth-line-chart-list
2、定义了垂直向上滚动策略,每次滚动一条记录,可以无缝滚动,TODO:其他滚动策略再议
3、TODO:滚动的时候发现,滚动项的高度是不一致,导致每次滚动,可能会看到最后的还未显示的记录项的头部,以及滚动到最后然后衔接第一条记录时会抖动,这些问题后面再议
front-end/h5/src/components/core/plugins/bsth/bsth-line-chart-scrollList-page.js 0 → 100644
  1 +/**
  2 + * 自定义线路模拟图滚动列表数据组件。
  3 + */
  4 +
  5 +import TestData from 'core/plugins/bsth/bsth-line-chart-list_testData'
  6 +import LineRouteDataOfApi from 'core/plugins/bsth/models/lineRouteDataOfApi'
  7 +import ScrollListInnerData from 'core/plugins/bsth/models/scrollListInnerData'
  8 +
  9 +export default {
  10 + props: {
  11 + // ---------------- 父组件传递的属性 ---------------- //
  12 + // 编辑模式
  13 + editorMode: { type: String, required: true },
  14 + // 每页大小
  15 + page_size: { type: Number, required: true },
  16 + // 列表宽度
  17 + list_width: { type: Number, required: true },
  18 + // 列表高度
  19 + list_height: { type: Number, required: true },
  20 + // 滚动时间间隔(毫秒)
  21 + scroll_milliseconds: { type: Number, required: true },
  22 + // gps数据刷新间隔(分钟)
  23 + gps_data_refresh_minute: { type: Number, required: true }
  24 + },
  25 + data () {
  26 + return {
  27 + innerDataSet: [], // 数据对象
  28 +
  29 + // --------------- 滚动翻页数据对象 --------------//
  30 + scrollListInnerData: null,
  31 +
  32 + // ---------------- 内部的定时timer --------------- //
  33 + scrollTimer: { // 滚动间隔timer
  34 + timer: null,
  35 + count: 0,
  36 + millisecond: 2000
  37 + }
  38 + }
  39 + },
  40 + mounted () {
  41 + // 1、创建滚动翻页对象
  42 + this.scrollListInnerData = new ScrollListInnerData(this.page_size, this.list_width, this.list_height)
  43 + // 2、初始化数据
  44 + // TODO:先使用测试数据,后面使用后台数据
  45 + const lineRouteDataOfApiArray = []
  46 + for (let obj of TestData.lineRouteList) {
  47 + lineRouteDataOfApiArray.push(new LineRouteDataOfApi(obj))
  48 + }
  49 + this.scrollListInnerData.initRouteData(lineRouteDataOfApiArray)
  50 + this.scrollListInnerData.refreshGps(TestData.lineGpsList)
  51 + this.innerDataSet = this.scrollListInnerData.scrollDataItemList
  52 +
  53 + // 3、发送refreshData事件
  54 + this.$emit('bindData', this.innerDataSet)
  55 +
  56 + // 4、preview模式下启动滚动分页
  57 + if (this.editorMode === 'preview') {
  58 + this.scrollTimer.count++
  59 + }
  60 + },
  61 + destroyed () {
  62 + let scrollTimer = this.scrollTimer.timer
  63 + if (scrollTimer) {
  64 + try {
  65 + clearTimeout(scrollTimer)
  66 + } catch (err) {}
  67 + }
  68 + this.scrollTimer.timer = null
  69 +
  70 + if (this.scrollListInnerData) {
  71 + this.scrollListInnerData.resetData()
  72 + }
  73 + },
  74 + watch: {
  75 + 'scrollTimer.count' () {
  76 + let timer = this.scrollTimer.timer
  77 + if (timer) {
  78 + try {
  79 + clearTimeout(timer)
  80 + } catch (err) {}
  81 + this.scrollTimer.timer = null
  82 + }
  83 + let self = this
  84 + this.scrollTimer.timer = setTimeout(function () {
  85 + self.startScroll()
  86 + self.scrollTimer.count++
  87 + }, this.scroll_milliseconds)
  88 + },
  89 + // -------------------- 监控父组件传递的属性 --------------- //
  90 + page_size (val) {
  91 + // 只在edit模式下监控,preview模式下不监控
  92 + if (this.editorMode === 'edit') { // 使用测试数据
  93 + this.scrollListInnerData.pageSize = val
  94 + }
  95 + },
  96 + list_width (val) {
  97 + this.scrollListInnerData.listWidth = val
  98 + },
  99 + list_height (val) {
  100 + this.scrollListInnerData.listHeight = val
  101 + }
  102 + },
  103 + render () {
  104 + // TODO:暂时是没有内容显示的,只处理内部数据,后面再根据不同的错误显示内容
  105 + return (
  106 + <div></div>
  107 + )
  108 + },
  109 + methods: {
  110 + startScroll () {
  111 + this.scrollListInnerData.scrollUp(this)
  112 + }
  113 + }
  114 +}
front-end/h5/src/components/core/plugins/bsth/bsth-line-chart-scrollList.js 0 → 100644
  1 +/**
  2 + * 自定义线路模拟图滚动列表组件。
  3 + */
  4 +import PropTypes from '@luban-h5/plugin-common-props'
  5 +
  6 +import ScrollPage from 'core/plugins/bsth/bsth-line-chart-scrollList-page'
  7 +
  8 +export default {
  9 + extra: {
  10 + defaultStyle: { // 默认属性
  11 + top: 0,
  12 + left: 0,
  13 + width: 350,
  14 + height: 300
  15 + }
  16 + },
  17 + name: 'bsth-line-chart-scrollList',
  18 + data () {
  19 + // eslint-disable-next-line
  20 + this.private_jQuery = jQuery.noConflict() // jquery引用
  21 + return {
  22 + /**
  23 + * 监控外层元素的宽度高度变化
  24 + * 1、由于编辑器缩放,自定义组件无法监控,所以需要定义一个定时器监控最外层元素的大小变化
  25 + * 2、建议所有自定义组件,最外层都定义一个div包在自定义组件最外层,不要关联任何自定义的属性,样式对应编辑器的通用样式
  26 + */
  27 + watchWidthHeightTimer: {
  28 + timer: null,
  29 + count: 0,
  30 + millisecond: 1000
  31 + },
  32 + list_width: 350, // 列表宽度
  33 + list_height: 300, // 列表高度
  34 + line_chart_outer_div_width: 0, // 线路图外层div宽度
  35 + line_chart_outer_div_height: 0, // 线路图外层div高度
  36 + internalDataSet: [], // 内部数据(由数据组件生成)
  37 + scrollTop: 0 // 控制垂直滚动 top
  38 + }
  39 + },
  40 + props: {
  41 + editorMode: PropTypes.string({ // 编辑模式会由编辑器自动注入(值:edit, preview)
  42 + defaultValue: 'preview',
  43 + label: '模式',
  44 + visible: false
  45 + }),
  46 + // --------------- 数据属性 -------------- //
  47 + _flag_1_: PropTypes.string({ label: '', component: null, extra (h) { return (<hr data-label='数据属性' class='bsth-line-item-divider'/>) } }),
  48 + page_size: PropTypes.number({ label: '每页显示线路图数量', defaultValue: 5, layout: { prefixCls: 'bsth-line' } }),
  49 + scroll_mode: PropTypes.select({ label: '滚动模式', defaultValue: 'up', options: [{ label: '向上滚动', value: 'up' }, { label: '向下滚动', value: 'down' }], layout: { prefixCls: 'bsth-line' } }),
  50 + scroll_milliseconds: PropTypes.number({ label: '滚动时间间隔(毫秒)', defaultValue: 2000, layout: { prefixCls: 'bsth-line' } }),
  51 + gps_data_refresh_minute: PropTypes.number({ label: 'gps数据刷新间隔(分钟)', defaultValue: 1, layout: { prefixCls: 'bsth-line' } }),
  52 + // --------------- 外层css属性 --------------- //
  53 + _flag_2_: PropTypes.string({ label: '', component: null, extra (h) { return (<hr data-label='外层css属性' class='bsth-line-item-divider'/>) } }),
  54 + margin_left: PropTypes.number({ label: '图左边margin', defaultValue: 0, layout: { prefixCls: 'bsth-line' } }),
  55 + margin_right: PropTypes.number({ label: '图右边margin', defaultValue: 0, layout: { prefixCls: 'bsth-line' } }),
  56 + margin_top: PropTypes.number({ label: '图上边margin', defaultValue: 0, layout: { prefixCls: 'bsth-line' } }),
  57 + margin_bottom: PropTypes.number({ label: '图底部margin', defaultValue: 0, layout: { prefixCls: 'bsth-line' } }),
  58 + border_size: PropTypes.number({ label: '图边框宽度', defaultValue: 1, layout: { prefixCls: 'bsth-line' } }),
  59 + background_color: PropTypes.color({ label: '背景颜色', defaultValue: '#FFFFFF', layout: { prefixCls: 'bsth-line' } }),
  60 + // --------------- 内部线路模拟图外层css属性 --------------- //
  61 + _flag_3_: PropTypes.string({ label: '', component: null, extra (h) { return (<hr data-label='内部线路模拟图外层css属性' class='bsth-line-item-divider'></hr>) } }),
  62 + line_chart_name_font_size: PropTypes.number({ label: '线路名称字体大小', defaultValue: 20, layout: { prefixCls: 'bsth-line' } }),
  63 + line_chart_name_font_color: PropTypes.color({ label: '线路名称字体颜色', defaultValue: '#babdbd', layout: { prefixCls: 'bsth-line' } }),
  64 + line_chart_margin_left: PropTypes.number({ label: '图左边margin', defaultValue: 10, layout: { prefixCls: 'bsth-line' } }),
  65 + line_chart_margin_right: PropTypes.number({ label: '图右边margin', defaultValue: 10, layout: { prefixCls: 'bsth-line' } }),
  66 + line_chart_margin_top: PropTypes.number({ label: '图上边margin', defaultValue: 5, layout: { prefixCls: 'bsth-line' } }),
  67 + line_chart_margin_bottom: PropTypes.number({ label: '图底部margin', defaultValue: 5, layout: { prefixCls: 'bsth-line' } }),
  68 + line_chart_border_size: PropTypes.number({ label: '图边框宽度', defaultValue: 1, layout: { prefixCls: 'bsth-line' } }),
  69 + line_chart_background_color: PropTypes.color({ label: '背景颜色', defaultValue: '#FFFFFF', layout: { prefixCls: 'bsth-line' } }),
  70 + // --------------- 内部线路模拟图内层css属性 --------------- //
  71 + _flag_4_: PropTypes.string({ label: '', component: null, extra (h) { return (<hr data-label='内部线路模拟图内层css属性' class='bsth-line-item-divider'></hr>) } }),
  72 + chart_left_padding: PropTypes.number({ label: '内部线路图距离左边padding', defaultValue: 30, layout: { prefixCls: 'bsth-line' } }),
  73 + chart_right_padding: PropTypes.number({ label: '内部线路图居中修正padding', defaultValue: 30, layout: { prefixCls: 'bsth-line' } }),
  74 + chart_center_top_padding: PropTypes.number({ label: '内部线路图居中修正padding', defaultValue: 4, layout: { prefixCls: 'bsth-line' } }),
  75 + chart_station_name_max_size: PropTypes.number({ label: '站定名显示最大文字个数', defaultValue: 7, layout: { prefixCls: 'bsth-line' } }),
  76 + chart_up_line_path_s_color: PropTypes.color({ label: '上行线颜色', defaultValue: '#5E96D2', layout: { prefixCls: 'bsth-line' } }),
  77 + chart_down_line_path_s_color: PropTypes.color({ label: '下行线颜色', defaultValue: '#c92121', layout: { prefixCls: 'bsth-line' } }),
  78 + chart_up_line_circle_f_color: PropTypes.color({ label: '上行线站点圆圈填充色', defaultValue: '#5e96d2', layout: { prefixCls: 'bsth-line' } }),
  79 + chart_down_line_circle_f_color: PropTypes.color({ label: '下行线站点圆圈填充色', defaultValue: '#c92121', layout: { prefixCls: 'bsth-line' } }),
  80 + chart_station_text_font_size: PropTypes.number({ label: '站名字体大小', defaultValue: 14, layout: { prefixCls: 'bsth-line' } }),
  81 + chart_up_station_text_font_f_color: PropTypes.color({ label: '上行站名颜色', defaultValue: '#4556b6', layout: { prefixCls: 'bsth-line' } }),
  82 + chart_down_station_text_font_f_color: PropTypes.color({ label: '下行站名颜色', defaultValue: '#c94f21', layout: { prefixCls: 'bsth-line' } }),
  83 + chart_up_down_station_text_font_f_color: PropTypes.color({ label: '上行下行同名站名颜色', defaultValue: '#3e3e3e', layout: { prefixCls: 'bsth-line' } }),
  84 + chart_gps_up_rect_color: PropTypes.color({ label: '上行gps车辆rect背景色', defaultValue: '#3e50b3', layout: { prefixCls: 'bsth-line' } }),
  85 + chart_gps_up_text_f_color: PropTypes.color({ label: '上行gps车辆文本颜色', defaultValue: '#FFFFFF', layout: { prefixCls: 'bsth-line' } }),
  86 + chart_gps_down_rect_color: PropTypes.color({ label: '下行gps车辆rect背景色', defaultValue: '#c94f21', layout: { prefixCls: 'bsth-line' } }),
  87 + chart_gps_down_text_f_color: PropTypes.color({ label: '下行gps车辆文本颜色', defaultValue: '#FFFFFF', layout: { prefixCls: 'bsth-line' } }),
  88 + chart_gps_up_merge_rect_color: PropTypes.color({ label: '上行合并gps车辆rect背景色', defaultValue: '#19a53a', layout: { prefixCls: 'bsth-line' } }),
  89 + chart_gps_up_merge_text_f_color: PropTypes.color({ label: '上行合并gps车辆文本颜色', defaultValue: '#FFFFFF', layout: { prefixCls: 'bsth-line' } }),
  90 + chart_gps_down_merge_rect_color: PropTypes.color({ label: '下行合并gps车辆rect背景色', defaultValue: '#19a53a', layout: { prefixCls: 'bsth-line' } }),
  91 + chart_gps_down_merge_text_f_color: PropTypes.color({ label: '下行合并gps车辆文本颜色', defaultValue: '#FFFFFF', layout: { prefixCls: 'bsth-line' } })
  92 + },
  93 + render () {
  94 + const innerDivStyle = {
  95 + 'width': this.list_width + 'px',
  96 + 'height': this.list_height + 'px',
  97 + 'border': this.border_size + 'px solid black',
  98 + 'margin-left': this.margin_left + 'px',
  99 + 'margin-right': this.margin_right + 'px',
  100 + 'margin-top': this.margin_top + 'px',
  101 + 'margin-bottom': this.margin_bottom + 'px',
  102 + 'background': this.background_color,
  103 + 'position': 'relative',
  104 + 'overflow': 'hidden'
  105 + }
  106 +
  107 + // TODO:
  108 + const wrapperDivStyle = {
  109 + 'top': this.scrollTop + 'px',
  110 + 'position': 'absolute',
  111 + 'width': this.list_width + 'px',
  112 + 'height': this.list_height * 4 + 'px'
  113 + }
  114 +
  115 + /* 最外层div对应编辑器的通用样式 */
  116 + return (
  117 + <div>
  118 + {
  119 + this.renderScrollDataComponent()
  120 + }
  121 + <div style={innerDivStyle}>
  122 + <div style={wrapperDivStyle}>
  123 + {
  124 + this.internalDataSet.map(dataItem => (
  125 + this.renderBsthLinechartDataList(dataItem)
  126 + ))
  127 + }
  128 + </div>
  129 + </div>
  130 + </div>
  131 + )
  132 + },
  133 + mounted () {
  134 + // 使用外部元素的框架定义图的长宽
  135 + let $jQuery = this.private_jQuery
  136 + this.list_width = $jQuery(this.$el).width() - this.margin_left - this.margin_right - this.border_size * 2
  137 + this.list_height = $jQuery(this.$el).height() - this.margin_top - this.margin_bottom - this.border_size * 2
  138 + // 线路图外层div长宽就是列表长宽,因为线路图作为子组件无法编辑通用样式,最外穿div的margin,border一律为0
  139 + this.line_chart_outer_div_width = this.list_width
  140 + this.line_chart_outer_div_height = Math.floor(this.list_height / this.page_size)
  141 + // 开启外部元素长宽监控计数
  142 + /**
  143 + * 开启外部元素长宽监控逻辑
  144 + * 1、必须在编辑模式下启效果,否则无效
  145 + */
  146 + if (this.editorMode === 'edit') {
  147 + this.watchWidthHeightTimer.count++
  148 + }
  149 + // TODO:使用数据组件获取数据
  150 + },
  151 + destroyed () {
  152 + // 消耗 watchWidthHeightTimer 定时器
  153 + let timer1 = this.watchWidthHeightTimer.timer
  154 + if (timer1) {
  155 + try {
  156 + clearTimeout(timer1)
  157 + } catch (e) {}
  158 + this.watchWidthHeightTimer.timer = null
  159 + }
  160 +
  161 + // TODO:其他
  162 + },
  163 + watch: {
  164 + 'watchWidthHeightTimer.count' () { // 长宽定时器监控
  165 + let timer = this.watchWidthHeightTimer.timer
  166 + if (timer) {
  167 + clearTimeout(timer)
  168 + this.watchWidthHeightTimer.timer = null
  169 + }
  170 +
  171 + let self = this
  172 + let $jQuery = this.private_jQuery
  173 + self.watchWidthHeightTimer.timer = setTimeout(function () {
  174 + // 处理逻辑
  175 + let width = $jQuery(self.$el).width()
  176 + let height = $jQuery(self.$el).height()
  177 +
  178 + if (width !== self.list_width) {
  179 + self.list_width = width - self.margin_left - self.margin_right - self.border_size * 2
  180 + self.line_chart_outer_div_width = self.list_width
  181 + }
  182 + if (height !== self.list_height) {
  183 + self.list_height = height - self.margin_top - self.margin_bottom - self.border_size * 2
  184 + self.line_chart_outer_div_height = Math.floor(self.list_height / self.page_size)
  185 + }
  186 +
  187 + self.watchWidthHeightTimer.count++
  188 + }, self.watchWidthHeightTimer.millisecond)
  189 + },
  190 + // ----------------- 数据属性 ---------------- //
  191 + page_size (val) {
  192 + self.line_chart_outer_div_height = Math.floor(self.list_height / self.page_size)
  193 + },
  194 + // ----------------- 本身宽高 监控 ---------------- //
  195 + list_width () {
  196 + let self = this
  197 + self.line_chart_outer_div_width = self.list_width
  198 + },
  199 + list_height () {
  200 + let self = this
  201 + self.line_chart_outer_div_height = Math.floor(self.list_height / self.page_size)
  202 + },
  203 + // ----------------- 外层css属性 监控 ----------------- //
  204 + margin_left () {
  205 + let self = this
  206 + self.list_width = self.list_width - self.margin_left - self.margin_right - self.border_size * 2
  207 + },
  208 + margin_right () {
  209 + let self = this
  210 + self.list_width = self.list_width - self.margin_left - self.margin_right - self.border_size * 2
  211 + },
  212 + margin_top () {
  213 + let self = this
  214 + self.list_height = self.list_height - self.margin_top - self.margin_bottom - self.border_size * 2
  215 + },
  216 + margin_bottom () {
  217 + let self = this
  218 + self.list_height = self.list_height - self.margin_top - self.margin_bottom - self.border_size * 2
  219 + },
  220 + border_size () {
  221 + let self = this
  222 + self.list_width = self.list_width - self.margin_left - self.margin_right - self.border_size * 2
  223 + self.list_height = self.list_height - self.margin_top - self.margin_bottom - self.border_size * 2
  224 + }
  225 + },
  226 + methods: {
  227 + onScrollTop (val) {
  228 + this.scrollTop = val
  229 + },
  230 + onBindData (dataSet) {
  231 + // 初始化的时候绑定一次数据,之后所有数据的变化都在ScrollPage内部变化
  232 + this.internalDataSet = dataSet
  233 + },
  234 + renderScrollDataComponent () {
  235 + return (
  236 + <ScrollPage
  237 + editorMode={this.editorMode}
  238 + page_size={this.page_size}
  239 + list_width={this.list_width}
  240 + list_height={this.list_height}
  241 + scroll_milliseconds={this.scroll_milliseconds}
  242 + gps_data_refresh_minute={this.gps_data_refresh_minute}
  243 + onBindData={this.onBindData}
  244 + onScrollTop={this.onScrollTop}
  245 + ></ScrollPage>
  246 + )
  247 + },
  248 + renderBsthLinechartDataList (dataItem) {
  249 + return (
  250 + <bsth-line-chart
  251 + useMode='child'
  252 + editorMode={this.editorMode}
  253 + line_chart_outer_div_width={this.line_chart_outer_div_width}
  254 + line_chart_outer_div_height={this.line_chart_outer_div_height}
  255 + line_route_data_child={dataItem.route}
  256 + line_gps_data_child={dataItem.gps}
  257 + line_name={dataItem.lineName}
  258 + line_code={dataItem.lineCode}
  259 + // 内部线路模拟图外层css属性
  260 + line_name_font_size={this.line_chart_name_font_size}
  261 + line_name_font_color={this.line_chart_name_font_color}
  262 + margin_left={this.line_chart_margin_left}
  263 + margin_right={this.line_chart_margin_right}
  264 + margin_top={this.line_chart_margin_top}
  265 + margin_bottom={this.line_chart_margin_bottom}
  266 + border_size={this.line_chart_border_size}
  267 + background_color={this.line_chart_background_color}
  268 + // 内部线路模拟图内层css属性
  269 + chart_left_padding={this.chart_left_padding}
  270 + chart_right_padding={this.chart_right_padding}
  271 + chart_center_top_padding={this.chart_center_top_padding}
  272 + chart_station_name_max_size={this.chart_station_name_max_size}
  273 + chart_up_line_path_s_color={this.chart_up_line_path_s_color}
  274 + chart_down_line_path_s_color={this.chart_down_line_path_s_color}
  275 + chart_up_line_circle_f_color={this.chart_up_line_circle_f_color}
  276 + chart_down_line_circle_f_color={this.chart_down_line_circle_f_color}
  277 + chart_station_text_font_size={this.chart_station_text_font_size}
  278 + chart_up_station_text_font_f_color={this.chart_up_station_text_font_f_color}
  279 + chart_down_station_text_font_f_color={this.chart_down_station_text_font_f_color}
  280 + chart_up_down_station_text_font_f_color={this.chart_up_down_station_text_font_f_color}
  281 + chart_gps_up_rect_color={this.chart_gps_up_rect_color}
  282 + chart_gps_up_text_f_color={this.chart_gps_up_text_f_color}
  283 + chart_gps_down_rect_color={this.chart_gps_down_rect_color}
  284 + chart_gps_down_text_f_color={this.chart_gps_down_text_f_color}
  285 + chart_gps_up_merge_rect_color={this.chart_gps_up_merge_rect_color}
  286 + chart_gps_up_merge_text_f_color={this.chart_gps_up_merge_text_f_color}
  287 + chart_gps_down_merge_rect_color={this.chart_gps_down_merge_rect_color}
  288 + chart_gps_down_merge_text_f_color={this.chart_gps_down_merge_text_f_color}
  289 + />
  290 + )
  291 + }
  292 + }
  293 +}
front-end/h5/src/components/core/plugins/bsth/models/lineGpsDataOfApi.js 0 → 100644
  1 +/**
  2 + * Api接口传过来的线路gps数据格式类。
  3 + * @param value 参数对象
  4 + * 参数对象说明:
  5 + * {
  6 + * lineName: '测试线路1',
  7 + * lineCode: '线路编码1',
  8 + * stopNo: 'ACODE', // 站点编码
  9 + * upDown: 1, // 上下行(0 上行 , 1 下行 , -1 无效)
  10 + * deviceId: '559L1045', // 设备编码
  11 + * instation: 1, // 0: 站外 1:站内 2:场内
  12 + * nbbm: 'W2B-046' // 车辆内部编码(自编号)
  13 + * }
  14 + */
  15 +class LineGpsDataOfApi {
  16 + constructor (value = {}) {
  17 + this.lineName = value.lineName // 线路名字
  18 + this.lineCode = value.lineCode // 线路编码
  19 + this.stopNo = value.stopNo // 站定编码
  20 + this.upDown = value.upDown // 上下行(0 上行 , 1 下行 , -1 无效)
  21 + this.deviceId = value.deviceId // 设备编码
  22 + this.instation = value.instation // 0: 站外 1:站内 2:场内
  23 + this.nbbm = value.nbbm // 车辆内部编码(自编号)
  24 + }
  25 +}
  26 +
  27 +export default LineGpsDataOfApi
front-end/h5/src/components/core/plugins/bsth/models/lineGpsDataOfLineChart.js 0 → 100644
  1 +/**
  2 + * 线路模拟图组件(bsth-line-chart)用的线路Gps数据格式类。
  3 + * @param value 参数对象
  4 + * 参数对象说明:
  5 + * {
  6 + * stopNo: 'BCODE', // 站点编码
  7 + * upDown: 0, // 上下行(0 上行 , 1 下行 , -1 无效)
  8 + * deviceId: '559L1014', // 设备编码
  9 + * instation: 1, // 0: 站外 1:站内 2:场内
  10 + * nbbm: 'W2B-064' // 车辆内部编码(自编号)
  11 + * }
  12 + */
  13 +class LineGpsDataOfLineChart {
  14 + constructor (value = {}) {
  15 + this.stopNo = value.stopNo // 站点编码
  16 + this.upDown = value.upDown // 上下行(0 上行 , 1 下行 , -1 无效)
  17 + this.deviceId = value.deviceId // 设备编码
  18 + this.instation = value.instation // 0: 站外 1:站内 2:场内
  19 + this.nbbm = value.nbbm // 车辆内部编码(自编号)
  20 + }
  21 +}
  22 +
  23 +export default LineGpsDataOfLineChart
front-end/h5/src/components/core/plugins/bsth/models/lineRouteDataOfApi.js 0 → 100644
  1 +/**
  2 + * Api接口传过来的线路路由数据格式类。
  3 + * @param value 参数对象
  4 + * 参数对象说明:
  5 + * {
  6 + * lineName: '测试线路1',
  7 + * lineCode: '线路编码1',
  8 + * directions: 0,
  9 + * stationRouteCode: 10,
  10 + * stationCode: 'ACODE',
  11 + * stationName: 'A起点站',
  12 + * stationMark: 'B'
  13 + * }
  14 + */
  15 +class LineRouteDataOfApi {
  16 + constructor (value = {}) {
  17 + this.lineName = value.lineName // 线路名字
  18 + this.lineCode = value.lineCode // 线路编码
  19 + this.directions = value.directions // 方向 0:上行,1:下行
  20 + this.stationRouteCode = value.stationRouteCode // 站点路由代码(用于站点排序)
  21 + this.stationCode = value.stationCode // 站点编码
  22 + this.stationName = value.stationName // 站点名字
  23 + this.stationMark = value.stationMark // 站定标识 B:起点站,E:终点站,Z:中途站
  24 + }
  25 +
  26 + toObject () {
  27 + return {
  28 + lineName: this.lineName,
  29 + lineCode: this.lineCode,
  30 + directions: this.directions,
  31 + stationRouteCode: this.stationRouteCode,
  32 + stationCode: this.stationCode,
  33 + stationName: this.stationName,
  34 + stationMark: this.stationMark
  35 + }
  36 + }
  37 +}
  38 +
  39 +export default LineRouteDataOfApi
front-end/h5/src/components/core/plugins/bsth/models/lineRouteDataOfLineChart.js 0 → 100644
  1 +/**
  2 + * 线路模拟图组件(bsth-line-chart)用的线路路由数据格式类。
  3 + * @param value 参数对象
  4 + * {
  5 + * names: ["A起点站"], // 站点名字数组
  6 + * ids: ["ACODE_0", "ACODE_1"], // 站点编码_上下行
  7 + * type: 2, // 0:上行 1:下行 2:同名合并 3:异名合并
  8 + * stationMark: "B" // 站点表识
  9 + * }
  10 + */
  11 +
  12 +import LineRouteDataOfApi from 'core/plugins/bsth/models/lineRouteDataOfApi'
  13 +
  14 +class LineRouteDataOfLineChart {
  15 + constructor (value = {}) {
  16 + this.names = value.names // 站定名字数组
  17 + this.ids = value.ids // // 站点编码_上下行 数组
  18 + this.type = value.type // 0:上行 1:下行 2:同名合并 3:异名合并
  19 + this.stationMark = value.stationMark // 站点表识 B:起点站,E:终点站,Z:中途站
  20 + }
  21 +
  22 + /**
  23 + * 构造内部数据
  24 + * @param up 上行站点数据(LineRouteDataOfApi类型)
  25 + * @param down 下行站点数据(LineRouteDataOfApi类型)
  26 + */
  27 + static fromLineRouteDataOfApi (up, down) {
  28 + if (up === null && down === null) {
  29 + return null
  30 + }
  31 +
  32 + if (up !== null && !(up instanceof LineRouteDataOfApi)) {
  33 + throw new Error('up不等于null,up参数不是LineRouteDataOfApi类型')
  34 + }
  35 + if (down !== null && !(down instanceof LineRouteDataOfApi)) {
  36 + throw new Error('down不等于null,down参数不是LineRouteDataOfApi类型')
  37 + }
  38 +
  39 + let [names, type, ids, stationMark] = [null, 2, null, null]
  40 + if (up === null) {
  41 + type = 1
  42 + names = [down.stationName]
  43 + ids = [down.stationCode + '_' + down.directions]
  44 + stationMark = down.stationMark
  45 + } else if (down === null) {
  46 + type = 0
  47 + names = [up.stationName]
  48 + ids = [up.stationCode + '_' + up.directions]
  49 + stationMark = up.stationMark
  50 + } else {
  51 + names = [up.stationName]
  52 + ids = [up.stationCode + '_' + up.directions, down.stationCode + '_' + down.directions]
  53 + stationMark = up.stationMark
  54 + if (up.stationName !== down.stationName) {
  55 + type = 3
  56 + names.push(down.stationName)
  57 + }
  58 + }
  59 + return new LineRouteDataOfLineChart({ names, ids, type, stationMark })
  60 + }
  61 +}
  62 +
  63 +export default LineRouteDataOfLineChart
front-end/h5/src/components/core/plugins/bsth/models/scrollListInnerData.js 0 → 100644
  1 +/**
  2 + * 线路模拟图滚动列表组件(bsth-line-chart-scrollList)内部数据类。
  3 + */
  4 +
  5 +import Utils from 'core/plugins/bsth/bsth-utils'
  6 +import LineRouteDataOfLineChart from 'core/plugins/bsth/models/lineRouteDataOfLineChart'
  7 +import LineGpsDataOfLineChart from 'core/plugins/bsth/models/lineGpsDataOfLineChart'
  8 +import ScrollListInnerDataItem from 'core/plugins/bsth/models/scrollListInnerDataItem'
  9 +
  10 +class ScrollListInnerData {
  11 + constructor (pageSize, listWidth, listHeight) {
  12 + this._pageSize = pageSize // 每页大小
  13 + this._listWidth = listWidth // 滚动列表宽度
  14 + this._listHeight = listHeight // 滚动列表高度
  15 +
  16 + // --------------- 内部数据对象 ---------------- //
  17 + this._innerDataItemList = [] // InnerDataItem类型数组
  18 + this._pageCount = 0 // 一共多少页
  19 +
  20 + // --------------- 内部滚动数据对象 --------------- //
  21 + this._scrollDataItemList = [] // 滚动用的InnerDataItem类型数组(根据不同的滚动策略会和_innerDataItemList不一致)
  22 + this._currentScrollIndex = 0 // 当前滚动项index
  23 + this._nextScrollIndex = 1 // 下一个滚动项index
  24 + this._scrollAnimateTimer = { // 滚动动画效果timer
  25 + timer: null,
  26 + count: 0,
  27 + millisecond: 1
  28 + }
  29 + }
  30 + set pageSize (val) { // 设置每页大小
  31 + this._pageSize = val
  32 + this._pageCount = Math.ceil(this._innerDataItemList.length / this._pageSize)
  33 + // 重新计算_scrollDataItemList
  34 + refreshScrollData.call(this)
  35 + }
  36 + get scrollDataItemList () { // 返回滚动数据集
  37 + return this._scrollDataItemList
  38 + }
  39 + set listWidth (val) {
  40 + this._listWidth = val
  41 + // 刷新滚动数据中 宽,高,top
  42 + refreshScrollDataSizeProperty.call(this)
  43 + }
  44 + set listHeight (val) {
  45 + this._listHeight = val
  46 + // 刷新滚动数据中 宽,高,top
  47 + refreshScrollDataSizeProperty.call(this)
  48 + }
  49 +
  50 + // -------------------------------- 移动方法 ---------------------------- //
  51 + scrollUp (scrollListComponent) { // 向上滚动
  52 + if (this._scrollDataItemList.length <= this._pageSize) { // 总数据小于页大小,不滚动
  53 + return
  54 + }
  55 +
  56 + if (this._scrollAnimateTimer.timer) {
  57 + try {
  58 + clearInterval(this._scrollAnimateTimer.timer)
  59 + } catch (err) {}
  60 + this._scrollAnimateTimer.timer = null
  61 + }
  62 +
  63 + let topFrom = this._scrollDataItemList[this._currentScrollIndex].top
  64 + let topTo = this._scrollDataItemList[this._nextScrollIndex].top
  65 + let step = (topTo - topFrom) / 100
  66 + let self = this
  67 + self._scrollAnimateTimer.timer = setInterval(function () {
  68 + self._scrollAnimateTimer.count++
  69 + let currentTop = topFrom + self._scrollAnimateTimer.count * step
  70 + // console.log(currentTop)
  71 + scrollListComponent.$emit('scrollTop', currentTop)
  72 + if (Math.abs(topTo - currentTop) < Math.abs(step)) {
  73 + scrollListComponent.$emit('scrollTop', topTo)
  74 + self._scrollAnimateTimer.count = 0
  75 +
  76 + // console.log(self._pageCount)
  77 + // console.log(self._scrollDataItemList[self._nextScrollIndex].pageIndex)
  78 + if (self._scrollDataItemList[self._nextScrollIndex].pageIndex === self._pageCount) {
  79 + scrollListComponent.$emit('scrollTop', self._scrollDataItemList[0].top)
  80 + self._currentScrollIndex = 0
  81 + self._nextScrollIndex = 1
  82 + } else {
  83 + self._currentScrollIndex = self._nextScrollIndex
  84 + self._nextScrollIndex++
  85 + }
  86 +
  87 + if (self._scrollAnimateTimer.timer) {
  88 + try {
  89 + clearInterval(self._scrollAnimateTimer.timer)
  90 + } catch (err) {}
  91 + self._scrollAnimateTimer.timer = null
  92 + }
  93 + }
  94 + }, 1)
  95 + }
  96 +
  97 + // -------------------------------- 数据处理方法 -------------------------------//
  98 + /**
  99 + * 初始化路由数据。
  100 + * @param routeDataArray LineRouteDataOfApi类型数组
  101 + */
  102 + initRouteData (routeDataArray) {
  103 + // 1、重置内部数据
  104 + this.resetData()
  105 +
  106 + // 2、计算内部数据对象
  107 + // 2.1、route数据
  108 + this._innerDataItemList.splice(0, this._innerDataItemList.length)
  109 + for (let tempItem of initInnerDataSetWithRoute(routeDataArray)) {
  110 + this._innerDataItemList.push(tempItem)
  111 + }
  112 + // 2.2、一共有多少页
  113 + this._pageCount = Math.ceil(this._innerDataItemList.length / this._pageSize)
  114 +
  115 + // 3、计算内部滚动数据对象
  116 + refreshScrollData.call(this)
  117 + }
  118 +
  119 + /**
  120 + * 刷新gps数据(依赖于initRouteData,注意调用顺序)
  121 + * @param gpsDataArray LineGpsDataOfApi类型数组
  122 + */
  123 + refreshGps (gpsDataArray) {
  124 + // refreshInnerDataSetWithGps(gpsDataArray, this._innerDataItemList)
  125 + refreshInnerDataSetWithGps(gpsDataArray, this._scrollDataItemList)
  126 + }
  127 +
  128 + /**
  129 + * 清除内部数据
  130 + */
  131 + resetData () {
  132 + // ------------ 重置内部数据对象 ------------ //
  133 + this._innerDataItemList.splice(0, this._innerDataItemList.length) // 直接删除数据内部数据,而不是 = []
  134 + this._pageCount = 0
  135 +
  136 + // ------------ 重置内部滚动数据对象 ---------- //
  137 + this._scrollDataItemList.splice(0, this._scrollDataItemList.length)
  138 + this._currentScrollIndex = 0
  139 + this._nextScrollIndex = 1
  140 + if (this._scrollAnimateTimer.timer) {
  141 + try {
  142 + clearInterval(this._scrollAnimateTimer.timer)
  143 + } catch (err) {}
  144 + }
  145 + this._scrollAnimateTimer.timer = null
  146 + this._scrollAnimateTimer.count = 0
  147 + }
  148 +}
  149 +
  150 +/**
  151 + * 计算内部滚动数据。
  152 + */
  153 +function refreshScrollData () {
  154 + this._scrollDataItemList.splice(0, this._scrollDataItemList.length)
  155 +
  156 + // 3.1、拷贝属性赋值
  157 + for (let dataItem of this._innerDataItemList) {
  158 + // 注意这里直接拷贝_innerDataItemList元素属性,然后new新的ScrollListInnerDataItem对象
  159 + this._scrollDataItemList.push(new ScrollListInnerDataItem(dataItem))
  160 + }
  161 + // 3.2、如果总数据小于页大小,不用计算其他滚动数据,返回
  162 + if (this._innerDataItemList.length <= this._pageSize) {
  163 + return
  164 + }
  165 + // 3.3、计算每条数据的 itemIndex pageIndex 属性
  166 + for (let i = 0; i < this._pageCount; i++) { // 计算itemIndex pageIndex
  167 + for (let j = 0; j < this._pageSize; j++) {
  168 + let index = i * this._pageSize + j
  169 + if (index === this._scrollDataItemList.length) { // 超出记录退出
  170 + break
  171 + }
  172 + let innerDataItem = this._scrollDataItemList[i * this._pageSize + j]
  173 + innerDataItem.itemIndex = j
  174 + innerDataItem.pageIndex = i
  175 + }
  176 + }
  177 + // 3.4、在最后添加第一页的数据(为了无缝滚动需要),同时pageIndex要加1
  178 + // 注意:添加的一页不计算进this.pageCount中
  179 + for (let i = 0; i < this._pageSize; i++) {
  180 + let InnerDataItem = new ScrollListInnerDataItem(this._innerDataItemList[i])
  181 + InnerDataItem.pageIndex = this._pageCount
  182 + this._scrollDataItemList.push(InnerDataItem)
  183 + }
  184 + // 3.5、计算每个元素的宽,高,top
  185 + refreshScrollDataSizeProperty.call(this)
  186 +}
  187 +
  188 +/**
  189 + * 刷新滚动数据中 宽,高,top
  190 + */
  191 +function refreshScrollDataSizeProperty () {
  192 + let scrollHeight = Math.floor(this._listHeight / this._pageSize) // TODO:先直接除假设每个item高度相同,后面再改
  193 + for (let i = 0; i < this._scrollDataItemList.length; i++) {
  194 + let innerDataItem = this._scrollDataItemList[i]
  195 + innerDataItem.width = this._listWidth
  196 + innerDataItem.height = scrollHeight
  197 + innerDataItem.top = -(i * scrollHeight)
  198 + }
  199 +}
  200 +
  201 +/**
  202 + * 使用原始gps数据刷新内部gps数据(依赖路由数据)
  203 + * @param gpsDataArray LineGpsDataOfApi类型数组
  204 + * @param innerDataSet InnerDataItem类型数组
  205 + */
  206 +function refreshInnerDataSetWithGps (gpsDataArray, innerDataSet) {
  207 + // 1、按照线路名字_线路编码分组数据
  208 + let gpsGroupByLineNameCode = Utils.listGroupBy(gpsDataArray, function (d) {
  209 + return d.lineName + '_' + d.lineCode
  210 + })
  211 + // 2、处理数据,循环计算每条线路的数据
  212 + for (let internalData of innerDataSet) {
  213 + let key = internalData.lineName + '_' + internalData.lineCode
  214 + internalData.gps.splice(0, internalData.gps.length) // 清除原来的gps数据
  215 + if (gpsGroupByLineNameCode[key]) {
  216 + for (let gpsData of gpsGroupByLineNameCode[key]) {
  217 + internalData.gps.push(new LineGpsDataOfLineChart(gpsData))
  218 + }
  219 + }
  220 + }
  221 +}
  222 +
  223 +/**
  224 + * 使用原始路由数据初始化内部路由数据
  225 + * @param routeDataArray LineRouteDataOfApi类型数组
  226 + * @return Array InnerDataItem类型数组
  227 + */
  228 +function initInnerDataSetWithRoute (routeDataArray) {
  229 + // 1、按照线路名字_线路编码分组数据
  230 + let routeGroupByLineNameCode = Utils.listGroupBy(routeDataArray, function (d) {
  231 + return d.lineName + '_' + d.lineCode
  232 + })
  233 + // 2、获取线路名字_线路编码列表(按照线路名字排序)
  234 + let lineNameCodeList = []
  235 + let routeGroupByLineNameCodeKeys = Object.keys(routeGroupByLineNameCode)
  236 + for (let key of routeGroupByLineNameCodeKeys) {
  237 + lineNameCodeList.push(key)
  238 + }
  239 + lineNameCodeList.sort(function (a, b) {
  240 + return a.localeCompare(b)
  241 + })
  242 + // 3、处理数据,循环计算每条线路的数据
  243 + if (lineNameCodeList.length === 0) {
  244 + return []
  245 + }
  246 +
  247 + let innerDataSet = []
  248 +
  249 + for (let key of lineNameCodeList) {
  250 + let lineName = key.split('_')[0]
  251 + let lineCode = key.split('_')[1]
  252 +
  253 + // 3.1 定义内部数据
  254 + let internalData = new ScrollListInnerDataItem({
  255 + lineName: lineName,
  256 + lineCode: lineCode,
  257 + route: [],
  258 + gps: [],
  259 +
  260 + // ------------- 滚动列表用的数据 ------------ //
  261 + width: 0, // 对应的LineChart组件的宽度
  262 + height: 0, // 对应的LineChart组件的高度
  263 + top: 0, // 距离顶部 css top值
  264 + itemIndex: 0, // 每页中的item下标(从0开始)
  265 + pageIndex: 0 // 第几页下标(从0开始)
  266 + })
  267 + // 3.2 处理路由数据
  268 + // 3.2.1 获取指定线路路由
  269 + let lineRoute = routeGroupByLineNameCode[key]
  270 + // 3.2.2 按照directions方向分组
  271 + let lineRouteGroupByDir = Utils.listGroupBy(lineRoute, function (d) {
  272 + return d.directions
  273 + })
  274 + // 3.2.3 排序分组,上行升序,下行降序(下行降序为了后面进行站点名合并)
  275 + try {
  276 + lineRouteGroupByDir[0].sort(function (a, b) {
  277 + return a.stationRouteCode - b.stationRouteCode
  278 + })
  279 + lineRouteGroupByDir[1].sort(function (a, b) {
  280 + return b.stationRouteCode - a.stationRouteCode
  281 + })
  282 + } catch (e) {
  283 + console.log('站定路由数据异常!')
  284 + console.log(e)
  285 + }
  286 +
  287 + // TODO:如果是环线,lineRouteGroupByDir[1] = []
  288 + /**
  289 + * 3.2.4 核心合并算法。
  290 + */
  291 + let mergeRouteData = []
  292 + let dirUpData
  293 + let dirDownData
  294 + for (let i = 0; i < 888; i++) {
  295 + dirUpData = lineRouteGroupByDir[0][i]
  296 + dirDownData = lineRouteGroupByDir[1][i]
  297 + if (dirUpData != null && dirDownData != null && dirUpData.stationName !== dirDownData.stationName) {
  298 + // arr2 包含 a1
  299 + let ii = Utils.listIndexOf(lineRouteGroupByDir[1], dirUpData, 'stationName')
  300 + if (ii > i) {
  301 + Utils.insertNullToList(lineRouteGroupByDir[0], i, ii - i)
  302 + i -= 1
  303 + continue
  304 + }
  305 + // arr1 包含 a2
  306 + ii = Utils.listIndexOf(lineRouteGroupByDir[0], dirDownData, 'stationName')
  307 + if (ii > i) {
  308 + Utils.insertNullToList(lineRouteGroupByDir[1], i, ii - i)
  309 + i -= 1
  310 + continue
  311 + }
  312 + }
  313 +
  314 + if (dirUpData == null && dirDownData == null) {
  315 + break
  316 + }
  317 + mergeRouteData.splice(i, 1, LineRouteDataOfLineChart.fromLineRouteDataOfApi(dirUpData, dirDownData))
  318 + }
  319 + internalData.route = mergeRouteData
  320 +
  321 + // 3.3 处理总数据集
  322 + innerDataSet.push(internalData)
  323 + }
  324 +
  325 + return innerDataSet
  326 +}
  327 +
  328 +export default ScrollListInnerData
front-end/h5/src/components/core/plugins/bsth/models/scrollListInnerDataItem.js 0 → 100644
  1 +/**
  2 + * 内部数据类(每个线路模拟图用数据)
  3 + * @param value 参数对象
  4 + */
  5 +class ScrollListInnerDataItem {
  6 + constructor (value = {}) {
  7 + this.lineName = value.lineName // 线路名称
  8 + this.lineCode = value.lineCode // 线路代码
  9 + this.route = value.route // LineRouteDataOfLineChart类型数组
  10 + this.gps = value.gps // LineGpsDataOfLineChart类型数组
  11 +
  12 + // ------------- 滚动列表用的数据 ------------ //
  13 + this.width = 0 // 对应的LineChart组件的宽度
  14 + this.height = 0 // 对应的LineChart组件的高度
  15 + this.top = value.top // 距离顶部 css top值
  16 + this.itemIndex = value.itemIndex // 每页中的item下标(从0开始)
  17 + this.pageIndex = value.pageIndex // 第几页下标(从0开始)
  18 + }
  19 +
  20 + toObject () {
  21 + return {
  22 + lineName: this.lineName,
  23 + lineCode: this.lineCode,
  24 + route: this.route,
  25 + gps: this.gps,
  26 + width: this.width,
  27 + height: this.height,
  28 + top: this.top,
  29 + itemIndex: this.itemIndex,
  30 + pageIndex: this.pageIndex
  31 + }
  32 + }
  33 +}
  34 +
  35 +export default ScrollListInnerDataItem
front-end/h5/src/components/core/plugins/index.js
@@ -19,8 +19,10 @@ import LbpTable from &#39;core/plugins/lbp-table&#39; @@ -19,8 +19,10 @@ import LbpTable from &#39;core/plugins/lbp-table&#39;
19 import LbpNewsList from 'core/plugins/lbp-news-list' 19 import LbpNewsList from 'core/plugins/lbp-news-list'
20 // import LbpTabs from 'core/components/plugins/lbp-tabs' 20 // import LbpTabs from 'core/components/plugins/lbp-tabs'
21 21
  22 +/** 自定义的组件 */
22 import BsthLineChart from 'core/plugins/bsth/bsth-line-chart' 23 import BsthLineChart from 'core/plugins/bsth/bsth-line-chart'
23 import BsthLineChartList from 'core/plugins/bsth/bsth-line-chart-list' 24 import BsthLineChartList from 'core/plugins/bsth/bsth-line-chart-list'
  25 +import BsthLineChartScrollList from 'core/plugins/bsth/bsth-line-chart-scrollList'
24 import BsthSlide from 'core/plugins/bsth/bsth-slide' 26 import BsthSlide from 'core/plugins/bsth/bsth-slide'
25 27
26 export const pluginsList = [ 28 export const pluginsList = [
@@ -335,6 +337,18 @@ export const pluginsList = [ @@ -335,6 +337,18 @@ export const pluginsList = [
335 337
336 { 338 {
337 i18nTitle: { 339 i18nTitle: {
  340 + 'en-US': 'LineChartScrollList',
  341 + 'zh-CN': '线路模拟图滚动列表'
  342 + },
  343 + title: '线路模拟图滚动列表',
  344 + icon: 'list',
  345 + component: BsthLineChartScrollList,
  346 + visible: true,
  347 + name: BsthLineChartScrollList.name
  348 + },
  349 +
  350 + {
  351 + i18nTitle: {
338 'en-US': 'Carousel2', 352 'en-US': 'Carousel2',
339 'zh-CN': '轮播图2' 353 'zh-CN': '轮播图2'
340 }, 354 },