• VUE3 中实现拖拽和缩放自定义看板 vue-grid-layout


    Vue Grid Layout官方文档

    Vue Grid Layout中文文档

    1. npm下载拖拽缩放库

    npm install vue-grid-layout@3.0.0-beta1 --save

    2. vue3 使用 vue-grid-layout报错:external_commonjs_vue_commonjs2_vue_root_Vue_default.a is not a constructor

     解决方案: vue3版本记得下载对应 vue-grid-layout@3.0.0-beta1版本的库,因为vue-grid-layout是vue2版本的,但用的是vue3版本,所以要安装vue3的依赖和相关配置

    3.  在main.js中注册

    1. // 将自动注册所有组件为全局组件
    2. import keycloakInit from '@/utils/util.keycloak'
    3. import VueGridLayout from 'vue-grid-layout'
    4. const app = createApp(App)
    5. app.use(store)
    6. app.use(router)
    7. app.use(ElementPlus)
    8. app.use(VueGridLayout)
    9. app.mount('#app')

    4. 页面中使用组件 -- 控制保存和编辑

    页面使用效果图:

    点击布局进行自定义拖拽功能 ----- 效果图 ----- 箭头处可进行拖拽大小及位置:

     页面代码如下:

    属性 GridLayout参数 和 GridItem参数 官网有详细介绍

    1. <template>
    2. <div class="nav-wrapper-b">
    3. <div class="bar-title-b">
    4. {{getChangeLine + ' ' + barTitle}}
    5. div>
    6. <div class="time-b">
    7. <span style="margin-left: 20px">{{ date }} {{ time }}span>
    8. <div style="display: inline-block;position: absolute;right: 12%;">
    9. <el-button v-if="isEditDraggable"
    10. type="success"
    11. size="small"
    12. @click="saveDragDataHome">保存
    13. el-button>
    14. <el-button v-else
    15. type="primary"
    16. size="small"
    17. @click="editDragDataHome">布局
    18. el-button>
    19. div>
    20. div>
    21. div>
    22. <div class="home-container-b">
    23. <div class="drag-body" :class="isEditDraggable ? 'drag-body-edit' : ''">
    24. <grid-layout :layout.sync="layoutDraggableList"
    25. :col-num="100"
    26. :row-height="5"
    27. :is-draggable="draggableLayout"
    28. :is-resizable="resizableLayout"
    29. :vertical-compact="true"
    30. :use-css-transforms="true">
    31. <grid-item v-for="item in layoutDraggableList"
    32. :static="false"
    33. :x="item.x"
    34. :y="item.y"
    35. :w="item.w"
    36. :h="item.h"
    37. :i="item.i"
    38. style="overflow: auto">
    39. <div class="layout-component top-left-first-components"
    40. v-if="item.i == 'topLeftFirst'">
    41. <box-container-is>
    42. 00001
    43. box-container-is>
    44. div>
    45. <div class="layout-component"
    46. v-if="item.i == 'topLeftSecond'">
    47. <box-container :boxTitle="'测试1'">
    48. 00002
    49. box-container>
    50. div>
    51. <div class="layout-component"
    52. v-if="item.i == 'topLeftThird'">
    53. <box-container :boxTitle="'测试2'">
    54. 00003
    55. box-container>
    56. div>
    57. <div class="layout-component"
    58. v-if="item.i == 'topRightFirst'">
    59. <box-container-is>
    60. 00004
    61. box-container-is>
    62. div>
    63. <div class="layout-component"
    64. v-if="item.i == 'topRightSecond'">
    65. <box-container>
    66. <topRightSecondBHome>topRightSecondBHome>
    67. box-container>
    68. div>
    69. <div class="layout-component"
    70. v-if="item.i == 'topRightThird'">
    71. <box-container-is>
    72. <topRightThirdBHome>topRightThirdBHome>
    73. box-container-is>
    74. div>
    75. grid-item>
    76. grid-layout>
    77. div>
    78. div>
    79. template>
    80. <script setup>
    81. import emitter from '@/utils/eventbus'
    82. import {getDate, getTime, getTimeHours} from "@/utils/date";
    83. import {useRoute, useRouter} from "vue-router";
    84. import boxContainer from "@/components/boxContainer/index";
    85. import boxContainerIs from "@/components/boxContainer/index1";
    86. import {
    87. workOrderLine,
    88. topRightSecondBHome,
    89. topRightThirdBHome,
    90. } from "./components";
    91. import {computed, ref} from "vue";
    92. import {getCurrentInstance, nextTick} from "@vue/runtime-core";
    93. import {onBeforeUnmount, onMounted, watch} from "vue";
    94. import {saveTemplateApi} from '@/api/workOrderLineApi'
    95. import {ElMessage} from "element-plus";
    96. const {proxy} = getCurrentInstance()
    97. //年月日
    98. const date = ref(getDate());
    99. //时分秒
    100. const time = ref(getTime());
    101. const getChangeLine = ref('')
    102. const hours = ref(getTimeHours())
    103. const barTitle = ref("")
    104. const router = useRouter();
    105. /*____________________________主页拖拽布局开始_______________________________*/
    106. let isEditDraggable = ref(false)
    107. const draggableLayout = ref(false)
    108. const resizableLayout = ref(false)
    109. const layoutDraggableList = ref([])
    110. //点击编辑布局
    111. function editDragDataHome() {
    112. isEditDraggable.value = true
    113. }
    114. //保存布局
    115. function saveDragDataHome() {
    116. isEditDraggable.value = false
    117. console.log(layoutDraggableList.value)
    118. saveTemplateApi(layoutDraggableList.value).then(response => {
    119. if (response.code == 200) {
    120. ElMessage({
    121. message: '模板布局已保存成功',
    122. type: 'success',
    123. duration: 6 * 1000
    124. })
    125. }
    126. })
    127. }
    128. /*_____________________________主页拖拽布局结束______________________________*/
    129. //模拟后端请求到的数据
    130. let demoData = ref({
    131. "id": 162,
    132. "subjectId": 161,
    133. "name": "主页",
    134. "title": "生产分析",
    135. "description": "第一个看板菜单信息",
    136. "templateList": [
    137. {
    138. "id": 163,
    139. "titleName": "人员信息",
    140. "disabled": true,
    141. "i": "topLeftFirst",
    142. "x": 0,
    143. "y": 0,
    144. "w": 41,
    145. "h": 10,
    146. "menuId": 162
    147. },
    148. {
    149. "id": 164,
    150. "titleName": "前五",
    151. "disabled": true,
    152. "i": "topLeftSecond",
    153. "x": 0,
    154. "y": 10,
    155. "w": 41,
    156. "h": 21,
    157. "menuId": 162
    158. },
    159. {
    160. "id": 165,
    161. "titleName": "吸嘴-抛料率前五",
    162. "disabled": true,
    163. "i": "topLeftThird",
    164. "x": 0,
    165. "y": 31,
    166. "w": 41,
    167. "h": 21,
    168. "menuId": 162
    169. },
    170. {
    171. "id": 166,
    172. "titleName": "",
    173. "disabled": true,
    174. "i": "topRightFirst",
    175. "x": 41,
    176. "y": 0,
    177. "w": 59,
    178. "h": 10,
    179. "menuId": 162
    180. },
    181. {
    182. "id": 167,
    183. "titleName": "",
    184. "disabled": true,
    185. "i": "topRightSecond",
    186. "x": 41,
    187. "y": 10,
    188. "w": 59,
    189. "h": 23,
    190. "menuId": 162
    191. },
    192. {
    193. "id": 168,
    194. "titleName": "",
    195. "disabled": true,
    196. "i": "topRightThird",
    197. "x": 41,
    198. "y": 33,
    199. "w": 59,
    200. "h": 19,
    201. "menuId": 162
    202. }
    203. ]
    204. })
    205. initialHeightFun(demoData.value)
    206. //根据高度进行调整尺寸
    207. function initialHeightFun(data) {
    208. nextTick(() => {
    209. layoutDraggableList.value = data.templateList
    210. barTitle.value = data.title
    211. })
    212. }
    213. onBeforeUnmount(() => {})
    214. //监听拖拽功能
    215. watch(isEditDraggable, (res) => {
    216. draggableLayout.value = !draggableLayout.value;
    217. resizableLayout.value = !resizableLayout.value;
    218. })
    219. script>
    220. <style lang="scss" scoped>
    221. /*----------------拖拽样式开始----------------*/
    222. .drag-body {
    223. width: 100%;
    224. height: 100%;
    225. }
    226. .layout-component {
    227. width: 100%;
    228. height: 100%;
    229. display: flex;
    230. flex-wrap: wrap;
    231. align-content: space-between;
    232. }
    233. .layout-component-low-warning-second {
    234. width: 95%;
    235. height: 100%;
    236. margin-right: 1%;
    237. float: left;
    238. }
    239. .layout-component-low-warning-text {
    240. width: 4%;
    241. height: 100%;
    242. float: right;
    243. }
    244. .layout-component-low-throwing-second {
    245. width: 100%;
    246. height: 100%;
    247. }
    248. .drag-body-edit {
    249. .vue-grid-item:not(.vue-grid-placeholder) {
    250. outline: 2px solid rgba(255, 96, 28, 0.71);
    251. }
    252. }
    253. .vue-grid-item {
    254. box-sizing: border-box !important;
    255. }
    256. .vue-grid-layout {
    257. background: url("~@/assets/image/bg1.png");
    258. -moz-background-size: 100% 100%;
    259. background-size: 100% 100%;
    260. }
    261. ::v-deep .vue-resizable-handle {
    262. background: url("~@/assets/image/ic_show_more.png") no-repeat 100% 100%;
    263. padding: 0 3px 3px 0;
    264. background-origin: content-box;
    265. -webkit-box-sizing: border-box;
    266. position: absolute;
    267. width: 45px;
    268. height: 45px;
    269. bottom: 0;
    270. right: 0;
    271. }
    272. .vue-grid-item:not(.vue-grid-placeholder) {
    273. //border: 1px solid #409eff;
    274. color: #ffffff;
    275. }
    276. .vue-grid-item .resizing {
    277. opacity: 0.9;
    278. }
    279. .vue-grid-item .static {
    280. background: transparent;
    281. }
    282. .vue-grid-item .text {
    283. font-size: 24px;
    284. text-align: center;
    285. position: absolute;
    286. top: 0;
    287. bottom: 0;
    288. left: 0;
    289. right: 0;
    290. margin: auto;
    291. height: 100%;
    292. width: 100%;
    293. }
    294. .vue-grid-item .no-drag {
    295. height: 100%;
    296. width: 100%;
    297. }
    298. .vue-grid-item .minMax {
    299. font-size: 12px;
    300. }
    301. .vue-grid-item .add {
    302. cursor: pointer;
    303. }
    304. /*----------------拖拽样式结束----------------*/
    305. .nav-wrapper-b {
    306. height: 60px;
    307. line-height: 60px;
    308. width: 100%;
    309. background: url("~@/assets/image/top.png") no-repeat;
    310. background-size: 100% 100%;
    311. text-align: center;
    312. position: relative;
    313. color: #d5dfe8;
    314. font-family: "黑体";
    315. .bar-title-b {
    316. font-size: 32px;
    317. color: #ffffff;
    318. font-weight: bolder;
    319. }
    320. .time-b {
    321. position: absolute;
    322. right: 1%;
    323. top: 50%;
    324. transform: translateY(-35%);
    325. font-family: "Time Number";
    326. font-weight: bold;
    327. font-size: 29px;
    328. width: 35%;
    329. }
    330. .mapChoose-b {
    331. position: absolute;
    332. left: 22px;
    333. bottom: 15px;
    334. color: #eee;
    335. }
    336. }
    337. .home-container-b {
    338. width: 100%;
    339. height: 100%;
    340. position: relative;
    341. margin-top: 0;
    342. }
    343. .nav_btn {
    344. position: absolute;
    345. top: 5px;
    346. width: 50%;
    347. height: auto;
    348. }
    349. style>

  • 相关阅读:
    微软正式发布:.NET Aspire 云原生开发框架
    学习学习之高效学习
    ubuntu-server部署hive-part2-安装hadoop
    设计模式再探——原型模式
    全国400电话办理,简单步骤帮您申请成功
    ​一文梳理ICML 2022中图机器学习热点和趋势
    继承框架 - 秒杀接口实现
    麒麟软件副总裁李震宁:中国开源社区是操作系统破局的土壤
    麒麟KYLINOS桌面操作系统2303上安装tigervnc
    leetcode算法题--求1+2+…+n
  • 原文地址:https://blog.csdn.net/lovexiuwei/article/details/127916659