vueFlow.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896
  1. <template lang="">
  2. <div class="vueFlow">
  3. <div id="container"></div>
  4. <div id="stencil"></div>
  5. <div id="graph-container"></div>
  6. </div>
  7. <el-button @click="submitAll" type="primary">保存</el-button>
  8. <el-dialog
  9. :title="modalType == 'add' ? '新增' : '编辑'"
  10. v-model="dialogVisible"
  11. width="500"
  12. v-loading="loading"
  13. >
  14. <byForm
  15. :formConfig="formConfig"
  16. :formOption="formOption"
  17. v-model="formData.data"
  18. :rules="rules"
  19. ref="byform"
  20. >
  21. </byForm>
  22. <template #footer>
  23. <el-button @click="dialogVisible = false" size="large"
  24. >取 消</el-button
  25. >
  26. <el-button
  27. type="danger"
  28. @click="deleteFlowDefinitionNodeObj()"
  29. size="large"
  30. :loading="submitLoading"
  31. >
  32. </el-button>
  33. <el-button
  34. type="primary"
  35. @click="submitForm('byform')"
  36. size="large"
  37. :loading="submitLoading"
  38. >
  39. </el-button>
  40. </template>
  41. </el-dialog>
  42. </template>
  43. <script lang="ts" setup>
  44. import {
  45. defineComponent,
  46. ref,
  47. onMounted,
  48. onUnmounted,
  49. watch,
  50. reactive,
  51. toRefs,
  52. computed,
  53. nextTick,
  54. getCurrentInstance,
  55. } from 'vue'
  56. import byForm from '@/components/byForm/index'
  57. import { Graph, Shape } from '@antv/x6'
  58. import { Stencil } from '@antv/x6-plugin-stencil'
  59. import { Transform } from '@antv/x6-plugin-transform'
  60. import { Selection } from '@antv/x6-plugin-selection'
  61. import { Snapline } from '@antv/x6-plugin-snapline'
  62. import { Keyboard } from '@antv/x6-plugin-keyboard'
  63. import { Clipboard } from '@antv/x6-plugin-clipboard'
  64. import { History } from '@antv/x6-plugin-history'
  65. import Cookies from 'js-cookie'
  66. import { ElMessage, ElMessageBox } from "element-plus";
  67. defineProps({
  68. title: {
  69. type: Object,
  70. default: '',
  71. },
  72. });
  73. const { proxy } = getCurrentInstance()
  74. const internalInstance = getCurrentInstance()
  75. const dialogVisible = ref(false)
  76. const modalType = ref('add')
  77. const loading = ref(false)
  78. const submitLoading = ref(false)
  79. const formData = reactive({
  80. data: {
  81. userName: '',
  82. password: '',
  83. },
  84. })
  85. const byform = ref(null)
  86. const flowDefinitionNodeObj = ref({})
  87. const rules = reactive({
  88. nodeName: [
  89. {
  90. required: true,
  91. message: '请输入节点名称',
  92. trigger: 'blur',
  93. },
  94. ],
  95. handleObjectType: [
  96. {
  97. required: true,
  98. message: '办理人类型不能为空',
  99. trigger: 'blur',
  100. },
  101. ],
  102. handleObjectId: [
  103. {
  104. required: true,
  105. message: '办理人不能为空',
  106. trigger: 'blur',
  107. },
  108. ],
  109. })
  110. const formConfig = computed(() => {
  111. return [
  112. {
  113. type: 'input',
  114. prop: 'nodeName',
  115. label: '节点名称',
  116. required: true,
  117. itemType: 'text',
  118. },
  119. {
  120. type: 'select',
  121. prop: 'handleObjectType',
  122. label: '办理人',
  123. placeholder: '请选择办理人类型',
  124. required: true,
  125. itemWidth: 30,
  126. fn: (e) => {
  127. console.log(e)
  128. gethandleObjectList(e)
  129. },
  130. //1用户 2部门负责人 3部门总监 4岗位 5角色
  131. data: [
  132. {
  133. label: '用户',
  134. value: 1,
  135. },
  136. {
  137. label: '部门负责人',
  138. value: 2,
  139. },
  140. {
  141. label: '部门总监',
  142. value: 3,
  143. },
  144. {
  145. label: '岗位',
  146. value: 4,
  147. },
  148. {
  149. label: '角色',
  150. value: 5,
  151. },
  152. ],
  153. },
  154. // {
  155. // type: "treeSelect",
  156. // prop: "handleObjectId",
  157. // label: "请选择办理人",
  158. // itemWidth: 30,
  159. // data: [],
  160. // },
  161. {
  162. type: 'select',
  163. label: ' ',
  164. itemWidth: 30,
  165. prop: 'handleObjectId',
  166. placeholder: '请选择办理人',
  167. data: [],
  168. },
  169. {
  170. type: 'input',
  171. prop: 'handlingMethod',
  172. label: '节点后置执行方法',
  173. required: true,
  174. itemType: 'text',
  175. },
  176. {
  177. type: 'input',
  178. prop: 'jumpCondition',
  179. label: '条件表达式',
  180. required: true,
  181. itemType: 'text',
  182. },
  183. {
  184. type: 'checkbox',
  185. prop: 'nodeButtonSet',
  186. label: '节点按钮',
  187. //1通过 2驳回 3返回上一步 4退回到发起人
  188. data: [
  189. {
  190. label: '通过',
  191. value: 1,
  192. },
  193. {
  194. label: '驳回',
  195. value: 2,
  196. },
  197. {
  198. label: '返回上一步',
  199. value: 3,
  200. },
  201. {
  202. label: '退回到发起人',
  203. value: 4,
  204. },
  205. ],
  206. },
  207. {
  208. type: 'radio',
  209. prop: 'jobNumber11',
  210. label: '审批意见必填',
  211. data: [
  212. {
  213. label: '是',
  214. value: 1,
  215. },
  216. {
  217. label: '否',
  218. value: 0,
  219. },
  220. ],
  221. },
  222. ]
  223. })
  224. const formOption = reactive({
  225. inline: true,
  226. labelWidth: 100,
  227. itemWidth: 100,
  228. })
  229. let graph
  230. const submitForm = () => {
  231. byform.value.handleSubmit((valid) => {
  232. flowDefinitionNodeObj.value[formData.data.id] = formData.data
  233. console.log(flowDefinitionNodeObj.value)
  234. })
  235. }
  236. const submitFormData = {
  237. flowInfoId:null,
  238. titleTemplate:null,
  239. tenantId:Cookies.get('tenantId'),
  240. nodeObject:'',
  241. lineObject:'',
  242. flowDefinitionNodeList:[],
  243. }
  244. const submitAll = () => {
  245. if(proxy.title == '') {
  246. ElMessage({
  247. message: '请输入流程标题',
  248. type: 'warning',
  249. })
  250. return
  251. }
  252. submitFormData.titleTemplate = proxy.title
  253. const nodeList = graph.toJSON().cells
  254. console.log(nodeList)
  255. const isStart = false
  256. for (let i = 0; i < nodeList.length; i++) {
  257. const element = nodeList[i];
  258. //是办理节点
  259. if(element.id != 1 && element.shape != "edge") {
  260. console.log(element)
  261. if(!flowDefinitionNodeObj.value[element.id]) {
  262. ElMessage({
  263. message: '有节点未配置,请检查节点123123123',
  264. type: 'warning',
  265. })
  266. return
  267. }
  268. submitFormData.flowDefinitionNodeList.push({...flowDefinitionNodeObj.value[element.id],nodeType:2})
  269. }
  270. if(element.id == "1") {
  271. submitFormData.flowDefinitionNodeList.push({
  272. nodeName:'开始',
  273. nodeType:1,
  274. id:1,
  275. nodeButtonSet:'',
  276. parentId:0,
  277. })
  278. }
  279. //说明是线
  280. if(element.shape == "edge") {
  281. console.log(flowDefinitionNodeObj)
  282. if(!flowDefinitionNodeObj.value[element.target.cell]) {
  283. ElMessage({
  284. message: '有节点未配置,请检查节点',
  285. type: 'warning',
  286. })
  287. return
  288. }
  289. flowDefinitionNodeObj.value[element.target.cell].id = element.target.cell
  290. flowDefinitionNodeObj.value[element.target.cell].parentId = element.source.cell
  291. submitFormData.flowDefinitionNodeList = []
  292. }
  293. }
  294. addVersion()
  295. console.log(flowDefinitionNodeObj.value)
  296. }
  297. //选取一个随机不重复的正整数id
  298. const randomId = () => {
  299. const id = Math.floor(Math.random() * 100000000000000000)
  300. if(flowDefinitionNodeObj.value[id]) {
  301. randomId()
  302. } else {
  303. return id
  304. }
  305. }
  306. const addVersion = () => {
  307. const idObg = {}
  308. for (let i = 0; i < submitFormData.flowDefinitionNodeList.length; i++) {
  309. const element = submitFormData.flowDefinitionNodeList[i];
  310. if(element.parentId == null && element.nodeName == '结束') {
  311. ElMessage({
  312. message: '有结束节点未连线,请配置',
  313. type: 'warning',
  314. })
  315. return
  316. }
  317. if(isNaN(element.id)) {
  318. if(idObg[element.id]) {
  319. element.id = idObg[element.id]
  320. } else {
  321. const id = randomId()
  322. idObg[element.id] = id
  323. element.id = id
  324. }
  325. }
  326. if(isNaN(element.parentId) && element.nodeName != '开始') {
  327. if(idObg[element.parentId]) {
  328. element.parentId = idObg[element.parentId]
  329. } else {
  330. const id = randomId()
  331. idObg[element.parentId] = id
  332. element.parentId = id
  333. }
  334. }
  335. //nodeButtonSet转成字符串类型,用逗号隔开
  336. if(element.nodeButtonSet) {
  337. element.nodeButtonSet = element.nodeButtonSet.join(',')
  338. }
  339. }
  340. console.log(submitFormData)
  341. proxy.post('/flowDefinition/addVersion',submitFormData)
  342. .then((res) => {
  343. console.log(res)
  344. ElMessage({
  345. message: '保存成功',
  346. type: 'success',
  347. })
  348. })
  349. }
  350. //将组数里的id和parentId转换成整正整数类型
  351. const changeId = (arr) => {
  352. for (let i = 0; i < arr.length; i++) {
  353. const element = arr[i];
  354. element.id = parseInt(element.id)
  355. element.parentId = parseInt(element.parentId)
  356. }
  357. }
  358. const deleteFlowDefinitionNodeObj = (id) => {
  359. graph.removeNode(formData.data.id)
  360. delete flowDefinitionNodeObj.value[id]
  361. dialogVisible.value = false
  362. }
  363. const gethandleObjectList = (e) => {
  364. formData.data.handleObjectId = ''
  365. if(e === 1) {
  366. proxy.get('/tenantUser/list?pageNum=1&pageSize=1000&tenantId=' + submitFormData.tenantId,{})
  367. .then((res) => {
  368. formConfig.value[2].data = res.rows.map((item) => {
  369. return {
  370. label: item.nickName,
  371. value: item.userId,
  372. }
  373. })
  374. })
  375. }
  376. if(e === 3 || e === 2) {
  377. proxy.get('/tenantDept/list?pageNum=1&pageSize=1000&tenantId=' + submitFormData.tenantId,{})
  378. .then((res) => {
  379. formConfig.value[2].data =res.data.map(item=> {
  380. return {
  381. label: item.deptName,
  382. value: item.deptId,
  383. }
  384. })
  385. })
  386. }
  387. if(e === 4) {
  388. }
  389. if(e === 5) {
  390. proxy.get('/tenantRole/list?pageNum=1&pageSize=1000&tenantId=' + submitFormData.tenantId,{})
  391. .then((res) => {
  392. formConfig.value[2].data = res.rows.map((item) => {
  393. return {
  394. label: item.roleName,
  395. value: item.roleId,
  396. }
  397. })
  398. })
  399. }
  400. }
  401. const getTenantDept = () => {
  402. }
  403. getTenantDept()
  404. const recursive = (data) => {
  405. data.map((item) => {
  406. item.label = item.deptName;
  407. item.id = item.deptId;
  408. if (item.children) {
  409. recursive(item.children);
  410. } else {
  411. item.children = [];
  412. }
  413. });
  414. };
  415. const pushRoom = (port: any) => {
  416. console.log(port)
  417. if(port.node.label == '结束') {
  418. flowDefinitionNodeObj.value[port.node.id] = {
  419. nodeName:'结束',
  420. nodeType:99,
  421. id:port.id,
  422. nodeButtonSet:'',
  423. parentId:null,
  424. }
  425. }
  426. console.log(flowDefinitionNodeObj.value)
  427. }
  428. //用于存储流程定义节点数据
  429. const antvInit = () => {
  430. graph = new Graph({
  431. height: 600,
  432. container: document.getElementById('graph-container')!,
  433. grid: true,
  434. onPortRendered: pushRoom,
  435. mousewheel: {
  436. enabled: true,
  437. zoomAtMousePosition: true,
  438. modifiers: 'ctrl',
  439. minScale: 0.5,
  440. maxScale: 3,
  441. },
  442. connecting: {
  443. router: 'manhattan',
  444. connector: {
  445. name: 'rounded',
  446. args: {
  447. radius: 8,
  448. },
  449. },
  450. anchor: 'center',
  451. connectionPoint: 'anchor',
  452. allowBlank: false,
  453. snap: {
  454. radius: 20,
  455. },
  456. createEdge() {
  457. return new Shape.Edge({
  458. attrs: {
  459. line: {
  460. stroke: '#A2B1C3',
  461. strokeWidth: 2,
  462. targetMarker: {
  463. name: 'block',
  464. width: 12,
  465. height: 8,
  466. },
  467. },
  468. },
  469. zIndex: 0,
  470. })
  471. },
  472. validateConnection({ targetMagnet }) {
  473. return !!targetMagnet
  474. },
  475. },
  476. highlighting: {
  477. magnetAdsorbed: {
  478. name: 'stroke',
  479. args: {
  480. attrs: {
  481. fill: '#5F95FF',
  482. stroke: '#5F95FF',
  483. },
  484. },
  485. },
  486. },
  487. })
  488. const stencil = new Stencil({
  489. title: '流程图',
  490. target: graph,
  491. stencilGraphWidth: 200,
  492. stencilGraphHeight: 180,
  493. collapsable: true,
  494. groups: [
  495. {
  496. title: '基础流程图',
  497. name: 'group1',
  498. },
  499. ],
  500. layoutOptions: {
  501. columns: 2,
  502. columnWidth: 80,
  503. rowHeight: 55,
  504. },
  505. })
  506. document.getElementById('stencil')!.appendChild(stencil.container)
  507. // #region 使用插件
  508. graph
  509. .use(
  510. new Transform({
  511. resizing: true,
  512. rotating: true,
  513. })
  514. )
  515. .use(
  516. new Selection({
  517. enabled: true,
  518. rubberband: true,
  519. showNodeSelectionBox: true,
  520. })
  521. )
  522. .use(
  523. new Snapline({
  524. enabled: true,
  525. })
  526. )
  527. .use(
  528. new Keyboard({
  529. enabled: true,
  530. })
  531. )
  532. .use(
  533. new Clipboard({
  534. enabled: true,
  535. })
  536. )
  537. .use(
  538. new History({
  539. enabled: true,
  540. })
  541. )
  542. // 控制连接桩显示/隐藏
  543. const showPorts = (ports: NodeListOf<SVGElement>, show: boolean) => {
  544. for (let i = 0, len = ports.length; i < len; i += 1) {
  545. ports[i].style.visibility = show ? 'visible' : 'hidden'
  546. }
  547. }
  548. graph.on('node:mouseenter', () => {
  549. const container = document.getElementById('graph-container')!
  550. const ports = container.querySelectorAll(
  551. '.x6-port-body'
  552. ) as NodeListOf<SVGElement>
  553. showPorts(ports, true)
  554. })
  555. graph.on('node:mouseleave', () => {
  556. const container = document.getElementById('graph-container')!
  557. const ports = container.querySelectorAll(
  558. '.x6-port-body'
  559. ) as NodeListOf<SVGElement>
  560. showPorts(ports, false)
  561. })
  562. // #endregion
  563. graph.on('cell:click', ({ e, x, y, cell, view }) => {
  564. console.log(cell)
  565. if (cell.label === '开始' || cell.label === '结束' || cell.shape === 'edge') {
  566. return
  567. }
  568. if (flowDefinitionNodeObj.value[cell.id]) {
  569. formData.data = flowDefinitionNodeObj.value[cell.id]
  570. } else {
  571. formData.data = {
  572. id: cell.id,
  573. cell: cell,
  574. }
  575. }
  576. dialogVisible.value = true
  577. })
  578. // #region 初始化图形
  579. const ports = {
  580. groups: {
  581. top: {
  582. position: 'top',
  583. attrs: {
  584. circle: {
  585. r: 4,
  586. magnet: true,
  587. stroke: '#5F95FF',
  588. strokeWidth: 1,
  589. fill: '#fff',
  590. style: {
  591. visibility: 'hidden',
  592. },
  593. },
  594. },
  595. },
  596. right: {
  597. position: 'right',
  598. attrs: {
  599. circle: {
  600. r: 4,
  601. magnet: true,
  602. stroke: '#5F95FF',
  603. strokeWidth: 1,
  604. fill: '#fff',
  605. style: {
  606. visibility: 'hidden',
  607. },
  608. },
  609. },
  610. },
  611. bottom: {
  612. position: 'bottom',
  613. attrs: {
  614. circle: {
  615. r: 4,
  616. magnet: true,
  617. stroke: '#5F95FF',
  618. strokeWidth: 1,
  619. fill: '#fff',
  620. style: {
  621. visibility: 'hidden',
  622. },
  623. },
  624. },
  625. },
  626. left: {
  627. position: 'left',
  628. attrs: {
  629. circle: {
  630. r: 4,
  631. magnet: true,
  632. stroke: '#5F95FF',
  633. strokeWidth: 1,
  634. fill: '#fff',
  635. style: {
  636. visibility: 'hidden',
  637. },
  638. },
  639. },
  640. },
  641. },
  642. items: [
  643. {
  644. group: 'top',
  645. },
  646. {
  647. group: 'right',
  648. },
  649. {
  650. group: 'bottom',
  651. },
  652. {
  653. group: 'left',
  654. },
  655. ],
  656. }
  657. Graph.registerNode(
  658. 'custom-rect',
  659. {
  660. inherit: 'rect',
  661. width: 66,
  662. height: 36,
  663. attrs: {
  664. body: {
  665. strokeWidth: 1,
  666. stroke: '#5F95FF',
  667. fill: '#EFF4FF',
  668. },
  669. text: {
  670. fontSize: 12,
  671. fill: '#262626',
  672. },
  673. },
  674. ports: { ...ports },
  675. },
  676. true
  677. )
  678. Graph.registerNode(
  679. 'custom-polygon',
  680. {
  681. inherit: 'polygon',
  682. width: 66,
  683. height: 36,
  684. attrs: {
  685. body: {
  686. strokeWidth: 1,
  687. stroke: '#5F95FF',
  688. fill: '#EFF4FF',
  689. },
  690. text: {
  691. fontSize: 12,
  692. fill: '#262626',
  693. },
  694. },
  695. ports: {
  696. ...ports,
  697. items: [
  698. {
  699. group: 'top',
  700. },
  701. {
  702. group: 'bottom',
  703. },
  704. ],
  705. },
  706. },
  707. true
  708. )
  709. Graph.registerNode(
  710. 'custom-circle',
  711. {
  712. inherit: 'circle',
  713. width: 45,
  714. height: 45,
  715. attrs: {
  716. body: {
  717. strokeWidth: 1,
  718. stroke: '#5F95FF',
  719. fill: '#EFF4FF',
  720. },
  721. text: {
  722. fontSize: 12,
  723. fill: '#262626',
  724. },
  725. },
  726. ports: { ...ports },
  727. },
  728. true
  729. )
  730. Graph.registerNode(
  731. 'custom-image',
  732. {
  733. inherit: 'rect',
  734. width: 52,
  735. height: 52,
  736. markup: [
  737. {
  738. tagName: 'rect',
  739. selector: 'body',
  740. },
  741. {
  742. tagName: 'image',
  743. },
  744. {
  745. tagName: 'text',
  746. selector: 'label',
  747. },
  748. ],
  749. attrs: {
  750. body: {
  751. stroke: '#5F95FF',
  752. fill: '#5F95FF',
  753. },
  754. image: {
  755. width: 26,
  756. height: 26,
  757. refX: 13,
  758. refY: 16,
  759. },
  760. label: {
  761. refX: 3,
  762. refY: 2,
  763. textAnchor: 'left',
  764. textVerticalAnchor: 'top',
  765. fontSize: 12,
  766. fill: '#fff',
  767. },
  768. },
  769. ports: { ...ports },
  770. },
  771. true
  772. )
  773. let firstLi = document.createElement('li')
  774. firstLi.innerText = '指标详情'
  775. console.log(firstLi)
  776. const r1 = graph.createNode({
  777. shape: 'custom-rect',
  778. label: '开始',
  779. zIndex: 100,
  780. attrs: {
  781. body: {
  782. rx: 20,
  783. ry: 26,
  784. },
  785. },
  786. tools: [
  787. {
  788. name: 'button',
  789. args: {
  790. firstLi,
  791. },
  792. },
  793. ],
  794. })
  795. const r2 = graph.createNode({
  796. shape: 'custom-rect',
  797. label: '办理',
  798. })
  799. const r3 = graph.createNode({
  800. shape: 'custom-rect',
  801. attrs: {
  802. body: {
  803. rx: 6,
  804. ry: 6,
  805. },
  806. },
  807. label: '分支',
  808. })
  809. const r4 = graph.createNode({
  810. shape: 'custom-polygon',
  811. attrs: {
  812. body: {
  813. refPoints: '0,10 10,0 20,10 10,20',
  814. },
  815. },
  816. label: '结束',
  817. })
  818. stencil.load([ r2, r4], 'group1')
  819. graph.addNode({
  820. shape: 'custom-rect',
  821. label: '开始',
  822. id: 1,
  823. x: 500,
  824. y: 100,
  825. })
  826. }
  827. onMounted(() => {
  828. antvInit()
  829. //获取url router参数
  830. const router = useRouter();
  831. submitFormData.flowInfoId = router.currentRoute.value.query.id
  832. submitFormData.tenantId = router.currentRoute.value.query.tenantId
  833. })
  834. </script>
  835. <style lang="scss">
  836. .x6-widget-stencil-title {
  837. display: none;
  838. }
  839. .x6-widget-stencil-content {
  840. top: 0 !important;
  841. }
  842. .vueFlow {
  843. position: relative;
  844. display: flex;
  845. justify-content: space-between;
  846. overflow: hidden;
  847. height: 600px;
  848. #stencil {
  849. position: absolute;
  850. top: 0;
  851. left: 0;
  852. z-index: 100;
  853. width: 200px;
  854. height: 500px;
  855. background: #fff;
  856. border-right: 1px solid #e8e8e8;
  857. }
  858. #container {
  859. }
  860. #graph-container {
  861. width: 100%;
  862. position: absolute;
  863. right: 0;
  864. top: 0;
  865. }
  866. }
  867. </style>