af-mobile-client-vue3 1.3.23 → 1.3.25

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,7 +1,7 @@
1
1
  {
2
2
  "name": "af-mobile-client-vue3",
3
3
  "type": "module",
4
- "version": "1.3.23",
4
+ "version": "1.3.25",
5
5
  "packageManager": "pnpm@10.13.1",
6
6
  "description": "Vue + Vite component lib",
7
7
  "engines": {
@@ -8,8 +8,10 @@ import { executeStrFunctionByContext } from '@af-mobile-client-vue3/utils/runEva
8
8
  import LoadError from '@af-mobile-client-vue3/views/common/LoadError.vue'
9
9
  import {
10
10
  showConfirmDialog,
11
+ ActionSheet as VanActionSheet,
11
12
  BackTop as VanBackTop,
12
13
  Button as VanButton,
14
+ Checkbox as VanCheckbox,
13
15
  Col as VanCol,
14
16
  Icon as VanIcon,
15
17
  List as VanList,
@@ -33,6 +35,9 @@ const {
33
35
  customEdit = false,
34
36
  customDelete = false,
35
37
  hideAllActions = false,
38
+ // 多选相关配置
39
+ enableMultiSelect = false, // 是否启用多选功能
40
+ multiSelectActions = [], // 多选操作按钮配置
36
41
  } = defineProps<{
37
42
  configName?: string
38
43
  fixQueryForm?: object
@@ -49,6 +54,14 @@ const {
49
54
  customDelete?: boolean
50
55
  // 是否隐藏所有操作按钮
51
56
  hideAllActions?: boolean
57
+ // 多选相关配置
58
+ enableMultiSelect?: boolean // 是否启用多选功能
59
+ multiSelectActions?: Array<{
60
+ name: string
61
+ key?: string
62
+ color?: string
63
+ icon?: string
64
+ }> // 多选操作按钮配置
52
65
  }>()
53
66
 
54
67
  const emit = defineEmits<{
@@ -58,6 +71,9 @@ const emit = defineEmits<{
58
71
  (e: 'add'): void
59
72
  (e: string, item: any): void
60
73
  (e: 'updateCondition', params: any): void
74
+ // 多选相关事件
75
+ (e: 'multiSelectAction', action: string, selectedItems: any[], selectedItemsArray: any[]): void
76
+ (e: 'selectionChange', selectedItems: any[]): void
61
77
  }>()
62
78
 
63
79
  const userState = useUserStore().getLogin()
@@ -133,6 +149,12 @@ const finishedText = ref('加载完成')
133
149
  // 避免查询多次
134
150
  const isLastPage = ref(false)
135
151
 
152
+ // 多选相关状态
153
+ const isMultiSelectMode = ref(false) // 是否处于多选模式
154
+ const selectedItems = ref<Set<string>>(new Set()) // 选中的项目ID集合
155
+ const longPressTimer = ref<number | null>(null) // 长按定时器
156
+ const longPressDelay = 500 // 长按延迟时间(毫秒)
157
+
136
158
  // 条件参数(查询框)
137
159
  const conditionParams = ref(undefined)
138
160
 
@@ -149,6 +171,10 @@ const buttonPermissions = ref([])
149
171
  // 默认参数
150
172
  const defaultParams = {}
151
173
 
174
+ // ActionSheet相关状态
175
+ const showActionSheet = ref(false)
176
+ const actionSheetActions = ref([])
177
+
152
178
  const slots = useSlots()
153
179
 
154
180
  // 当前组件实例(不推荐使用,可能会在后续的版本更迭中调整,暂时用来绑定函数的上下文)
@@ -546,6 +572,144 @@ defineExpose({
546
572
  updateConditionAndRefresh,
547
573
  onRefresh,
548
574
  })
575
+
576
+ // 多选相关计算属性
577
+ const selectedItemsArray = computed(() => {
578
+ return list.value.filter(item => selectedItems.value.has(item[idKey]))
579
+ })
580
+
581
+ const hasSelectedItems = computed(() => {
582
+ return selectedItems.value.size > 0
583
+ })
584
+
585
+ // 多选相关方法
586
+ function startLongPress(item: any) {
587
+ if (!enableMultiSelect)
588
+ return
589
+
590
+ longPressTimer.value = window.setTimeout(() => {
591
+ enterMultiSelectMode(item)
592
+ }, longPressDelay)
593
+ }
594
+
595
+ function cancelLongPress() {
596
+ if (longPressTimer.value) {
597
+ clearTimeout(longPressTimer.value)
598
+ longPressTimer.value = null
599
+ }
600
+ }
601
+
602
+ function enterMultiSelectMode(item: any) {
603
+ isMultiSelectMode.value = true
604
+ selectedItems.value.clear()
605
+ const itemId = item[idKey]
606
+ selectedItems.value.add(itemId)
607
+ updateActionSheetActions()
608
+ }
609
+
610
+ function exitMultiSelectMode() {
611
+ isMultiSelectMode.value = false
612
+ selectedItems.value.clear()
613
+ showActionSheet.value = false
614
+ }
615
+
616
+ function toggleItemSelection(item: any) {
617
+ if (!isMultiSelectMode.value)
618
+ return
619
+
620
+ const itemId = item[idKey]
621
+ console.log('Toggle selection:', { itemId, currentSelectedItems: Array.from(selectedItems.value) })
622
+
623
+ if (selectedItems.value.has(itemId)) {
624
+ selectedItems.value.delete(itemId)
625
+ }
626
+ else {
627
+ selectedItems.value.add(itemId)
628
+ }
629
+
630
+ console.log('After toggle:', { selectedItems: Array.from(selectedItems.value) })
631
+
632
+ // 更新ActionSheet状态,但不退出多选模式
633
+ updateActionSheetActions()
634
+
635
+ emit('selectionChange', selectedItemsArray.value)
636
+ }
637
+
638
+ function updateActionSheetActions() {
639
+ if (multiSelectActions.length > 0) {
640
+ actionSheetActions.value = multiSelectActions.map(action => ({
641
+ name: action.name,
642
+ key: action.key || action.name,
643
+ color: action.color || '#000000',
644
+ icon: action.icon || 'records-o',
645
+ }))
646
+ }
647
+ else {
648
+ // 默认操作按钮
649
+ actionSheetActions.value = [
650
+ { name: '批量操作', key: 'batchOperation', color: '#000000', icon: 'records-o' },
651
+ ]
652
+ }
653
+
654
+ if (hasSelectedItems.value) {
655
+ showActionSheet.value = true
656
+ }
657
+ }
658
+
659
+ function handleActionSheetSelect(action: any) {
660
+ showActionSheet.value = false
661
+ emit('multiSelectAction', action, Array.from(selectedItems.value), selectedItemsArray.value)
662
+
663
+ // 执行操作后退出多选模式
664
+ exitMultiSelectMode()
665
+ }
666
+
667
+ function handleCardClick(item: any, event: any) {
668
+ // 如果处于多选模式,阻止默认的详情跳转
669
+ if (isMultiSelectMode.value) {
670
+ event.preventDefault()
671
+ event.stopPropagation()
672
+ return
673
+ }
674
+
675
+ // 正常点击跳转到详情
676
+ emit('toDetail', item)
677
+ }
678
+
679
+ function handleTitleClick(item: any, event: any) {
680
+ event.stopPropagation()
681
+
682
+ if (isMultiSelectMode.value) {
683
+ // 多选模式下,点击标题切换选中状态
684
+ toggleItemSelection(item)
685
+ }
686
+ else {
687
+ // 非多选模式下,正常跳转到详情
688
+ emit('toDetail', item)
689
+ }
690
+ }
691
+
692
+ function handleCheckboxChange(item: any, checked: boolean) {
693
+ if (!isMultiSelectMode.value)
694
+ return
695
+
696
+ const itemId = item[idKey]
697
+ console.log('Checkbox change:', { itemId, checked, currentSelectedItems: Array.from(selectedItems.value) })
698
+
699
+ if (checked) {
700
+ selectedItems.value.add(itemId)
701
+ }
702
+ else {
703
+ selectedItems.value.delete(itemId)
704
+ }
705
+
706
+ console.log('After change:', { selectedItems: Array.from(selectedItems.value) })
707
+
708
+ // 更新ActionSheet状态,但不退出多选模式
709
+ updateActionSheetActions()
710
+
711
+ emit('selectionChange', selectedItemsArray.value)
712
+ }
549
713
  </script>
550
714
 
551
715
  <template>
@@ -563,6 +727,7 @@ defineExpose({
563
727
  <VanSearch
564
728
  v-model="searchValue"
565
729
  class="title-search"
730
+ :class="{ 'multi-select-mode': isMultiSelectMode }"
566
731
  clearable
567
732
  placeholder="综合查询框..."
568
733
  shape="round"
@@ -576,6 +741,7 @@ defineExpose({
576
741
  type="primary"
577
742
  size="small"
578
743
  class="add-action-btn"
744
+ :disabled="isMultiSelectMode"
579
745
  @click="addOption"
580
746
  />
581
747
  </VanCol>
@@ -604,6 +770,26 @@ defineExpose({
604
770
  </VanCol>
605
771
  </VanRow>
606
772
  <slot name="search-after" />
773
+
774
+ <!-- 多选功能提示 -->
775
+ <div v-if="enableMultiSelect && !isMultiSelectMode" class="multi-select-tip">
776
+ <VanIcon name="info-o" />
777
+ <span>长按卡片可进入多选模式</span>
778
+ </div>
779
+
780
+ <!-- 多选模式提示 -->
781
+ <div v-if="isMultiSelectMode" class="multi-select-tip">
782
+ <VanIcon name="info-o" />
783
+ <span>多选模式 - 已选择 {{ selectedItems.size }} 项</span>
784
+ <VanButton
785
+ size="small"
786
+ type="default"
787
+ @click="exitMultiSelectMode"
788
+ >
789
+ 退出
790
+ </VanButton>
791
+ </div>
792
+
607
793
  <div class="main">
608
794
  <VanPullRefresh v-model="refreshing" :success-text="finishedText" head-height="70" @refresh="onRefresh">
609
795
  <template v-if="!isError">
@@ -615,14 +801,35 @@ defineExpose({
615
801
  :immediate-check="isInitQuery"
616
802
  @load="onLoad"
617
803
  >
618
- <div v-for="(item, index) in list" :key="`card_${index}`" class="card_item_main">
619
- <VanRow gutter="20" class="card_item_header" align="center" @click="emit('toDetail', item)">
804
+ <div
805
+ v-for="(item, index) in list"
806
+ :key="`card_${index}`"
807
+ class="card_item_main"
808
+ :class="{ 'multi-select-mode': isMultiSelectMode }"
809
+ @touchstart="startLongPress(item)"
810
+ @touchend="cancelLongPress"
811
+ @touchcancel="cancelLongPress"
812
+ @mousedown="startLongPress(item)"
813
+ @mouseup="cancelLongPress"
814
+ @mouseleave="cancelLongPress"
815
+ >
816
+ <VanRow gutter="20" class="card_item_header" align="center" @click="handleCardClick(item, $event)">
620
817
  <VanCol :span="24">
621
- <div class="title-row">
818
+ <div class="title-row" :class="{ 'multi-select-title-row': isMultiSelectMode }">
819
+ <!-- 多选模式下的选择框 -->
820
+ <div v-if="isMultiSelectMode" class="selection-checkbox">
821
+ <VanCheckbox
822
+ :model-value="selectedItems.has(item[idKey])"
823
+ shape="square"
824
+ @update:model-value="(checked) => handleCheckboxChange(item, checked)"
825
+ />
826
+ </div>
622
827
  <div v-for="(column) in mainColumns" :key="`main_${column.dataIndex}`" class="main-title">
623
828
  <p
624
829
  class="card_item_title"
625
830
  :style="handleFunctionStyle(column.styleFunctionForValue, item)"
831
+ :class="{ 'selectable-title': isMultiSelectMode }"
832
+ @click="handleTitleClick(item, $event)"
626
833
  >
627
834
  {{ item[column.dataIndex] ?? '--' }}
628
835
  </p>
@@ -646,13 +853,14 @@ defineExpose({
646
853
  class="action-button"
647
854
  :icon="btn.btnIcon"
648
855
  size="small"
856
+ :disabled="isMultiSelectMode"
649
857
  @click.stop="handleButtonClick(btn, item)"
650
858
  />
651
859
  </div>
652
860
  </div>
653
861
  </VanCol>
654
862
  </VanRow>
655
- <VanRow gutter="20" class="card_item_details" @click="emit('toDetail', item)">
863
+ <VanRow gutter="20" class="card_item_details" @click="handleCardClick(item, $event)">
656
864
  <VanCol v-for="column of detailColumns" :key="`details_${column.dataIndex}`" :span="column.span">
657
865
  <p>
658
866
  {{ (column.showLabel === undefined || column.showLabel) ? `${column.title}: ` : '' }}
@@ -664,7 +872,7 @@ defineExpose({
664
872
  </p>
665
873
  </VanCol>
666
874
  </VanRow>
667
- <VanRow v-if="tagList.length > 0" gutter="20" class="tag-row" @click="emit('toDetail', item)">
875
+ <VanRow v-if="tagList.length > 0" gutter="20" class="tag-row" @click="handleCardClick(item, $event)">
668
876
  <VanCol :span="24">
669
877
  <div class="tag-container">
670
878
  <div class="tag-wrapper">
@@ -697,7 +905,7 @@ defineExpose({
697
905
  v-if="footColumns && footColumns.length > 0"
698
906
  gutter="20"
699
907
  class="card_item_footer"
700
- @click="emit('toDetail', item)"
908
+ @click="handleCardClick(item, $event)"
701
909
  >
702
910
  <VanCol v-for="column of footColumns" :key="`foot_${column.dataIndex}`" :span="12">
703
911
  <p>
@@ -724,7 +932,7 @@ defineExpose({
724
932
  @select="onSelectMenu(item, $event)"
725
933
  >
726
934
  <template #reference>
727
- <div class="more-button">
935
+ <div class="more-button" :class="{ disabled: isMultiSelectMode }">
728
936
  <span>⋯</span>
729
937
  </div>
730
938
  </template>
@@ -739,6 +947,7 @@ defineExpose({
739
947
  type="primary"
740
948
  size="normal"
741
949
  class="action-btn"
950
+ :disabled="isMultiSelectMode"
742
951
  @click="onSelectMenu(item, button)"
743
952
  >
744
953
  {{ button.text }}
@@ -756,6 +965,14 @@ defineExpose({
756
965
  </VanPullRefresh>
757
966
  <VanBackTop />
758
967
  </div>
968
+
969
+ <!-- 多选操作面板 -->
970
+ <VanActionSheet
971
+ v-model:show="showActionSheet"
972
+ :actions="actionSheetActions"
973
+ :overlay="false"
974
+ @select="handleActionSheetSelect"
975
+ />
759
976
  </div>
760
977
  </template>
761
978
 
@@ -790,6 +1007,30 @@ defineExpose({
790
1007
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
791
1008
  transition: all 0.3s ease;
792
1009
  border: 1px solid rgba(0, 0, 0, 0.04);
1010
+ position: relative;
1011
+
1012
+ &.multi-select-mode {
1013
+ border-color: var(--van-primary-color);
1014
+ background-color: rgba(25, 137, 250, 0.02);
1015
+ }
1016
+
1017
+ .selection-checkbox {
1018
+ position: relative;
1019
+ margin-right: 8px;
1020
+ z-index: 1;
1021
+ flex-shrink: 0;
1022
+
1023
+ :deep(.van-checkbox__icon) {
1024
+ border-color: var(--van-primary-color);
1025
+ border-radius: 2px;
1026
+ }
1027
+
1028
+ :deep(.van-checkbox__icon--checked) {
1029
+ background-color: var(--van-primary-color);
1030
+ border-color: var(--van-primary-color);
1031
+ border-radius: 2px;
1032
+ }
1033
+ }
793
1034
 
794
1035
  &:active {
795
1036
  transform: scale(0.98);
@@ -805,6 +1046,10 @@ defineExpose({
805
1046
  margin-bottom: 2px;
806
1047
  width: 100%;
807
1048
 
1049
+ &.multi-select-title-row {
1050
+ padding-left: 0;
1051
+ }
1052
+
808
1053
  .main-title {
809
1054
  display: inline-flex;
810
1055
  align-items: center;
@@ -813,6 +1058,21 @@ defineExpose({
813
1058
  font-weight: 700;
814
1059
  color: var(--van-text-color);
815
1060
  margin: 0;
1061
+
1062
+ &.selectable-title {
1063
+ cursor: pointer;
1064
+ padding: 4px 8px;
1065
+ border-radius: 4px;
1066
+ transition: all 0.2s ease;
1067
+
1068
+ &:hover {
1069
+ background-color: rgba(25, 137, 250, 0.1);
1070
+ }
1071
+
1072
+ &:active {
1073
+ background-color: rgba(25, 137, 250, 0.2);
1074
+ }
1075
+ }
816
1076
  }
817
1077
  }
818
1078
 
@@ -964,6 +1224,12 @@ defineExpose({
964
1224
  background-color: var(--van-background-2);
965
1225
  transform: scale(0.95);
966
1226
  }
1227
+
1228
+ &.disabled {
1229
+ opacity: 0.5;
1230
+ cursor: not-allowed;
1231
+ pointer-events: none;
1232
+ }
967
1233
  }
968
1234
 
969
1235
  .action-btn {
@@ -1033,7 +1299,8 @@ defineExpose({
1033
1299
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.02);
1034
1300
  position: sticky;
1035
1301
  top: 0;
1036
- z-index: 10;
1302
+ z-index: 100;
1303
+
1037
1304
  .add-col {
1038
1305
  display: flex;
1039
1306
  align-items: center;
@@ -1064,6 +1331,10 @@ defineExpose({
1064
1331
  width: 100%;
1065
1332
  padding: var(--van-search-padding);
1066
1333
  background-color: transparent;
1334
+
1335
+ &.multi-select-mode {
1336
+ opacity: 0.8;
1337
+ }
1067
1338
  }
1068
1339
  :deep(.van-search__content) {
1069
1340
  border-radius: 8px;
@@ -1113,5 +1384,48 @@ defineExpose({
1113
1384
  }
1114
1385
  }
1115
1386
  }
1387
+
1388
+ .multi-select-hint {
1389
+ display: flex;
1390
+ align-items: center;
1391
+ padding: 8px 12px;
1392
+ background-color: rgba(0, 0, 0, 0.04);
1393
+ border: 1px solid rgba(0, 0, 0, 0.08);
1394
+ border-radius: 6px;
1395
+ margin: 8px 12px;
1396
+ font-size: var(--van-font-size-sm);
1397
+ color: var(--van-text-color-2);
1398
+
1399
+ .van-icon {
1400
+ margin-right: 6px;
1401
+ color: var(--van-text-color-3);
1402
+ }
1403
+
1404
+ span {
1405
+ flex: 1;
1406
+ }
1407
+ }
1408
+
1409
+ .multi-select-tip {
1410
+ display: flex;
1411
+ align-items: center;
1412
+ justify-content: space-between;
1413
+ padding: 8px 12px;
1414
+ background-color: rgba(25, 137, 250, 0.1);
1415
+ border: 1px solid rgba(25, 137, 250, 0.2);
1416
+ border-radius: 6px;
1417
+ margin: 8px 12px;
1418
+ font-size: var(--van-font-size-sm);
1419
+ color: var(--van-primary-color);
1420
+
1421
+ .van-icon {
1422
+ margin-right: 6px;
1423
+ }
1424
+
1425
+ span {
1426
+ flex: 1;
1427
+ margin-right: 8px;
1428
+ }
1429
+ }
1116
1430
  }
1117
1431
  </style>
@@ -208,7 +208,7 @@ async function initWithGroupFormItems() {
208
208
  function setupFormConfig(config: GroupFormItems) {
209
209
  loadParamLogicNameData(config.paramLogicName)
210
210
  formConfig.value = config
211
- myServiceName.value = props.serviceName
211
+ myServiceName.value = props.serviceName || ''
212
212
  formGroupName.value = config.groupName || 'default'
213
213
  form.value = props.formData || {}
214
214
  // 清理并重新设置验证规则
@@ -119,4 +119,4 @@ onUnmounted(() => {
119
119
  color: #ff976a;
120
120
  }
121
121
  }
122
- </style>
122
+ </style>
@@ -99,4 +99,4 @@ onUnmounted(() => {
99
99
  .user-form {
100
100
  padding: 16px;
101
101
  }
102
- </style>
102
+ </style>
@@ -1,33 +1,33 @@
1
- <script setup lang="ts">
2
- import { onMounted, ref } from 'vue'
3
- import XReport from './XReport.vue'
4
-
5
- const mainRef = ref()
6
-
7
- onMounted(() => {
8
- // 初始化逻辑
9
- })
10
- </script>
11
-
12
- <template>
13
- <div id="test">
14
- <van-card :bordered="false">
15
- <XReport
16
- ref="mainRef"
17
- :use-oss-for-img="false"
18
- config-name="nurseWorkstationCover"
19
- server-name="af-his"
20
- :show-img-in-cell="true"
21
- :display-only="true"
22
- :edit-mode="false"
23
- :show-save-button="false"
24
- :no-padding="true"
25
- :dont-format="true"
26
- />
27
- </van-card>
28
- </div>
29
- </template>
30
-
31
- <style scoped>
32
-
33
- </style>
1
+ <script setup lang="ts">
2
+ import { onMounted, ref } from 'vue'
3
+ import XReport from './XReport.vue'
4
+
5
+ const mainRef = ref()
6
+
7
+ onMounted(() => {
8
+ // 初始化逻辑
9
+ })
10
+ </script>
11
+
12
+ <template>
13
+ <div id="test">
14
+ <van-card :bordered="false">
15
+ <XReport
16
+ ref="mainRef"
17
+ :use-oss-for-img="false"
18
+ config-name="nurseWorkstationCover"
19
+ server-name="af-his"
20
+ :show-img-in-cell="true"
21
+ :display-only="true"
22
+ :edit-mode="false"
23
+ :show-save-button="false"
24
+ :no-padding="true"
25
+ :dont-format="true"
26
+ />
27
+ </van-card>
28
+ </div>
29
+ </template>
30
+
31
+ <style scoped>
32
+
33
+ </style>