Advertisement
Guest User

Untitled

a guest
Oct 14th, 2019
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.82 KB | None | 0 0
  1. <template>
  2. <div>
  3. <div class="row">
  4. <div class="col-sm-12">
  5. <div class="content-header">Цветок</div>
  6. </div>
  7. </div>
  8.  
  9. <section id="grid-option">
  10. <div class="row">
  11. <div class="col-12">
  12. <div class="card">
  13. <div class="card-body">
  14. <div class="card-block">
  15. <div class="col-md-12">
  16. <div class="card">
  17. <div class="card-body">
  18. <div class="card-block">
  19. <div class="card-text text-center">
  20. <div id="flowerElem" class="flower">
  21. <svg v-if="states.flowerEl"></svg>
  22. </div>
  23. </div>
  24. </div>
  25. </div>
  26. </div>
  27. </div>
  28. </div>
  29. </div>
  30. </div>
  31. </div>
  32. </div>
  33. </section>
  34. <!--Modal-->
  35. <b-modal id="selectRefModal" @ok="addRefToFlower" ref="showSelectRefModal" title="Вставить в цветок"
  36. cancel-title="Отмена" ok-title="Вставить" :ok-disabled="!refSelected">
  37. <b-form-select v-model="refSelected" :options="refOptions" class="mb-3">
  38. <template slot="first">
  39. <option :value="null" disabled>{{ refOptions.length > 0 ? '-- Выберите человека для вставки --' : '-- Нет людей для вставки --' }}</option>
  40. </template>
  41. </b-form-select>
  42. </b-modal>
  43. </div>
  44. </template>
  45.  
  46. <script>
  47. import * as d3 from 'd3'
  48. import _ from 'lodash'
  49. import {Flower} from '../classes/API'
  50. import Auth from "../classes/API/Auth"
  51.  
  52. export default {
  53. name: 'Flower',
  54. data() {
  55. return {
  56. refOptions: [],
  57. refSelected: '',
  58. currentXY: {},
  59. states: {flowerEl: true},
  60. flowerData: []
  61. }
  62. },
  63. mounted() {
  64. this.init()
  65. },
  66. methods: {
  67. init(reload) {
  68. let self = this
  69.  
  70. if (reload) {
  71. self.states.flowerEl = false
  72.  
  73. setTimeout(function () {
  74. self.states.flowerEl = true
  75. return self.init()
  76. }, 500)
  77. }
  78.  
  79. Flower.getFlowerView(this, {x: 0, y: 0}).then(response => {
  80. self.flowerData = response.data
  81. self.initFlower(self.flowerData)
  82. })
  83. },
  84. addRefToFlower() {
  85. Flower.flowerSetReferral(this, {
  86. userId: this.refSelected.id,
  87. x: this.currentXY.x,
  88. y: this.currentXY.y
  89. }).then(response => {
  90. if (response.status === 200) {
  91. this.init(true)
  92. } else {
  93. alert('Прозошла ошибка, попробуйте немного позже')
  94. }
  95. })
  96. },
  97. showSelectRefModal(elem) {
  98. this.currentXY = elem
  99. Flower.getFlowerToPlacement(this).then(response => {
  100. let refOption = []
  101. _.forEach(response.data, function (value) {
  102. refOption.push({value: value, text: value.name + ' (' + value.nickname + ')'})
  103. })
  104. this.refOptions = refOption
  105. this.refSelected = null
  106. this.$root.$emit('bv::show::modal', 'selectRefModal')
  107. })
  108. },
  109. initFlower(pages) {
  110. // index
  111. // Pan & Zooming
  112. let svg = d3.select("svg")
  113. .call(d3.zoom().on("zoom", function () {
  114. svg.attr("transform", d3.event.transform + "translate(100, 750), rotate(-90)")
  115. }).scaleExtent([1, 2]))
  116.  
  117. svg = d3.select("svg")
  118. .append('g')
  119. .attr('id', 'svgMain')
  120. .attr("transform", "translate(100, 750) rotate(-90)")
  121.  
  122. let tooltip = d3.select("body")
  123. .append("div")
  124. .attr("class", "tooltipFlower")
  125. .style("opacity", 0)
  126. let margin = {
  127. top: 10,
  128. right: 10,
  129. bottom: 10,
  130. left: 10
  131. }
  132. let height = 1000 - margin.left - margin.right
  133. let width = 1000 - margin.top - margin.bottom
  134.  
  135. let self = this
  136.  
  137. const
  138. w = 60,
  139. h = 45,
  140. radius = 55,
  141. radiusCos = radius * Math.cos(Math.PI / 6);
  142.  
  143. let
  144. userCoordinates = {x: 0, y: 0},
  145. center = userCoordinates,
  146. parityCenter = Math.abs(center.x % 2),
  147. r = radius * 0.45,
  148. cos60 = Math.cos(Math.PI / 6),
  149. sin60 = Math.sin(Math.PI / 6),
  150. hexData = [
  151. {y: r, x: 0},
  152. {y: r * sin60, x: r * cos60},
  153. {y: -r * sin60, x: r * cos60},
  154. {y: -r, x: 0},
  155. {y: -r * sin60, x: -r * cos60},
  156. {y: r * sin60, x: -r * cos60},
  157. {y: r, x: 0}
  158. ];
  159.  
  160. let hexGroup = svg.selectAll("g")
  161. .data(laps(calcCoordinates(fillMesh(mergePages(pages), center))))
  162. .enter()
  163. .append("g")
  164. .attr("transform", function (d) {
  165. return "translate(" + d._x + "," + d._y + "), rotate(90)";
  166. });
  167.  
  168. let drawHexagon = d3.line()
  169. .x(function (d) {
  170. return d.x;
  171. })
  172. .y(function (d) {
  173. return d.y;
  174. });
  175.  
  176. hexGroup
  177. .append('path')
  178. .attr("d", drawHexagon(hexData))
  179. .each(function (d) {
  180. switch (d.type) {
  181. case 'empty':
  182. d3.select(this)
  183. .style("stroke-dasharray", "4,2")
  184. .style("opacity", .3)
  185. .style("stroke", "lightblue")
  186. .style("stroke-width", "2")
  187. .style("stroke-linejoin", "round")
  188. .style("fill", "white");
  189. break;
  190. case 'append':
  191. d3.select(this)
  192. .style("stroke-dasharray", "4,2")
  193. .style("stroke", "#8fc5b7")
  194. .style("stroke-width", "2")
  195. .style("stroke-linejoin", "round")
  196. .style("fill", "white")
  197. .on('mouseover', function () {
  198. d3.select(this)
  199. .attr("cursor", "pointer")
  200. .transition()
  201. .ease(d3.easeElastic)
  202. .duration(1000)
  203. .attr("transform", function () {
  204. return "scale(1.2)";
  205. });
  206. })
  207. .on('mousemove', function () {
  208.  
  209. })
  210. .on('mouseout', function () {
  211. tooltip.style('visibility', 'hidden');
  212. d3.select(this)
  213. .transition()
  214. .ease(d3.easeElastic)
  215. .duration(1000)
  216. .attr("transform", function () {
  217. return "scale(1)";
  218. });
  219. })
  220. .on('click', function (d) {
  221. self.showSelectRefModal(d)
  222. });
  223. break;
  224. case 'exist':
  225. d3.select(this)
  226. .style("stroke", function (d) {
  227. if (d.id === Auth.getUserRow('uid', true)) {
  228. return '#1f6e6a'
  229. }
  230. // color lap by current user qualification
  231. if (d.qLap === 1) {
  232. return '#c3786e'
  233. } else if (d.qLap === 2 || d.qLap === 3) {
  234. return '#e1a76c'
  235. } else if (d.qLap >= 4 && d.qLap <= 6) {
  236. return '#c38588'
  237. } else if (d.qLap >= 7 && d.qLap <= 10) {
  238. return '#7aaf77'
  239. } else if (d.qLap >= 11 && d.qLap <= 15) {
  240. return '#a773a0'
  241. } else if (d.qLap >= 16 && d.qLap <= 20) {
  242. return '#7172a1'
  243. } else if (d.qLap >= 21 && d.qLap <= 25) {
  244. return '#6a5692'
  245. } else if (d.qLap >= 26 && d.qLap <= 30) {
  246. return '#006961'
  247. } else {
  248. return '#84b9b5'
  249. }
  250. })
  251. .style("stroke-width", "2")
  252. .style("stroke-linejoin", "round")
  253. .attr("class", function (d) {
  254. return 'filled' + ' ' + d.qualification
  255. })
  256. .style("fill", function (d) {
  257. if (d.id === Auth.getUserRow('uid', true)) {
  258. return '#4ca398'
  259. }
  260. // fill lap by current user qualification
  261. if (d.qLap === 1) {
  262. return '#f5aeb1'
  263. } else if (d.qLap === 2 || d.qLap === 3) {
  264. return '#ffe4a2'
  265. } else if (d.qLap >= 4 && d.qLap <= 6) {
  266. return '#dcb495'
  267. } else if (d.qLap >= 7 && d.qLap <= 10) {
  268. return '#81af7e'
  269. } else if (d.qLap >= 11 && d.qLap <= 15) {
  270. return '#a7819d'
  271. } else if (d.qLap >= 16 && d.qLap <= 20) {
  272. return '#7c78a1'
  273. } else if (d.qLap >= 21 && d.qLap <= 25) {
  274. return '#746592'
  275. } else if (d.qLap >= 26 && d.qLap <= 30) {
  276. return '#1b6966'
  277. } else {
  278. return '#81d8cd'
  279. }
  280. })
  281. .style("opacity", function (d) {
  282. if (_.isUndefined(d.qLap) && d.id !== Auth.getUserRow('uid', true)) {
  283. return .7
  284. }
  285. })
  286. .on('mouseover', function () {
  287. tooltip.style('visibility', 'visible');
  288.  
  289. d3.select(this)
  290. .attr("cursor", "pointer")
  291. .transition()
  292. .duration(200)
  293. .style("opacity", .8);
  294. })
  295. .on('mousemove', showTooltip)
  296. .on('mouseout', function () {
  297. tooltip.style('visibility', 'hidden');
  298.  
  299. // opacity for elems
  300. d3.select(this)
  301. .transition()
  302. .duration(200)
  303. .style("opacity", function (d) {
  304. if (_.isUndefined(d.qLap) && d.id !== Auth.getUserRow('uid', true)) {
  305. return .7
  306. }
  307. return 1
  308. });
  309. });
  310. break;
  311. }
  312. });
  313.  
  314. // Text on Hex
  315. hexGroup.append("text")
  316. .each(function (d) {
  317. switch (d.type) {
  318. case 'empty':
  319. break;
  320. case 'append':
  321. d3.select(this)
  322. .attr("text-anchor", "middle")
  323. .attr("y", 7)
  324. .attr("id", function (d, i) {
  325. return "append" + i;
  326. })
  327. .attr("font-size", "20px")
  328. .attr("font-family", "Arial")
  329. .attr("fill", "#8fc5b7")
  330. .attr("pointer-events", "none")
  331. .text("+");
  332. break;
  333. case 'exist':
  334. d3.select(this)
  335. .attr("text-anchor", "middle")
  336. .attr("y", 6)
  337. .attr("id", function (d, i) {
  338. return "exist" + i;
  339. })
  340. .attr("font-size", "18px")
  341. .attr("letter-spacing", "2px")
  342. .attr("font-family", "Arial")
  343. .attr("fill", "#FFF")
  344. .attr("pointer-events", "none")
  345. .text(function (d) {
  346. let initials = d.name.split(' ');
  347. let fName = initials[0][0] || '';
  348.  
  349. if (initials[1]) {
  350. let lName = initials[1][0] || '';
  351. return (fName + lName).toUpperCase()
  352. }
  353. return fName.toUpperCase();
  354.  
  355. });
  356. break;
  357. }
  358. });
  359.  
  360. // Show tooltip
  361. function showTooltip(d) {
  362. let textBox = 'Имя: <b>' + d.name + '</b></b><br>'
  363. + 'Групповой объем: <b>' + self.$options.filters.round(d.teamVolume, 2) + ' (Б)</b><br>'
  364. + 'Личный объем: <b>' + self.$options.filters.round(d.personalVolume, 2) + ' (Б)</b><br>'
  365. + 'Квалификация: <b>' + d.qualification + '</b><br>';
  366. tooltip.style('visibility', 'visible');
  367. tooltip.transition()
  368. .style("opacity", .85);
  369. tooltip
  370. .html(textBox)
  371. .style("left", (d3.event.pageX + 5) + "px")
  372. .style("top", (d3.event.pageY - 36) + "px");
  373. }
  374.  
  375. // draw
  376. function createEmptyMesh(centerPos, halfCountByWidth, halfCountByHeight) {
  377. let emptyMesh = [];
  378. for (let i = 0; i <= 2 * halfCountByHeight; i++) {
  379. for (let j = 0; j <= 2 * halfCountByWidth; j++) {
  380. emptyMesh.push({
  381. x: centerPos.x - halfCountByHeight + i,
  382. y: centerPos.y - halfCountByWidth + j,
  383. type: 'empty'
  384. });
  385. }
  386. }
  387.  
  388. return emptyMesh;
  389. }
  390.  
  391. function fillMesh(elems, centerPos) {
  392. function findAndReplace(_mesh, _elem) {
  393. let index = _.findIndex(_mesh, {x: _elem.x, y: _elem.y});
  394. if (index !== -1 && (_elem.type === 'exist' || (_elem.type === 'append' && _mesh[index].type === 'empty'))) {
  395. _mesh[index] = _elem;
  396. }
  397. }
  398.  
  399. const halfCountByWidth = Math.round(width / 1.5 / radius * 3 / 2);
  400. const halfCountByHeight = Math.round(height / 1.5 / radiusCos * 3 / 2);
  401.  
  402. let mesh = createEmptyMesh(centerPos, halfCountByWidth, halfCountByHeight);
  403. _.forEach(_.filter(
  404. elems,
  405. function (d) {
  406. return d.x <= centerPos.x + halfCountByWidth &&
  407. d.x >= centerPos.x - halfCountByWidth &&
  408. d.y <= centerPos.y + halfCountByHeight &&
  409. d.y >= centerPos.y - halfCountByHeight;
  410. }
  411. ), function (elem) {
  412. elem.type = 'exist';
  413. findAndReplace(mesh, elem);
  414.  
  415. _.forEach(['left', 'right', 'left-up', 'right-up', 'left-down', 'right-down'], function (direction) {
  416. findAndReplace(mesh, stepPos({x: elem.x, y: elem.y, type: 'append'}, direction));
  417. });
  418. });
  419.  
  420. return mesh;
  421. }
  422.  
  423. function findLap(center, radius) {
  424. let lap = [{x: center.x, y: center.y - radius}]
  425. _.forEach(['right-up', 'right', 'right-down', 'left-down', 'left', 'left-up'], function (direction) {
  426. for (let i = 1; i <= radius; i++) {
  427. lap.push(stepPos(_.last(lap), direction))
  428. }
  429. })
  430. lap.pop()
  431.  
  432. return lap
  433. }
  434.  
  435. function laps(mesh) {
  436. let elemOnLaps = [],
  437. elemObj, centerUser = _.find(mesh, function (d) {
  438. return d.id === Auth.getUserRow('uid', true)
  439. })
  440.  
  441. _.forEach(calcLapsByQualification(_.find(mesh, centerUser).qualification), function (lap) {
  442. _.map(findLap(centerUser, lap), function (obj) {
  443. elemObj = _.find(mesh, obj)
  444. elemObj.qLap = lap
  445. elemOnLaps.push(elemObj)
  446. })
  447. })
  448.  
  449. return mesh
  450. }
  451.  
  452. function calcLapsByQualification(q) {
  453. let lastLap, res = []
  454.  
  455. switch (q) {
  456. case 'Q0':
  457. lastLap = 0
  458. break;
  459. case 'Q1':
  460. lastLap = 1
  461. break;
  462. case 'Q2':
  463. lastLap = 3
  464. break;
  465. case 'Q3':
  466. lastLap = 6
  467. break;
  468. case 'Q4':
  469. lastLap = 10
  470. break;
  471. case 'Q5':
  472. lastLap = 15
  473. break;
  474. case 'Q6':
  475. lastLap = 20
  476. break;
  477. case 'Q7':
  478. lastLap = 25
  479. break;
  480. case 'Q8':
  481. lastLap = 30
  482. break;
  483. }
  484.  
  485. for (let i = 0; i <= lastLap; i++) {
  486. res.push(i)
  487. }
  488.  
  489. return res
  490. }
  491.  
  492. function calcCoordinates(mesh) {
  493. return _.flatten(_.values(_.mapValues(
  494. _.groupBy(
  495. mesh,
  496. 'x'
  497. ),
  498. function (rowElems, rowXCoordinate) {
  499. let x = (width / 2) - (center.x - rowXCoordinate) * radiusCos,
  500. res;
  501.  
  502. if (Math.abs(rowXCoordinate % 2) === parityCenter) {
  503. res = _.map(rowElems, function (d) {
  504. d._x = x;
  505. d._y = (height / 2) - (center.y - d.y) * radius;
  506. return d;
  507. });
  508. } else if (parityCenter === 1) {
  509. res = _.map(rowElems, function (d) {
  510. d._x = x;
  511. d._y = (height / 2) - (center.y - d.y) * radius + radius / 2
  512. return d;
  513. });
  514. } else {
  515. res = _.map(rowElems, function (d) {
  516. d._x = x;
  517. d._y = (height / 2) - (center.y - d.y) * radius - radius / 2
  518. return d;
  519. });
  520. }
  521.  
  522. return res;
  523. })));
  524. }
  525.  
  526. // pos-support
  527. function pageWithPos(x, y) {
  528. return {x: w * (x / w), y: h * (y / h)};
  529. }
  530.  
  531. function pagesForPos(x, y) {
  532. let xyPagePos = pageWithPos(x, y),
  533. res;
  534.  
  535. if (x % w < w / 2. && y % h < h / 2.) {
  536. res = [{x: xyPagePos.x, y: xyPagePos.y},
  537. {x: xyPagePos.x - w, y: xyPagePos.y},
  538. {x: xyPagePos.x, y: xyPagePos.y - h},
  539. {x: xyPagePos.x - w, y: xyPagePos.y - h}];
  540. } else if (x % w > w / 2. && y % h < h / 2.) {
  541. res = [{x: xyPagePos.x, y: xyPagePos.y},
  542. {x: xyPagePos.x + w, y: xyPagePos.y},
  543. {x: xyPagePos.x, y: xyPagePos.y - h},
  544. {x: xyPagePos.x + w, y: xyPagePos.y - h}];
  545. } else {
  546. res = [{x: 0, y: 0}];
  547. }
  548.  
  549. return res;
  550. }
  551.  
  552. function pagesForPosWithout(x, y, needlessPages) {
  553. return _.filter(pagesForPos(x, y), function (page) {
  554. return _.isUndefined(_.find(needlessPages, function (needlessPage) {
  555. return needlessPage.x === page.x && needlessPage.y === page.y;
  556. }));
  557. });
  558. }
  559.  
  560. function mergePages(pages) {
  561. return _.reduce(pages, function (acc, page) {
  562. return acc.concat(page.elems);
  563. }, []);
  564. }
  565.  
  566. function updatePages(pages, newPages) {
  567. let notAffectedPages = _.filter(pages, function (page) {
  568. return _.isUndefined(_.find(newPages, function (newPage) {
  569. return newPage.x === page.x && newPage.y === page.y;
  570. }));
  571. });
  572. return notAffectedPages.concat(newPages);
  573. }
  574.  
  575. function stepPos(elem, direction) {
  576. const parity = Math.abs(elem.x % 2);
  577. let res = {};
  578.  
  579. switch (direction) {
  580. case 'left' :
  581. res = {
  582. x: elem.x,
  583. y: elem.y - 1
  584. };
  585. break;
  586. case 'right' :
  587. res = {
  588. x: elem.x,
  589. y: elem.y + 1
  590. };
  591. break;
  592. case 'left-up' :
  593. res = {
  594. x: elem.x + 1,
  595. y: (parity) ? elem.y - 1 : elem.y
  596. };
  597. break;
  598. case 'left-down' :
  599. res = {
  600. x: elem.x - 1,
  601. y: (parity) ? elem.y - 1 : elem.y
  602. };
  603. break;
  604. case 'right-up' :
  605. res = {
  606. x: elem.x + 1,
  607. y: (parity) ? elem.y : elem.y + 1
  608. };
  609. break;
  610. case 'right-down' :
  611. res = {
  612. x: elem.x - 1,
  613. y: (parity) ? elem.y : elem.y + 1
  614. };
  615. break;
  616. }
  617.  
  618. if (elem.type) {
  619. res.type = elem.type;
  620. }
  621.  
  622. return res;
  623. }
  624. }
  625. }
  626.  
  627. }
  628. </script>
  629.  
  630. <style scoped>
  631. .card .card-block {
  632. padding: 0;
  633. }
  634.  
  635. .flower svg {
  636. width: 100%;
  637. height: 650px;
  638. }
  639. </style>
  640.  
  641. <style>
  642.  
  643. .tooltipFlower {
  644. text-align: center;
  645. position: absolute;
  646. font: 14px "Helvetica Neue", Helvetica, sans-serif;
  647. z-index: 99999999;
  648. border-radius: 8px;
  649. pointer-events: none;
  650. background-color: #ffffff;
  651. padding: 3px 12px;
  652. border: 1px solid #bbbbbb;
  653. box-shadow: 1px 1px 4px #bbbbbb;
  654. }
  655.  
  656. </style>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement