hy-app 0.5.7 → 0.5.9

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 (107) hide show
  1. package/components/hy-action-sheet/index.scss +1 -1
  2. package/components/hy-address-picker/index.scss +1 -1
  3. package/components/hy-avatar/index.scss +1 -1
  4. package/components/hy-back-top/index.scss +1 -1
  5. package/components/hy-badge/index.scss +1 -1
  6. package/components/hy-button/index.scss +1 -1
  7. package/components/hy-calendar/index.scss +1 -1
  8. package/components/hy-card/hy-card.vue +1 -1
  9. package/components/hy-card/index.scss +1 -1
  10. package/components/hy-cell-item/hy-cell-item.vue +1 -0
  11. package/components/hy-cell-item/index.scss +1 -1
  12. package/components/hy-check-button/hy-check-button.vue +1 -1
  13. package/components/hy-checkbox/index.scss +1 -1
  14. package/components/hy-checkbox-group/index.scss +1 -1
  15. package/components/hy-checkbox-item/index.scss +1 -1
  16. package/components/hy-code-input/hy-code-input.vue +1 -1
  17. package/components/hy-code-input/index.scss +1 -1
  18. package/components/hy-config-provider/hy-config-provider.vue +4 -2
  19. package/components/hy-config-provider/index.scss +1 -1
  20. package/components/hy-count-down/index.scss +1 -1
  21. package/components/hy-count-to/index.scss +1 -1
  22. package/components/hy-coupon/index.scss +1 -1
  23. package/components/hy-datetime-picker/index.scss +1 -1
  24. package/components/hy-divider/index.scss +1 -1
  25. package/components/hy-dropdown/index.scss +1 -1
  26. package/components/hy-dropdown-item/index.scss +1 -1
  27. package/components/hy-empty/index.scss +1 -1
  28. package/components/hy-flex/index.scss +1 -1
  29. package/components/hy-float-button/hy-float-button.vue +10 -11
  30. package/components/hy-float-button/index.scss +13 -13
  31. package/components/hy-folding-panel-item/index.scss +1 -1
  32. package/components/hy-form-item/index.scss +1 -1
  33. package/components/hy-grid/index.scss +1 -1
  34. package/components/hy-icon/index.scss +1 -1
  35. package/components/hy-image/index.scss +1 -1
  36. package/components/hy-input/hy-input.vue +0 -1
  37. package/components/hy-input/index.scss +1 -1
  38. package/components/hy-input/props.ts +1 -1
  39. package/components/hy-input/typing.d.ts +4 -0
  40. package/components/hy-line/index.scss +1 -1
  41. package/components/hy-line-progress/index.scss +1 -1
  42. package/components/hy-list/index.scss +1 -1
  43. package/components/hy-loading/index.scss +1 -1
  44. package/components/hy-menu/index.scss +1 -1
  45. package/components/hy-modal/index.scss +1 -1
  46. package/components/hy-navbar/hy-navbar.vue +26 -23
  47. package/components/hy-navbar/index.scss +2 -2
  48. package/components/hy-notice-bar/index.scss +1 -1
  49. package/components/hy-notify/index.scss +1 -1
  50. package/components/hy-number-step/index.scss +1 -1
  51. package/components/hy-overlay/index.scss +1 -1
  52. package/components/hy-pagination/index.scss +1 -1
  53. package/components/hy-picker/hy-picker.vue +92 -87
  54. package/components/hy-picker/index.scss +2 -2
  55. package/components/hy-picker/props.ts +1 -1
  56. package/components/hy-popover/index.scss +1 -1
  57. package/components/hy-popup/hy-popup.vue +3 -3
  58. package/components/hy-popup/index.scss +2 -1
  59. package/components/hy-price/index.scss +1 -1
  60. package/components/hy-qrcode/index.scss +1 -1
  61. package/components/hy-radio/index.scss +1 -1
  62. package/components/hy-rate/index.scss +1 -1
  63. package/components/hy-read-more/index.scss +2 -2
  64. package/components/hy-rolling-num/hy-rolling-num.vue +188 -0
  65. package/components/hy-rolling-num/index.scss +34 -0
  66. package/components/hy-rolling-num/props.ts +68 -0
  67. package/components/hy-rolling-num/typing.d.ts +0 -0
  68. package/components/hy-scroll-list/index.scss +1 -1
  69. package/components/hy-search/index.scss +1 -1
  70. package/components/hy-signature/index.scss +1 -1
  71. package/components/hy-slider/index.scss +1 -1
  72. package/components/hy-status-bar/index.scss +1 -1
  73. package/components/hy-steps/index.scss +1 -1
  74. package/components/hy-sticky/hy-sticky.vue +5 -5
  75. package/components/hy-sticky/index.scss +1 -1
  76. package/components/hy-submit-bar/index.scss +1 -1
  77. package/components/hy-subsection/index.scss +1 -1
  78. package/components/hy-swipe-action/index.scss +1 -1
  79. package/components/hy-swiper/index.scss +2 -2
  80. package/components/hy-switch/index.scss +1 -1
  81. package/components/hy-tabbar/hy-tabbar.vue +0 -1
  82. package/components/hy-tabbar/index.scss +1 -1
  83. package/components/hy-tabbar-group/index.scss +1 -1
  84. package/components/hy-tabbar-item/index.scss +1 -1
  85. package/components/hy-table/hy-table.vue +358 -0
  86. package/components/hy-table/index.scss +197 -0
  87. package/components/hy-table/props.ts +47 -0
  88. package/components/hy-table/typing.d.ts +34 -0
  89. package/components/hy-tabs/hy-tabs.vue +21 -23
  90. package/components/hy-tabs/index.scss +1 -1
  91. package/components/hy-tag/index.scss +1 -1
  92. package/components/hy-text/hy-text.vue +1 -1
  93. package/components/hy-text/index.scss +1 -1
  94. package/components/hy-textarea/index.scss +1 -1
  95. package/components/hy-toast/index.scss +2 -1
  96. package/components/hy-tooltip/index.scss +2 -2
  97. package/components/hy-upload/index.scss +1 -1
  98. package/components/hy-warn/hy-warn.vue +2 -2
  99. package/components/hy-warn/index.scss +1 -1
  100. package/components/hy-waterfall/index.scss +1 -1
  101. package/components/hy-watermark/index.scss +1 -1
  102. package/global.d.ts +1 -0
  103. package/index.scss +1 -1
  104. package/libs/css/{mixin.scss → _mixin.scss} +27 -13
  105. package/libs/css/theme.scss +4 -1
  106. package/libs/css/vars.scss +6 -0
  107. package/package.json +2 -2
@@ -0,0 +1,68 @@
1
+ import type { CSSProperties, PropType } from 'vue'
2
+
3
+ const rollingNumProps = {
4
+ /** 要显示的数字或字符串值 */
5
+ value: {
6
+ type: [String, Number],
7
+ default: 0
8
+ },
9
+ /** 数字字体大小 */
10
+ size: {
11
+ type: [String, Number],
12
+ default: '32rpx'
13
+ },
14
+ /** 数字颜色 */
15
+ color: {
16
+ type: String,
17
+ default: '#333'
18
+ },
19
+ /** 字体粗细 */
20
+ fontWeight: {
21
+ type: [String, Number],
22
+ default: 'normal'
23
+ },
24
+ /** 单个数字的高度 */
25
+ height: {
26
+ type: [String, Number],
27
+ default: '40rpx'
28
+ },
29
+ /** 滚动动画持续时间(秒) */
30
+ duration: {
31
+ type: Number,
32
+ default: 1.5
33
+ },
34
+ /** 数字间距 */
35
+ letterSpacing: {
36
+ type: [String, Number],
37
+ default: 0
38
+ },
39
+ /**
40
+ * 滚动方向:向上、向下或交替
41
+ * @values up,down,alternate
42
+ * */
43
+ scrollDirection: {
44
+ type: String,
45
+ default: 'up'
46
+ },
47
+ /**
48
+ * 滚动停止顺序:从左到右或从右到左
49
+ * @values ltr,rtl
50
+ * */
51
+ stopOrder: {
52
+ type: String,
53
+ default: 'ltr'
54
+ },
55
+ /** 每个数字滚动的延迟时间间隔(秒) */
56
+ delayStep: {
57
+ type: Number,
58
+ default: 0.1
59
+ },
60
+ /** 定义需要用到的外部样式 */
61
+ customStyle: {
62
+ type: Object as PropType<CSSProperties>
63
+ },
64
+ /** 自定义外部类名 */
65
+ customClass: String
66
+ }
67
+
68
+ export default rollingNumProps
File without changes
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
  /* 暗色主题 */
5
5
  @include b(theme){
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
  $hy-search-close-size: 20px !default;
5
5
 
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
  @include b(signature) {
5
5
  @include e(content) {
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
 
5
5
  @include b(slider) {
@@ -1,4 +1,4 @@
1
- @use "../../libs/css/mixin.scss" as *;
1
+ @use "../../libs/css/mixin" as *;
2
2
 
3
3
  @include b(status-bar) {
4
4
  // nvue会默认100%,如果nvue下,显式写100%的话,会导致宽度不为100%而异常
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
  @include b(steps) {
5
5
  @include flex;
@@ -20,7 +20,7 @@
20
20
  import { ref, computed, onMounted, onUnmounted, nextTick, getCurrentInstance, watch } from 'vue'
21
21
  import type { CSSProperties } from 'vue'
22
22
  import { onPageScroll } from '@dcloudio/uni-app'
23
- import { addUnit, getRect } from '../../libs'
23
+ import { addUnit, getRect, guid } from '../../libs'
24
24
  import type { IStickyEmits } from './typing'
25
25
  import stickyProps from './props'
26
26
 
@@ -28,7 +28,7 @@ const props = defineProps(stickyProps)
28
28
  const emit = defineEmits<IStickyEmits>()
29
29
 
30
30
  const instance = getCurrentInstance()
31
- const sentinelId = `hy-sticky-sentinel-${Math.random().toString(36).slice(2, 9)}`
31
+ const sentinelId = guid()
32
32
  const stickyRef = ref<HTMLElement | null>(null)
33
33
  const isFixed = ref(false)
34
34
  const placeholderHeight = ref(0)
@@ -81,9 +81,9 @@ const querySentinel = (): Promise<{
81
81
  .exec((r2: any[]) => {
82
82
  const stickyRect = (r2 && r2[0]) || {}
83
83
  resolve({
84
- sentinelTop: sentinelRect.top ?? 0,
85
- sentinelBottom: sentinelRect.bottom ?? 0,
86
- stickyHeight: stickyRect.height ?? 0
84
+ sentinelTop: sentinelRect.top || 0,
85
+ sentinelBottom: sentinelRect.bottom || 0,
86
+ stickyHeight: stickyRect.height || 0
87
87
  })
88
88
  })
89
89
  })
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
  @include b(sticky) {
5
5
  width: 100%;
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
 
5
5
  @include b(submit-bar) {
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
 
5
5
  @include b(subsection) {
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
  @include b(swipe-action) {
5
5
  position: relative;
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
 
5
5
  @include b(swiper) {
@@ -24,7 +24,7 @@
24
24
  flex: 1;
25
25
 
26
26
 
27
- @include m([image, video]){
27
+ @include m(image, video){
28
28
  flex: 1;
29
29
  }
30
30
 
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
  @include b(switch) {
5
5
  @include flex(row);
@@ -95,7 +95,6 @@ watch(
95
95
  onMounted(() => {
96
96
  nextTick(() => {
97
97
  getRect('.list', true, instance).then((res) => {
98
- console.log(res, current.value)
99
98
  rectList.value = res
100
99
  })
101
100
  })
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
  @include b(tabbar) {
5
5
  height: 70px;
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
  @include b(tabbar-group) {
5
5
  display: flex;
@@ -1,5 +1,5 @@
1
1
  @use "../../libs/css/theme" as *;
2
- @use "../../libs/css/mixin.scss" as *;
2
+ @use "../../libs/css/mixin" as *;
3
3
 
4
4
 
5
5
  @include b(tabbar-item) {
@@ -0,0 +1,358 @@
1
+ <template>
2
+ <view class="hy-table" :style="{ height: addUnit(height) }">
3
+ <!-- 主容器 -->
4
+ <view class="hy-table__container">
5
+ <!-- 表头区域 -->
6
+ <view class="hy-table__header">
7
+ <!-- 固定左列表头 -->
8
+ <view v-if="leftFixedColumns.length > 0" class="hy-table__header--left">
9
+ <view
10
+ class="hy-table__header__th"
11
+ v-for="(col, index) in leftFixedColumns"
12
+ :key="col.key || index"
13
+ :style="[getCellStyle(col, true)]"
14
+ @click="handleSort(col)"
15
+ >
16
+ <text class="hy-table__header__th--text">{{ col.title }}</text>
17
+ <!-- 排序图标 -->
18
+ <view v-if="col.sortable" class="hy-table__header__th--sort">
19
+ <hy-icon
20
+ :name="IconConfig.ARROW_UP_FILL"
21
+ size="10"
22
+ :custom-class="sortKey === col.key && sortType === 'asc' ? 'is-active' : ''"
23
+ />
24
+ <hy-icon
25
+ :name="IconConfig.ARROW_DOWN_FILL"
26
+ size="10"
27
+ :custom-class="sortKey === col.key && sortType === 'desc' ? 'is-active' : ''"
28
+ />
29
+ </view>
30
+ </view>
31
+ </view>
32
+ <!-- 中间表头 -->
33
+ <view class="hy-table__header--center" :style="{ paddingLeft: `${leftFixedWidth}px`, paddingRight: `${rightFixedWidth}px` }">
34
+ <view
35
+ class="hy-table__header__th"
36
+ v-for="(col, index) in scrollColumns"
37
+ :key="col.key || index"
38
+ :style="[getCellStyle(col, true)]"
39
+ @click="handleSort(col)"
40
+ >
41
+ <text class="hy-table__header__th--text">{{ col.title }}</text>
42
+ <!-- 排序图标 -->
43
+ <view v-if="col.sortable" class="hy-table__header__th--sort">
44
+ <hy-icon
45
+ :name="IconConfig.ARROW_UP_FILL"
46
+ size="10"
47
+ :custom-class="sortKey === col.key && sortType === 'asc' ? 'is-active' : ''"
48
+ />
49
+ <hy-icon
50
+ :name="IconConfig.ARROW_DOWN_FILL"
51
+ size="10"
52
+ :custom-class="sortKey === col.key && sortType === 'desc' ? 'is-active' : ''"
53
+ />
54
+ </view>
55
+ </view>
56
+ </view>
57
+ <!-- 固定右列表头 -->
58
+ <view v-if="rightFixedColumns.length > 0" class="hy-table__header--right">
59
+ <view
60
+ class="hy-table__header__th"
61
+ v-for="(col, index) in rightFixedColumns"
62
+ :key="col.key || index"
63
+ :style="[getCellStyle(col, true)]"
64
+ @click="handleSort(col)"
65
+ >
66
+ <text class="hy-table__header__th--text">{{ col.title }}</text>
67
+ <!-- 排序图标 -->
68
+ <view v-if="col.sortable" class="hy-table__header__th--sort">
69
+ <hy-icon
70
+ :name="IconConfig.ARROW_UP_FILL"
71
+ size="10"
72
+ :custom-class="sortKey === col.key && sortType === 'asc' ? 'is-active' : ''"
73
+ />
74
+ <hy-icon
75
+ :name="IconConfig.ARROW_DOWN_FILL"
76
+ size="10"
77
+ :custom-class="sortKey === col.key && sortType === 'desc' ? 'is-active' : ''"
78
+ />
79
+ </view>
80
+ </view>
81
+ </view>
82
+ </view>
83
+
84
+ <!-- 内容区域 -->
85
+ <scroll-view
86
+ class="hy-table__body"
87
+ scroll-y
88
+ :scroll-top="scrollTop"
89
+ @scroll="handleScroll"
90
+ >
91
+ <!-- 内容包装器 -->
92
+ <view class="hy-table__body__wrapper">
93
+ <!-- 固定左列内容 -->
94
+ <view v-if="leftFixedColumns.length > 0" class="hy-table__body--left">
95
+ <view
96
+ class="hy-table__body__tr"
97
+ v-for="(row, rowIndex) in tableData"
98
+ :key="`left-${row[rowKey] || rowIndex}`"
99
+ >
100
+ <view
101
+ class="hy-table__body__td"
102
+ v-for="(col, colIndex) in leftFixedColumns"
103
+ :key="col.key || colIndex"
104
+ :style="[getCellStyle(col, false)]"
105
+ @click.stop="handleCellClick(row, col)"
106
+ >
107
+ <slot name="default" :row="row" :col="col" :index="rowIndex">
108
+ <text :class="{ 'hy-table__body__td--ellipsis': col.ellipsis }">{{ row[col.key] }}</text>
109
+ </slot>
110
+ </view>
111
+ </view>
112
+ </view>
113
+ <!-- 中间内容 -->
114
+ <view class="hy-table__body--center" :style="{ paddingLeft: `${leftFixedWidth}px`, paddingRight: `${rightFixedWidth}px` }">
115
+ <view
116
+ class="hy-table__body__tr"
117
+ v-for="(row, rowIndex) in tableData"
118
+ :key="row[rowKey] || rowIndex"
119
+ @click="handleRowClick(row, rowIndex)"
120
+ >
121
+ <view
122
+ class="hy-table__body__td"
123
+ v-for="(col, colIndex) in scrollColumns"
124
+ :key="col.key || colIndex"
125
+ :style="[getCellStyle(col, false)]"
126
+ @click.stop="handleCellClick(row, col)"
127
+ >
128
+ <slot name="default" :row="row" :col="col" :index="rowIndex">
129
+ <text :class="{ 'hy-table__body__td--ellipsis': col.ellipsis }">{{ row[col.key] }}</text>
130
+ </slot>
131
+ </view>
132
+ </view>
133
+ </view>
134
+ <!-- 固定右列内容 -->
135
+ <view v-if="rightFixedColumns.length > 0" class="hy-table__body--right">
136
+ <view
137
+ class="hy-table__body__tr"
138
+ v-for="(row, rowIndex) in tableData"
139
+ :key="`right-${row[rowKey] || rowIndex}`"
140
+ >
141
+ <view
142
+ class="hy-table__body__td"
143
+ v-for="(col, colIndex) in rightFixedColumns"
144
+ :key="col.key || colIndex"
145
+ :style="[getCellStyle(col, false)]"
146
+ @click.stop="handleCellClick(row, col)"
147
+ >
148
+ <slot name="default" :row="row" :col="col" :index="rowIndex">
149
+ <text :class="{ 'hy-table__body__td--ellipsis': col.ellipsis }">{{ row[col.key] }}</text>
150
+ </slot>
151
+ </view>
152
+ </view>
153
+ </view>
154
+ </view>
155
+
156
+ <!-- 空状态 -->
157
+ <hy-empty
158
+ v-if="!loading && tableData.length === 0"
159
+ :description="emptyText"
160
+ ></hy-empty>
161
+
162
+ <!-- 加载状态 -->
163
+ <hy-loading
164
+ v-if="loading"
165
+ text="加载中..."
166
+ mode="circle"
167
+ direction="column"
168
+ custom-class="hy-table__body--loading"
169
+ ></hy-loading>
170
+ </scroll-view>
171
+ </view>
172
+ </view>
173
+ </template>
174
+
175
+ <script lang="ts">
176
+ export default {
177
+ name: 'hy-table',
178
+ options: {
179
+ addGlobalClass: true,
180
+ virtualHost: true,
181
+ styleIsolation: 'shared'
182
+ }
183
+ }
184
+ </script>
185
+
186
+ <script setup lang="ts">
187
+ import { computed, ref, watch } from 'vue'
188
+ import tableProps from './props'
189
+ import type { ITableColumn, ITableEmits, SortType } from './typing'
190
+ import { addUnit, IconConfig } from '../../libs'
191
+ // 组件
192
+ import HyEmpty from '../hy-empty/hy-empty.vue'
193
+ import HyLoading from '../hy-loading/hy-loading.vue'
194
+ import HyIcon from '../hy-icon/hy-icon.vue'
195
+
196
+ /**
197
+ * 用于展示多条结构类似的数据, 可对数据进行排序等操作。
198
+ * @displayName hy-table
199
+ */
200
+ defineOptions({})
201
+
202
+ const props = defineProps(tableProps)
203
+ const emit = defineEmits<ITableEmits>()
204
+
205
+ // --- 状态管理 ---
206
+ const sortKey = ref<string>('')
207
+ const sortType = ref<SortType>('normal')
208
+ const tableData = ref<any[]>([]) // 内部维护的数据,用于排序展示
209
+ const scrollTop = ref<number>(0) // 滚动位置,用于控制表头定位
210
+
211
+ // --- 监听数据变化 ---
212
+ watch(
213
+ () => props.data,
214
+ (val) => {
215
+ tableData.value = [...val]
216
+ // 如果当前有排序状态,重新应用排序
217
+ if (sortKey.value && sortType.value) {
218
+ localSort()
219
+ }
220
+ },
221
+ { immediate: true, deep: true }
222
+ )
223
+
224
+ // --- 计算固定列和滚动列 ---
225
+ const leftFixedColumns = computed(() => {
226
+ return props.columns.filter(col => col.fixed === 'left')
227
+ })
228
+
229
+ const rightFixedColumns = computed(() => {
230
+ return props.columns.filter(col => col.fixed === 'right')
231
+ })
232
+
233
+ const scrollColumns = computed(() => {
234
+ return props.columns.filter(col => !col.fixed)
235
+ })
236
+
237
+ // --- 计算固定列总宽度 ---
238
+ const leftFixedWidth = computed(() => {
239
+ let width = 0
240
+ leftFixedColumns.value.forEach(col => {
241
+ const w = parseInt(String(col.width || 100))
242
+ width += w
243
+ })
244
+ return width
245
+ })
246
+
247
+ const rightFixedWidth = computed(() => {
248
+ let width = 0
249
+ rightFixedColumns.value.forEach(col => {
250
+ const w = parseInt(String(col.width || 100))
251
+ width += w
252
+ })
253
+ return width
254
+ })
255
+
256
+ // --- 计算总宽度 (用于 scroll-view 横向滚动) ---
257
+ const totalMinWidth = computed(() => {
258
+ let width = 0
259
+ scrollColumns.value.forEach((col) => {
260
+ // 简单估算:如果有明确宽度则累加,否则给个默认值 100
261
+ const w = parseInt(String(col.width || 100))
262
+ width += w
263
+ })
264
+ return width
265
+ })
266
+
267
+ // --- 事件处理 ---
268
+ // 处理滚动事件
269
+ const handleScroll = (e: any) => {
270
+ scrollTop.value = e.detail.scrollTop
271
+ }
272
+
273
+ // --- 样式计算 ---
274
+ const getCellStyle = (col: ITableColumn, isHeader: boolean) => {
275
+ const style: any = {}
276
+
277
+ // 处理宽度
278
+ if (col.width) {
279
+ const w = typeof col.width === 'number' ? `${col.width}px` : col.width
280
+ style.width = w
281
+ style.flex = `0 0 ${w}` // 固定宽度,不伸缩
282
+ } else {
283
+ style.flex = 1 // 自动填充
284
+ style.minWidth = '150rpx' // 最小宽度防止太挤
285
+ }
286
+
287
+ // 处理对齐
288
+ style.justifyContent =
289
+ col.align === 'center' ? 'center' : col.align === 'right' ? 'flex-end' : 'flex-start'
290
+ style.textAlign = col.align || 'left'
291
+
292
+ return style
293
+ }
294
+
295
+ // --- 排序逻辑 ---
296
+ const handleSort = (col: ITableColumn) => {
297
+ if (!col.sortable) return
298
+
299
+ // 切换排序状态: '' -> 'asc' -> 'desc' -> ''
300
+ if (sortKey.value !== col.key) {
301
+ sortKey.value = col.key
302
+ sortType.value = 'asc'
303
+ } else {
304
+ if (sortType.value === 'asc') sortType.value = 'desc'
305
+ else if (sortType.value === 'desc') sortType.value = 'normal'
306
+ else sortType.value = 'asc' // 逻辑闭环
307
+ }
308
+
309
+ // 触发本地排序或远程排序事件
310
+ localSort()
311
+ emit('sort-change', { key: sortKey.value, type: sortType.value })
312
+ }
313
+
314
+ const localSort = () => {
315
+ if (!sortType.value) {
316
+ tableData.value = [...props.data] // 还原
317
+ return
318
+ }
319
+
320
+ tableData.value.sort((a, b) => {
321
+ const valA = a[sortKey.value]
322
+ const valB = b[sortKey.value]
323
+
324
+ // 简单的数字和字符串比较
325
+ if (valA === valB) return 0
326
+
327
+ let result = 0
328
+ if (typeof valA === 'number' && typeof valB === 'number') {
329
+ result = valA - valB
330
+ } else {
331
+ result = String(valA).localeCompare(String(valB))
332
+ }
333
+
334
+ return sortType.value === 'asc' ? result : -result
335
+ })
336
+ }
337
+
338
+ // --- 交互逻辑 ---
339
+ const handleRowClick = (row: any, index: number) => {
340
+ emit('row-click', row, index)
341
+ }
342
+
343
+ // 处理单元格点击(主要用于Tooltip)
344
+ const handleCellClick = (row: any, col: ITableColumn) => {
345
+ // 如果开启了 tooltip,显示完整内容
346
+ if (col.tooltip && col.ellipsis) {
347
+ const content = row[col.key]
348
+ if (content) {
349
+ uni.showToast({ title: String(content), icon: 'none' })
350
+ }
351
+ }
352
+ emit('cell-click', row, col)
353
+ }
354
+ </script>
355
+
356
+ <style lang="scss" scoped>
357
+ @import './index.scss';
358
+ </style>