diy-img-setting-drag.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. <?php
  2. use common\helpers\ComponentHelper;
  3. ComponentHelper::loadComponentView('com-attachment');
  4. ComponentHelper::loadComponentView('com-pick-link');
  5. ?>
  6. <template id="diy-img-setting-drag">
  7. <diy-style-contain class="diy-img-setting" :title="topName" :show-title="showTitle" :contain-style="imgContainStyle" :style="{'border-top': showTitle? '':'none'}">
  8. <div class="suggest" v-if="suggestSite == 1">{{suggestSizeText}}</div>
  9. <div class="diy-img-models">
  10. <draggable v-if="newImgInfos.length" flex="dif:left" @update="handleDragUpdate">
  11. <div class="diy-img-model" :style="{'margin-bottom': '10px','padding': showHotZone?'0':''}" v-for="(item,index) in newImgInfos" :key="index" >
  12. <div flex="cross:center" style="padding-bottom:10px;">
  13. <com-attachment :style="{'width': showHotZone?'100%':''}" display="unset" :multiple="false" :max="1" :type="'images'" @selected="coverPic($event,index)">
  14. <div class="card-upload img-upload-bor" :style="{width: showHotZone?'':imgPopSize[0] + 'px',height: showHotZone?'':imgPopSize[1] + 'px'}" v-if="!item.imgUrl.length">
  15. <i class="el-icon-plus icon-plugs-center"></i>
  16. </div>
  17. <div class="diy-sty-img" flex="main:center cross:center" :class="{'diy-img-hot-zone':showHotZone}" v-else :style="{width: showHotZone?'':imgPopSize[0] + 'px',height: showHotZone?'':imgPopSize[1] + 'px'}">
  18. <el-image class="diy-sty-img-u" :src="item.imgUrl"></el-image>
  19. <div class="diy-upd-img" :class="{'diy-upd-hot': showHotZone}" >替换</div>
  20. <i class="el-icon-close diy-upd-icon" @click.stop="delImg(index)" ></i>
  21. <div v-if="showHotZone" class="img-hot-model" @click.stop="updatedHotZone(index)">
  22. <div v-for="(hot,hotIndex) in item.hotZone" :key="hotIndex" flex="main:center cross:center" :style="{'height': (hot.heightPer * 100) + '%','width': (hot.widthPer * 100) + '%','left': (hot.leftPer * 100) + '%','top': (hot.topPer * 100) + '%',}" class="img-hot-zone-model">
  23. {{hot.urlName || '暂无链接'}}
  24. </div>
  25. </div>
  26. </div>
  27. </com-attachment>
  28. <div :style="{width:'83%',...getVideoUrlStyle(item.selectType)}">
  29. <diy-url :text="item.urlName" @add="addUrl(index)" style="margin-left: 10px;" v-if="showUrl"></diy-url>
  30. </div>
  31. <div class="diy-img-del-model" v-show="newImgInfos.length > 1">
  32. <el-popover placement="bottom" width="120" v-model="item.visible">
  33. <p class="diy-del-text">确定删除吗?</p>
  34. <div flex="cross:center">
  35. <el-button class="diy-del-btn" size="mini" plain @click.stop="delModel(index)">删除</el-button>
  36. <el-button size="mini" plain @click.stop="item.visible = false">取消</el-button>
  37. </div>
  38. <i class="el-icon-close" slot="reference" style="color:#999;"></i>
  39. </el-popover>
  40. </div>
  41. </div>
  42. <div v-if="item.selectType == 2" style="display:flex;justify-content:space-between;align-items:center;border-top:3px solid #FFF;padding-top:10px;">
  43. <div style="width:160px">
  44. 上传视频
  45. </div>
  46. <div style="width:60px;height:60px;display:flex;align-items:center" class="diy-sty-img">
  47. <com-attachment :style="{'width': showHotZone?'100%':''}" display="unset" :multiple="false" :max="1" :type="'videos'" @selected="videoUrlPic($event,index)">
  48. <div class="card-upload img-upload-bor" :style="{width: showHotZone?'':imgPopSize[0] + 'px',height: showHotZone?'':imgPopSize[1] + 'px'}" v-if="!item.videoUrl">
  49. <i class="el-icon-plus icon-plugs-center"></i>
  50. </div>
  51. <div class="diy-sty-img" flex="main:center cross:center" :class="{'diy-img-hot-zone':showHotZone}" v-else :style="{width: showHotZone?'':imgPopSize[0] + 'px',height: showHotZone?'':imgPopSize[1] + 'px'}">
  52. <div style="width: 60px;height: 30px;position: relative">
  53. <video style="width: 60px;height: 30px;" error="预览图加载失败" :id="'app_attachment_'+ _uid + '_' + index" :src="item.videoUrl">
  54. </video>
  55. <div class="com-attachment-video-info" style="margin-left: 3px;">
  56. <i class="el-icon-video-play"></i>
  57. <span>{{item.duration?item.duration:'--:--'}}</span>
  58. </div>
  59. </div>
  60. <div class="diy-upd-img" :class="{'diy-upd-hot': showHotZone}" >替换</div>
  61. <i class="el-icon-close diy-upd-icon" @click.stop="delVideoUrl(index)" ></i>
  62. <div v-if="showHotZone" class="img-hot-model" @click.stop="updatedHotZone(index)">
  63. <div v-for="(hot,hotIndex) in item.hotZone" :key="hotIndex" flex="main:center cross:center" :style="{'height': (hot.heightPer * 100) + '%','width': (hot.widthPer * 100) + '%','left': (hot.leftPer * 100) + '%','top': (hot.topPer * 100) + '%',}" class="img-hot-zone-model">
  64. {{hot.urlName || '暂无链接'}}
  65. </div>
  66. </div>
  67. </div>
  68. </com-attachment>
  69. </div>
  70. </div>
  71. <div class="img_button_text" v-if="suggestSite == 2">{{suggestSizeText}}</div>
  72. </div>
  73. </draggable>
  74. <el-popover trigger="click" placement="top" width="200" v-model="popoverVisible">
  75. <div style="display:flex;justify-content:center">
  76. <el-button size="mini" type="success" @click="changePopoverVisible(1)">图片</el-button>
  77. <el-button size="mini" type="primary" @click="changePopoverVisible(2)">视频</el-button>
  78. </div>
  79. <div slot="reference" class="diy-add-img" v-if="openAddImg">
  80. +添加 {{maxImgLength >= 999?'':newImgInfos.length + '/' + maxImgLength}}
  81. </div>
  82. </el-popover>
  83. </div>
  84. <el-dialog width="50%" title="热区编辑器" append-to-body :visible.sync="hotZonePop">
  85. <div class="diy-hotzone-pop">
  86. <HotZone :image="selHotZoneImg.imgUrl" :zones-init="selHotZoneImg.hotZone" @add-url="addHotZoneUrl" @save="getSave"></HotZone>
  87. </div>
  88. </el-dialog>
  89. <com-pick-link @close="updatePop" :pop-boo="urlPop" @selected="selectedUrl" :default-tabs="defaultTabs"></com-pick-link>
  90. </diy-style-contain>
  91. </template>
  92. <script>
  93. /**
  94. * 颜色组件
  95. *
  96. */
  97. Vue.component('diy-img-setting-drag', {
  98. template: '#diy-img-setting-drag',
  99. name: 'diy-img-setting-drag',
  100. props: {
  101. showTitle: { //是否开启标题
  102. type: Boolean,
  103. default: true
  104. },
  105. imgContainStyle: { //最外层样式
  106. type: Object,
  107. default: () => {}
  108. },
  109. showHotZone: { //是否有热区:开启需要关闭showUrl
  110. type: Boolean,
  111. default: false
  112. },
  113. showUrl: { //是否开启链接选择
  114. type: Boolean,
  115. default: true
  116. },
  117. suggestSite: { //尺寸建议位置 1:在顶部 2:在图片下方
  118. type: [Number, String],
  119. default: 1
  120. },
  121. suggestSizeText: { //建议尺寸文本
  122. type: String,
  123. default: "建议图片宽为750,高度420"
  124. },
  125. openAddImg: { //是否开启添加按钮
  126. type: Boolean,
  127. default: false
  128. },
  129. topName: { //头部标题
  130. type: String,
  131. default: "图片设置",
  132. },
  133. imgPopSize: { //上传图片框的宽高展示 [宽度,高度]
  134. type: Array,
  135. default: () => {
  136. return ['60', '60']
  137. }
  138. },
  139. defImgUrl: { //全局默认图片
  140. type: String,
  141. default: "",
  142. },
  143. maxImgLength: { //最多添加图片数量
  144. type: [String, Number],
  145. default: 5
  146. },
  147. /**
  148. 是否开启默认创建
  149. */
  150. defAddImgBoo: {
  151. type: Boolean,
  152. default: false,
  153. },
  154. /**
  155. 默认创建多少个
  156. 注:defAddImgBoo需要开启
  157. */
  158. defImgCount: {
  159. type: [String, Number],
  160. default: 1
  161. },
  162. /**
  163. 数据格式
  164. {
  165. url: "", //链接路径
  166. urlName: '', //链接名
  167. urlType: '', //链接类型
  168. imgUrl: "" //图片路径
  169. visible: false, //删除弹窗
  170. hotZone: [{
  171. leftPer: 0,
  172. topPer: 0,
  173. widthPer: 0.30,
  174. heightPer: 0.30,
  175. url: '', //链接路径
  176. urlName: '', //链接名
  177. urlType: '', //链接类型
  178. }], //热区数据
  179. }
  180. 注:可传一个空数组
  181. */
  182. imgInfos: { //图片数据
  183. type: Array,
  184. default: () => {
  185. return []
  186. }
  187. },
  188. defaultTabs: { // 选择需要的tabs栏
  189. type: Array,
  190. default () {
  191. return ["mall", "goods", "addons", "other"]
  192. },
  193. },
  194. },
  195. data() {
  196. return {
  197. newImgInfos: [],
  198. selModelIndex: -1, //鼠标移入的模块下标
  199. selImgIndex: -1, //鼠标移入的图片下标
  200. visible: false,
  201. hotZonePop: false,
  202. selHotZoneIndex: -1, //选中图片下标
  203. selHotZoneImg: [], //选中图片数据热区
  204. urlPop: false,
  205. urlIndex: -1,
  206. hotZoneIndex: -1, //点击热区的下标
  207. selectType: 1, //1图片,2视频
  208. selVideoImgIndex: -1, //鼠标移入的视频图片下标
  209. popoverVisible: false,
  210. is_drag_end: false,
  211. dragImgInfos: [],
  212. }
  213. },
  214. created() {
  215. },
  216. watch: {
  217. imgInfos: {
  218. deep: true,
  219. immediate: true,
  220. handler(val) {
  221. // let that = this;
  222. // this.newImgInfos = [];
  223. if (val.length == 0) {
  224. this.addModel();
  225. } else {
  226. this.newImgInfos = val
  227. }
  228. }
  229. },
  230. },
  231. methods: {
  232. changePopoverVisible(selectType) {
  233. this.selectType = selectType
  234. this.popoverVisible = false
  235. this.addModel()
  236. },
  237. openSelectType() {
  238. this.popoverVisible = true
  239. },
  240. // 添加
  241. addModel() {
  242. if (this.newImgInfos.length >= this.maxImgLength) {
  243. this.$notify({
  244. title: '',
  245. message: '最多添加' + this.maxImgLength + '个',
  246. type: 'warning'
  247. });
  248. return;
  249. }
  250. let obj = {
  251. url: "", //链接路径
  252. imgUrl: this.defImgUrl ? this.defImgUrl : "", //图片路径
  253. visible: false, //删除弹窗
  254. urlName: this.selectType == 1 ? '' : '视频无需配置链接', //链接名
  255. urlType: '', //链接类型
  256. hotZone: [{
  257. leftPer: 0,
  258. topPer: 0,
  259. widthPer: 0.30,
  260. heightPer: 0.30,
  261. url: '',
  262. urlName: '',
  263. urlType: '', //链接类型
  264. }], //热区数据
  265. selectType: this.selectType,
  266. videoUrl: this.defImgUrl ? this.defImgUrl : '', //视频地址
  267. }
  268. if (this.newImgInfos.length == 0) {
  269. this.newImgInfos = [];
  270. for (let i = 0; i < Number(this.defImgCount); i++) {
  271. this.newImgInfos.push(JSON.parse(JSON.stringify(obj)));
  272. }
  273. // 关闭默认创建
  274. // this.$emit("closeAddImg",false);
  275. } else {
  276. this.newImgInfos.push(obj);
  277. }
  278. this.changImg();
  279. },
  280. // 删除
  281. delModel(index) {
  282. if (this.newImgInfos.length == 1) {
  283. this.newImgInfos[index].visible = false;
  284. this.$notify({
  285. title: '',
  286. message: '最少保留一个',
  287. type: 'warning'
  288. });
  289. return;
  290. }
  291. this.newImgInfos.splice(index, 1);
  292. this.changImg();
  293. },
  294. // 添加图片
  295. coverPic(e, i) {
  296. console.info(e, i);
  297. this.newImgInfos[i].imgUrl = e[0].url;
  298. console.info(this.newImgInfos);
  299. this.changImg();
  300. },
  301. videoUrlPic(e, i) {
  302. this.newImgInfos[i].videoUrl = e[0].url;
  303. console.info(this.newImgInfos);
  304. this.changImg();
  305. },
  306. // 删除图片
  307. delImg(index) {
  308. this.newImgInfos[index].imgUrl = "";
  309. this.changImg();
  310. },
  311. delVideoUrl(index) {
  312. this.newImgInfos[index].videoUrl = "";
  313. this.changImg();
  314. },
  315. // 鼠标移入图片
  316. mouseOverImg(event, index) {
  317. this.selImgIndex = index;
  318. },
  319. // 鼠标移出图片
  320. mouseLeaveImg() {
  321. this.selImgIndex = -1;
  322. },
  323. // 鼠标移入图片
  324. mouseOverVideoImg(event, index) {
  325. this.selVideoImgIndex = index;
  326. },
  327. // 鼠标移出图片
  328. mouseLeaveVideoImg() {
  329. this.selVideoImgIndex = -1;
  330. },
  331. // 鼠标移入模块
  332. overModel(index) {
  333. this.selModelIndex = index;
  334. },
  335. // 鼠标移出模块
  336. leaveModel() {
  337. this.selModelIndex = -1;
  338. },
  339. // 编辑热区
  340. updatedHotZone(index) {
  341. this.hotZonePop = true;
  342. // this.selHotZoneImg = [];
  343. this.selHotZoneImg = JSON.parse(JSON.stringify(this.newImgInfos[index]));
  344. this.urlIndex = index;
  345. },
  346. // 获取热区数据
  347. getSave(val) {
  348. this.newImgInfos[this.urlIndex].hotZone = val;
  349. this.hotZonePop = false;
  350. this.changImg();
  351. },
  352. // 图片添加链接
  353. addUrl(index) {
  354. this.urlIndex = index;
  355. this.urlPop = true;
  356. },
  357. updatePop(boo) {
  358. this.urlPop = boo;
  359. },
  360. selectedUrl(val) {
  361. if (!val.length) {
  362. return false
  363. }
  364. let urlInfo = val[0];
  365. if (this.hotZoneIndex == -1) {
  366. this.newImgInfos[this.urlIndex].urlName = getOtherUrlName(urlInfo.open_type) || urlInfo.title;
  367. this.newImgInfos[this.urlIndex].url = urlInfo.params;
  368. this.newImgInfos[this.urlIndex].urlType = urlInfo.open_type;
  369. this.urlIndex = -1;
  370. } else {
  371. this.selHotZoneImg.hotZone[this.hotZoneIndex].urlName = getOtherUrlName(urlInfo.open_type) || urlInfo.title;
  372. this.selHotZoneImg.hotZone[this.hotZoneIndex].url = urlInfo.params;
  373. this.selHotZoneImg.hotZone[this.hotZoneIndex].urlType = urlInfo.open_type;
  374. // this.newImgInfos[this.urlIndex].hotZone[this.hotZoneIndex].urlName = urlInfo.title;
  375. // this.newImgInfos[this.urlIndex].hotZone[this.hotZoneIndex].url = urlInfo.route;
  376. // this.newImgInfos[this.urlIndex].hotZone[this.hotZoneIndex].urlType = urlInfo.open_type;
  377. }
  378. this.urlPop = false;
  379. this.changImg();
  380. },
  381. //热区添加链接
  382. addHotZoneUrl(index) {
  383. console.log(index);
  384. this.hotZoneIndex = index;
  385. this.urlPop = true;
  386. },
  387. // 通信
  388. changImg(type = null) {
  389. this.$emit("change", this.newImgInfos, type);
  390. },
  391. handleDragUpdate(e) {
  392. let newImgInfos = JSON.parse(JSON.stringify(this.newImgInfos))
  393. let currentItem = newImgInfos.splice(e.oldIndex, 1)
  394. newImgInfos.splice(e.newIndex, 0, currentItem[0])
  395. this.newImgInfos = []
  396. this.$nextTick(() => {
  397. for (let newImgInfosKey in newImgInfos) {
  398. this.$set(this.newImgInfos, newImgInfosKey, newImgInfos[newImgInfosKey])
  399. }
  400. this.changImg(this.newImgInfos)
  401. })
  402. },
  403. getVideoUrlStyle(selectType) {
  404. let style = {}
  405. if (selectType == 2) {
  406. style['cursor'] = ' not-allowed'
  407. style['pointer-events'] = 'none'
  408. }
  409. return style
  410. },
  411. }
  412. });
  413. </script>
  414. <style scoped>
  415. .suggest {
  416. margin: 20px 0 12px;
  417. background: #EAF3FE;
  418. border: 1px solid #2D8CF0;
  419. opacity: 1;
  420. border-radius: 3px;
  421. padding: 14px 17px 17px;
  422. font-size: 12px;
  423. font-weight: bold;
  424. color: #999999;
  425. }
  426. .diy-img-models {
  427. margin-bottom: 6px;
  428. }
  429. .diy-img-model {
  430. padding: 12px 9px 7px;
  431. background: #F6F5F8;
  432. position: relative;
  433. border-radius: 4px;
  434. }
  435. .img-upload-bor {
  436. position: relative;
  437. border: 1px dashed #D5D5D5;
  438. }
  439. .icon-plugs-center {
  440. position: absolute;
  441. top: 50%;
  442. left: 50%;
  443. transform: translate(-50%, -50%);
  444. }
  445. .diy-url {
  446. height: 39px;
  447. background: #EDF2FC;
  448. border-radius: 2px;
  449. font-size: 11px;
  450. font-weight: bold;
  451. line-height: 0px;
  452. color: #999999;
  453. flex: 1;
  454. padding: 9px 15px 14px;
  455. margin-left: 14px;
  456. }
  457. .diy-url-icon {
  458. font-size: 13px;
  459. margin-right: 10px;
  460. }
  461. .diy-add-img {
  462. font-size: 14px;
  463. font-weight: bold;
  464. color: #999999;
  465. border: 1px dashed #707070;
  466. border-radius: 5px;
  467. padding: 9px 0;
  468. text-align: center;
  469. margin-top: 24px;
  470. }
  471. .diy-sty-img {
  472. position: relative;
  473. background: #FFF;
  474. }
  475. .diy-upd-img {
  476. position: absolute;
  477. bottom: 0;
  478. left: 0;
  479. color: #999999;
  480. background: rgb(0 0 0 / 50%);
  481. width: 100%;
  482. text-align: center;
  483. min-height: 6%;
  484. z-index: 101;
  485. }
  486. .diy-upd-icon {
  487. position: absolute;
  488. top: -6px;
  489. right: -6px;
  490. background: rgb(0 0 0 / 50%);
  491. border-radius: 50%;
  492. color: #FFF;
  493. padding: 2px;
  494. font-size: 12px;
  495. }
  496. .diy-upd-img:hover,
  497. .diy-upd-icon:hover,
  498. .diy-url:hover,
  499. .diy-add-img:hover {
  500. cursor: pointer;
  501. }
  502. .diy-img-del-model {
  503. position: absolute;
  504. top: 10px;
  505. right: 9px;
  506. z-index: 101;
  507. }
  508. .diy-del-text {
  509. text-align: center;
  510. margin-bottom: 10px;
  511. }
  512. .diy-del-btn {
  513. border: 1px solid #bb3f3f;
  514. color: #bb3f3f;
  515. }
  516. .img_button_text {
  517. font-size: 12px;
  518. font-weight: bold;
  519. color: #999999;
  520. margin-top: 2px;
  521. }
  522. .diy-sty-img-u {
  523. max-height: 100%;
  524. width: 100%;
  525. }
  526. .diy-img-hot-zone {
  527. width: 100%;
  528. min-height: 200px;
  529. max-height: 950px;
  530. }
  531. .diy-upd-hot {
  532. height: 30px;
  533. line-height: 30px;
  534. }
  535. .img-hot-model {
  536. position: absolute;
  537. height: 100%;
  538. width: 100%;
  539. top: 0;
  540. left: 0;
  541. z-index: 100;
  542. }
  543. .img-hot-model:hover,
  544. .diy-img-del-model:hover {
  545. cursor: pointer;
  546. }
  547. .img-hot-zone-model {
  548. border: 1px solid red;
  549. position: absolute;
  550. background: rgba(233, 243, 253, 0.8);
  551. color: #3892F1;
  552. border: 1px solid #3892F1;
  553. }
  554. .diy-img-setting .el-dialog__header {
  555. border-bottom: 1px solid #f2f2f2;
  556. padding: 12px 22px;
  557. }
  558. .diy-img-setting .el-dialog__body {
  559. padding: 0;
  560. }
  561. .diy-hotzone-pop {
  562. padding: 10px 20px;
  563. }
  564. </style>