oxy-uni-ui 1.0.1 → 1.2.0

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 (57) hide show
  1. package/LICENSE +1 -1
  2. package/attributes.json +1 -1
  3. package/components/common/abstracts/variable.scss +40 -4
  4. package/components/common/util.ts +44 -0
  5. package/components/composables/index.ts +1 -0
  6. package/components/composables/useVirtualScroll.ts +172 -0
  7. package/components/oxy-checkbox/index.scss +36 -6
  8. package/components/oxy-checkbox/oxy-checkbox.vue +5 -4
  9. package/components/oxy-checkbox/types.ts +2 -1
  10. package/components/oxy-col-picker/index.scss +18 -15
  11. package/components/oxy-col-picker/oxy-col-picker.vue +28 -3
  12. package/components/oxy-col-picker/types.ts +12 -0
  13. package/components/oxy-corner/index.scss +258 -0
  14. package/components/oxy-corner/oxy-corner.vue +67 -0
  15. package/components/oxy-corner/types.ts +50 -0
  16. package/components/oxy-drop-menu/index.scss +4 -0
  17. package/components/oxy-drop-menu/oxy-drop-menu.vue +5 -3
  18. package/components/oxy-drop-menu/types.ts +1 -1
  19. package/components/oxy-drop-menu-item/index.scss +4 -4
  20. package/components/oxy-drop-menu-item/oxy-drop-menu-item.vue +2 -0
  21. package/components/oxy-file-list/index.scss +83 -0
  22. package/components/oxy-file-list/oxy-file-list.vue +213 -0
  23. package/components/oxy-file-list/types.ts +54 -0
  24. package/components/oxy-list/index.scss +5 -0
  25. package/components/oxy-list/oxy-list.vue +206 -0
  26. package/components/oxy-list/types.ts +38 -0
  27. package/components/oxy-slider/index.scss +2 -2
  28. package/components/oxy-swiper/index.scss +1 -2
  29. package/components/oxy-textarea/oxy-textarea.vue +0 -4
  30. package/components/oxy-tree/components/tree-node-content.vue +72 -0
  31. package/components/oxy-tree/index.scss +83 -0
  32. package/components/oxy-tree/index.ts +51 -0
  33. package/components/oxy-tree/oxy-tree.vue +406 -0
  34. package/components/oxy-tree/types.ts +85 -0
  35. package/components/oxy-tree/utils.ts +51 -0
  36. package/components/oxy-upload/images/audio.png +0 -0
  37. package/components/oxy-upload/images/excle.png +0 -0
  38. package/components/oxy-upload/images/other.png +0 -0
  39. package/components/oxy-upload/images/pdf.png +0 -0
  40. package/components/oxy-upload/images/pic.png +0 -0
  41. package/components/oxy-upload/images/txt.png +0 -0
  42. package/components/oxy-upload/images/video.png +0 -0
  43. package/components/oxy-upload/images/word.png +0 -0
  44. package/components/oxy-upload/index.scss +50 -0
  45. package/components/oxy-upload/oxy-upload.vue +93 -7
  46. package/components/oxy-upload/types.ts +22 -1
  47. package/components/oxy-virtual-scroll/index.scss +35 -0
  48. package/components/oxy-virtual-scroll/oxy-virtual-scroll.vue +143 -0
  49. package/components/oxy-virtual-scroll/types.ts +155 -0
  50. package/components/oxy-virtual-scroll/virtual-scroll.ts +81 -0
  51. package/global.d.ts +3 -0
  52. package/locale/lang/ar-SA.ts +2 -1
  53. package/locale/lang/en-US.ts +10 -9
  54. package/locale/lang/zh-CN.ts +7 -6
  55. package/package.json +1 -1
  56. package/tags.json +1 -1
  57. package/web-types.json +1 -1
@@ -0,0 +1,213 @@
1
+ <template>
2
+ <view :class="['oxy-file-list', { 'oxy-file-list__list': props.type === 'list' }, customClass]">
3
+ <block v-if="props.type === 'list'">
4
+ <view v-for="(file, index) in props.data" :key="index" class="oxy-file-list__list--item" @click="onPreview(file)">
5
+ <image :src="getUploadImage(file)" mode="aspectFill" class="oxy-file-list__icon" />
6
+ <text class="oxy-file-list__name">{{ file.name }}</text>
7
+ </view>
8
+ </block>
9
+ <block v-else>
10
+ <view v-for="(file, index) in props.data" :key="index" class="oxy-file-list__item">
11
+ <view class="oxy-file-list__content">
12
+ <image v-if="isImage(file)" :src="file.url" mode="aspectFill" class="oxy-file-list__picture" @click="onPreview(file)" />
13
+ <image v-else :src="getUploadImage(file)" mode="aspectFill" class="oxy-file-list__icon" @click="onPreview(file)" />
14
+ </view>
15
+ <oxy-tooltip placement="top" :content="file.name">
16
+ <text class="oxy-file-list__name">{{ file.name }}</text>
17
+ </oxy-tooltip>
18
+ </view>
19
+ </block>
20
+ </view>
21
+ <oxy-video-preview ref="videoPreview"></oxy-video-preview>
22
+ </template>
23
+
24
+ <script lang="ts">
25
+ export default {
26
+ name: 'oxy-file-list',
27
+ options: {
28
+ addGlobalClass: true,
29
+ virtualHost: true,
30
+ styleIsolation: 'shared'
31
+ }
32
+ }
33
+ </script>
34
+ <script lang="ts" setup>
35
+ import ImgPdf from '../oxy-upload/images/pdf.png'
36
+ import ImgWord from '../oxy-upload/images/word.png'
37
+ import ImgAudio from '../oxy-upload/images/audio.png'
38
+ import ImgVideo from '../oxy-upload/images/video.png'
39
+ import ImgPic from '../oxy-upload/images/pic.png'
40
+ import ImgOther from '../oxy-upload/images/other.png'
41
+ import OxyVideoPreview from '../oxy-video-preview/oxy-video-preview.vue'
42
+ import { ref } from 'vue'
43
+ import { isImageUrl, isVideoUrl, isAudioUrl, isPdfUrl, isDocUrl } from '../common/util'
44
+ import { type FileListItem, fileListProps } from './types'
45
+ import type { VideoPreviewInstance } from '../oxy-video-preview/types'
46
+
47
+ const imgs: AnyObject = {
48
+ pdf: ImgPdf,
49
+ word: ImgWord,
50
+ audio: ImgAudio,
51
+ video: ImgVideo,
52
+ pic: ImgPic,
53
+ other: ImgOther
54
+ }
55
+
56
+ const props = defineProps(fileListProps)
57
+
58
+ const videoPreview = ref<VideoPreviewInstance>()
59
+ /**
60
+ * 预览图片
61
+ * @param file
62
+ */
63
+ function handlePreviewImage(file: FileListItem) {
64
+ const { onPreviewFail } = props
65
+ uni.previewImage({
66
+ urls: [file.url],
67
+ current: 0,
68
+ fail() {
69
+ if (onPreviewFail) {
70
+ onPreviewFail({
71
+ file
72
+ })
73
+ } else {
74
+ uni.showToast({ title: '预览图片失败', icon: 'none' })
75
+ }
76
+ }
77
+ })
78
+ }
79
+
80
+ /**
81
+ * 预览视频
82
+ * @param file
83
+ */
84
+ function handlePreviewVieo(file: FileListItem) {
85
+ const { onPreviewFail } = props
86
+ // #ifdef MP-WEIXIN
87
+ uni.previewMedia({
88
+ current: 0,
89
+ sources: [
90
+ {
91
+ url: file.url,
92
+ type: 'video',
93
+ poster: file.thumb
94
+ }
95
+ ],
96
+ fail() {
97
+ if (onPreviewFail) {
98
+ onPreviewFail({
99
+ file
100
+ })
101
+ } else {
102
+ uni.showToast({ title: '预览视频失败', icon: 'none' })
103
+ }
104
+ }
105
+ })
106
+ // #endif
107
+ // #ifndef MP-WEIXIN
108
+ videoPreview.value?.open({ url: file.url, poster: file.thumb, title: file.name })
109
+ // #endif
110
+ }
111
+
112
+ /**
113
+ * 预览文件
114
+ * @param file
115
+ */
116
+ function handlePreviewFile(file: FileListItem) {
117
+ uni.openDocument({
118
+ filePath: file.url,
119
+ showMenu: true
120
+ })
121
+ }
122
+
123
+ function onPreviewImage(file: FileListItem) {
124
+ const { beforePreview } = props
125
+ if (beforePreview) {
126
+ beforePreview({
127
+ file,
128
+ resolve: (isPass: boolean) => {
129
+ isPass && handlePreviewImage(file)
130
+ }
131
+ })
132
+ } else {
133
+ handlePreviewImage(file)
134
+ }
135
+ }
136
+
137
+ function onPreviewVideo(file: FileListItem) {
138
+ const { beforePreview } = props
139
+ if (beforePreview) {
140
+ beforePreview({
141
+ file,
142
+ resolve: (isPass: boolean) => {
143
+ isPass && handlePreviewVieo(file)
144
+ }
145
+ })
146
+ } else {
147
+ handlePreviewVieo(file)
148
+ }
149
+ }
150
+
151
+ function onPreviewFile(file: FileListItem) {
152
+ const { beforePreview } = props
153
+ if (beforePreview) {
154
+ beforePreview({
155
+ file,
156
+ resolve: (isPass: boolean) => {
157
+ isPass && handlePreviewFile(file)
158
+ }
159
+ })
160
+ } else {
161
+ handlePreviewFile(file)
162
+ }
163
+ }
164
+
165
+ function onPreview(file: FileListItem) {
166
+ if (isImage(file)) {
167
+ onPreviewImage(file)
168
+ } else if (isVideo(file)) {
169
+ onPreviewVideo(file)
170
+ } else {
171
+ onPreviewFile(file)
172
+ }
173
+ }
174
+
175
+ function isVideo(file: FileListItem) {
176
+ return (file.name && isVideoUrl(file.name)) || isVideoUrl(file.url)
177
+ }
178
+
179
+ function isImage(file: FileListItem) {
180
+ return (file.name && isImageUrl(file.name)) || isImageUrl(file.url)
181
+ }
182
+
183
+ function isAudio(file: FileListItem) {
184
+ return (file.name && isAudioUrl(file.name)) || isAudioUrl(file.url)
185
+ }
186
+
187
+ function isPdf(file: FileListItem) {
188
+ return (file.name && isPdfUrl(file.name)) || isPdfUrl(file.url)
189
+ }
190
+
191
+ function isDoc(file: FileListItem) {
192
+ return (file.name && isDocUrl(file.name)) || isDocUrl(file.url)
193
+ }
194
+
195
+ function getUploadImage(file: FileListItem) {
196
+ if (isPdf(file)) {
197
+ return imgs.pdf
198
+ } else if (isDoc(file)) {
199
+ return imgs.word
200
+ } else if (isAudio(file)) {
201
+ return imgs.audio
202
+ } else if (isVideo(file)) {
203
+ return imgs.video
204
+ } else if (isImage(file)) {
205
+ return imgs.pic
206
+ } else {
207
+ return imgs.other
208
+ }
209
+ }
210
+ </script>
211
+ <style lang="scss" scoped>
212
+ @import './index.scss';
213
+ </style>
@@ -0,0 +1,54 @@
1
+ import type { ExtractPropTypes, PropType } from 'vue'
2
+ import { baseProps, makeArrayProp, makeStringProp } from '../common/props'
3
+
4
+ export type FileListItem = {
5
+ [key: string]: any
6
+ // 文件名称
7
+ name?: string
8
+ // 文件地址
9
+ url: string
10
+ }
11
+
12
+ export type FileListType = 'card' | 'list'
13
+
14
+ export type FileListBeforePreviewOption = {
15
+ file: FileListItem
16
+ resolve: (isPass: boolean) => void
17
+ }
18
+ export type FileListBeforePreview = (option: FileListBeforePreviewOption) => void
19
+
20
+ export type FileListOnPreviewFailOption = {
21
+ file: FileListItem
22
+ }
23
+ export type FileListOnPreviewFail = (option: FileListOnPreviewFailOption) => void
24
+
25
+ export const fileListProps = {
26
+ ...baseProps,
27
+
28
+ /**
29
+ * 文件列表
30
+ * 类型:array
31
+ * 默认值:[]
32
+ */
33
+ data: makeArrayProp<FileListItem>(),
34
+ /**
35
+ * 文件列表类型
36
+ * 类型:string
37
+ * 默认值:'card'
38
+ */
39
+ type: makeStringProp<FileListType>('list'),
40
+ /**
41
+ * 文件预览前的钩子,参数为预览的文件,若返回false或者返回Promise且被reject,则停止预览。
42
+ * 类型:function({file,resolve})
43
+ * 默认值:-
44
+ */
45
+ beforePreview: Function as PropType<FileListBeforePreview>,
46
+ /**
47
+ * 预览失败执行操作
48
+ * 类型:function({file})
49
+ * 默认值:-
50
+ */
51
+ onPreviewFail: Function as PropType<FileListOnPreviewFail>
52
+ }
53
+
54
+ export type CornerProps = ExtractPropTypes<typeof fileListProps>
@@ -0,0 +1,5 @@
1
+ @import './../common/abstracts/_mixin.scss';
2
+ @import './../common/abstracts/variable.scss';
3
+ @import './../oxy-virtual-scroll/index.scss';
4
+
5
+ @include b(list) {}
@@ -0,0 +1,206 @@
1
+ <template>
2
+ <view :class="['oxy-list', 'oxy-virtual-scroll', customClass]" :style="customStyle">
3
+ <!-- 滚动内容区域 -->
4
+ <scroll-view
5
+ v-if="data.length"
6
+ :style="{ height: height }"
7
+ class="oxy-virtual-scroll__view"
8
+ :scroll-y="true"
9
+ :scroll-x="scrollX"
10
+ :upper-threshold="upperThreshold"
11
+ :lower-threshold="lowerThreshold"
12
+ :scroll-top="scrollTop"
13
+ :scroll-left="scrollLeft"
14
+ :scroll-with-animation="scrollWithAnimation"
15
+ :enable-back-to-top="enableBackToTop"
16
+ :show-scrollbar="showScrollbar"
17
+ :refresher-enabled="refresherEnabled"
18
+ :refresher-threshold="refresherThreshold"
19
+ :refresher-default-style="refresherDefaultStyle"
20
+ :refresher-background="refresherBackground"
21
+ :refresher-triggered="triggered"
22
+ :enable-flex="enableFlex"
23
+ :scroll-anchoring="scrollAnchoring"
24
+ @scroll="handleScroll"
25
+ @scrolltoupper="onScrollToUpper"
26
+ @scrolltolower="onScrollToLower"
27
+ @refresherrefresh="onRefresh"
28
+ @refresherrestore="onRestore"
29
+ @refresherabort="onAbort"
30
+ >
31
+ <view class="oxy-virtual-scroll__container" :style="{ height: totalHeight + 'px' }">
32
+ <view class="oxy-virtual-scroll__items" :style="{ transform: `translateY(${virtualOffsetY}px)` }">
33
+ <view v-for="(item, index) in virtualData" :key="index">
34
+ <slot name="item" :item="item" :index="startIndex + index"></slot>
35
+ </view>
36
+ <slot name="bottom">
37
+ <oxy-loadmore
38
+ v-if="loadmoreState"
39
+ :state="loadmoreState"
40
+ :loading-text="loadingText"
41
+ :finished-text="finishedText"
42
+ :error-text="errorText"
43
+ :loading-props="loadingProps"
44
+ @reload="onReload"
45
+ />
46
+ </slot>
47
+ </view>
48
+ </view>
49
+ </scroll-view>
50
+
51
+ <view v-else>
52
+ <oxy-status-tip image="content" :tip="emptyText" />
53
+ </view>
54
+
55
+ <!-- 回到顶部按钮 -->
56
+ <view v-if="showBackToTop && showBackTopBtn" class="oxy-virtual-scroll__back-top" @click="scrollToTop">
57
+ <oxy-icon name="backtop" color="#fff" size="20px"></oxy-icon>
58
+ </view>
59
+ </view>
60
+ </template>
61
+
62
+ <script lang="ts">
63
+ export default {
64
+ name: 'oxy-list',
65
+ options: {
66
+ addGlobalClass: true,
67
+ virtualHost: true,
68
+ styleIsolation: 'shared'
69
+ }
70
+ }
71
+ </script>
72
+
73
+ <script lang="ts" setup>
74
+ import { computed, toRefs } from 'vue'
75
+ import { type ListExpose, listProps } from './types'
76
+ import type {
77
+ ScrollViewOnRefresherabortEvent,
78
+ ScrollViewOnRefresherrefreshEvent,
79
+ ScrollViewOnRefresherrestoreEvent,
80
+ ScrollViewOnScrollEvent
81
+ } from '@uni-helper/uni-app-types/index'
82
+ import { useVirtualScroll } from '../composables/useVirtualScroll'
83
+
84
+ const props = defineProps(listProps)
85
+
86
+ const emit = defineEmits(['scroll-to-lower', 'reload', 'scroll-to-upper', 'scroll', 'refresh', 'restore', 'abort'])
87
+
88
+ const defaultscrollConfig = {
89
+ refresherEnabled: false,
90
+ refresherThreshold: 60,
91
+ scrollWithAnimation: false
92
+ } as const
93
+
94
+ // 解构props用于组合式函数
95
+ const {
96
+ data,
97
+ virtual,
98
+ height,
99
+ itemHeight,
100
+ idKey,
101
+ showBackToTop,
102
+ backToTopThreshold,
103
+ scrollY,
104
+ scrollX,
105
+ upperThreshold,
106
+ lowerThreshold,
107
+ scrollLeft,
108
+ scrollWithAnimation,
109
+ enableBackToTop,
110
+ showScrollbar,
111
+ refresherEnabled,
112
+ refresherThreshold,
113
+ refresherDefaultStyle,
114
+ refresherBackground,
115
+ triggered,
116
+ enableFlex,
117
+ scrollAnchoring
118
+ } = toRefs(props)
119
+
120
+ // 使用虚拟滚动组合式函数
121
+ const {
122
+ scrollTop,
123
+ showBackTopBtn,
124
+ virtualData,
125
+ startIndex,
126
+ virtualOffsetY,
127
+ totalHeight,
128
+ displayData,
129
+ initScrollData,
130
+ initScrollEngine,
131
+ updateVisibleData,
132
+ scrollToTop: virtualScrollToTop,
133
+ scrollToBottom: virtualScrollToBottom,
134
+ scrollToPosition: virtualScrollToPosition,
135
+ scrollToElement: virtualScrollToElement,
136
+ scrollToElementById: virtualScrollToElementById,
137
+ onScroll: handleVirtualScroll
138
+ } = useVirtualScroll({
139
+ data,
140
+ virtual,
141
+ height,
142
+ itemHeight,
143
+ idKey,
144
+ backToTopThreshold
145
+ })
146
+
147
+ const onScrollToLower = async () => {
148
+ emit('scroll-to-lower')
149
+ }
150
+ const onScrollToUpper = async () => {
151
+ emit('scroll-to-upper')
152
+ }
153
+ const onReload = async () => {
154
+ emit('reload')
155
+ }
156
+
157
+ // 滚动事件处理
158
+ const handleScroll = async (event: ScrollViewOnScrollEvent) => {
159
+ handleVirtualScroll(event.detail.scrollTop)
160
+ emit('scroll', event)
161
+ }
162
+
163
+ // 下拉刷新
164
+ const onRefresh = (e: ScrollViewOnRefresherrefreshEvent) => {
165
+ emit('refresh', e)
166
+ }
167
+
168
+ // 下拉刷新被复位
169
+ const onRestore = (e: ScrollViewOnRefresherrestoreEvent) => {
170
+ emit('restore', e)
171
+ }
172
+
173
+ // 下拉刷新被中止
174
+ const onAbort = (e: ScrollViewOnRefresherabortEvent) => {
175
+ emit('abort', e)
176
+ }
177
+
178
+ // 滚动方法
179
+ const scrollToTop = () => {
180
+ virtualScrollToTop()
181
+ }
182
+ const scrollToBottom = () => {
183
+ virtualScrollToBottom()
184
+ }
185
+ const scrollToPosition = (position: number | string) => {
186
+ virtualScrollToPosition(position)
187
+ }
188
+ const scrollToElement = (item: any) => {
189
+ virtualScrollToElement(item)
190
+ }
191
+ const scrollToElementById = (id: string | number) => {
192
+ virtualScrollToElementById(id)
193
+ }
194
+
195
+ defineExpose<ListExpose>({
196
+ scrollToTop,
197
+ scrollToBottom,
198
+ scrollToPosition,
199
+ scrollToElement,
200
+ scrollToElementById
201
+ })
202
+ </script>
203
+
204
+ <style lang="scss" scoped>
205
+ @import './index.scss';
206
+ </style>
@@ -0,0 +1,38 @@
1
+ import type { ComponentPublicInstance, ExtractPropTypes, PropType } from 'vue'
2
+ import { makeBooleanProp, makeNumberProp, makeStringProp } from '../common/props'
3
+ import { type LoadMoreState } from '../oxy-loadmore/types'
4
+ import { type LoadingProps } from '../oxy-loading/types'
5
+ import { virtualScrollProps } from '../oxy-virtual-scroll/types'
6
+
7
+ export const listProps = {
8
+ ...virtualScrollProps,
9
+
10
+ loadmoreState: String as PropType<LoadMoreState>,
11
+
12
+ /**
13
+ * 加载提示文案
14
+ */
15
+ loadingText: String,
16
+ /**
17
+ * 全部加载完的提示文案
18
+ */
19
+ finishedText: String,
20
+ /**
21
+ * 加载失败的提示文案
22
+ */
23
+ errorText: String,
24
+ /**
25
+ * 加载中loading组件的属性
26
+ * 参考loading组件
27
+ */
28
+ loadingProps: Object as PropType<Partial<LoadingProps>>
29
+ }
30
+ export type ListExpose = {
31
+ scrollToTop: () => void
32
+ scrollToBottom: () => void
33
+ scrollToPosition: (position: number | string) => void
34
+ scrollToElement: (item: any) => void
35
+ scrollToElementById: (id: string | number) => void
36
+ }
37
+ export type ListProps = ExtractPropTypes<typeof listProps>
38
+ export type ListInstance = ComponentPublicInstance<ListProps, ListExpose>
@@ -25,7 +25,7 @@
25
25
  display: flex;
26
26
  flex-flow: row nowrap;
27
27
  align-items: center;
28
- height: calc($-slider-handle-radius * 3);
28
+ // height: calc($-slider-handle-radius * 3);
29
29
 
30
30
  @include e(label-min, label-max) {
31
31
  font-size: $-slider-fs;
@@ -95,4 +95,4 @@
95
95
  color: $-slider-disabled-color;
96
96
  }
97
97
  }
98
- }
98
+ }
@@ -17,7 +17,6 @@
17
17
  padding: $-swiper-item-padding;
18
18
 
19
19
  @include m(slot) {
20
- // 问题来自 https://github.com/dcloudio/uni-app/issues/4629,支付宝小程序不支持属性选择器
21
20
  /* #ifdef MP */
22
21
  :deep() {
23
22
  /* #ifdef MP-WEIXIN */
@@ -50,4 +49,4 @@
50
49
  color: $-swiper-item-text-color;
51
50
  font-size: $-swiper-item-text-fs;
52
51
  }
53
- }
52
+ }
@@ -179,10 +179,6 @@ const isRequired = computed(() => {
179
179
 
180
180
  // 当前文本域文字长度
181
181
  const currentLength = computed(() => {
182
- /**
183
- * 使用Array.from处理多码元字符以获取正确的长度
184
- * @link https://github.com/Moonofweisheng/oxy-uni-ui/issues/933
185
- */
186
182
  return Array.from(String(formatValue(props.modelValue))).length
187
183
  })
188
184
 
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <view class="oxy-tree-node-content" :style="getNodeStyle()" :class="computedClass" @click="handleNodeClick">
3
+ <oxy-icon @click.stop="toggleExpand" name="fill-arrow-down" class="oxy-tree-node-icon" size="22px"></oxy-icon>
4
+ <oxy-checkbox
5
+ v-if="treeProps?.showCheckbox"
6
+ :modelValue="node.checked"
7
+ :disabled="node.disabled"
8
+ :indeterminate="node.immediate"
9
+ shape="square"
10
+ ></oxy-checkbox>
11
+ <view class="oxy-tree-node">
12
+ <slot :node="node" :data="node.data">
13
+ {{ node.label }}
14
+ </slot>
15
+ </view>
16
+ </view>
17
+ </template>
18
+
19
+ <script lang="ts">
20
+ export default {
21
+ name: 'oxy-tree-node-content'
22
+ }
23
+ </script>
24
+
25
+ <script lang="ts" setup>
26
+ import { computed, toRefs, type Ref } from 'vue'
27
+ import type { TextProps, TreeNode } from '../types'
28
+ import { inject } from 'vue'
29
+ // 获取组件的 props 和 emit 函数
30
+ const emit = defineEmits<{
31
+ (e: 'click', node: TreeNode): void
32
+ (e: 'toggle-expand', node: TreeNode, flag: boolean): void
33
+ (e: 'toggle-checked', node: TreeNode, flag: boolean, isClick: boolean): void
34
+ }>()
35
+
36
+ const props = defineProps<{
37
+ node: TreeNode
38
+ }>()
39
+ const treeProps = inject<TextProps>('treeProps')
40
+ const currentNode = inject<Ref<TreeNode | undefined>>('currentNode')
41
+
42
+ function getNodeStyle() {
43
+ return {
44
+ paddingLeft: `${(props.node.level - 1) * (treeProps?.indent || 16)}px`
45
+ }
46
+ }
47
+
48
+ function toggleExpand() {
49
+ emit('toggle-expand', props.node, !props.node.expanded)
50
+ }
51
+
52
+ const computedClass = computed(() => {
53
+ const currentValue = currentNode?.value
54
+ return {
55
+ expanded: props.node.expanded,
56
+ checked: props.node.checked,
57
+ 'is-leaf': props.node.isLeaf,
58
+ immediate: props.node.immediate,
59
+ 'is-current': currentValue === props.node,
60
+ 'is-disabled': props.node.disabled
61
+ }
62
+ })
63
+
64
+ const handleNodeClick = () => {
65
+ emit('click', props.node)
66
+ emit('toggle-checked', props.node, !props.node.checked, true)
67
+ }
68
+ </script>
69
+
70
+ <style lang="scss" scoped>
71
+ @import '../index.scss';
72
+ </style>