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