diy-rubik-cube.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. <?php
  2. // use common\helpers\ComponentHelper;
  3. // ComponentHelper::loadComponentView('components/style-components/diy-img-setting', __DIR__);
  4. ?>
  5. <template id="diy-rubik-cube">
  6. <div class="diy-rubik-cube">
  7. <div class="decorate-cube">
  8. <ul class="cube-col" v-for="(n,nIndex) in densityNum" :key="nIndex">
  9. <li :class="['cube-item', {'item-selecting': isSelecting(n,i), 'item-selected':isSelected(n,i)}]"
  10. v-for="(i,iIndex) in densityNum" :key="iIndex+100"
  11. :style="{'width':cubeCellWidth+'px','height':cubeCellHeight+'px'}"
  12. :data-x="n" :data-y="i"
  13. @click="onClickCubeItem($event)"
  14. @mouseenter="onEnterCubeItem($event)">
  15. <i class="el-icon-plus" :style="{'line-height':cubeCellHeight+'px'}"></i>
  16. </li>
  17. </ul>
  18. <div class="cube-selected" v-for="(item, index) in selectedList" :key="index + 100"
  19. :style="{'width':getSelectedWidth(item)+'px','height':getSelectedHeight(item)+'px','top':getSelectedTop(item)+'px','left':getSelectedLeft(item)+'px'}"
  20. :class="{'click-cube-selected': index == selCubeIndex}"
  21. @click="selCubeInfo(index)"
  22. >
  23. <div class="cube-selected-text" v-if="item.img == ''">
  24. {{ Math.round(cubeHeight/density*((parseInt(item.end.x) - parseInt(item.start.x) + 1))) }}
  25. x
  26. {{ Math.round(cubeWidth/density*((parseInt(item.end.y) - parseInt(item.start.y) + 1))) }}
  27. 像素
  28. </div>
  29. <el-image style="width:100%;height: 100%;" :src="item.img" fit="cover" v-else></el-image>
  30. <div class="cube-del-icon" @click.stop="delCubeList(index)" v-show="index == selCubeIndex && styleArr.length == 0">
  31. <i class="el-icon-error del-sel-icon"></i>
  32. </div>
  33. </div>
  34. </div>
  35. <div :style="{'width':cubeExhibitWidth+'px','height':cubeExhibitHeight+'px'}"></div>
  36. <diy-img-setting
  37. v-show="selCubeIndex != -1"
  38. :img-infos="cubeImgInfo"
  39. def-img-count="1"
  40. :suggest-site="99"
  41. suggest-size-text=""
  42. :img-contain-style="{padding: 0,'margin-top': '20px'}"
  43. :show-title="false"
  44. @change="addCubeImg"
  45. ></diy-img-setting>
  46. </div>
  47. </template>
  48. <script>
  49. Vue.component('diy-rubik-cube', {
  50. template: '#diy-rubik-cube',
  51. name: 'diy-rubik-cube',
  52. props: {
  53. density: {//密度
  54. type: [String,Number],
  55. default: 4
  56. },
  57. cubeWidth: { //魔方宽度
  58. type: [String,Number],
  59. default: 750
  60. },
  61. cubeHeight: { //魔方高度
  62. type: [String,Number],
  63. default: 750
  64. },
  65. cubeExhibitWidth: { //魔方展示宽度。
  66. type: [String,Number],
  67. default: 320
  68. },
  69. cubeExhibitHeight: { //魔方展示宽度。
  70. type: [String,Number],
  71. default: 320
  72. },
  73. cubeList: { //魔方数据
  74. type: Array,
  75. default: () => {
  76. return [];
  77. }
  78. },
  79. styleArr: { //风格样式
  80. type: Array,
  81. default: () => {
  82. return [];
  83. }
  84. },
  85. styleBoo: { //用于装修切回去数据丢失问题
  86. type: Boolean,
  87. default: false,
  88. }
  89. },
  90. data() {
  91. return {
  92. selectingItem: {
  93. 'tempStart': null,
  94. 'tempEnd': null,
  95. 'start': null,
  96. 'end': null
  97. },
  98. selectedList: [], //已经生成的单元。
  99. selCubeIndex: -1,
  100. addGridArr: [], // 保存每个选中的格子
  101. cubeImgInfo: [], //图片信息
  102. maxHeight: 0,//魔方以选中的最高高度
  103. }
  104. },
  105. watch: {
  106. cubeList: {
  107. deep:true,
  108. immediate: true,
  109. handler(val) {
  110. this.selectedList = val;
  111. this.addGridArr = [];
  112. let that = this;
  113. this.selectedList.forEach(v => {
  114. let gridData = that.getGridSeat(v.start,v.end);
  115. that.addGridArr.push(...gridData);
  116. })
  117. }
  118. },
  119. styleArr: {
  120. deep:true,
  121. immediate: true,
  122. handler(val,oldV) {
  123. if(!oldV) return;
  124. // if(!val.length) return;
  125. let that = this;
  126. this.addGridArr = [];
  127. let newList = [];
  128. val.forEach(v => {
  129. let gridData = this.getGridSeat(v.start,v.end);
  130. let dataObj = {
  131. 'start': v.start,
  132. 'end': v.end,
  133. 'img': '',
  134. 'url': '',
  135. 'originalData': [],// 图片组件传过来的数据
  136. }
  137. // +判断是否为切回来的
  138. if(this.selectedList.length > 0 && this.styleBoo){
  139. let dataIndex = this.selectedList.findIndex(listVal => listVal.start.x == v.start.x && listVal.start.y == v.start.y && listVal.end.x == v.end.x && listVal.end.y == v.end.y);
  140. if(dataIndex != -1){
  141. dataObj = this.selectedList[dataIndex];
  142. }
  143. // console.info(dataIndex);
  144. }
  145. let style = that.getCubeStyle(v.start,v.end);
  146. that.$set(dataObj,"style",style);
  147. that.addGridArr.push(...gridData);
  148. newList.push(dataObj);
  149. })
  150. this.selCubeIndex = -1;
  151. this.cubeImgInfo = [];
  152. this.selectedList = newList;
  153. this.getCubeMaxHeight();
  154. this.updateData();
  155. }
  156. }
  157. },
  158. computed: {
  159. //密度值。
  160. densityNum: function () {
  161. return parseInt(this.density);
  162. },
  163. //单元魔方高度。
  164. cubeCellHeight: function () {
  165. return this.cubeExhibitHeight / this.density;
  166. },
  167. //单元魔方宽度。
  168. cubeCellWidth: function () {
  169. return this.cubeExhibitWidth / this.density;
  170. },
  171. },
  172. methods: {
  173. //获取开始和结束之间的所有魔方单元。
  174. updateSelecting() {
  175. var tempStart = this.selectingItem.tempStart;
  176. var tempEnd = this.selectingItem.tempEnd;
  177. this.selectingItem.start = {
  178. x: Math.min(tempStart.x, tempEnd.x),
  179. y: Math.min(tempStart.y, tempEnd.y)
  180. };
  181. this.selectingItem.end = {
  182. x: Math.max(tempStart.x, tempEnd.x),
  183. y: Math.max(tempStart.y, tempEnd.y)
  184. };
  185. },
  186. //清除正在选择的。
  187. clearSelecting() {
  188. this.selectingItem.tempStart = null;
  189. this.selectingItem.tempEnd = null;
  190. this.selectingItem.start = null;
  191. this.selectingItem.end = null;
  192. },
  193. // 获取鼠标移动的位置
  194. coordFromCubeEvent(event) {
  195. var el = event.currentTarget;
  196. var x = el.getAttribute('data-x');
  197. var y = el.getAttribute('data-y');
  198. // console.log('track_x_y_[' + x + ', ' + y + ']');
  199. return { x: x, y: y };
  200. },
  201. isContain(x, y, item) {
  202. return (item.start.x <= x && x <= item.end.x && item.start.y <= y && y <= item.end.y);
  203. },
  204. //魔方点击事件。
  205. onClickCubeItem: function (event) {
  206. var domclass = event.currentTarget.getAttribute('class');
  207. // console.info(domclass);
  208. if (-1 !== domclass.indexOf('item-selected')) {
  209. console.log("已经被占用");
  210. return;
  211. }
  212. var coord = this.coordFromCubeEvent(event);
  213. // 开始点击
  214. if (null == this.selectingItem.tempStart) {
  215. this.selectingItem.tempStart = coord;
  216. this.selectingItem.tempEnd = coord;
  217. this.selectingItem.start = coord;
  218. this.selectingItem.end = coord;
  219. return;
  220. }
  221. this.selectingItem.tempEnd = coord;
  222. this.updateSelecting();
  223. // 添加以选中的格子
  224. let gridData = this.getGridSeat(this.selectingItem.start,this.selectingItem.end);
  225. // 用于判断格子是否被选中
  226. let boo = this.verifyGridExist(this.selectingItem.start,this.selectingItem.end);
  227. if(boo){
  228. return;
  229. }
  230. this.addGridArr.push(...gridData);
  231. this.getCubeMaxHeight();
  232. //加入选中的。
  233. var selectedItem = {
  234. 'start': this.selectingItem.start,
  235. 'end': this.selectingItem.end,
  236. 'img': '',
  237. 'url': '',
  238. 'originalData': [],// 图片组件传过来的数据
  239. }
  240. //计算选中层的宽度。
  241. let style = this.getCubeStyle(selectedItem.start,selectedItem.end);
  242. this.$set(selectedItem,"style",style);
  243. this.selectedList.push(selectedItem);
  244. this.selCubeInfo(this.selectedList.length - 1);
  245. // 清楚状态
  246. this.clearSelecting();
  247. this.updateData();
  248. },
  249. // 获取每个各种的位置 type: 1 添加 2:删除
  250. getGridSeat(start,end,type=1){
  251. let gridData = [];
  252. // 添加以选中的格子
  253. // console.info(this.selectingItem);
  254. for(let i = start.x;i<=end.x;i++){
  255. let arr = [];
  256. for(let j = start.y;j<=end.y;j++){
  257. arr.push({
  258. x: i,
  259. y: j
  260. })
  261. }
  262. if(type == 1){
  263. gridData.push(...arr);
  264. } else {
  265. arr.forEach(val => {
  266. let startIndex = this.addGridArr.findIndex(v => (v.x == val.x && v.y == val.y));
  267. if(startIndex != -1){
  268. this.addGridArr.splice(startIndex,1)
  269. }
  270. })
  271. this.getCubeMaxHeight();
  272. }
  273. }
  274. return gridData;
  275. },
  276. // 判断是否在已选中的范围中
  277. verifyGridExist(start,end){
  278. let gridData = this.getGridSeat(start,end);
  279. // 用于判断格子是否被选中
  280. let mergeArr = [...this.addGridArr];
  281. mergeArr.push(...gridData);
  282. let delRepeatArr = [];
  283. let repeatCount = 0;
  284. mergeArr.forEach(function(a) {
  285. let boo = delRepeatArr.every(function(b) {
  286. return a.x != b.x || a.y != b.y;
  287. })
  288. boo ? delRepeatArr.push(a) : repeatCount++;
  289. })
  290. if(repeatCount > 0){
  291. this.$message.error('该位置无法选择');
  292. return true;
  293. }
  294. return false;
  295. },
  296. // 计算魔方已选中的最高高度
  297. getCubeMaxHeight(){
  298. let size = this.cubeWidth/this.density; //每个格子的大小
  299. let maxHeight = 0;
  300. let maxY = 0;
  301. for(let i = 1;i<=this.density;i++){
  302. let yInfo = this.addGridArr.filter(v => v.y == i);
  303. if(yInfo.length){
  304. let y = yInfo[yInfo.length -1].y;
  305. y>maxY?maxY = y:'';
  306. }
  307. }
  308. maxHeight = maxY * size;
  309. this.maxHeight = maxHeight;
  310. this.$emit("max",this.maxHeight);
  311. },
  312. // 计算魔方样式
  313. getCubeStyle(start,end){
  314. //计算选中层的宽度。
  315. let infoWidth = Math.round(this.cubeHeight/this.density*((parseInt(end.x) - parseInt(start.x) + 1)));
  316. let infoHeight = Math.round(this.cubeWidth/this.density*((parseInt(end.y) - parseInt(start.y) + 1)));
  317. let infoTop = Math.round(this.cubeWidth/this.density*((start.y - 1) ));
  318. let infoLeft = Math.round(this.cubeWidth/this.density*((start.x - 1)));
  319. let style = {
  320. top: (infoTop/2) + 'px',
  321. left: (infoLeft/2) + 'px',
  322. width: (infoWidth/2) + 'px',
  323. height: (infoHeight/2) + 'px',
  324. }
  325. return style;
  326. },
  327. // 鼠标移动触发
  328. onEnterCubeItem: function (event) {
  329. // console.log("track_onEnterCube");
  330. if (this.selectingItem.tempStart) {
  331. var coord = this.coordFromCubeEvent(event);
  332. this.selectingItem.tempEnd = coord;
  333. // console.info(this.selectingItem.tempEnd);
  334. this.updateSelecting();
  335. }
  336. },
  337. //判断是否正在选择
  338. isSelecting: function (x, y) {
  339. var item = this.selectingItem;
  340. if (item.tempStart) {
  341. return this.isContain(x, y, item);
  342. }
  343. return false;
  344. },
  345. //判断是否已经选择。
  346. isSelected: function (x, y) {
  347. var list = this.selectedList;
  348. for (var i = 0; i < list.length; i++) {
  349. if (this.isContain(x, y, list[i])) {
  350. return true;
  351. }
  352. }
  353. return false;
  354. },
  355. // 选中
  356. selCubeInfo(index){
  357. this.selCubeIndex = index;
  358. if(this.selectedList[this.selCubeIndex]){
  359. this.cubeImgInfo = this.selectedList[this.selCubeIndex].originalData;
  360. }
  361. },
  362. // 删除
  363. delCubeList(index){
  364. this.getGridSeat(this.selectedList[index].start,this.selectedList[index].end,2);
  365. this.selectedList.splice(index, 1);
  366. if(index == this.selCubeIndex){
  367. this.selCubeInfo(this.selCubeIndex-1);
  368. }
  369. this.updateData();
  370. },
  371. // 通信
  372. updateData(){
  373. this.$emit("chang",this.selectedList)
  374. },
  375. // 添加图片
  376. addCubeImg(imgArr){
  377. // console.info(this.selCubeIndex,"0",this.selectedList);
  378. if(this.selCubeIndex != -1){
  379. this.selectedList[this.selCubeIndex].img = imgArr[0].imgUrl;
  380. this.selectedList[this.selCubeIndex].url = imgArr[0].url;
  381. this.selectedList[this.selCubeIndex].originalData = imgArr;
  382. }
  383. this.updateData();
  384. },
  385. //计算选中层的宽度。
  386. getSelectedWidth: function (item) {
  387. return (parseInt(item.end.x) - parseInt(item.start.x) + 1) * this.cubeCellWidth;
  388. },
  389. //计算选中层的高度。
  390. getSelectedHeight: function (item) {
  391. return (parseInt(item.end.y) - parseInt(item.start.y) + 1) * this.cubeCellHeight;
  392. },
  393. //计算选中层的右边距离。
  394. getSelectedTop: function (item) {
  395. return (item.start.y - 1) * this.cubeCellHeight;
  396. },
  397. //计算选中层的左边距离。
  398. getSelectedLeft: function (item) {
  399. return (item.start.x - 1) * this.cubeCellWidth;
  400. }
  401. }
  402. })
  403. </script>
  404. <style>
  405. .diy-rubik-cube{
  406. display: inline-block;
  407. width: 100%;
  408. }
  409. .decorate-cube {
  410. position: relative;
  411. }
  412. .decorate-cube .cube-item {
  413. border-right: 1px solid #B7B7B7;
  414. }
  415. .decorate-cube .cube-selected {
  416. position: absolute;
  417. background-color: #E8F7FD;
  418. border: 1px solid #BBDDFF;
  419. text-align: center;
  420. color: #88C4DC;
  421. cursor: pointer;
  422. box-sizing: border-box;
  423. }
  424. .decorate-cube .click-cube-selected{
  425. border: 1px solid #3388FF;
  426. }
  427. /* .decorate-cube .cube-selected.cube-selected-click {
  428. background: #e0edff;
  429. border: 1px solid #155bd4;
  430. color: #155bd4;
  431. z-index: 2;
  432. cursor: auto;
  433. } */
  434. .decorate-cube .cube-selected-text {
  435. font-size: 12px;
  436. width: 100%;
  437. position: absolute;
  438. top: 50%;
  439. left: 50%;
  440. transform: translateX(-50%) translateY(-50%);
  441. }
  442. .decorate-cube .cube-col {
  443. float: left;
  444. list-style: none;
  445. padding: 0;
  446. margin: 0;
  447. }
  448. .decorate-cube .cube-item:first-child {
  449. border-top: 1px solid #B7B7B7;
  450. }
  451. .decorate-cube .cube-item {
  452. background: #FFF;
  453. border-left: 1px solid #B7B7B7;
  454. border-bottom: 1px solid #B7B7B7;
  455. cursor: pointer;
  456. text-align: center;
  457. box-sizing: border-box;
  458. }
  459. .decorate-cube .cube-item.item-selecting {
  460. background: #e0edff;
  461. }
  462. .decorate-cube .cube-item.item-selected {
  463. background: #e0edff;
  464. visibility: hidden;
  465. }
  466. .cube-del-icon{
  467. position: absolute;
  468. right: -8px;
  469. top: -8px;
  470. z-index: 10;
  471. }
  472. .del-sel-icon{
  473. font-size: 19px;
  474. color: #666;
  475. background: #FFF;
  476. border-radius: 50%;
  477. }
  478. </style>