centaline-data-driven-v3 0.1.40 → 0.1.42

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "centaline-data-driven-v3",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "private": false,
5
5
  "description": "centaline-data-driven-v3",
6
6
  "main": "dist/centaline-data-driven-v3.umd.js",
@@ -3,8 +3,9 @@
3
3
  :style="{ width: pageWidth ? pageWidth + 'px' : '100%', margin: 'auto', 'min-height': minHeight }">
4
4
  <div style="display: flex; width: 100%;">
5
5
  <div style="flex: 1; min-width: 0;">
6
- <div v-if="model !== null && !loading" class="ct-form">
7
- <el-affix target=".ct-form" v-if="model.tip" :offset="62">
6
+ <div v-if="model !== null && !loading" class="ct-form"
7
+ :style="{ margin: openType != 'tree' ? '10px' : '0px' }">
8
+ <el-affix target=".ct-form" v-if="model.tip" :offset="tipTopHight">
8
9
  <div class="ct-form-tip" ref="refTip">
9
10
  <span v-html="model.tip"></span>
10
11
  </div>
@@ -57,7 +58,8 @@
57
58
  <template v-if="model.isHorizontalLayout">
58
59
  <component :is="model.flagFixedTabOnHorizontalLayout ? 'el-affix' : 'div'"
59
60
  v-bind="model.flagFixedTabOnHorizontalLayout ? { offset: tabTopHight, target: '.ct-form' } : {}">
60
- <el-tabs v-model="activeName" @tab-click="tabClick" style="background: #fff;">
61
+ <el-tabs v-model="activeName" @tab-click="tabClick" style="background: #fff;"
62
+ ref="refTabs">
61
63
  <template v-for="(item, index) in model.collapse" :key="index">
62
64
  <el-tab-pane :name="index.toString()" :lazy="item.lazyLoad"
63
65
  v-if="item.show !== false" :key="index">
@@ -110,10 +112,11 @@
110
112
  v-if="col.show !== false && col.lineFeed"></div>
111
113
  <el-col :span="col.colspan" v-if="col.show !== false" style="padding:5px"
112
114
  :class="[col.is == 'ct-button' && col.labelPlacement == '1' ? 'el-col1' : '']">
113
- <component ref="Fields" :is="col.is" :vmodel="col" :key="col.fieldItemKey"
114
- :listHeight="listHeight" :parameterAction="model.parameterAction"
115
- v-bind="col.bindPara" :fileData="getFileData(col)"
116
- @change="changeHandler" @fieldClick="fieldClickHandler"
115
+ <component ref="Fields" :is="col.is" :vmodel="col"
116
+ :key="col.fieldItemKey" :listHeight="listHeight"
117
+ :parameterAction="model.parameterAction" v-bind="col.bindPara"
118
+ :fileData="getFileData(col)" @change="changeHandler"
119
+ @fieldClick="fieldClickHandler"
117
120
  @popupLocation="popupLocationHandler"
118
121
  @popupSearchList="popupSearchListHandler"
119
122
  @importComplete="importComplete"
@@ -272,7 +275,6 @@ const props = defineProps({
272
275
  dialoWidth: Number,
273
276
  listHeight: Number,
274
277
  })
275
-
276
278
  const itemKey = ref(1)
277
279
  const router = useRouter()
278
280
  const loading = ref(true)
@@ -288,7 +290,10 @@ const showAI = ref(false);
288
290
  const dialogHeight = ref(props.dialogHeight || (window.innerHeight - 60));
289
291
  const tabActiveNameKey = ref('')
290
292
  const refTip = ref()
291
- const tabTopHight = ref(52)
293
+ const tipTopHight = ref(0)
294
+ const tabTopHight = ref(0)
295
+ const refTabs = ref()
296
+ const translateX = ref(0)
292
297
 
293
298
  const qrtimer1 = ref(null)
294
299
  const qrtimer2 = ref(null)
@@ -320,8 +325,27 @@ onUpdated(() => {
320
325
  onDeactivated(() => {
321
326
  downloadUrl.value = ''
322
327
  })
328
+
323
329
  onMounted(() => {
324
330
  setCss();
331
+ if (refForm.value) {
332
+ const rect = refForm.value.getBoundingClientRect()
333
+ const distanceFromTop = rect.top + window.scrollY
334
+ tipTopHight.value = distanceFromTop;
335
+ if (refTip.value) {
336
+ tabTopHight.value = tabTopHight.value + refTip.value.clientHeight + 10
337
+ }
338
+ else {
339
+ tabTopHight.value = tipTopHight.value;
340
+ }
341
+ }
342
+ nextTick(() => {
343
+
344
+ setTimeout(() => {
345
+ attachScrollButtonListeners()
346
+ }, 1000);
347
+
348
+ });
325
349
  })
326
350
  init()
327
351
  //初始化数据
@@ -379,11 +403,6 @@ function load(data) {
379
403
 
380
404
  }
381
405
  }
382
- nextTick(() => {
383
- if (refTip.value) {
384
- tabTopHight.value = tabTopHight.value + refTip.value.clientHeight + 10
385
- }
386
- })
387
406
  //通知父组件加载完成
388
407
  emit('loaded', model.value);
389
408
  }
@@ -703,11 +722,29 @@ function setCss() {
703
722
  if (props.topHeight > -1) {
704
723
  minHeight.value = (document.documentElement.clientHeight - props.topHeight - 20) + 'px';
705
724
  }
706
- else if(props.dialogHeight){
725
+ else if (props.dialogHeight) {
707
726
  minHeight.value = (props.dialogHeight) + 'px';
708
727
  }
709
- else{
710
- minHeight.value = document.documentElement.clientHeight + 'px';
728
+ else if (props.openType == 'tree') {
729
+ if (refForm.value) {
730
+ let parentDom = refForm.value.parentElement;
731
+ minHeight.value = parentDom.clientHeight + 'px';
732
+ refForm.value.style.height = minHeight.value
733
+ refForm.value.style.overflowY = 'auto'
734
+ refForm.value.style.paddingBottom = '45px'
735
+ }
736
+ }
737
+ else if (props.openType == 'detail') {
738
+ if (refForm.value) {
739
+ let parentDom = refForm.value.parentElement;
740
+ minHeight.value = parentDom.clientHeight + 'px';
741
+ }
742
+ }
743
+ else {
744
+ if (refForm.value) {
745
+ let parentDom = refForm.value.parentElement;
746
+ minHeight.value = parentDom.clientHeight + 'px';
747
+ }
711
748
  }
712
749
  }
713
750
 
@@ -726,16 +763,235 @@ function buttonsWidth() {
726
763
  rtn = props.dialoWidth - model.value.aiAttr.width - 4 + 'px';
727
764
  }
728
765
  else if (props.pageWidth) {
729
- rtn = props.pageWidth - 20 + 'px';
766
+ rtn = props.pageWidth + 'px';
730
767
 
731
768
  }
769
+
732
770
  return rtn;
733
771
  }
734
772
 
735
- function tabClick(event) {
773
+
774
+ // 点击后让激活 Tab 居中
775
+ async function tabClick(pane: TabsPaneContext) {
736
776
  if (props.openType == 'detail') {
737
777
  window.localStorage.setItem(tabActiveNameKey.value, model.value.collapse[event.index].fieldName1);//存储
738
778
  }
779
+ await nextTick() // 等 DOM 更新完再取节点
780
+ const navWrapScroll = document.querySelector('.el-tabs__nav-scroll') as HTMLElement
781
+ const navWrap = document.querySelector('.el-tabs__nav') as HTMLElement
782
+ const activeBtn = document.querySelector(`.el-tabs__item#tab-${pane.paneName}`) as HTMLElement
783
+ if (!navWrapScroll || !navWrap || !activeBtn) return
784
+ const visibleCount = getVisibleTabCount()
785
+ if (pane.index <= visibleCount / 2) {
786
+ setTimeout(() => {
787
+ navWrap.style.transition = 'transform .3s ease-out'
788
+ navWrap.style.transform = 'translateX(0px)'
789
+ }, 50);
790
+ translateX.value = 0
791
+ return
792
+ }
793
+ const allTabs = Array.from(navWrap.querySelectorAll('.el-tabs__item')) as HTMLElement[]
794
+ const total = allTabs.length
795
+ if (pane.index >= total - 5) {
796
+ const lastTab = allTabs[total - 1]
797
+ const wrapWidth = navWrapScroll.clientWidth
798
+ const lastWidth = lastTab.offsetWidth
799
+ const lastRight = lastTab.offsetLeft + lastWidth // 最后一个右边到 nav 左边的距离
800
+ const needShift = wrapWidth - lastRight // 负值,让右边对齐容器右边
801
+ setTimeout(() => {
802
+ navWrap.style.transition = 'transform .3s ease-out'
803
+ navWrap.style.transform = `translateX(${needShift}px)`
804
+ }, 50);
805
+ translateX.value = needShift
806
+ return
807
+ }
808
+ /* ---------- 1. 初始状态:还没滑过 ---------- */
809
+ const neverSlid = navWrap.style.transform == 'translateX(0px)'
810
+
811
+ if (neverSlid && pane.index >= visibleCount / 2) {
812
+ /* 第一次往后走,直接允许滑动 → 走居中逻辑 */
813
+ const wrapWidth = navWrapScroll.clientWidth
814
+ const btnLeft = activeBtn.offsetLeft
815
+ const btnWidth = activeBtn.offsetWidth
816
+ const dx = (wrapWidth - btnWidth) / 2 - btnLeft
817
+ setTimeout(() => {
818
+ navWrap.style.transition = 'transform .3s ease-out'
819
+ navWrap.style.transform = `translateX(${dx}px)`
820
+ }, 50);
821
+ translateX.value = dx
822
+ return
823
+ }
824
+
825
+ /* ---------- 2. 已经滑过 → 恢复两端贴边即锁死 ---------- */
826
+ const firstTab = navWrap.querySelector('.el-tabs__item') as HTMLElement
827
+ const lastTab = navWrap.querySelector('.el-tabs__item:last-child') as HTMLElement
828
+ if (!firstTab || !lastTab) return
829
+
830
+ const lastAtRight = lastTab.getBoundingClientRect().right <= navWrapScroll.getBoundingClientRect().right
831
+
832
+ if ((lastAtRight && pane.index >= total - 5)) {
833
+ return
834
+ } // 已贴边,不滑
835
+
836
+ /* 需要居中 */
837
+ const wrapWidth = navWrapScroll.clientWidth
838
+ const btnLeft = activeBtn.offsetLeft
839
+ const btnWidth = activeBtn.offsetWidth
840
+ const dx = (wrapWidth - btnWidth) / 2 - btnLeft
841
+ translateX.value = dx
842
+ setTimeout(() => {
843
+ navWrap.style.transition = 'transform .3s ease-out'
844
+ navWrap.style.transform = `translateX(${dx}px)`
845
+ }, 50);
846
+ }
847
+ /** 返回当前屏幕完全可见的 tab 个数 */
848
+ function getVisibleTabCount(): number {
849
+ const navWrapScroll = document.querySelector('.el-tabs__nav-scroll') as HTMLElement
850
+ const navWrap = document.querySelector('.el-tabs__nav') as HTMLElement
851
+ if (!navWrapScroll || !navWrap) return 0
852
+
853
+ const wrapWidth = navWrapScroll.clientWidth // 容器可视宽
854
+ const allTabs = Array.from(navWrap.querySelectorAll('.el-tabs__item')) as HTMLElement[]
855
+ if (!allTabs.length) return 0
856
+
857
+ // ① 如果所有 tab 总宽度 ≤ 容器宽,全部可见
858
+ const totalWidth = allTabs.reduce((sum, tab) => sum + tab.offsetWidth, 0)
859
+ if (totalWidth <= wrapWidth) return allTabs.length
860
+
861
+ // ② 否则按“平均宽度”向下取整
862
+ const avgWidth = totalWidth / allTabs.length
863
+ return Math.floor(wrapWidth / avgWidth)
864
+ }
865
+ function attachScrollButtonListeners() {
866
+ if (refTabs.value) {
867
+ const tabsEl = refTabs.value.$el
868
+
869
+ // 监听原生左右箭头点击事件(事件委托)
870
+ const arrowLeft = tabsEl.querySelector('.el-tabs__nav-prev')
871
+ const arrowRight = tabsEl.querySelector('.el-tabs__nav-next')
872
+
873
+ if (arrowLeft) {
874
+ // 1. 移除所有已有事件监听(保险)
875
+ const newArrowLeft = arrowLeft.cloneNode(true)
876
+ arrowLeft.parentNode.replaceChild(newArrowLeft, arrowLeft)
877
+ // 2. 添加自定义事件,彻底阻断原生行为
878
+ newArrowLeft.addEventListener('click', handleArrowLeft, { capture: true })
879
+ newArrowLeft.addEventListener('mousedown', (e) => e.preventDefault(), { capture: true })
880
+ arrowLeft.addEventListener('click', handleArrowLeft)
881
+ }
882
+ if (arrowRight) {
883
+ // 1. 移除所有已有事件监听(保险)
884
+ const newArrowRight = arrowRight.cloneNode(true)
885
+ arrowRight.parentNode.replaceChild(newArrowRight, arrowRight)
886
+ // 2. 添加自定义事件,彻底阻断原生行为
887
+ newArrowRight.addEventListener('click', handleArrowRight, { capture: true })
888
+ newArrowRight.addEventListener('mousedown', (e) => e.preventDefault(), { capture: true })
889
+ }
890
+ }
891
+ }
892
+ function handleArrowLeft(e) {
893
+ // 彻底阻断原生事件传播和默认行为
894
+ e.stopImmediatePropagation()
895
+ e.stopPropagation()
896
+ e.preventDefault()
897
+ scrollTabs('left')
898
+ }
899
+ function handleArrowRight(e) {
900
+ // 彻底阻断原生事件传播和默认行为
901
+ e.stopImmediatePropagation()
902
+ e.stopPropagation()
903
+ e.preventDefault()
904
+ scrollTabs('right')
905
+ }
906
+ function scrollTabs(direction) {
907
+ const navWrapScroll = document.querySelector('.el-tabs__nav-scroll') as HTMLElement
908
+ const navWrap = document.querySelector('.el-tabs__nav') as HTMLElement
909
+ if (!navWrapScroll || !navWrap) return
910
+ const allTabs = Array.from(navWrap.querySelectorAll('.el-tabs__item')) as HTMLElement[]
911
+ if (!allTabs.length) return
912
+ // 找到当前第一个可见的标签索引
913
+ const firstVisibleIndex = getFirstVisibleTabIndex(allTabs)
914
+ if (direction == 'left') {
915
+ if (translateX.value >= 0) {
916
+ translateX.value = 0
917
+ setTimeout(() => {
918
+ navWrap.style.transition = 'transform .3s ease-out'
919
+ navWrap.style.transform = `translateX(${translateX.value}px)`
920
+ }, 50);
921
+ return
922
+ }
923
+
924
+ // 找到当前第一个可见的标签索引
925
+ const firstVisibleIndex = getFirstVisibleTabIndex(allTabs)
926
+ // 计算上一个标签的宽度
927
+ const prevTabWidth = getFirstVisibleTabIndex(allTabs) > 0 ? getTabWidth(allTabs, firstVisibleIndex - 1) : 0
928
+ if (prevTabWidth == 0) {
929
+ translateX.value = 0
930
+ }
931
+ else {
932
+ // 更新偏移量(向右平移)
933
+ translateX.value += prevTabWidth
934
+ }
935
+
936
+ setTimeout(() => {
937
+ navWrap.style.transition = 'transform .3s ease-out'
938
+ navWrap.style.transform = `translateX(${translateX.value}px)`
939
+ }, 50);
940
+ }
941
+ else if (direction == 'right') {
942
+
943
+ // 判断最后一个标签是否已靠右,若是则禁止滚动
944
+ if (isLastTabAlignedRight(navWrapScroll, navWrap)) {
945
+ return
946
+ }
947
+
948
+ // 计算下一个标签的宽度
949
+ const nextTabWidth = getTabWidth(allTabs, firstVisibleIndex)
950
+ // 更新偏移量(向左平移)
951
+ translateX.value -= nextTabWidth
952
+ setTimeout(() => {
953
+ navWrap.style.transition = 'transform .3s ease-out'
954
+ navWrap.style.transform = `translateX(${translateX.value}px)`
955
+ }, 50);
956
+ }
957
+ }
958
+ // 获取当前第一个可见的标签索引
959
+ const getFirstVisibleTabIndex = (allTabs) => {
960
+ const scrollLeft = Math.abs(translateX.value)
961
+ let sumWidth = 0
962
+ for (let i = 0; i < allTabs.length; i++) {
963
+ sumWidth += getTabWidth(allTabs, i)
964
+ if (sumWidth > scrollLeft) {
965
+ return i
966
+ }
967
+ }
968
+ return 0
969
+ }
970
+ // 计算单个标签宽度(包含间距)
971
+ const getTabWidth = (allTabs, index) => {
972
+ if (!allTabs[index]) return 0
973
+ const tab = allTabs[index]
974
+ // 获取标签的实际宽度(包含内边距、边框)
975
+ const width = tab.offsetWidth
976
+ // 获取标签的左右外边距(ElementPlus 默认有间距)
977
+ const style = window.getComputedStyle(tab)
978
+ const marginLeft = parseFloat(style.marginLeft) || 0
979
+ const marginRight = parseFloat(style.marginRight) || 0
980
+ return width + marginLeft + marginRight
981
+ }
982
+ // 检查最后一个标签是否已靠最右侧
983
+ const isLastTabAlignedRight = (navWrapScroll, navWrap) => {
984
+
985
+ // 滚动容器的可视宽度
986
+ const containerWidth = navWrapScroll.clientWidth
987
+ // 所有标签的总宽度
988
+ const totalTabsWidth = navWrap.scrollWidth
989
+ // 当前向左偏移的总距离(绝对值)
990
+ const currentOffset = Math.abs(translateX.value)
991
+
992
+ // 最后一个标签靠右的判定条件:偏移量 + 容器宽度 ≥ 总标签宽度
993
+ // 预留2px误差,避免因像素精度问题导致判断失误
994
+ return (currentOffset + containerWidth) >= (totalTabsWidth - 2)
739
995
  }
740
996
 
741
997
  </script>