bootstrap-select使用、relation-graph使用
bootstrap-select
这里要实现的是带有搜索功能的select框,
bootstrap 官网没有可以直接拿来用的。如下是官网给出的解释,带搜索功能的select需要自定义。
在网上找到了有自己用javascript+html+css生写的,代码量比较大,有两百多行,我拷贝了,试运行过,是没有问题,可以直接用的。
如下是我找的别人的原创博客地址。
https://blog.csdn.net/L333333333/article/details/102556003
我自己另外找了一个可以引用的组件,在特定的位置加上属性,即可实现bootstrap渲染的带搜索框的select标签,下面是我找的另一人的原创博客地址,
博客中的demo,直接copy可以运行,达到预期的效果,亲测有效。
https://blog.csdn.net/xb12369/article/details/50999265
我自己在实际生产中,发现不能直接拿着人家的组件直接用,会有其他问题引出来,我已经解决了,所以写博客加以补充。
先介绍一下缘由,上面的两个例子里面,都能实现select框带模糊查询功能,因为是给出的demo示例,所以,option标签都是预先在页面上写死的,
但是,我们的实际生产中,select里面的option标签值,有的时候,是需要从后端接口拿到数据,然后才能进行渲染的,这个时候,就会牵扯到浏览器异步的问题。
我自己的这个项目里面,select里面是人员名单,而人员名单是需要从后端接口拿到数据,然后再用js进行渲染,这个时候,就会出现,Ajax调用后端接口,
还没有等Ajax拿到返回值结果,bootstrap-select组件就已经预加载了,而这个时候,option标签里面是没有数据信息的,所以,按照上面的用法,
bootstrap-select组件渲染的结果里面是没有数据的。
解决办法就是让他们变成同步的。
我自己的项目里面,window.onload里面是一个Ajax函数,这个Ajax函数是调用后端接口拿到返回值信息,然后在Ajax的回调函数里面用js去创建option标签,渲染页面。
我的解决办法就是,把触发bootstrap-select的jquery代码,放到我的Ajax回调函数里面,这样,在Ajax内部就完成了同步。
<link rel="stylesheet" href="{% static bootstrap %}bootstrap-3.3.7-dist/css/bootstrap.css"> <!-- 这里是我自己项目内部配置的本地的bootstrap的css引用库 --> <title>Title</title>
<!-- 这里是引用的cdn的jquery,jQuery的引用一定要放在最顶上,因为下面boostrap-select.js以及boostrap.js都需要建立在jQuery基础上 -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- 这里是bootstrap-select css&js cdn引用 --> <script type="text/javascript" src="http://cdn.bootcss.com/bootstrap-select/2.0.0-beta1/js/bootstrap-select.js"></script> <link rel="stylesheet" type="text/css" href="http://cdn.bootcss.com/bootstrap-select/2.0.0-beta1/css/bootstrap-select.css"> <!-- 这里是我项目本地配置的bootstrap js引用库3.0 --> <script src="{% static bootstrap %}bootstrap-3.3.7-dist/js/bootstrap.js"></script>
<script>
//Ajax的前半截就不复制了,直接上Ajax回调函数部分,
xmlHttp.onreadystatechange = function (data) {
if (xmlHttp.readyState === 4 &&
(
(xmlHttp.status >= 200 && xmlHttp.status < 300)
|| xmlHttp.status === 304)
) {
//拿到Ajax的回调函数,解析出来
let response_data = data.srcElement.response;
//get object type of data which backend transfer
let parsed_data = JSON.parse(response_data).data;
//拿到返回值之后,用js渲染标签
show_option_data(parsed_data);
//渲染完成之后,再触发bootstrap-select的组件
$('.test-select').selectpicker();
}
}
</script>
<!-- 如下是标签的属性绑定,这是单选的用法,如果需要多选,只需要在此基础上加上 multipule 即可-->
<select data-live-search="true" class="test-select"></select>
我自己把上面两个人的博客里面的代码,在自己博客里面备份一下,以防别人博客删除,没有线索了。
JavaScript+html+css
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>javascript+html+css--select-search</title> 6 <style type="text/css"> 7 8 *{ 9 padding: 0; 10 margin: 0; 11 } 12 13 body{ 14 width: 100vw; 15 height: calc(100vh - 20px); 16 } 17 18 div.select select{ 19 display: none; 20 } 21 22 div.select-box{ 23 width: 200px; 24 margin: 20px 20px; 25 } 26 27 div.select-head{ 28 position: relative; 29 height: 30px; 30 width: 100%; 31 display: flex; 32 border: solid 1px #000; 33 align-items: center; 34 cursor: pointer; 35 } 36 37 div.select-head span{ 38 font-size: 16px; 39 margin-left: 5px; 40 color: #AAA; 41 } 42 43 div.select-head span.fill{ 44 color: #000; 45 } 46 47 div.select-head i{ 48 position: absolute; 49 height: 16px; 50 width: 16px; 51 right: 5px; 52 background-image: url(./arrow.png); 53 background-size: 16px auto; 54 } 55 56 div.select-body{ 57 display: none; 58 width: 100%; 59 border: solid 1px #000; 60 border-top: none; 61 } 62 63 div.search-input{ 64 position: relative; 65 height: 40px; 66 } 67 68 div.search-input input{ 69 height: 30px; 70 width: 150px; 71 margin: 5px 8px; 72 text-indent: 10px; 73 padding-right: 30px; 74 } 75 76 div.search-input i{ 77 position: absolute; 78 display: block; 79 height: 20px; 80 width: 20px; 81 top: 12px; 82 right: 15px; 83 background-image: url(./search-normal.png); 84 background-size: 20px 20px; 85 cursor: pointer; 86 } 87 88 div.search-input i:hover{ 89 background-image: url(./search-active.png); 90 } 91 92 div.value-body{ 93 max-height: 150px; 94 overflow: auto; 95 } 96 97 div.value-body li{ 98 display: flex; 99 height: 24px; 100 padding: 5px 5px; 101 font-size: 14px; 102 align-items: center; 103 cursor: pointer; 104 } 105 106 div.value-body li:hover,li.active{ 107 background-color: #F5F6FA; 108 } 109 110 div.value-body li.none,div.none{ 111 display: none; 112 } 113 114 div.value-body div{ 115 text-align: center; 116 height: 30px; 117 line-height: 30px; 118 color: #AAA; 119 } 120 </style> 121 <script type="text/javascript"> 122 window.onload = function () { 123 //清空select的value 124 document.querySelector('div.select>select').value = '' 125 126 /** 127 * 点击自定义的select框开启或收回选择框 128 */ 129 document.querySelector('div.select-head').onclick = function () { 130 //清空输入框内容 131 document.querySelector('div.search-input>input').value = '' 132 133 document.querySelectorAll('div.value-body>li').forEach( function(element, index) { 134 if (element.classList.contains('active')) { 135 element.classList = 'active' 136 }else { 137 element.classList = '' 138 } 139 }); 140 141 document.querySelector('div.value-body>div').classList = 'none' 142 143 var select_body = document.querySelector('div.select-body') 144 if (select_body.style.display == 'block') 145 select_body.style.display = 'none' 146 else 147 select_body.style.display = 'block' 148 }; 149 150 /** 151 * 点击空白处关闭select框 152 */ 153 document.onclick = function (argument) { 154 if(!argument.target.classList.contains('s')){ 155 var select_body = document.querySelector('div.select-body') 156 if (select_body.style.display == 'block') 157 select_body.style.display = 'none' 158 } 159 } 160 161 /** 162 * 自定义的select的选值功能 163 */ 164 document.querySelectorAll('div.value-body>li').forEach( function(element, index) { 165 element.onclick = function () { 166 //初始化下样式 167 document.querySelectorAll('div.value-body>li').forEach( function(element, index) { 168 element.classList = '' 169 }); 170 element.classList = 'active' 171 //更新select框的value和自定义的select框的value 172 var data_value = element.getAttribute('data-value') 173 var select_head_span = document.querySelector('div.select-head>span') 174 document.querySelector('div.select>select').value = data_value 175 select_head_span.innerHTML = data_value 176 if(!select_head_span.classList.contains('fill')) 177 select_head_span.classList = 'fill' 178 179 //关闭select-body 180 document.querySelector('div.select-body').style.display = 'none' 181 } 182 }); 183 184 /** 185 * 搜素功能实现 186 */ 187 document.querySelector('div.search-input>input').oninput = function () { 188 var input_value = document.querySelector('div.search-input>input').value 189 if(input_value == '') { 190 document.querySelectorAll('div.value-body>li').forEach( function(element, index) { 191 if (element.classList.contains('active')) { 192 element.classList = 'active' 193 }else { 194 element.classList = '' 195 } 196 }); 197 }else{ 198 document.querySelectorAll('div.value-body>li').forEach( function(element, index) { 199 if(element.getAttribute('data-value').indexOf(input_value) == -1){ 200 if (element.classList.contains('active')) { 201 element.classList += ' none' 202 }else { 203 element.classList = 'none' 204 } 205 }else { 206 if(element.classList.contains('none')) { 207 document.querySelectorAll('div.value-body>li').forEach( function(element, index) { 208 if (element.classList.contains('active')) { 209 element.classList = 'active' 210 }else { 211 element.classList = '' 212 } 213 }); 214 } 215 } 216 }); 217 } 218 //记一下结果数量 219 var length = 0 220 document.querySelectorAll('div.value-body>li').forEach( function(element, index) { 221 if (!element.classList.contains('none')) length++ 222 }); 223 224 if (length == 0) { 225 document.querySelector('div.value-body>div').classList = '' 226 }else{ 227 document.querySelector('div.value-body>div').classList = 'none' 228 } 229 } 230 }; 231 </script> 232 </head> 233 <body> 234 <div class="select"> 235 <select name="select-name"> 236 <option value="" disabled="true">请选择</option> 237 <option value="选择1">选择1</option> 238 <option value="选择2">选择2</option> 239 <option value="选择3">选择3</option> 240 <option value="选择4">选择4</option> 241 <option value="选择5">选择5</option> 242 <option value="选择6">选择6</option> 243 <option value="选择7">选择7</option> 244 <option value="选择8">选择8</option> 245 <option value="选择9">选择9</option> 246 <option value="选择10">选择10</option> 247 </select> 248 <div class="s select-box"> 249 <div class="s select-head"> 250 <span class="s">请选择</span> 251 <i class="s"></i> 252 </div> 253 <div class="s select-body"> 254 <div class="s search-input"> 255 <input class="s" type="text" placeholder="搜索"> 256 <i class="s"></i> 257 </div> 258 <div class="s value-body"> 259 <li data-value="选择1">选择1</li> 260 <li data-value="选择2">选择2</li> 261 <li data-value="选择3">选择3</li> 262 <li data-value="选择4">选择4</li> 263 <li data-value="选择5">选择5</li> 264 <li data-value="选择6">选择6</li> 265 <li data-value="选择7">选择7</li> 266 <li data-value="选择8">选择8</li> 267 <li data-value="选择9">选择9</li> 268 <li data-value="选择10">选择10</li> 269 <div class="none">暂无匹配选项</div> 270 </div> 271 </div> 272 </div> 273 </div> 274 </body> 275 </html>
boostrap-select
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>jQuery bootstrap-select</title> 5 <!-- set jquery cdn --> 6 <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> 7 8 <!-- set bootstrap-select js and css --> 9 <script type="text/javascript" src="http://cdn.bootcss.com/bootstrap-select/2.0.0-beta1/js/bootstrap-select.js"></script> 10 <link rel="stylesheet" type="text/css" href="http://cdn.bootcss.com/bootstrap-select/2.0.0-beta1/css/bootstrap-select.css"> 11 12 <!-- 3.0 cnd bootstrap css and js --> 13 <link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"> 14 <script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> 15 <script type="text/javascript"> 16 $(window).on('load', function () { 17 18 $('.selectpicker').selectpicker();//one way set no params 19 //$('.selectpicker').selectpicker( //another way set params 20 // { 'selectedText': 'cat' } 21 // ) 22 23 // $('.selectpicker').selectpicker('hide'); 24 }); 25 </script> 26 </head> 27 <body> 28 <div> 29 30 <label for="id_select">Test label YEag</label> 31 <select id="id_select" class="selectpicker bla bla bli" 32 multiple data-live-search="true"> 33 <option>cow</option> 34 <option>bull</option> 35 <option class="get-class" disabled>ox</option> 36 <optgroup label="test" data-subtext="another test" data-icon="icon-ok"> 37 <option>ASD</option> 38 <option selected>Bla</option> 39 <option>Ble</option> 40 </optgroup> 41 </select> 42 43 <div class="container"> 44 <form class="form-horizontal" role="form"> 45 <div class="form-group"> 46 <label for="bs3Select" class="col-lg-2 control-label">Test bootstrap 3 form</label> 47 <div class="col-lg-10"> 48 <select id="bs3Select" class="selectpicker show-tick form-control" multiple data-live-search="true"> 49 <option>cow</option> 50 <option>bull</option> 51 <option class="get-class" disabled>ox</option> 52 <optgroup label="test" data-subtext="another test" data-icon="icon-ok"> 53 <option>ASD</option> 54 <option selected>Bla</option> 55 <option>Ble</option> 56 </optgroup> 57 </select> 58 </div> 59 </div> 60 </form> 61 </div> 62 </div> 63 64 </body> 65 </html>
relation-graph
这里是relation-graph,vue插件实际使用示例:
.vue文件,可以放到vue项目直接运行
<template> <div> <h3>hi relation-graph</h3> <div> <label>暂仅支持PCS-ID查询</label> <input type="text" v-model="pcsID"> <button type="button" @click="doClick">submit</button> </div> <div v-loading="g_loading" style="margin-top:50px;width: calc(100% - 10px);height:calc(100vh - 140px);"> <SeeksRelationGraph ref="seeksRelationGraph" :options="graphOptions" :on-node-expand="onNodeExpand" :on-node-collapse="onNodeCollapse"> </SeeksRelationGraph> </div> <el-button type="success" class="c-show-code-button"> <el-link href="https://github.com/seeksdream/relation-graph/blob/master/doc/demo/Demo4ExpandGradually.vue" target="_blank" style="color: #ffffff;">查看代码 </el-link> </el-button> </div> </template> <script> import SeeksRelationGraph from 'relation-graph' import {ajaxGetNodeData} from "../static_frontend/js/basic_call_func"; //调用自己封装的javascript写的ajax函数 export default { name: 'GraphRelationTest', components: {SeeksRelationGraph}, data() { return { g_loading: true, demoname: '---', graphOptions: { 'backgrounImage': 'http://ai-mark.cn/images/ai-mark-desc.png', 'backgrounImageNoRepeat': true, 'layouts': [ { 'label': '中心', 'layoutName': 'tree', 'layoutClassName': 'seeks-layout-center', 'defaultJunctionPoint': 'border', 'defaultNodeShape': 0, 'defaultLineShape': 1, 'from': 'left', 'max_per_width': '300', 'min_per_height': '40' } ], 'defaultLineMarker': { 'markerWidth': 12, 'markerHeight': 12, 'refX': 6, 'refY': 6, 'data': 'M2,2 L10,6 L2,10 L6,6 L2,2' }, "defaultExpandHolderPosition": "right", 'defaultNodeShape': 1, 'defaultNodeWidth': '100', 'defaultLineShape': 4, 'defaultJunctionPoint': 'lr', 'defaultNodeBorderWidth': 0, 'defaultLineColor': 'rgba(0, 186, 189, 1)', 'defaultNodeColor': 'rgba(0, 206, 209, 1)' } } }, created() { }, mounted(){}, methods: { setGraphData(__graph_json_data) { // 使用要点:通过节点属性expandHolderPosition: 'right' 和 expanded: false 可以让节点在没有子节点的情况下展示一个"展开"按钮 // 通过onNodeExpand事件监听节点,在被展开的时候有选择的去从后台获取数据,如果已经从后台加载过数据,则让当前图谱根据当前的节点重新布局 this.$refs.seeksRelationGraph.setJsonData(__graph_json_data, (seeksRGGraph) => { // 这些写上当图谱初始化完成后需要执行的代码 // 获取根节点的子节点,即可获得图谱第一层中的节点 var level_1_nodes = seeksRGGraph.getNodeById(__graph_json_data.rootId).lot.childs level_1_nodes.forEach(thisLevel1Node => { this.applyCollapseStyle2Node(thisLevel1Node) }) this.$refs.seeksRelationGraph.refresh() }) }, applyCollapseStyle2Node(_node) { // _node的子节点将被隐藏,同时让_node右侧显示一个加号,点击后可以展开子节点 if (_node.lot.childs.length > 0) { _node.lot.childs.forEach(thisChildNode => { thisChildNode.isShow = false this.applyCollapseStyle2Node(thisChildNode) }) _node.expanded = false _node.expandHolderPosition = 'right' } }, onNodeCollapse(node, e) { // console.log('onNodeCollapse:', node) // 当有一些节点被显示或隐藏起来时,会让图谱看着很难看,需要布局器重新为节点分配位置,所以这里需要调用refresh方法来重新布局 this.$refs.seeksRelationGraph.refresh() }, onNodeExpand(node, e) { // 当有一些节点被显示或隐藏起来时,会让图谱看着很难看,需要布局器重新为节点分配位置,所以这里需要调用refresh方法来重新布局 // console.log('onNodeExpand:', node) this.$refs.seeksRelationGraph.refresh() }, doClick() { console.log("this-object>>>", this, typeof(this)) //跟标签绑定的触发函数 let pcs_id = this.pcsID; // v-model是标签属性,在标签中设置它之后,methods下面的每个函数里面都可以通过this.属性值取标签值, // 比如v-model='pcsID',取值就用this.pcsID==document.getElementById("pcsID").value // 这里要跟ajax的返回值挂上钩,从ajax拿到后端返回值数据,拼接到下面的nodes、links里面 ajaxGetNodeData(this.setGraphData, pcs_id) } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>
补充ajax封装的函数:
export function ajaxGetNodeData(call_back_func, id_value=null) { //这里封装ajax函数,把ajax请求的回调函数里面,传入加载渲染节点的插件调用方法 let xmlHttp = new XMLHttpRequest(); xmlHttp.open("POST", "http://localhost:8000/api/v1/graph_test", true); xmlHttp.setRequestHeader("Access-Control-Allow-Origin", "*") xmlHttp.setRequestHeader( "Content-Type", "application/json;charset=UTF-8" ); let post_data = JSON.stringify({ "name": id_value, }); xmlHttp.send(post_data); xmlHttp.onreadystatechange = function (data) { if (xmlHttp.readyState === 4 && ( (xmlHttp.status >= 200 && xmlHttp.status < 300) || xmlHttp.status === 304) ) { let response_data = data.target.response; //get object type of data which backend transfor let parsed_data = JSON.parse(response_data); var __graph_json_data = parsed_data.data; //在success回调函数中,拿到后端请求得到的数据,启动插件方法,把数据传入进去,渲染到页面 call_back_func(__graph_json_data); if (parsed_data.error != null) { alert(parsed_data.info) } } //get ajax error response xmlHttp.onerror = function () { alert("got error function"); console.log(xmlHttp.response); console.log(xmlHttp.responseXML); console.log(xmlHttp.getAllResponseHeaders()); }; }; }