@bdsoft/element 1.1.13 → 1.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/index.html +13 -0
  2. package/index.js +12 -12
  3. package/package.json +19 -23
  4. package/src/App.vue +28 -0
  5. package/src/assets/css/element.scss +196 -0
  6. package/src/assets/css/layout.scss +215 -0
  7. package/src/assets/css/tailwind.scss +67 -0
  8. package/src/assets/images/banner.gif +0 -0
  9. package/src/assets/images/banner.png +0 -0
  10. package/src/assets/images/bg1.svg +22 -0
  11. package/src/assets/images/bg2.png +0 -0
  12. package/src/assets/images/ff.png +0 -0
  13. package/src/assets/images/home-file1.png +0 -0
  14. package/src/assets/images/ky.png +0 -0
  15. package/src/assets/images/menu/add.png +0 -0
  16. package/src/assets/images/menu/del.png +0 -0
  17. package/src/assets/images/menu/down.png +0 -0
  18. package/src/assets/images/menu/dr.png +0 -0
  19. package/src/assets/images/menu/edit.png +0 -0
  20. package/src/assets/images/menu/kx.png +0 -0
  21. package/src/assets/images/menu/mb.png +0 -0
  22. package/src/assets/images/menu/pz.png +0 -0
  23. package/src/assets/images/menu/save.png +0 -0
  24. package/src/assets/images/menu/sj.png +0 -0
  25. package/src/assets/images/menu/sjk.png +0 -0
  26. package/src/assets/images/menu/up.png +0 -0
  27. package/src/assets/images/po-i.png +0 -0
  28. package/src/assets/images/po1-i1.png +0 -0
  29. package/src/assets/images/po1-i2.png +0 -0
  30. package/src/assets/images/po1-i3.png +0 -0
  31. package/src/assets/images/po1-i4.png +0 -0
  32. package/src/assets/images/po1-i5.png +0 -0
  33. package/src/assets/images/po1-i6.png +0 -0
  34. package/src/assets/images/po1-i7.png +0 -0
  35. package/src/assets/images/po1-i8.png +0 -0
  36. package/src/assets/images/po2-i1.png +0 -0
  37. package/src/assets/images/po2-i2.png +0 -0
  38. package/src/assets/images/po3-i1.png +0 -0
  39. package/src/assets/images/po4-i1.png +0 -0
  40. package/src/assets/images/po5-i1.png +0 -0
  41. package/src/assets/images/po6-i1.png +0 -0
  42. package/src/assets/images/po6-i2.png +0 -0
  43. package/src/assets/images/po7-i1.png +0 -0
  44. package/src/assets/images/tj1.png +0 -0
  45. package/src/assets/images/tj2.png +0 -0
  46. package/src/assets/images/tj3.png +0 -0
  47. package/src/components/3dcloudwords/index.js +346 -0
  48. package/src/components/3dcloudwords/index.vue +99 -0
  49. package/src/components/3dcloudwords/readme.md +66 -0
  50. package/src/components/badge/index.js +167 -0
  51. package/src/components/badge/index.scss +166 -0
  52. package/src/components/badge/index.vue +98 -0
  53. package/src/components/badge/readme.md +18 -0
  54. package/src/components/basic/Finish.vue +107 -0
  55. package/src/components/basic/button.vue +19 -0
  56. package/src/components/basic/readme.md +7 -0
  57. package/src/components/button/index.vue +48 -0
  58. package/src/components/button/readme.md +62 -0
  59. package/src/components/carousel/index.vue +104 -0
  60. package/src/components/carousel/readme.md +12 -0
  61. package/src/components/chartconfig/index.vue +141 -0
  62. package/src/components/chartconfig/readme.md +25 -0
  63. package/src/components/contextMenu/hookContxtMenu.js +41 -0
  64. package/src/components/contextMenu/index.vue +245 -0
  65. package/src/components/contextMenu/readme.md +55 -0
  66. package/src/components/contextMenu/useElementBounding.js +40 -0
  67. package/src/components/countup/countUp.js +196 -0
  68. package/src/components/countup/index.vue +114 -0
  69. package/src/components/countup/readme.md +9 -0
  70. package/src/components/empty/assets/build.png +0 -0
  71. package/src/components/empty/assets/emptybg.gif +0 -0
  72. package/src/components/empty/assets/emptybg.png +0 -0
  73. package/src/components/empty/assets/emptybg2.jpg +0 -0
  74. package/src/components/empty/assets/emptybg3.jpg +0 -0
  75. package/src/components/empty/assets/wuxiao.png +0 -0
  76. package/src/components/empty/assets/wuxiao.webp +0 -0
  77. package/src/components/empty/building.vue +117 -0
  78. package/src/components/empty/empty.vue +120 -0
  79. package/src/components/empty/index.js +12 -0
  80. package/src/components/empty/invalid.vue +56 -0
  81. package/src/components/error/Error.vue +79 -0
  82. package/src/components/error/readme.md +20 -0
  83. package/src/components/form/Form.vue +84 -0
  84. package/src/components/form/FormItem.vue +143 -0
  85. package/src/components/form/data.js +52 -0
  86. package/src/components/form/readme.md +69 -0
  87. package/src/components/layout/banner.vue +412 -0
  88. package/src/components/layout/bar.vue +43 -0
  89. package/src/components/layout/layout1.vue +60 -0
  90. package/src/components/layout/layout2.vue +134 -0
  91. package/src/components/layout/layout3.vue +107 -0
  92. package/src/components/layout/layout4.vue +66 -0
  93. package/src/components/layout/nav.vue +333 -0
  94. package/src/components/layout/readme.md +61 -0
  95. package/src/components/loading/index.vue +122 -0
  96. package/src/components/loading/readme.md +6 -0
  97. package/src/components/notice/NoticeList.vue +198 -0
  98. package/src/components/notice/NoticeListPaging.vue +281 -0
  99. package/src/components/notice/NoticeView.vue +92 -0
  100. package/src/components/notice/readme.md +1 -0
  101. package/src/components/pagination/index.vue +100 -0
  102. package/src/components/pagination/readme.md +19 -0
  103. package/src/components/pagination/scroll-to.js +51 -0
  104. package/src/components/progress/bar.vue +72 -0
  105. package/src/components/progress/progress.vue +58 -0
  106. package/src/components/screenfull/index.js +3 -0
  107. package/src/components/screenfull/index.vue +65 -0
  108. package/src/components/screenfull/package.json +15 -0
  109. package/src/components/screenfull/readme.md +6 -0
  110. package/src/components/statisticalCount/index.vue +80 -0
  111. package/src/components/statisticalCount/readme.md +21 -0
  112. package/src/components/username/index.vue +79 -0
  113. package/src/components/username/readme.md +22 -0
  114. package/src/components/username//346/225/210/346/236/234/345/233/276.png +0 -0
  115. package/src/global/index.ts +6 -0
  116. package/src/global/register-properties.ts +10 -0
  117. package/src/index.js +88 -0
  118. package/src/utils/coms/load.jsx +10 -0
  119. package/src/utils/func.js +62 -0
  120. package/src/utils/hookDialog.js +38 -0
  121. package/src/utils/hookPage.js +49 -0
  122. package/src/utils/index.js +5 -0
  123. package/src/utils/lib/console.js +39 -0
  124. package/src/utils/lib/debounce.js +19 -0
  125. package/src/utils/lib/deepextend.js +51 -0
  126. package/src/utils/lib/deepset.js +14 -0
  127. package/src/utils/lib/extend.js +28 -0
  128. package/src/utils/lib/index.js +13 -0
  129. package/src/utils/lib/json.js +90 -0
  130. package/src/utils/lib/mergeprops.js +62 -0
  131. package/src/utils/lib/mitt.js +43 -0
  132. package/src/utils/lib/modify.js +8 -0
  133. package/src/utils/lib/slot.js +19 -0
  134. package/src/utils/lib/toarray.js +5 -0
  135. package/src/utils/lib/tocase.js +11 -0
  136. package/src/utils/lib/todate.js +10 -0
  137. package/src/utils/lib/toline.js +10 -0
  138. package/src/utils/lib/tostring.js +7 -0
  139. package/src/utils/lib/type.js +45 -0
  140. package/src/utils/lib/unique.js +6 -0
  141. package/src/utils/message.js +164 -0
  142. package/src/utils/type.js +45 -0
  143. package/src/xm_components/HeadSearch/hook/hookSearch.js +96 -0
  144. package/src/xm_components/HeadSearch/index.vue +206 -0
  145. package/src/xm_components/HeadSearch/readme.md +12 -0
  146. package/src/xm_components/HeadSearch//346/220/234/347/264/242/345/210/227/350/241/250.png +0 -0
  147. package/src/xm_components/Milestone/index.vue +213 -0
  148. package/src/xm_components/Milestone/readme.md +15 -0
  149. package/src/xm_components/MultiStatisticalCard/image.png +0 -0
  150. package/src/xm_components/MultiStatisticalCard/index.vue +114 -0
  151. package/src/xm_components/MultiStatisticalCard/readme.md +29 -0
  152. package/src/xm_components/StatisticalCard/image.png +0 -0
  153. package/src/xm_components/StatisticalCard/index.vue +196 -0
  154. package/src/xm_components/StatisticalCard/readme.md +41 -0
  155. package/src/xm_components/readme.md +1 -0
  156. package/vite.config.js +58 -0
  157. package/dist/BdElement.js +0 -23432
@@ -0,0 +1,104 @@
1
+ <!--
2
+ * @FileDescription: 轮播组件
3
+ * @Author: 李兵泉
4
+ * @Date: 2021-06-08 09 时
5
+ * @LastEditors: 最后更新作者
6
+ * @LastEditTime: 最后更新时间
7
+ -->
8
+ <script setup>
9
+ import { ref, reactive, computed, watch, onMounted } from 'vue'
10
+ // import { _getData, _saveData, _deleteData, _motifyData, _addData, getBaseImgUrl } from '@/api/newrabbit'
11
+ // import { getPic } from '@/api/page'
12
+ const props = defineProps({
13
+ // // 图片的url
14
+ // picUrl: {
15
+ // type: String,
16
+ // default: 'ptService/sysconfig/pic'
17
+ // },
18
+ // // 图片的code
19
+ // picCode: {
20
+ // type: String,
21
+ // default: 'ptxmpic'
22
+ // },
23
+ // categoryid: {
24
+ // type: String,
25
+ // },
26
+ // fl: {
27
+ // type: String,
28
+ // }
29
+ carouselData: {
30
+ type: Array,
31
+ default: () => []
32
+ },
33
+ // 是否自动切换
34
+ autoplay: {
35
+ type: Boolean,
36
+ default: true
37
+ }
38
+ })
39
+ // const carouselData = ref([])
40
+ // 首页的图片列表
41
+ // const _getPic = () => {
42
+ // let p = { categoryid: props.categoryid || window.$bd.xmPicType.categoryid, fl: props.fl || window.$bd.xmPicType.fl }
43
+ // _getData(props.picUrl, p).then((res) => {
44
+ // if (res.succeed) {
45
+ // carouselData.value = res.data[0].datas.map((v) => {
46
+ // v.imgUrl = ''
47
+ // if (v.url) {
48
+ // v.imgUrl = getBaseImgUrl(v.url, props.picCode)
49
+ // }
50
+ // return v
51
+ // })
52
+ // // console.log('_getPic', carouselData.value)
53
+ // }
54
+ // })
55
+ // }
56
+ onMounted(() => {
57
+ // _getPic()
58
+ })
59
+ </script>
60
+ <template>
61
+ <div class="banner-bg">
62
+ <div class="banner-warp">
63
+ <div class="carousel-warp" style="">
64
+ <el-carousel height="560px" motion-blur style="" :autoplay="autoplay">
65
+ <el-carousel-item v-for="item in props.carouselData" :key="item">
66
+ <!-- 增加视频 组件轮播支持 -->
67
+
68
+ <video v-if="item.filtype == 'video'" autoplay muted loop style="visibility: visible;object-fit: cover;object-position: center;width:100%;height: 560px;">
69
+ <source :src="item.imgUrl" type="video/mp4" />
70
+ </video>
71
+ <img v-else :src="item.imgUrl" class="img" />
72
+ <component v-if="item.component" :is="item.component" :item="item" />
73
+ </el-carousel-item>
74
+ </el-carousel>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </template>
79
+
80
+ <style lang="scss" scoped>
81
+ .banner-bg {
82
+ height: 560px;
83
+ background-color: #ffffff19;
84
+ // background-image: url(./u5.png);
85
+ border-radius: 10px;
86
+ // width: 1200px;
87
+ margin: 0 auto;
88
+ .banner-warp {
89
+ padding-top: 0px;
90
+ }
91
+ }
92
+
93
+ .carousel-warp {
94
+ width: 100%;
95
+ margin: 0 auto;
96
+ // background-color: #fff;
97
+ // border-radius: 8px;
98
+ }
99
+
100
+ .img {
101
+ width: 100%;
102
+ height: 100%;
103
+ }
104
+ </style>
@@ -0,0 +1,12 @@
1
+ ## 轮播组件
2
+ // 支持图片、视频、及自定义组件的传递
3
+
4
+ ```数据格式
5
+ const carouselData = ref([
6
+ { imgUrl: bg0, title: '1', component: CurrentCompoent['Coma'] },
7
+ { imgUrl: bg1, title: '2', component: CurrentCompoent['Comb'] },
8
+ { imgUrl: bg2, title: '2', component: CurrentCompoent['Comc'] },
9
+ { imgUrl: bg3, title: '2', component: CurrentCompoent['ComData'], filtype: 'video' }
10
+ ])
11
+
12
+ ```
@@ -0,0 +1,141 @@
1
+ <!--
2
+ * @FileDescription: 图表配置窗口
3
+ * @Author: 李兵泉
4
+ * @Date: 2021-06-08 09 时
5
+ * @LastEditors: 最后更新作者
6
+ * @LastEditTime: 最后更新时间
7
+ -->
8
+ <template>
9
+ <el-dialog v-model="visible" :width="600" title="图表配置" class="bdDialog chartConfig" :style="{ borderRadius: '10px' }" draggable>
10
+ <el-form v-model="chartColorForm" label-suffix=":" label-width='80px' class="formconfig" style="padding: 10px 10px">
11
+ <el-tabs type="border-card" class="chart-tabs" style="height: 100%">
12
+ <el-tab-pane label="坐标轴配置">
13
+ <el-divider content-position="left">X坐标轴</el-divider>
14
+ <el-form-item label="抽稀个数">
15
+ <el-input-number v-model="chartForm.xAxis_axisLabel_interval" :min="0" controls-position="right" />
16
+ </el-form-item>
17
+ <el-form-item label="旋转角度">
18
+ <el-input-number v-model="chartForm.xAxis_axisLabel_rotate" :min="-90" :max="90" controls-position="right" />
19
+ </el-form-item>
20
+
21
+ <el-divider content-position="left">Y坐标轴</el-divider>
22
+
23
+ <el-form-item label="最小值">
24
+ <el-input-number v-model="chartForm.yAxis_min" controls-position="right" />
25
+ </el-form-item>
26
+ <el-form-item label="最大值">
27
+ <el-input-number v-model="chartForm.yAxis_max" controls-position="right" />
28
+ </el-form-item>
29
+ <el-form-item label="间隔值">
30
+ <el-input-number v-model="chartForm.yAxis_interval" :min="0" controls-position="right" />
31
+ </el-form-item>
32
+ </el-tab-pane>
33
+ <el-tab-pane label="曲线配置">
34
+ <el-divider content-position="left">曲线节点</el-divider>
35
+ <el-form-item :label="item.name" v-for="(item, index) in chartForm.seriesConfig" :key="index">
36
+ <el-row>
37
+ <!-- 颜色数组 -->
38
+ <el-form-item label="颜色">
39
+ <el-color-picker v-model="item.itemStyle_color" size="small" />
40
+ </el-form-item>
41
+ <el-form-item label="显示数值">
42
+ <el-checkbox v-model="item.label_show" label="" />
43
+ </el-form-item>
44
+ <el-form-item label="抽稀个数">
45
+ <el-input-number v-model="item.label_interval" :min="0" controls-position="right" />
46
+ </el-form-item>
47
+ </el-row>
48
+ </el-form-item>
49
+ <el-divider content-position="left">grid边距</el-divider>
50
+ <el-row>
51
+ <el-col :span="12">
52
+ <el-form-item label="左侧边距">
53
+ <el-input v-model="chartForm.grid_left" placeholder="示例: 20 或 20%" />
54
+ </el-form-item>
55
+ </el-col>
56
+ <el-col :span="12">
57
+ <el-form-item label="右侧边距">
58
+ <el-input v-model="chartForm.grid_right" placeholder="示例: 20 或 20%" />
59
+ </el-form-item>
60
+ </el-col>
61
+ </el-row>
62
+ <el-row>
63
+ <el-col :span="12">
64
+ <el-form-item label="上侧边距">
65
+ <el-input v-model="chartForm.grid_top" placeholder="示例: 20 或 20%" />
66
+ </el-form-item>
67
+ </el-col>
68
+ <el-col :span="12">
69
+ <el-form-item label="下侧边距">
70
+ <el-input v-model="chartForm.grid_bottom" placeholder="示例: 20 或 20%" />
71
+ </el-form-item>
72
+ </el-col>
73
+ </el-row>
74
+ </el-tab-pane>
75
+ </el-tabs>
76
+ </el-form>
77
+ <template #footer>
78
+ <div class="dialog-footer">
79
+ <el-button @click="visible = false">取消</el-button>
80
+ <el-button type="primary" @click="handleSetChartColorSave"> 确定 </el-button>
81
+ </div>
82
+ </template>
83
+ </el-dialog>
84
+ </template>
85
+ <script setup>
86
+ import { ref, reactive, computed, watch, onMounted } from 'vue'
87
+ import { useDialog } from '../../utils/hookDialog.js'
88
+
89
+ const emits = defineEmits(['setNewChartConfig'])
90
+
91
+
92
+ // 图表表单
93
+ const chartForm = ref({
94
+ xAxis_axisLabel_interval: 0,
95
+ xAxis_axisLabel_rotate: 0,
96
+ yAxis_min: null,
97
+ yAxis_max: null,
98
+ yAxis_interval: null,
99
+ grid_left: null,
100
+ grid_top: null,
101
+ grid_right: null,
102
+ grid_bottom: null,
103
+ seriesConfig: [] // 颜色配置
104
+ })
105
+
106
+ // 打开回调
107
+ const callShowDialog = () => {
108
+ let config = openParms.value
109
+ chartColorForm.sourceid = config.sourceid
110
+ chartColorForm.chartid = config.api.getId()
111
+ chartColorForm.api = config.api
112
+ chartForm.value = Object.assign({}, chartForm.value, extParms.value)
113
+ }
114
+ const { visible, showDialog, openParms, extParms, hideDialog } = useDialog(callShowDialog)
115
+
116
+ // 颜色配置
117
+ const chartColorForm = reactive({
118
+ sourceid: '',
119
+ chartid: '', // 当前操作的图表对象
120
+ api: null, // 当前操作的图表对象
121
+ visiable: false,
122
+ })
123
+ /**
124
+ * 保存配置
125
+ */
126
+ const handleSetChartColorSave = () => {
127
+ emits('setNewChartConfig', chartForm.value) // 不同vue对象好像不通信改用bus
128
+
129
+ dialogClose()
130
+ }
131
+ const dialogClose = () => {
132
+ visible.value = false
133
+ }
134
+ defineExpose({
135
+ showDialog,
136
+ hideDialog,
137
+ dialogClose
138
+ })
139
+ </script>
140
+
141
+ <style lang="scss" scoped></style>
@@ -0,0 +1,25 @@
1
+ ## 图表配置界面
2
+
3
+ ### 使用
4
+
5
+ ```
6
+ <!-- 颜色配置窗口 -->
7
+ <dialogChartConfig ref="dialogChartConfigRef" @saveColor="handleSetChartColorSave"></dialogChartConfig>
8
+
9
+ import {dialogChartConfig} from '@bdsoft/element'
10
+
11
+ const dialogChartConfigRef = ref()
12
+ // 打开图表配置窗口
13
+ const handleSetChartConfigDialog = (config) => {
14
+ // 这里接收的是图表的配置
15
+ dialogChartConfigRef.value.showDialog(config)
16
+ }
17
+ // 项目中引入
18
+ onMounted(() => {
19
+ bus.on('setChartConfigDialog', handleSetChartConfigDialog) // 图表颜色配置
20
+ })
21
+
22
+ onUnmounted(() => {
23
+ bus.off('setChartConfigDialog', handleSetChartConfigDialog) // 图表颜色配置
24
+ })
25
+ ```
@@ -0,0 +1,41 @@
1
+ import { ref, onUnmounted } from 'vue'
2
+
3
+ export const useContextMenu = (container) => {
4
+ const x = ref(0)
5
+ const y = ref(0)
6
+ const visible = ref(false)
7
+ // 代开菜单
8
+ const openMenu = (e) => {
9
+ debugger
10
+ // 阻止默认行为
11
+ e.preventDefault()
12
+ // 阻止冒泡
13
+ e.stopPropagation()
14
+ // 显示菜单
15
+ visible.value = true
16
+ // 获取鼠标位置
17
+ x.value = e.clientX
18
+ y.value = e.clientY
19
+ }
20
+ // 关闭菜单
21
+ const closeMenu = () => {
22
+ console.log('关闭菜单')
23
+ visible.value = false
24
+ }
25
+
26
+ container.addEventListener('contextmenu', openMenu)
27
+ window.addEventListener('click', closeMenu)
28
+ window.addEventListener('contextmenu', closeMenu)
29
+
30
+ onUnmounted(() => {
31
+ // 移除事件
32
+ container?.removeEventListener('contextmenu', openMenu)
33
+ window.removeEventListener('click', closeMenu)
34
+ window.removeEventListener('contextmenu', closeMenu)
35
+ })
36
+ return {
37
+ x,
38
+ y,
39
+ visible
40
+ }
41
+ }
@@ -0,0 +1,245 @@
1
+ <!--
2
+ * @FileDescription: 适用整体区域的右键菜单
3
+ * @Author: 李兵泉
4
+ * @Date: 2024-11-01
5
+ * @LastEditors: 最后更新作者
6
+ * @LastEditTime: 最后更新时间
7
+ -->
8
+ <template>
9
+ <div class="context-menu-container">
10
+ <slot></slot>
11
+
12
+ <!-- 右键菜单通过Teleport挂载到body -->
13
+ <Teleport to="body">
14
+ <transition name="menu-fade" @enter="handleEnter">
15
+ <div
16
+ class="context-menu"
17
+ ref="menuRef"
18
+ v-if="isMenuVisible"
19
+ :style="{ top: menuY + 'px', left: menuX + 'px' }"
20
+ >
21
+ <ul>
22
+ <li
23
+ v-for="(item, index) in menuOptions"
24
+ :key="index"
25
+ class="menu-item"
26
+ @click="handleMenuItemClick(item, index)"
27
+ >
28
+ {{ item.label }}
29
+ <span v-if="item.icon" :class="item.icon"></span>
30
+ </li>
31
+ </ul>
32
+ </div>
33
+ </transition>
34
+ </Teleport>
35
+ </div>
36
+ </template>
37
+
38
+ <script setup>
39
+ import { ref, computed , onMounted, onUnmounted, watch,provide } from 'vue';
40
+ import { useElementBounding } from './useElementBounding.js'; // 自定义钩子,用于获取元素位置
41
+
42
+ // 定义组件属性
43
+ const props = defineProps({
44
+ options: {
45
+ type: Array,
46
+ default: () => []
47
+ }
48
+ });
49
+
50
+ // 定义组件事件
51
+ const emit = defineEmits(['menu-click', 'show-menu', 'hide-menu']);
52
+
53
+ // 菜单状态和位置
54
+ const menuRef = ref(null);
55
+ const isMenuVisible = ref(false);
56
+ const menuX = ref(0);
57
+ const menuY = ref(0);
58
+ const currentItem = ref(null); // 当前右键的元素数据
59
+ const menuOptions = computed(() => props.options);
60
+
61
+ const currentItemData = ref(null);
62
+
63
+ // 提供showMenu方法供子组件使用
64
+ provide('showContextMenu', (itemData, event) => {
65
+ event.preventDefault();
66
+ currentItemData.value = itemData;
67
+
68
+ // 计算位置逻辑...
69
+ const { clientX, clientY } = event;
70
+ const menuWidth = 180;
71
+ const menuHeight = props.options.length * 40;
72
+
73
+ let x = clientX;
74
+ if (x + menuWidth > window.innerWidth) {
75
+ x = Math.max(0, window.innerWidth - menuWidth);
76
+ }
77
+
78
+ let y = clientY;
79
+ if (y + menuHeight > window.innerHeight) {
80
+ y = Math.max(0, window.innerHeight - menuHeight);
81
+ }
82
+
83
+ menuX.value = x;
84
+ menuY.value = y;
85
+ isMenuVisible.value = true;
86
+ });
87
+
88
+ // 处理菜单显示
89
+ const showMenu = (item, event) => {
90
+ event.preventDefault(); // 阻止默认右键菜单
91
+
92
+ // 存储当前右键的元素数据
93
+ currentItem.value = item;
94
+
95
+ // 计算菜单位置,确保不超出视口
96
+ const { x, y } = calculateMenuPosition(event.clientX, event.clientY);
97
+ menuX.value = x;
98
+ menuY.value = y;
99
+
100
+ // 显示菜单
101
+ isMenuVisible.value = true;
102
+ emit('show-menu', { item, x, y });
103
+ };
104
+
105
+ // 计算菜单位置,避免超出视口
106
+ const calculateMenuPosition = (clientX, clientY) => {
107
+ const menuWidth = 180;
108
+ const menuHeight = 40 * menuOptions.value.length; // 假设每个菜单项40px高度
109
+
110
+ let x = clientX;
111
+ let y = clientY;
112
+
113
+ // 检查是否超出右边界
114
+ if (x + menuWidth > window.innerWidth) {
115
+ x = window.innerWidth - menuWidth;
116
+ }
117
+
118
+ // 检查是否超出下边界
119
+ if (y + menuHeight > window.innerHeight) {
120
+ y = window.innerHeight - menuHeight;
121
+ }
122
+
123
+ return { x, y };
124
+ };
125
+
126
+ // 处理菜单项点击
127
+ const handleMenuItemClick = (item, index) => {
128
+ isMenuVisible.value = false;
129
+ emit('menu-click', { item, index, data: currentItem.value });
130
+
131
+ // 如果菜单项有回调函数,执行它
132
+ if (typeof item.click === 'function') {
133
+ item.click(currentItem.value);
134
+ }
135
+ };
136
+
137
+ // 处理菜单进入动画
138
+ const handleEnter = (el) => {
139
+ el.style.opacity = '0';
140
+ el.style.transform = 'translateY(-10px)';
141
+ el.style.transition = 'opacity 0.2s, transform 0.2s';
142
+
143
+ setTimeout(() => {
144
+ el.style.opacity = '1';
145
+ el.style.transform = 'translateY(0)';
146
+ }, 10);
147
+ };
148
+
149
+ // 点击页面其他地方关闭菜单
150
+ const closeMenuOnClickOutside = (event) => {
151
+ if (menuRef.value && !menuRef.value.contains(event.target)) {
152
+ isMenuVisible.value = false;
153
+ emit('hide-menu');
154
+ }
155
+ };
156
+
157
+ // 键盘ESC关闭菜单
158
+ const closeMenuOnEsc = (event) => {
159
+ if (event.key === 'Escape') {
160
+ isMenuVisible.value = false;
161
+ emit('hide-menu');
162
+ }
163
+ };
164
+
165
+ // 生命周期钩子
166
+ onMounted(() => {
167
+ document.addEventListener('click', closeMenuOnClickOutside);
168
+ document.addEventListener('keydown', closeMenuOnEsc);
169
+ });
170
+
171
+ onUnmounted(() => {
172
+ document.removeEventListener('click', closeMenuOnClickOutside);
173
+ document.removeEventListener('keydown', closeMenuOnEsc);
174
+ });
175
+
176
+ defineExpose({
177
+ showMenu
178
+ })
179
+ // 监听选项变化
180
+ watch(() => props.options, (newOptions) => {
181
+ // 菜单选项变化时,如果菜单显示中,重新计算高度
182
+ if (isMenuVisible.value && menuRef.value) {
183
+ const menuHeight = 40 * newOptions.length;
184
+ menuRef.value.style.height = `${menuHeight}px`;
185
+ }
186
+ });
187
+ </script>
188
+
189
+ <style lang="scss" scoped>
190
+ .context-menu-container {
191
+ position: relative;
192
+ width: 100%;
193
+ height: 100%;
194
+ }
195
+
196
+ .context-menu {
197
+ position: fixed;
198
+ background-color: #fff;
199
+ width: 180px;
200
+ border-radius: 4px;
201
+ padding: 0;
202
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
203
+ z-index: 1000;
204
+ overflow: hidden;
205
+
206
+ ul {
207
+ margin: 0;
208
+ padding: 0;
209
+ list-style: none;
210
+
211
+ .menu-item {
212
+ margin: 0;
213
+ padding: 0 16px;
214
+ height: 40px;
215
+ line-height: 40px;
216
+ text-align: left;
217
+ cursor: pointer;
218
+ font-size: 14px;
219
+ color: #333;
220
+ display: flex;
221
+ align-items: center;
222
+
223
+ &:hover {
224
+ background-color: #f5f7fa;
225
+ color: #409eff;
226
+ }
227
+
228
+ span {
229
+ margin-left: 8px;
230
+ font-size: 12px;
231
+ }
232
+ }
233
+ }
234
+ }
235
+
236
+ /* 菜单动画 */
237
+ .menu-fade-enter-active, .menu-fade-leave-active {
238
+ transition: opacity 0.2s, transform 0.2s;
239
+ }
240
+
241
+ .menu-fade-enter-from, .menu-fade-leave-to {
242
+ opacity: 0;
243
+ transform: translateY(-10px);
244
+ }
245
+ </style>
@@ -0,0 +1,55 @@
1
+ # 右键菜单
2
+
3
+ ```
4
+ import {BdContextMenu} from '@bdsoft/element'
5
+ <BdContextMenu :options="treeMenuDatas" @menu-click="handleMenuClick" ref="menuRef">
6
+ <ul>
7
+ <li class="liwarp" v-for="item in treeData" @dblclick="handleTreeNodeDbClick(item)" @contextmenu.stop="showMenu(item, $event)">
8
+ <el-tooltip class="box-item" effect="dark" :content="item.AskContent" placement="right">
9
+ {{ item.AskContent }}
10
+ </el-tooltip>
11
+ </li>
12
+ </ul>
13
+ </BdContextMenu>
14
+
15
+ const menuRef = ref(null);
16
+ const options = [
17
+ {
18
+ label: "新建",
19
+ },
20
+ {
21
+ label: "编辑",
22
+ },
23
+ {
24
+ label: "删除",
25
+ },
26
+ {
27
+ label: "复制",
28
+ }
29
+ ]
30
+ const treeMenuDatas = [
31
+ {
32
+ value: 'qa-delcollect', label: '取消收藏', icon: 'del', type: 'title'
33
+ }
34
+ ]
35
+ // 从上下文注入showContextMenu方法
36
+ const showContextMenu = inject('showContextMenu');
37
+ // 存储显示菜单数据
38
+ const showMenu = (item, event) => {
39
+ if (showContextMenu) {
40
+ showContextMenu(item, event);
41
+ } else if (menuRef.value) {
42
+ // 方式2:通过ref调用(备用方案)
43
+ menuRef.value?.showMenu(item, event);
44
+ }
45
+ }
46
+
47
+ // 处理菜单项点击
48
+ const handleMenuClick = (params) => {
49
+ const { item, index, data } = params;
50
+ if(item.value=='qa-delcollect'){
51
+ handleDelCollect(data)
52
+ }
53
+ // ElMessage(`点击了${item.label},数据ID: ${data.id}`);
54
+ };
55
+ ```
@@ -0,0 +1,40 @@
1
+ // hooks/useElementBounding.js
2
+ import { ref, onMounted, onUnmounted } from 'vue';
3
+
4
+ export function useElementBounding() {
5
+ const bounding = ref(null);
6
+
7
+ const updateBounding = (el) => {
8
+ if (el) {
9
+ bounding.value = el.getBoundingClientRect();
10
+ }
11
+ };
12
+
13
+ const bind = (el) => {
14
+ updateBounding(el);
15
+
16
+ const observer = new MutationObserver(() => {
17
+ updateBounding(el);
18
+ });
19
+
20
+ observer.observe(el, {
21
+ attributes: true,
22
+ childList: true,
23
+ subtree: true
24
+ });
25
+
26
+ window.addEventListener('resize', () => updateBounding(el));
27
+ window.addEventListener('scroll', () => updateBounding(el));
28
+
29
+ onUnmounted(() => {
30
+ observer.disconnect();
31
+ window.removeEventListener('resize', () => updateBounding(el));
32
+ window.removeEventListener('scroll', () => updateBounding(el));
33
+ });
34
+ };
35
+
36
+ return {
37
+ bind,
38
+ bounding
39
+ };
40
+ }