1. 数据结构的设计
实现sql语句数据结构存储。精巧的地方在于对于and
,or
条件逻辑的设计。
当然最后实现的时候还是有一些差别,哈哈哈。
Column = {
distinct: true,
aggr_func: 'sum',
name: 'col1', # func(col) sum/count/avg/distinct # db.tb
id: 12344,
alias: 'a', # 重命名
table: Table()
}
Table = {
id: 234,
name: "table.name",
alias: 't'
}
Condition = {
op: {
name: 'eq',
value: '='
}
value: [
# type: condition,column,expr
# 如果type是condition,表示and,or类型,value内的对象都是condition类型。
{
type: 'column',
col: Column()
},
{
type: 'expr',
value: '20170291'
}
...
]
}
# 单条查询语句,支持mysql,hive语法
# union 链接多个select
json = [{
select: [Column(),..],
from: [Table(),..] # db.tb
join: [{
type: 'left join'
table: Table()
on: Condition()
}],
where: Condition(),
group_by: [Column(),..]
having:Condition(),
order_by: [{
col: Column(),
sort: 'desc'
}]
limit: 10,
}]
2. 从脏检查到观察者模式
- angularjs到vue项目的迁移
vue项目越来越火,组件化的趋势越来越明显。大而全的angularjs,慢慢被新的技术替代。
我们老的项目想迁移到vue上进行开发。
ngvue
一个directive
封装vue功能,使得angularjs可以使用vue。
vue-component(v-props="ng" name="QueryVisualComponent")
QueryVisualComponent = Vue.component('query-visual-component', {
props: ['ng']
data: ->
return {
info: null
columns: null
}
template: '#query-visual-component-template'
})
MetronicApp.value('QueryVisualComponent', QueryVisualComponent)
- 处理angularjs,vue通信
将angularjs依赖注入的方法都打包放在vue中也可以使用,$state
props生成this会循环引用,栈溢出。
全局变量整存整取。方便在vue中使用$http
依赖请求数据。(当然也可以引入axios)
g.utils.setDependencies($state.current.controller, arguments, {ctrl: @})
@ng = g.utils.dependencies
setDependencies: (controller_name, args, extra) ->
g.utils.dependencies = extra ? {}
for i in MetronicApp._invokeQueue
if i[0] == '$controllerProvider'
if i[2][0] == controller_name
for dependency, index in i[2][1].slice(0, -1)
g.utils.dependencies[dependency] = args[index]
break
3. 页面功能设计与开发
因为有审批流开发的基础,想当然的设计成了流程图。
最后参照了navicat。
可视化
实际开发过程中,问题很多,宛若红军当年两万五千里长征,还好现在算是到达了延安,哈哈哈。
如何在svg上生成没有问题的node节点?
1.使用了svg forignObject 元素 https://github.com/memloom-development/svg.foreignobject.js
2.vue compile extend (获取vue元素的dom结构)
#vue compile extend
visual_header = Header(world, snippet, table_id)
VisualHeader = Vue.extend(visual_header)
h_comp = new VisualHeader().$mount()
@header = world.svg.foreignObject()
.addClass('visual-header-object')
.appendChild(h_comp.$el, {className: 'visual-header-box flex'})
3.mac foreignObject 内部元素不能有滚动条,overflow: auto
超过高度或者宽度后,dom占位位置正确,实际显示偏移。
4.node draggable会影响点击事件触发。双击响应,手动focus。
.on('beforedrag', (e) ->
# 只能拖拽节点标题
dom = $(e.detail.event.target)
inHeader = dom.add(dom.parent()).hasClass('visual-header')
if not inHeader
e.preventDefault()
e.detail.event.stopPropagation()
)
5.vuex全局数据交互。
6.右下边栏拖拽node页面大小。
.on('mousedown', (e) =>
box = $("foreignObject[id='table_#{table_id}']")
if not e?
e = window.event
y = e.clientY
box_y = box.height()
document.onmousemove = (e) =>
box_height = e.clientY - y + box_y
box.css('height', Math.max(100, box_height) + "px")
@bottombar.move(0, Math.max(100, box_height) - 5)
@rightbar.height(Math.max(100, box_height) + 27)
document.onmouseup = ->
document.onmousemove = null
)
7.存储还原ViewBox缩放比例,存储还原画布节点线条,保证切换还原配置正常。
setViewBox: (viewbox) ->
if viewbox?
@svg.viewbox(viewbox)
@svg.zoom(viewbox.zoom)
else
@svg.viewbox(0, 0, $('#world').width(), $('#world').height())
- 滚轮缩放事件处理。
.on('wheel', (e) ->
if (navigator.userAgent.toLowerCase().indexOf('se 2.x') > -1)
w_dom = $(e.target).parents('.visual-node-box')[0]
else
w_dom = $(e.target).parents('.visual-node-object')[0]
if w_dom?
y = w_dom.scrollTop
w_dom.scrollTop = y + e.deltaY
).on('panEnd', (e) =>
@saveViewBox()
).on( 'zoom', (e) =>
# 找到点击的坐标,获取坐标位置dom元素,判断parents
p = @translatePosFromSvgToBrowser(e.detail.focus.x, e.detail.focus.y)
dom = document.elementFromPoint(p.x, p.y)
if $(dom).parents('.visual-node-object').length
e.preventDefault()
else
@saveViewBox()
)
9.画线。
10.右键线条,节点菜单处理。
11.删除表删除条件。
count: 1
sql: {
'1':{
columns: [],
from: [],
join: {},
where: null,
limit: 10,
where_sql: ''
join_sql: {}
add_item: []
max_select: -1,
max_order_by: -1,
max_group_by: -1,
max_from: -1
}
}
columns_dict: {}
table_dict: {}
checkedCol: {}
}
select、from、where、join、group by、order by、limit
1.select(distinct、计算方法、别名)
2.from与join 语句结合
从无向图到有向图处理join语句。
join规则有方向,控制join条件一定放在key大的那个table上。(这样不会产生环,之前无向图时,需要先判断环,再分组……)
3.处理最后一个逗号问题(这里的确和上面设计的数据结构不一致,哈哈哈。)
找到key的最大值,记录不显示最大值的逗号。
4.where jqueryBuilder 处理and
or
条件和sql展示。
1.处理where条件有字段和值两种情况,需要select框支持输入和筛选两种功能。(selectize)
2.处理默认字段和值两种不同。用@001@#{G_AUTO_COL_INDEX}@002@
进行标记。
3.处理in,not null, is_empty 特殊情况。
$('#where-builder').queryBuilder(options)
.on('afterUpdateRuleOperator.queryBuilder', (e, rule) ->
if ["is_empty", "is_not_empty", "is_null", "is_not_null"].indexOf(rule.operator.type) > -1
$("##{rule.id} .selectize-control").hide()
)
getSQL: ->
@where_sql = $('#where-builder').queryBuilder('getSQL')
if @where_sql
# 变量替换@001@#{G_AUTO_COL_INDEX}@002@为alias+name
@where_sql.sql = @where_sql.sql.replace(/'@001@(\w+)@002@'/g, '@001@$1@002@')
@where_sql.sql = @where_sql.sql.replace( /@001@(\w+)@002@/g,
(col) ->
if store.state.columns_dict[col].table.alias
return "#{store.state.columns_dict[col].table.alias}.#{store.state.columns_dict[col].name}"
else
return "#{store.state.columns_dict[col].table.name}.#{store.state.columns_dict[col].name}"
)
@where_sql.sql = @where_sql.sql.replace(/IN\('(.*)'\)/g,
(col) ->
values = col.replace(/IN\('(.*)'\)/g, '$1').split(',')
v = ''
v = ("\'#{c}\'" for c in values).join(',')
v = "IN(#{v})"
return v
)
5.记录join条件。
join条件针对两个表之间进行配置,将条件存放在两个表中ID大的表上。展现是合并。
4. 数据组装
columns + from + join + where + group by + order by + limit
1.别名的处理
2.where语句拼装
3.join语句拼装
网友评论