centaline-data-driven-v3 0.1.58 → 0.1.60

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.58",
3
+ "version": "0.1.60",
4
4
  "private": false,
5
5
  "description": "centaline-data-driven-v3",
6
6
  "main": "dist/centaline-data-driven-v3.umd.js",
@@ -52,18 +52,28 @@
52
52
  <span>操作</span>
53
53
  </th>
54
54
  <template v-for="(column, i) in model.rows[0].field" :key="i">
55
- <th v-if="column.show" class="ct-th" :rowspan="column.rowspan"
56
- :colspan="column.colspan">
55
+ <th v-if="column.show" class="ct-th" :class="column.sortAction"
56
+ :rowspan="column.rowspan" :colspan="column.colspan">
57
57
  <span>{{ column.controlLabel }}</span>
58
+ <span class="caret-wrapper" v-if="column.autoSearch"
59
+ @click="toSort($event, column)"
60
+ @contextmenu.prevent="clearSort($event, column)">
61
+ <i class="sort-caret ascending"
62
+ @click.left="toSort($event, column, 'asc')"
63
+ @contextmenu.prevent="clearSort($event, column)"></i>
64
+ <i class="sort-caret descending"
65
+ @click.left="toSort($event, column, 'desc')"
66
+ @contextmenu.prevent="clearSort($event, column)"></i>
67
+ </span>
58
68
  </th>
59
69
  </template>
60
70
  </tr>
61
71
  </thead>
62
72
  <!--表体-->
63
73
  <tbody>
64
- <template v-if="model.tableData.length > 0">
65
- <tr v-for="(row, rowindex) in model.tableData" :key="rowindex"
66
- :ref="'rows.' + rowindex" class="ct-tr">
74
+ <template v-if="renderList.length > 0">
75
+ <tr v-for="(row, rowindex) in renderList" :key="rowindex" :ref="'rows.' + rowindex"
76
+ class="ct-tr">
67
77
  <template
68
78
  v-if="model.rows[0].edit || model.rows[0].delete || (model.buttons.length > 0 && model.buttonsShow.length > 0)">
69
79
  <td class="ct-td td-edit" align="center" valign="middle">
@@ -74,7 +84,7 @@
74
84
 
75
85
  <van-image v-if="row.delete && !row.isSet"
76
86
  style="cursor: pointer;margin-right: 5px;" width="20" height="20"
77
- @click="deleteRow(rowindex, row.$sourceIndex)"
87
+ @click="deleteRow(row.$sourceIndex)"
78
88
  :src="util.getAssetsImage('btn_del.png')" />
79
89
  </td>
80
90
  <template v-for="(b, vi) in model.buttons" :key="vi">
@@ -157,6 +167,8 @@ const Fields = ref()
157
167
  const colspansum = ref(0)
158
168
  const sumsList = ref([])
159
169
  const itemKey = ref(0)
170
+ const renderList = ref([])
171
+ const sortInfo = ref(null)
160
172
  const dragOptions = {
161
173
  animation: 200,
162
174
  group: model.value.fieldName1,
@@ -193,6 +205,26 @@ function Init() {
193
205
  colspansum.value++;
194
206
  }
195
207
  }
208
+ refreshRenderList()
209
+
210
+ }
211
+ function refreshRenderList() {
212
+ // 拷贝一份全新数组,防止sort原地修改原始数据
213
+ const copyArr = [...model.value.tableData]
214
+ if (sortInfo.value) {
215
+ const { prop, order } = sortInfo.value
216
+ copyArr.sort((a, b) => {
217
+ const aValue = getLableShow(a[prop])
218
+ const bValue = getLableShow(b[prop])
219
+ if (typeof a === 'number') {
220
+ return order === 'asc' ? aValue - bValue : bValue - aValue
221
+ } else {
222
+ const res = aValue.localeCompare(bValue, 'zh-CN')
223
+ return order === 'asc' ? res : -res
224
+ }
225
+ })
226
+ }
227
+ renderList.value = copyArr
196
228
  }
197
229
  function popupSearchListHandle(field) {
198
230
  if (field.isSearchPageWithList) {
@@ -212,18 +244,24 @@ function addRow() {
212
244
  return
213
245
  }
214
246
  FormList.addRow(model.value);
247
+ refreshRenderList()
215
248
  }
216
249
  //修改
217
250
  function editRow(index) {
218
251
  FormList.editRow(index, model.value);
252
+ refreshRenderList()
219
253
  }
220
254
  //删除
221
- function deleteRow(index, sourceIndex) {
255
+ function deleteRow(sourceIndex) {
222
256
  FormList.deleteRow(sourceIndex, model.value, () => {
223
- model.value.tableData.splice(index, 1);
257
+ const targetIndex = model.value.tableData.findIndex(v => v.$sourceIndex === sourceIndex)
258
+ if (targetIndex > -1) {
259
+ model.value.tableData.splice(targetIndex, 1)
260
+ }
224
261
  model.value.onChanged = model.value.formListChange;
225
262
  model.value.createText = '';
226
263
  model.value.disabled = false;
264
+ refreshRenderList()
227
265
  emit('change', model.value);
228
266
  });
229
267
  }
@@ -350,13 +388,18 @@ function getSummaries() {
350
388
  function rolRouterCellClickHandler(routerKey, rowindex, sourceIndex) {
351
389
  if (routerKey == 'edit') {
352
390
  FormList.editRow(sourceIndex, model.value);
391
+ refreshRenderList()
353
392
  }
354
393
  else if (routerKey == 'delete') {
355
394
  FormList.deleteRow(sourceIndex, model.value, () => {
356
- model.value.tableData.splice(rowindex, 1);
395
+ const targetIndex = model.value.tableData.findIndex(v => v.$sourceIndex === sourceIndex)
396
+ if (targetIndex > -1) {
397
+ model.value.tableData.splice(targetIndex, 1)
398
+ }
357
399
  model.value.onChanged = model.value.formListChange;
358
400
  model.value.createText = '';
359
401
  model.value.disabled = false;
402
+ refreshRenderList()
360
403
  emit('change', model.value);
361
404
  });
362
405
  } else {
@@ -412,6 +455,107 @@ function delRow(row) {
412
455
  function deleteAll() {
413
456
  FormList.deleteAll(model.value);
414
457
  }
458
+ function toSort(ev, col, action) {
459
+ model.value.rows[0].field.forEach((v1) => {
460
+ v1.sortAction = "";
461
+ });
462
+ if (sortInfo.value && sortInfo.value.prop == col.fieldName1 && sortInfo.value.order == action) {
463
+ sortInfo.value = null
464
+ }
465
+ else {
466
+ sortInfo.value = {
467
+ prop: col.fieldName1,
468
+ order: action
469
+ }
470
+ col.sortAction = action;
471
+ }
472
+ refreshRenderList()
473
+ ev.cancelBubble = true;
474
+ ev.stopPropagation();
475
+ }
476
+ function getLableShow(data) {
477
+ let labelShow = data.name1;
478
+ if (!labelShow) {
479
+ labelShow = data.code1;
480
+ }
481
+ if (labelShow && data.unitName1) {
482
+ labelShow = labelShow + data.unitName1;
483
+ }
484
+ switch (data.controlType) {
485
+ case Enum.ControlType.Label:
486
+ if (data.value) {
487
+ labelShow = data.value;
488
+ }
489
+ break;
490
+ case Enum.ControlType.MultiSelectNoSearch:
491
+ case Enum.ControlType.MultiSelectWithSearch:
492
+ labelShow = '';
493
+ if (data.code1) {
494
+ JSON.parse(data.code1).forEach((op) => {
495
+ if (op.flagDeleted !== 1) {
496
+ if (op.flagDeleted != undefined && op.flagDeleted == true) {
497
+ }
498
+ else {
499
+ labelShow += op['name'] + ' ';
500
+ }
501
+ }
502
+ });
503
+ }
504
+ break;
505
+ case Enum.ControlType.PhoneNumberText:
506
+ case Enum.ControlType.EmailText:
507
+ if (data.code2) {
508
+ labelShow = data.name1 + data.code2;
509
+ }
510
+ break;
511
+ case Enum.ControlType.NumericRange:
512
+ if (data.code1 && data.code2) {
513
+ }
514
+ else if (data.code1) {
515
+ labelShow = data.code1 + (data.unitName1 ? data.unitName1 : '') + '以上';
516
+ }
517
+ else if (data.code2) {
518
+ labelShow = data.code2 + (data.unitName1 ? data.unitName1 : '') + '以下';
519
+ }
520
+ break;
521
+ case Enum.ControlType.DateTimeRange:
522
+ case Enum.ControlType.DateRange:
523
+ if (data.code1 && data.code2) {
524
+ }
525
+ else if (data.code1) {
526
+ labelShow = data.code1 + (data.unitName1 ? data.unitName1 : '') + '以后';
527
+ }
528
+ else if (data.code2) {
529
+ labelShow = data.code2 + (data.unitName1 ? data.unitName1 : '') + '以前';
530
+ }
531
+ break;
532
+ case Enum.ControlType.Time:
533
+ case Enum.ControlType.TimeRange:
534
+ if (data.flagrange) {
535
+ if (data.code1 && data.code2) {
536
+ labelShow = data.code1 + '-' + data.code2;
537
+ }
538
+ }
539
+ else {
540
+ if (data.code1) {
541
+ labelShow = data.code1
542
+ }
543
+ }
544
+ break;
545
+ case Enum.ControlType.CheckBoxList:
546
+ labelShow = data.getCheckedName();;
547
+ break;
548
+ case Enum.ControlType.Switch:
549
+ labelShow = data.value ? '开启' : '关闭';
550
+ break;
551
+ case Enum.ControlType.CheckBox:
552
+ labelShow = data.value ? '是' : '否';
553
+ break;
554
+ default:
555
+ break;
556
+ }
557
+ return labelShow;
558
+ }
415
559
  defineExpose({
416
560
  model
417
561
  })
@@ -494,4 +638,45 @@ th.th-edit {
494
638
  z-index: 1;
495
639
  background-color: #ffffff;
496
640
  }
641
+
642
+ .ct-th>.caret-wrapper {
643
+ display: inline-flex;
644
+ flex-direction: column;
645
+ align-items: center;
646
+ height: 34px;
647
+ width: 10px;
648
+ vertical-align: middle;
649
+ cursor: pointer;
650
+ overflow: initial;
651
+ position: relative;
652
+ }
653
+
654
+ .ct-th>.caret-wrapper>.sort-caret {
655
+ width: 0;
656
+ height: 0;
657
+ border: 5px solid transparent;
658
+ position: absolute;
659
+ left: 7px;
660
+ }
661
+
662
+ .ct-th>.caret-wrapper>.sort-caret.ascending {
663
+ border-bottom-color: var(--centalinePlaceholder);
664
+ top: 5px;
665
+ }
666
+
667
+ .ct-th>.caret-wrapper>.sort-caret.descending {
668
+ border-top-color: var(--centalinePlaceholder);
669
+ bottom: 7px;
670
+ }
671
+
672
+
673
+ .ct-th.asc>.caret-wrapper>.sort-caret.ascending {
674
+ border-bottom-color: #409eff;
675
+ top: 5px;
676
+ }
677
+
678
+ .ct-th.desc>.caret-wrapper>.sort-caret.descending {
679
+ border-top-color: #409eff;
680
+ bottom: 7px;
681
+ }
497
682
  </style>
@@ -166,7 +166,7 @@
166
166
  </template>
167
167
  </template>
168
168
  <script setup lang="ts">
169
- import { ref, nextTick, computed, onBeforeUnmount } from 'vue'
169
+ import { ref, nextTick, computed, onBeforeUnmount,inject ,watch } from 'vue'
170
170
  import { ElMessage, UploadProps } from 'element-plus'
171
171
  import common from '../../utils/common'
172
172
  import { changeHandler } from '../../utils/mixins';
@@ -623,12 +623,31 @@ function addClass(el, className) {
623
623
  newClassName.push(className);
624
624
  el.className = newClassName.join(" ");
625
625
  }
626
+ const injectedViewerFileToggle = inject('onViewerFileToggle')
626
627
 
627
628
 
628
629
  //预览文件
629
630
  function viewerfile(file) {
630
- File.viewerfile(file, model.value)
631
+
632
+ File.viewerfile(file, model.value, function (MediaAlbum, groupIndex, index) {
633
+ injectedViewerFileToggle(true, MediaAlbum, groupIndex, index);
634
+ })
631
635
  }
636
+ const ChangeFileList = inject('ChangeFileList')
637
+
638
+ watch(
639
+ () => model.value?.fileList, // 直接监听 fileList 数组
640
+ (newFileList) => {
641
+ nextTick(() => {
642
+ if (model.value?.mediaViewPageType === 3) {
643
+ const { album } = File.buildMediaAlbum(model.value);
644
+ const MediaAlbum = [album];
645
+ ChangeFileList(MediaAlbum, 0, 0);
646
+ }
647
+ });
648
+ },
649
+ { deep: true } // 深度监听,以便捕获数组内元素属性变化(与原 JSON.stringify 效果一致)
650
+ );
632
651
 
633
652
  function PasteUpload(event) {
634
653
  if (!model.value.locked && !model.value.disableUpload) {
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div ref="refForm" v-loading="loading"
3
3
  :style="{ width: pageWidth ? pageWidth + 'px' : '100%', margin: 'auto', 'min-height': minHeight }">
4
- <div style="display: flex; width: 100%;">
4
+ <div style="display: flex; width: 100%;" ref="leftContainerRef">
5
5
  <div style="flex: 1; min-width: 0;">
6
6
  <div v-if="model !== null && !loading" class="ct-form" :class="{ 'domDisabled': model.pageDisabled }"
7
7
  :style="{ margin: openType != 'tree' ? '10px' : '0px' }">
@@ -194,9 +194,22 @@
194
194
  @hideAI="AIToggle" :isVisible="showAI" :tablewidth="model.aiAttr.width">
195
195
 
196
196
  </AIChat>
197
+
198
+
197
199
  </div>
198
200
  </div>
199
201
  </template>
202
+ <div :style="{ flex: ' 0 0 ' + 650 + 'px', position: 'sticky', top: '0', 'box-shadow': '-10px 0 5px -9px rgba(0, 0, 0, 0.3)' }"
203
+ v-if="showViewerFile">
204
+ <div
205
+ :style="{ position: 'sticky', top: '0', height: (dialogHeight) + 'px', overflow: 'hidden', 'border-bottom-right-radius': '4px' }">
206
+ <ViewerFile :MediaAlbum="media.MediaAlbum" :groupIndex="media.groupIndex" :index="media.index"
207
+ :key="media.random" :flagLeft="media.flagLeft" @closeViewerFile="ViewerFileToggle(false)"
208
+ @ViewerFilechangeIndex="ViewerFilechangeIndex">
209
+ </ViewerFile>
210
+
211
+ </div>
212
+ </div>
200
213
 
201
214
  </div>
202
215
  <template v-if="model?.aiAttr?.showAI && !showAI">
@@ -205,13 +218,14 @@
205
218
  :alt="model?.aiRouter.controlLabel" :title="model?.aiRouter.controlLabel">
206
219
  </div>
207
220
  </template>
221
+
208
222
  <div style="min-height:200px" v-if="loading"></div>
209
223
  <iframe :src="downloadUrl" style="height:0px;width:0px;border-width: 0px;display: none;"> </iframe>
210
224
 
211
225
  </div>
212
226
  </template>
213
227
  <script lang="ts" setup>
214
- import { ref, nextTick, onUpdated, onDeactivated, onMounted, onBeforeUnmount,provide } from 'vue'
228
+ import { ref, nextTick, onUpdated, onDeactivated, onMounted, onBeforeUnmount, provide } from 'vue'
215
229
  import { RouterClickHandler } from '../../utils/mixins';
216
230
  import common from '../../utils/common'
217
231
  import Form from '../../loader/src/Form'
@@ -220,7 +234,9 @@ import { useRouter } from 'vue-router';
220
234
  import util from '../../utils/pub-use'
221
235
  import Enum from '../../utils/Enum';
222
236
  import AIChat from '../web/AIChat.vue';
223
- const emit = defineEmits(['loaded', 'failLoad', 'submit', 'AIToggle'])
237
+ import ViewerFile from '../web/ViewerFile.vue';
238
+
239
+ const emit = defineEmits(['loaded', 'failLoad', 'submit', 'ToggleWdth'])
224
240
  const props = defineProps({
225
241
  api: String,
226
242
  vmodel: Object,
@@ -283,6 +299,14 @@ const props = defineProps({
283
299
  dialoWidth: Number,
284
300
  listHeight: Number,
285
301
  })
302
+ const media = ref({
303
+ MediaAlbum: [],
304
+ groupIndex: 0,
305
+ index: 0,
306
+ flagLeft: false,
307
+ random: Math.random()
308
+ })
309
+ const showViewerFile = ref(false)
286
310
  const itemKey = ref(1)
287
311
  const router = useRouter()
288
312
  const loading = ref(true)
@@ -292,6 +316,7 @@ const isScroll = ref(false)
292
316
  const isWebScroll = ref(false)
293
317
  const Fields = ref()
294
318
  const refForm = ref()
319
+
295
320
  const downloadUrl = ref('')
296
321
  const minHeight = ref('auto')
297
322
  const showAI = ref(false);
@@ -366,11 +391,11 @@ function init() {
366
391
  }
367
392
  if (typeof props.api !== 'undefined') {
368
393
  //根据接口获取数据
369
- Form.loadFormApi(props.api, load, props.apiParam, failLoad, false,props.appRootUrl);
394
+ Form.loadFormApi(props.api, load, props.apiParam, failLoad, false, props.appRootUrl);
370
395
  }
371
396
  else if (typeof props.source !== 'undefined') {
372
397
  //根据modelFrom获取数据
373
- load(Form.loadFromModel(props.source,false,props.appRootUrl));
398
+ load(Form.loadFromModel(props.source, false, props.appRootUrl));
374
399
  }
375
400
  else if (props.vmodel) {
376
401
  load(props.vmodel);
@@ -764,20 +789,223 @@ function setCss() {
764
789
 
765
790
  function AIToggle() {
766
791
  showAI.value = !showAI.value;
792
+ if (showAI.value) {
793
+ if (showViewerFile.value) {
794
+ ViewerFileToggle(false, [], 0, 0);
795
+ }
796
+ }
797
+
767
798
  let width = model.value.aiAttr.width;
768
799
  dialogHeight.value = (props.dialogHeight || (window.innerHeight - 60));
769
- emit('AIToggle', (showAI.value ? width : -width));
800
+ emit('ToggleWdth', (showAI.value ? width : -width));
801
+ }
802
+ const getVisibleDimensions = () => {
803
+ if (!refForm.value) return { width: 0, height: 0 };
804
+ const rect = refForm.value.getBoundingClientRect();
805
+ const viewWidth = window.innerWidth || document.documentElement.clientWidth;
806
+ const viewHeight = window.innerHeight || document.documentElement.clientHeight;
807
+
808
+ const visibleTop = Math.max(0, rect.top);
809
+ const visibleBottom = Math.min(viewHeight, rect.bottom);
810
+ const visibleLeft = Math.max(0, rect.left);
811
+ const visibleRight = Math.min(viewWidth, rect.right);
812
+
813
+ return {
814
+ width: Math.max(0, visibleRight - visibleLeft),
815
+ height: Math.max(0, visibleBottom - visibleTop)-60
816
+ };
817
+ };
818
+ function ViewerFileToggle(isOpen, mediaAlbum, groupIndex, index) {
819
+ if (isOpen) {
820
+ media.value = { MediaAlbum: mediaAlbum, groupIndex, index, flagLeft: false, random: Math.random() };
821
+ if (showAI.value) {
822
+ AIToggle();
823
+ }
824
+ }
825
+ if (showViewerFile.value != isOpen) {
826
+ showViewerFile.value = isOpen;
827
+ let width = 650;
828
+ dialogHeight.value = getVisibleDimensions().height || parseInt(minHeight.value) || window.innerHeight - 60;
829
+ emit('ToggleWdth', (showViewerFile.value ? width : -width));
830
+ }
831
+ }
832
+ provide('onViewerFileToggle', ViewerFileToggle)
833
+ function ViewerFilechangeIndex({ groupIndex, index }) {
834
+ if (media.value) {
835
+ media.value.groupIndex = groupIndex;
836
+ media.value.index = index;
837
+ }
838
+ }
839
+
840
+ function ChangeFileList(mediaAlbum, groupIndex, index) {
841
+ if (!showViewerFile.value) {
842
+ return;
843
+ } else {
844
+ if (mediaAlbum[0].medias.length <= 0) {
845
+ ViewerFileToggle(false);
846
+ return;
847
+ }
848
+ }
849
+ const oldMedia = media.value;
850
+ let newGroupIndex, newIndex, shouldShow;
851
+
852
+ // 情况1:没有旧记录,直接用传入索引,无效则找第一张
853
+ if (!oldMedia || !oldMedia.MediaAlbum) {
854
+ if (validatePosition(mediaAlbum, groupIndex, index)) {
855
+ newGroupIndex = groupIndex;
856
+ newIndex = index;
857
+ shouldShow = true;
858
+ } else {
859
+ const first = findFirstValidPosition(mediaAlbum);
860
+ if (first) {
861
+ newGroupIndex = first.groupIndex;
862
+ newIndex = first.index;
863
+ shouldShow = true;
864
+ } else {
865
+ // 整个相册无图,直接关闭查看器,但依然要更新 MediaAlbum
866
+ newGroupIndex = 0;
867
+ newIndex = 0;
868
+ shouldShow = false;
869
+ }
870
+ }
871
+ } else {
872
+ // 取出旧图片
873
+ const oldAlbumList = oldMedia.MediaAlbum;
874
+ const oldGroup = oldAlbumList && oldAlbumList[oldMedia.groupIndex];
875
+ const oldFile = oldGroup && oldGroup.medias && oldGroup.medias[oldMedia.index];
876
+
877
+ if (oldFile) {
878
+ // 尝试在新相册中定位旧图
879
+ const found = findFileInAlbum(mediaAlbum, oldFile);
880
+ if (found) {
881
+ // 找到了,移动到新位置
882
+ newGroupIndex = found.groupIndex;
883
+ newIndex = found.index;
884
+ shouldShow = true;
885
+ } else {
886
+ // 旧图被删,回退
887
+ const fallback = fallbackPosition(mediaAlbum, oldMedia.groupIndex, oldMedia.index);
888
+ newGroupIndex = fallback.groupIndex;
889
+ newIndex = fallback.index;
890
+ shouldShow = fallback.show;
891
+ }
892
+ } else {
893
+ // 旧记录无效,同样回退
894
+ const fallback = fallbackPosition(mediaAlbum, oldMedia.groupIndex, oldMedia.index);
895
+ newGroupIndex = fallback.groupIndex;
896
+ newIndex = fallback.index;
897
+ shouldShow = fallback.show;
898
+ }
899
+ }
900
+ ViewerFileToggle(showViewerFile.value, mediaAlbum, newGroupIndex, newIndex);
770
901
  }
902
+ provide('ChangeFileList', ChangeFileList)
771
903
 
904
+ function validatePosition(albumList, gIdx, idx) {
905
+ if (!albumList || gIdx < 0 || gIdx >= albumList.length) {
906
+ return false;
907
+ }
908
+ var group = albumList[gIdx];
909
+ var medias = group && group.medias;
910
+ return medias && idx >= 0 && idx < medias.length;
911
+ }
912
+ function findFirstValidPosition(albumList) {
913
+ if (!albumList) {
914
+ return null;
915
+ }
916
+ for (var g = 0; g < albumList.length; g++) {
917
+ var group = albumList[g];
918
+ var medias = group && group.medias;
919
+ if (medias && medias.length > 0) {
920
+ return { groupIndex: g, index: 0 };
921
+ }
922
+ }
923
+ return null;
924
+ }
925
+ function findFileInAlbum(albumList, targetFile) {
926
+ if (!albumList) {
927
+ return null;
928
+ }
929
+ // 先按引用查找
930
+ for (var g = 0; g < albumList.length; g++) {
931
+ var medias = albumList[g].medias;
932
+ if (!medias) continue;
933
+ for (var i = 0; i < medias.length; i++) {
934
+ if (medias[i] === targetFile) {
935
+ return { groupIndex: g, index: i };
936
+ }
937
+ }
938
+ }
939
+ // 如果引用匹配不到,尝试用 id(如果存在)
940
+ if (targetFile && targetFile.id != null) {
941
+ for (var g2 = 0; g2 < albumList.length; g2++) {
942
+ var medias2 = albumList[g2].medias;
943
+ if (!medias2) continue;
944
+ for (var i2 = 0; i2 < medias2.length; i2++) {
945
+ if (medias2[i2].id === targetFile.id) {
946
+ return { groupIndex: g2, index: i2 };
947
+ }
948
+ }
949
+ }
950
+ }
951
+ return null;
952
+ }
953
+ function fallbackPosition(albumList, prefGroup, prefIndex) {
954
+ if (!albumList || albumList.length === 0) {
955
+ return { groupIndex: 0, index: 0, show: false };
956
+ }
957
+
958
+ // 确保参考相册索引在有效范围内
959
+ var gIdx = Math.min(prefGroup, albumList.length - 1);
960
+ var group = albumList[gIdx];
961
+ var medias = group && group.medias ? group.medias : [];
962
+
963
+ // 1. 同相册内回退
964
+ if (medias.length > 0) {
965
+ var newIdx;
966
+ if (prefIndex > 0 && prefIndex <= medias.length) {
967
+ // 优先上一张
968
+ newIdx = prefIndex - 1;
969
+ } else if (prefIndex === 0 && medias.length > 1) {
970
+ // 第一张被删了,取下一张(索引 1)
971
+ newIdx = 1;
972
+ } else {
973
+ // 只剩这一张,还是它
974
+ newIdx = 0;
975
+ }
976
+ return { groupIndex: gIdx, index: newIdx, show: true };
977
+ }
978
+
979
+ // 2. 向前找非空相册,取最后一张
980
+ for (var g = gIdx - 1; g >= 0; g--) {
981
+ var m = albumList[g].medias || [];
982
+ if (m.length > 0) {
983
+ return { groupIndex: g, index: m.length - 1, show: true };
984
+ }
985
+ }
986
+
987
+ // 3. 向后找非空相册,取第一张
988
+ for (var g2 = gIdx + 1; g2 < albumList.length; g2++) {
989
+ var m2 = albumList[g2].medias || [];
990
+ if (m2.length > 0) {
991
+ return { groupIndex: g2, index: 0, show: true };
992
+ }
993
+ }
994
+
995
+ // 整个相册没有任何图片了
996
+ return { groupIndex: 0, index: 0, show: false };
997
+ }
772
998
 
773
999
  function buttonsWidth() {
774
1000
  let rtn = '100%';
775
1001
  if (showAI.value) {
776
- rtn = props.dialoWidth - model.value.aiAttr.width - 4 + 'px';
1002
+ rtn = (props.dialoWidth||getVisibleDimensions().width) - model.value.aiAttr.width - 4 + 'px';
1003
+ }
1004
+ else if (showViewerFile.value) {
1005
+ rtn = (props.dialoWidth||getVisibleDimensions().width) - 650 - 4 + 'px';
777
1006
  }
778
1007
  else if (props.pageWidth) {
779
1008
  rtn = props.pageWidth + 'px';
780
-
781
1009
  }
782
1010
 
783
1011
  return rtn;