hw-cus-ui 1.0.25 → 1.1.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.
@@ -0,0 +1,111 @@
1
+ # HwCheckbox 组件使用说明
2
+
3
+ HwCheckbox 是一个复选框组件,支持编辑模式和详情模式,可以显示复选框或只读文本。
4
+
5
+ ## 功能特性
6
+
7
+ - 支持编辑模式和详情模式
8
+ - 支持双向数据绑定
9
+ - 支持自定义选项数据
10
+ - 支持必填标识显示
11
+ - 支持自定义样式
12
+
13
+ ## 属性 (Props)
14
+
15
+ | 属性 | 类型 | 默认值 | 说明 |
16
+ |------|------|--------|------|
17
+ | [marginTop](file:///e:/temp/my-components/src/components/HwCheckbox/HwCheckbox.vue#L41-L43) | string | '20px' | 上边距 |
18
+ | [pageType](file:///e:/temp/my-components/src/components/HwCheckbox/HwCheckbox.vue#L44-L46) | string | 'checkbox' | 页面类型,用于区分编辑页面还是详情页面 |
19
+ | [label](file:///e:/temp/my-components/src/components/HwCheckbox/HwCheckbox.vue#L47-L49) | string | - | 字段标签名称(必填) |
20
+ | [types](file:///e:/temp/my-components/src/components/HwCheckbox/HwCheckbox.vue#L50-L52) | string | 'add' | 类型标识,用于控制样式显示 |
21
+ | [required](file:///e:/temp/my-components/src/components/HwCheckbox/HwCheckbox.vue#L53-L55) | boolean | false | 是否必填项,true时会显示红色星号 |
22
+ | [localdata](file:///e:/temp/my-components/src/components/HwCheckbox/HwCheckbox.vue#L56-L59) | Array<any> | - | 下拉选项数据列表,每项应包含value和text属性 |
23
+
24
+ ## 事件 (Events)
25
+
26
+ | 事件名 | 参数 | 说明 |
27
+ |-------|------|------|
28
+ | update:modelValue | (value: string \| number) | 当值变化时触发 |
29
+
30
+ ## 使用示例
31
+
32
+ ### 基础用法
33
+
34
+ ```vue
35
+ <template>
36
+ <HwCheckbox
37
+ v-model="checkboxValue"
38
+ label="选择项"
39
+ :localdata="checkboxOptions"
40
+ />
41
+ </template>
42
+
43
+ <script setup>
44
+ import { ref } from 'vue';
45
+ import HwCheckbox from '@/components/HwCheckbox/HwCheckbox.vue';
46
+
47
+ const checkboxValue = ref('');
48
+ const checkboxOptions = [
49
+ { value: 'option1', text: '选项1' },
50
+ { value: 'option2', text: '选项2' },
51
+ { value: 'option3', text: '选项3' }
52
+ ];
53
+ </script>
54
+ ```
55
+
56
+ ### 详情模式
57
+
58
+ ```vue
59
+ <template>
60
+ <HwCheckbox
61
+ v-model="checkboxValue"
62
+ label="选择项"
63
+ :localdata="checkboxOptions"
64
+ pageType="detail"
65
+ />
66
+ </template>
67
+
68
+ <script setup>
69
+ import { ref } from 'vue';
70
+ import HwCheckbox from '@/components/HwCheckbox/HwCheckbox.vue';
71
+
72
+ const checkboxValue = ref('option1');
73
+ const checkboxOptions = [
74
+ { value: 'option1', text: '选项1' },
75
+ { value: 'option2', text: '选项2' },
76
+ { value: 'option3', text: '选项3' }
77
+ ];
78
+ </script>
79
+ ```
80
+
81
+ ### 必填项
82
+
83
+ ```vue
84
+ <template>
85
+ <HwCheckbox
86
+ v-model="checkboxValue"
87
+ label="必填选择项"
88
+ :required="true"
89
+ :localdata="checkboxOptions"
90
+ />
91
+ </template>
92
+
93
+ <script setup>
94
+ import { ref } from 'vue';
95
+ import HwCheckbox from '@/components/HwCheckbox/HwCheckbox.vue';
96
+
97
+ const checkboxValue = ref('');
98
+ const checkboxOptions = [
99
+ { value: 'option1', text: '选项1' },
100
+ { value: 'option2', text: '选项2' }
101
+ ];
102
+ </script>
103
+ ```
104
+
105
+ ## 注意事项
106
+
107
+ 1. 组件使用了 uni-data-checkbox 作为基础组件
108
+ 2. 在详情模式下,组件只显示选中项的文本,不提供编辑功能
109
+ 3. [localdata](file:///e:/temp/my-components/src/components/HwCheckbox/HwCheckbox.vue#L56-L59) 属性的每个选项对象需要包含 [value](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L35-L40) 和 text 属性
110
+ 4. [pageType](file:///e:/temp/my-components/src/components/HwCheckbox/HwCheckbox.vue#L44-L46) 设置为 'detail' 时会显示为只读的详情模式
111
+ 5. 组件支持通过插槽自定义内容
@@ -0,0 +1,127 @@
1
+ # HwDraggableBottomPopup 组件使用说明
2
+
3
+ HwDraggableBottomPopup 是一个可拖拽的底部弹窗组件,支持触摸拖拽调整位置,适用于需要用户交互调整内容区域的场景。
4
+
5
+ ## 功能特性
6
+
7
+ - 支持触摸拖拽调整弹窗位置
8
+ - 可自定义弹窗高度
9
+ - 支持拖拽过程中实时调整内容区域高度
10
+ - 防抖处理确保性能
11
+ - 灵活的内容插槽
12
+
13
+ ## 属性 (Props)
14
+
15
+ | 属性 | 类型 | 默认值 | 说明 |
16
+ |------|------|--------|------|
17
+ | [heightCalc](file:///e:/temp/my-components/src/components/HwDraggableBottomPopup/HwDraggableBottomPopup.vue#L31-L35) | String | '0.4' | 弹窗高度计算比例,相对于屏幕高度的百分比 |
18
+ | [isDrag](file:///e:/temp/my-components/src/components/HwDraggableBottomPopup/HwDraggableBottomPopup.vue#L36-L40) | Boolean | true | 是否启用拖拽功能 |
19
+
20
+ ## 事件 (Events)
21
+
22
+ | 事件名 | 参数 | 说明 |
23
+ |-------|------|------|
24
+ | changeTop | (top: number) | 拖拽过程中顶部位置变化时触发 |
25
+
26
+ ## 插槽 (Slots)
27
+
28
+ | 插槽名 | 说明 |
29
+ |--------|------|
30
+ | default | 默认插槽,用于放置弹窗内容 |
31
+
32
+ ## 使用示例
33
+
34
+ ### 基础用法
35
+
36
+ ```vue
37
+ <template>
38
+ <view>
39
+ <HwDraggableBottomPopup>
40
+ <view class="popup-content">
41
+ <p>这里是弹窗内容</p>
42
+ <p>可以放置任何内容</p>
43
+ </view>
44
+ </HwDraggableBottomPopup>
45
+ </view>
46
+ </template>
47
+
48
+ <script setup>
49
+ import HwDraggableBottomPopup from '@/components/HwDraggableBottomPopup/HwDraggableBottomPopup.vue';
50
+ </script>
51
+
52
+ <style>
53
+ .popup-content {
54
+ padding: 20rpx;
55
+ height: 100%;
56
+ }
57
+ </style>
58
+ ```
59
+
60
+ ### 自定义高度
61
+
62
+ ```vue
63
+ <template>
64
+ <view>
65
+ <HwDraggableBottomPopup :heightCalc="'0.6'">
66
+ <view class="popup-content">
67
+ <p>初始高度为屏幕高度的60%</p>
68
+ </view>
69
+ </HwDraggableBottomPopup>
70
+ </view>
71
+ </template>
72
+
73
+ <script setup>
74
+ import HwDraggableBottomPopup from '@/components/HwDraggableBottomPopup/HwDraggableBottomPopup.vue';
75
+ </script>
76
+ ```
77
+
78
+ ### 禁用拖拽
79
+
80
+ ```vue
81
+ <template>
82
+ <view>
83
+ <HwDraggableBottomPopup :isDrag="false">
84
+ <view class="popup-content">
85
+ <p>此弹窗不可拖拽</p>
86
+ </view>
87
+ </HwDraggableBottomPopup>
88
+ </view>
89
+ </template>
90
+
91
+ <script setup>
92
+ import HwDraggableBottomPopup from '@/components/HwDraggableBottomPopup/HwDraggableBottomPopup.vue';
93
+ </script>
94
+ ```
95
+
96
+ ### 监听位置变化
97
+
98
+ ```vue
99
+ <template>
100
+ <view>
101
+ <HwDraggableBottomPopup @changeTop="onTopChange">
102
+ <view class="popup-content">
103
+ <p>拖拽弹窗查看位置变化</p>
104
+ </view>
105
+ </HwDraggableBottomPopup>
106
+ </view>
107
+ </template>
108
+
109
+ <script setup>
110
+ import { ref } from 'vue';
111
+ import HwDraggableBottomPopup from '@/components/HwDraggableBottomPopup/HwDraggableBottomPopup.vue';
112
+
113
+ const onTopChange = (top) => {
114
+ console.log('弹窗顶部位置:', top);
115
+ };
116
+ </script>
117
+ ```
118
+
119
+ ## 注意事项
120
+
121
+ 1. 组件使用了 uni-app 的触摸事件和系统信息API
122
+ 2. 弹窗默认从屏幕底部开始,初始位置在屏幕外
123
+ 3. 拖拽功能通过触摸事件实现,支持触摸开始、移动和结束
124
+ 4. 弹窗内容区域高度会根据拖拽位置自动调整
125
+ 5. 组件使用了防抖函数优化性能,避免频繁更新内容区域高度
126
+ 6. 当启用拖拽功能时,组件会添加圆角边框以提供更好的视觉效果
127
+ 7. 组件内部使用了 flex 布局,内容区域会自动适应可用空间
@@ -0,0 +1,119 @@
1
+ # HwFileUpload 组件使用说明
2
+
3
+ HwFileUpload 是一个文件上传组件,支持图片和文件上传、预览、下载和删除功能。可通过 [isImg](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L63-L63) 属性控制是图片模式还是文件模式展示。
4
+
5
+ ## 功能特性
6
+
7
+ - 支持图片和文件上传
8
+ - 支持图片预览功能
9
+ - 支持文件下载功能
10
+ - 支持文件删除功能
11
+ - 可切换图片模式和文件模式
12
+ - 支持多种文件类型的上传限制
13
+ - 支持详情模式和添加模式
14
+
15
+ ## 属性 (Props)
16
+
17
+ | 属性 | 类型 | 默认值 | 说明 |
18
+ |------|------|--------|------|
19
+ | [fileType](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L54-L54) | Array | ['pdf', 'xlsx', 'docx', 'doc', 'zip', 'rar', 'exe'] | 允许上传的文件类型列表 |
20
+ | [modelValue](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L55-L55) | Array | - | 绑定值,文件列表 |
21
+ | [isImg](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L63-L63) | boolean | false | 是否为图片模式 |
22
+ | [border](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L64-L64) | boolean | true | 是否显示下边框 |
23
+ | [marginTop](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L65-L65) | string | '20px' | 上边距 |
24
+ | [types](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L66-L66) | string | 'add' | 组件类型,'add' 为添加模式,'detail' 为详情模式 |
25
+ | [required](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L67-L67) | boolean | false | 是否必填 |
26
+ | [label](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L68-L68) | string | - | 标签文本 |
27
+ | [isLink](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L59-L59) | boolean | false | 是否为链接模式 |
28
+ | [baseUrl](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L60-L60) | string | '' | 基础URL |
29
+ | [apiUrl](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L61-L61) | string | '' | API上传地址 |
30
+ | [headers](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L62-L62) | object | {} | 上传请求头 |
31
+
32
+ ## 事件 (Events)
33
+
34
+ | 事件名 | 参数 | 说明 |
35
+ |-------|------|------|
36
+ | update:modelValue | (value: any[]) | 当文件列表发生变化时触发 |
37
+
38
+ ## 使用示例
39
+
40
+ ### 图片上传模式
41
+
42
+ ```vue
43
+ <template>
44
+ <HwFileUpload
45
+ v-model="imageList"
46
+ label="图片上传"
47
+ :isImg="true"
48
+ :fileType="['jpg', 'jpeg', 'png', 'gif']"
49
+ :required="true"
50
+ apiUrl="/api/upload"
51
+ :headers="headers"
52
+ />
53
+ </template>
54
+
55
+ <script setup>
56
+ import { ref } from 'vue';
57
+ import HwFileUpload from '@/components/HwFileUpload/HwFileUpload.vue';
58
+
59
+ const imageList = ref([]);
60
+ const headers = { 'Authorization': 'Bearer token' };
61
+ </script>
62
+ ```
63
+
64
+ ### 文件上传模式
65
+
66
+ ```vue
67
+ <template>
68
+ <HwFileUpload
69
+ v-model="fileList"
70
+ label="附件上传"
71
+ :isImg="false"
72
+ :fileType="['pdf', 'doc', 'docx', 'xlsx']"
73
+ :required="true"
74
+ apiUrl="/api/upload"
75
+ :headers="headers"
76
+ />
77
+ </template>
78
+
79
+ <script setup>
80
+ import { ref } from 'vue';
81
+ import HwFileUpload from '@/components/HwFileUpload/HwFileUpload.vue';
82
+
83
+ const fileList = ref([]);
84
+ const headers = { 'Authorization': 'Bearer token' };
85
+ </script>
86
+ ```
87
+
88
+ ### 详情模式(只读)
89
+
90
+ ```vue
91
+ <template>
92
+ <HwFileUpload
93
+ v-model="fileList"
94
+ label="附件列表"
95
+ :isImg="false"
96
+ types="detail"
97
+ :required="false"
98
+ />
99
+ </template>
100
+
101
+ <script setup>
102
+ import { ref } from 'vue';
103
+ import HwFileUpload from '@/components/HwFileUpload/HwFileUpload.vue';
104
+
105
+ const fileList = ref([
106
+ { name: 'example.pdf', url: '/path/to/example.pdf' },
107
+ { name: 'document.docx', url: '/path/to/document.docx' }
108
+ ]);
109
+ </script>
110
+ ```
111
+
112
+ ## 注意事项
113
+
114
+ 1. 上传功能需要配置 [apiUrl](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L61-L61) 和 [baseUrl](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L60-L60) 属性
115
+ 2. 文件类型限制通过 [fileType](file:///e:/temp/my-components/src/components/HwFileUpload/HwFileUpload.vue#L54-L54) 属性控制
116
+ 3. 在详情模式下,组件只显示文件列表,不提供上传、删除等操作
117
+ 4. 文件大小显示单位为KB
118
+ 5. 图片上传使用 [uni.previewImage](file:///e:/temp/my-components/node_modules/@dcloudio/uni-app-plus/types/index.d.ts#L210-L216) 进行预览
119
+ 6. 文件下载使用 [file-saver](file:///e:/temp/my-components/package.json#L24-L24) 库实现
@@ -0,0 +1,134 @@
1
+ # HwInput 组件使用说明
2
+
3
+ HwInput 是一个输入框组件,支持编辑模式和详情模式,可以显示输入框或只读文本。
4
+
5
+ ## 功能特性
6
+
7
+ - 支持编辑模式和详情模式
8
+ - 支持双向数据绑定
9
+ - 支持自定义样式
10
+ - 支持必填标识显示
11
+ - 支持子值显示
12
+
13
+ ## 属性 (Props)
14
+
15
+ | 属性 | 类型 | 默认值 | 说明 |
16
+ |------|------|--------|------|
17
+ | [marginTop](file:///e:/temp/my-components/src/components/HwInput/HwInput.vue#L42-L42) | string | '20px' | 上边距 |
18
+ | [types](file:///e:/temp/my-components/src/components/HwInput/HwInput.vue#L43-L43) | string | 'add' | 组件类型,'add'为编辑模式,'detail'为详情模式 |
19
+ | [subValue](file:///e:/temp/my-components/src/components/HwInput/HwInput.vue#L44-L44) | string | '' | 子值,在详情模式下额外显示的信息 |
20
+ | [styles](file:///e:/temp/my-components/src/components/HwInput/HwInput.vue#L45-L45) | object | {} | 自定义样式对象 |
21
+ | [required](file:///e:/temp/my-components/src/components/HwInput/HwInput.vue#L46-L46) | boolean | false | 是否必填,为true时显示红色星号 |
22
+ | [borderBottom](file:///e:/temp/my-components/src/components/HwInput/HwInput.vue#L47-L47) | string | '1rpx solid rgba(17, 31, 44, 0.12)' | 设置下边框 |
23
+ | [label](file:///e:/temp/my-components/src/components/HwInput/HwInput.vue#L48-L48) | string | - | 标签文本,必填项 |
24
+
25
+ ## 事件 (Events)
26
+
27
+ | 事件名 | 参数 | 说明 |
28
+ |-------|------|------|
29
+ | update:modelValue | (value: string \| number) | 当值变化时触发 |
30
+
31
+ ## 使用示例
32
+
33
+ ### 基础用法
34
+
35
+ ```vue
36
+ <template>
37
+ <HwInput
38
+ v-model="inputValue"
39
+ label="姓名"
40
+ />
41
+ </template>
42
+
43
+ <script setup>
44
+ import { ref } from 'vue';
45
+ import HwInput from '@/components/HwInput/HwInput.vue';
46
+
47
+ const inputValue = ref('');
48
+ </script>
49
+ ```
50
+
51
+ ### 详情模式
52
+
53
+ ```vue
54
+ <template>
55
+ <HwInput
56
+ v-model="inputValue"
57
+ label="姓名"
58
+ types="detail"
59
+ />
60
+ </template>
61
+
62
+ <script setup>
63
+ import { ref } from 'vue';
64
+ import HwInput from '@/components/HwInput/HwInput.vue';
65
+
66
+ const inputValue = ref('张三');
67
+ </script>
68
+ ```
69
+
70
+ ### 必填项
71
+
72
+ ```vue
73
+ <template>
74
+ <HwInput
75
+ v-model="inputValue"
76
+ label="邮箱"
77
+ :required="true"
78
+ />
79
+ </template>
80
+
81
+ <script setup>
82
+ import { ref } from 'vue';
83
+ import HwInput from '@/components/HwInput/HwInput.vue';
84
+
85
+ const inputValue = ref('');
86
+ </script>
87
+ ```
88
+
89
+ ### 带子值的详情模式
90
+
91
+ ```vue
92
+ <template>
93
+ <HwInput
94
+ v-model="inputValue"
95
+ label="价格"
96
+ types="detail"
97
+ subValue="元"
98
+ />
99
+ </template>
100
+
101
+ <script setup>
102
+ import { ref } from 'vue';
103
+ import HwInput from '@/components/HwInput/HwInput.vue';
104
+
105
+ const inputValue = ref('100');
106
+ </script>
107
+ ```
108
+
109
+ ### 自定义样式
110
+
111
+ ```vue
112
+ <template>
113
+ <HwInput
114
+ v-model="inputValue"
115
+ label="描述"
116
+ :styles="{ color: 'blue', fontSize: '36rpx' }"
117
+ />
118
+ </template>
119
+
120
+ <script setup>
121
+ import { ref } from 'vue';
122
+ import HwInput from '@/components/HwInput/HwInput.vue';
123
+
124
+ const inputValue = ref('');
125
+ </script>
126
+ ```
127
+
128
+ ## 注意事项
129
+
130
+ 1. 组件使用了 uni-easyinput 作为基础组件
131
+ 2. 在详情模式下,组件只显示值,不提供编辑功能
132
+ 3. [types](file:///e:/temp/my-components/src/components/HwInput/HwInput.vue#L43-L43) 设置为 'detail' 时会显示为只读的详情模式
133
+ 4. 如果 [value](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L35-L40) 为空,在详情模式下会显示为 '-'
134
+ 5. [subValue](file:///e:/temp/my-components/src/components/HwInput/HwInput.vue#L44-L44) 只在详情模式下且有值时显示
@@ -4,7 +4,8 @@
4
4
  @click="_show"
5
5
  v-if="isShowLabel"
6
6
  class="label"
7
- :style="{ color: curLabel ? 'rgba(23, 26, 29, 1)' : 'rgba(23, 26, 29, 0.4)' }">
7
+ :style="{ color: curLabel ? 'rgba(23, 26, 29, 1)' : 'rgba(23, 26, 29, 0.4)' }"
8
+ >
8
9
  <slot name="title" :data="{ curLabel, placeholder }">
9
10
  <view class="text">{{ curLabel ? curLabel : placeholder }}</view>
10
11
  <view class="triangle"></view>
@@ -14,57 +15,76 @@
14
15
  <view class="tki-tree-mask" :class="{ show: showTree }" @tap="_cancel" />
15
16
  <view class="tki-tree-cnt" :class="{ show: showTree }">
16
17
  <view class="tki-tree-bar">
17
- <view class="tki-tree-bar-cancel" :style="{ color: cancelColor }" hover-class="hover-c" @tap="_cancel"
18
- >取消
18
+ <view class="tki-tree-bar-cancel" :style="{ color: cancelColor }" hover-class="hover-c" @tap="_cancel">
19
+ 取消
19
20
  </view>
20
21
  <view class="tki-tree-bar-title" :style="{ color: titleColor }">{{ title }}</view>
21
- <view class="tki-tree-bar-confirm" :style="{ color: confirmColor }" hover-class="hover-c" @tap="_confirm"
22
- >确定</view
23
- >
22
+ <view class="tki-tree-bar-confirm" :style="{ color: confirmColor }" hover-class="hover-c" @tap="_confirm">
23
+ 确定
24
+ </view>
24
25
  </view>
26
+
25
27
  <view class="tki-tree-view">
26
- <scroll-view class="tki-tree-view-sc" :scroll-y="true">
27
- <block v-for="(item, index) in treeList" :key="index">
28
- <view
29
- class="tki-tree-item"
30
- :style="[
31
- {
32
- paddingLeft: item.rank * 15 + 'px',
33
- zIndex: item.rank * -1 + 50,
34
- },
35
- ]"
36
- :class="{
37
- show: item.show,
38
- last: item.lastRank,
39
- showchild: item.showChild,
40
- open: item.open,
41
- }">
42
- <view class="tki-tree-label" @tap.stop="_treeItemTap(item, index)">
43
- <image
44
- class="tki-tree-icon"
45
- :src="item.lastRank ? lastIcon : item.showChild ? currentIcon : defaultIcon" />
46
- {{ item.name }}
47
- </view>
28
+ <uni-easyinput
29
+ v-if="isSearch"
30
+ class="search-input"
31
+ :styles="{
32
+ backgroundColor: 'rgba(126, 134, 142, 0.16)'
33
+ }"
34
+ prefixIcon="search"
35
+ placeholder="请输入"
36
+ v-model="searchName"
37
+ @input="changeSelect"
38
+ />
39
+ <view class="tki-tree-view-sc" :scroll-y="true">
40
+ <z-paging ref="paging" :fixed="false" v-model="dataList" @query="queryList">
41
+ <block v-for="(item, index) in treeList" :key="index">
48
42
  <view
49
- class="tki-tree-check"
50
- @tap.stop="_treeItemSelect(item, index)"
51
- v-if="selectParent ? true : item.lastRank">
52
- <view
53
- class="tki-tree-check-yes"
54
- v-if="item.checked"
55
- :class="{ radio: !multiple }"
56
- :style="{ 'border-color': confirmColor }">
57
- <view class="tki-tree-check-yes-b" :style="{ color: confirmColor }" />
43
+ class="tki-tree-item"
44
+ :style="[
45
+ {
46
+ paddingLeft: item.rank * 15 + 'px',
47
+ zIndex: item.rank * -1 + 50
48
+ }
49
+ ]"
50
+ :class="{
51
+ show: item.show,
52
+ last: item.lastRank,
53
+ showchild: item.showChild,
54
+ open: item.open
55
+ }"
56
+ >
57
+ <view class="tki-tree-label" @tap.stop="_treeItemTap(item, index)">
58
+ <image
59
+ class="tki-tree-icon"
60
+ :src="item.lastRank ? lastIcon : item.showChild ? currentIcon : defaultIcon"
61
+ />
62
+ {{ item.name }}
58
63
  </view>
59
64
  <view
60
- class="tki-tree-check-no"
61
- v-else
62
- :class="{ radio: !multiple }"
63
- :style="{ 'border-color': confirmColor }" />
65
+ class="tki-tree-check"
66
+ @tap.stop="_treeItemSelect(item, index)"
67
+ v-if="selectParent ? true : item.lastRank"
68
+ >
69
+ <view
70
+ class="tki-tree-check-yes"
71
+ v-if="item.checked"
72
+ :class="{ radio: !multiple }"
73
+ :style="{ 'border-color': confirmColor }"
74
+ >
75
+ <view class="tki-tree-check-yes-b" :style="{ color: confirmColor }" />
76
+ </view>
77
+ <view
78
+ class="tki-tree-check-no"
79
+ v-else
80
+ :class="{ radio: !multiple }"
81
+ :style="{ 'border-color': confirmColor }"
82
+ />
83
+ </view>
64
84
  </view>
65
- </view>
66
- </block>
67
- </scroll-view>
85
+ </block>
86
+ </z-paging>
87
+ </view>
68
88
  </view>
69
89
  </view>
70
90
  </view>
@@ -78,119 +98,160 @@ const props = defineProps({
78
98
  type: Array,
79
99
  default: function () {
80
100
  return [];
81
- },
101
+ }
102
+ },
103
+ rangeNext: {
104
+ type: Array,
105
+ default: function () {
106
+ return [];
107
+ }
108
+ },
109
+ request: {
110
+ type: Function,
111
+ default: function () {
112
+ return null;
113
+ }
82
114
  },
83
115
  idKey: {
84
116
  //字段key值
85
117
  type: String,
86
- default: 'id',
118
+ default: 'id'
87
119
  },
88
120
  // 字符串或者字符串数组
89
121
  modelValue: {
90
122
  type: String,
91
- default: '',
123
+ default: ''
92
124
  },
93
125
  nameKey: {
94
126
  //字段value值
95
127
  type: String,
96
- default: 'name',
128
+ default: 'name'
97
129
  },
98
130
  allKey: {
99
131
  //冗余字段
100
132
  type: String,
101
- default: 'value',
133
+ default: 'value'
102
134
  },
103
135
  placeholder: {
104
136
  //冗余字段
105
137
  type: String,
106
- default: '请选择',
138
+ default: '请选择'
107
139
  },
108
140
  title: {
109
141
  //头
110
142
  type: String,
111
- default: '',
143
+ default: ''
112
144
  },
113
145
  multiple: {
114
146
  // 是否可以多选
115
147
  type: Boolean,
116
- default: true,
148
+ default: true
117
149
  // default: true
118
150
  },
151
+ isSearch: {
152
+ type: Boolean,
153
+ default: false
154
+ },
119
155
  disabled: {
120
156
  // 是否禁用
121
157
  type: Boolean,
122
- default: false,
158
+ default: false
123
159
  },
124
160
  cascade: {
125
161
  // 是否级联选择
126
162
  type: Boolean,
127
- default: false,
163
+ default: false
128
164
  // default: true
129
165
  },
130
166
  isShowLabel: {
131
167
  // 是否显示标题
132
168
  type: Boolean,
133
- default: true,
169
+ default: true
134
170
  },
135
171
  selectParent: {
136
172
  //是否可以选父级
137
173
  type: Boolean,
138
- default: true,
174
+ default: true
139
175
  },
140
176
  foldAll: {
141
177
  //折叠时关闭所有已经打开的子集,再次打开时需要一级一级打开
142
178
  type: Boolean,
143
- default: false,
179
+ default: false
144
180
  },
145
181
  confirmColor: {
146
182
  // 确定按钮颜色
147
183
  type: String,
148
- default: '#007aff', // #1facac
184
+ default: '#007aff' // #1facac
149
185
  },
150
186
  cancelColor: {
151
187
  // 取消按钮颜色
152
188
  type: String,
153
- default: '#757575', // #757575
189
+ default: '#757575' // #757575
154
190
  },
155
191
  titleColor: {
156
192
  // 标题颜色
157
193
  type: String,
158
- default: '#757575', // #757575
194
+ default: '#757575' // #757575
159
195
  },
160
196
  currentIcon: {
161
197
  // 展开时候的ic
162
198
  type: String,
163
199
  default:
164
- 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MEQ0QTM0MzQ1Q0RBMTFFOUE0MjY4NzI1Njc1RjI1ODIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MEQ0QTM0MzU1Q0RBMTFFOUE0MjY4NzI1Njc1RjI1ODIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowRDRBMzQzMjVDREExMUU5QTQyNjg3MjU2NzVGMjU4MiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowRDRBMzQzMzVDREExMUU5QTQyNjg3MjU2NzVGMjU4MiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PidwepsAAAK0SURBVHja7JxbTsJAFIYHww7ciStgCeoGvGxAiOsgURegoL5720AXYLiIr0aJviq3Zx3PhIEnKG3ndtr+f3KixrSUj/ZjzjClIqUUiFm2gAAQAREQEUAEREAERAQQAREQAREBREAEREBEEqa67h9RFDWllDv0awWYlqlQHmu1WjMRRMoV1QFttA12y3xRtdNczq8EsE4/f8FumX2q77ROvNXk8UGMEKdUz6tYJHljaZAbuyUH+UR1to5BEohTuqwPCeS4pAA/qY6o/kyHOAMCeRK3owJnj+rH1jjxhqpVsstaebCz6TmnHWyXyY+xHjSBWBY/bvSgadtXBj9u9KCN3rnIfkzkQVsTEEX0Y2IP2oKo/HhMICcFAThUcwVZNGU6FdbX/XURzkbVF4+ybGhjPrFdgP66QdXNurGtSdk6Xdb9nAJ8oDo3OQlsQZzkdPw41ONBo6vI5scDefRjZg+6gpg3Pxp50CXEvPjR2IOuIXL3oxUPuobI3Y9WPOgDIlc/WvOgL4iL/vqFCcD7LH0xB4hj7cfQ/fWH9qCT+FhG0tN+DBk1PzjOM0SVllixcsBT1AvYc/kAPhc0hRg/3uvxoCgKRN9+dOrBUBB9+9GpB0NC9OVH5x4MDdG1H714kANEV3705kEOEBf9dcPi/lQnsuvLg1wgSu3Ha0v7Uh4MMgUXeuG71H407a+VBy9CPQkOdw+MtB+nGbd/D+FBbhBNxo9SjwcngJjNj0E9yBFiFj8G9SBXiGn8GNyDnCEm8SMLD3KHGOdHNh7kDjHOj2w8mAeIi/5arX+c6b/fxHz9oADEdGdjR/fXCw/OOB5oVfCOgnepz8IB14PMw03jCmTE+QBx5z0gAmKSqK9OUF+hcAeIhu/QYr4Qie8rjW83hhMBERARQAREQAREBBABERCLnH8BBgA+TQI7U4t53AAAAABJRU5ErkJggg==',
200
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MEQ0QTM0MzQ1Q0RBMTFFOUE0MjY4NzI1Njc1RjI1ODIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MEQ0QTM0MzU1Q0RBMTFFOUE0MjY4NzI1Njc1RjI1ODIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowRDRBMzQzMjVDREExMUU5QTQyNjg3MjU2NzVGMjU4MiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowRDRBMzQzMzVDREExMUU5QTQyNjg3MjU2NzVGMjU4MiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PidwepsAAAK0SURBVHja7JxbTsJAFIYHww7ciStgCeoGvGxAiOsgURegoL5720AXYLiIr0aJviq3Zx3PhIEnKG3ndtr+f3KixrSUj/ZjzjClIqUUiFm2gAAQAREQEUAEREAERAQQAREQAREBREAEREBEEqa67h9RFDWllDv0awWYlqlQHmu1WjMRRMoV1QFttA12y3xRtdNczq8EsE4/f8FumX2q77ROvNXk8UGMEKdUz6tYJHljaZAbuyUH+UR1to5BEohTuqwPCeS4pAA/qY6o/kyHOAMCeRK3owJnj+rH1jjxhqpVsstaebCz6TmnHWyXyY+xHjSBWBY/bvSgadtXBj9u9KCN3rnIfkzkQVsTEEX0Y2IP2oKo/HhMICcFAThUcwVZNGU6FdbX/XURzkbVF4+ybGhjPrFdgP66QdXNurGtSdk6Xdb9nAJ8oDo3OQlsQZzkdPw41ONBo6vI5scDefRjZg+6gpg3Pxp50CXEvPjR2IOuIXL3oxUPuobI3Y9WPOgDIlc/WvOgL4iL/vqFCcD7LH0xB4hj7cfQ/fWH9qCT+FhG0tN+DBk1PzjOM0SVllixcsBT1AvYc/kAPhc0hRg/3uvxoCgKRN9+dOrBUBB9+9GpB0NC9OVH5x4MDdG1H714kANEV3705kEOEBf9dcPi/lQnsuvLg1wgSu3Ha0v7Uh4MMgUXeuG71H407a+VBy9CPQkOdw+MtB+nGbd/D+FBbhBNxo9SjwcngJjNj0E9yBFiFj8G9SBXiGn8GNyDnCEm8SMLD3KHGOdHNh7kDjHOj2w8mAeIi/5arX+c6b/fxHz9oADEdGdjR/fXCw/OOB5oVfCOgnepz8IB14PMw03jCmTE+QBx5z0gAmKSqK9OUF+hcAeIhu/QYr4Qie8rjW83hhMBERARQAREQAREBBABERCLnH8BBgA+TQI7U4t53AAAAABJRU5ErkJggg=='
165
201
  },
166
202
  defaultIcon: {
167
203
  // 折叠时候的ic
168
204
  type: String,
169
205
  default:
170
- 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAACE0lEQVR4Xu3c200DMRCF4XEltJAOkEugA+ggpUAHoQMqiFMCdEAJUMEiS4mEELlIO7bPOeN9i6K1rG/952myyea1WiCtXmEuYBPR4RBMxInoIOCwhOtJLKVszWyXc/5y2BvNEq6I+/3+kFK6M7OHnPM7jcLKjbZAvD/uaZtzflm5P4rbWyJWgDcze1LPuzVihfxUz7sH4ilJ2bx7Isrm3RtRMu8RiHJ5j0SUyXs0okTeCIj0eSMh0uaNhkiZNyIiXd7IiDR5oyNS5M2ACJ83EyJs3myIkHkzIsLlzYwIkzc7IkTeCojD81ZCHJa3GuKQvBURu+etjNgtb3XELnlHQGyedyTEZnlHQ2ySd0RE97wjI7rlHR3RJe+JeIrbLOecD6ePpZQ6W1kn2epo4MUrPOKyLN8ppYq1+y1VStncOjIdGnFZlo+U0uOtWOeOY2TE12Ouq//pEA7xXL7XfvcufR8K0Svfv6CREN3yDYfYIt9QiK3yjYTYLF95xB75SiP2ylcZsVu+cogj8pVCHJWvEuKwfOkREfKlRkTJlxkRJl86RMR8qRBR82VChM0XHpEhX2hElnyREWnyhUNkzBcKkTVfJETafIcjKuQ7FFEl35GIMvl2R1TMtyuiar49EWXzbY5oZpv/hibXTF2h3+s60FRKeT6+3TjMS3nrA3ZFRD8xrfY3ER1kJ+JEdBBwWGKeRAfEH1wS5WFZSDB/AAAAAElFTkSuQmCC',
206
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAACE0lEQVR4Xu3c200DMRCF4XEltJAOkEugA+ggpUAHoQMqiFMCdEAJUMEiS4mEELlIO7bPOeN9i6K1rG/952myyea1WiCtXmEuYBPR4RBMxInoIOCwhOtJLKVszWyXc/5y2BvNEq6I+/3+kFK6M7OHnPM7jcLKjbZAvD/uaZtzflm5P4rbWyJWgDcze1LPuzVihfxUz7sH4ilJ2bx7Isrm3RtRMu8RiHJ5j0SUyXs0okTeCIj0eSMh0uaNhkiZNyIiXd7IiDR5oyNS5M2ACJ83EyJs3myIkHkzIsLlzYwIkzc7IkTeCojD81ZCHJa3GuKQvBURu+etjNgtb3XELnlHQGyedyTEZnlHQ2ySd0RE97wjI7rlHR3RJe+JeIrbLOecD6ePpZQ6W1kn2epo4MUrPOKyLN8ppYq1+y1VStncOjIdGnFZlo+U0uOtWOeOY2TE12Ouq//pEA7xXL7XfvcufR8K0Svfv6CREN3yDYfYIt9QiK3yjYTYLF95xB75SiP2ylcZsVu+cogj8pVCHJWvEuKwfOkREfKlRkTJlxkRJl86RMR8qRBR82VChM0XHpEhX2hElnyREWnyhUNkzBcKkTVfJETafIcjKuQ7FFEl35GIMvl2R1TMtyuiar49EWXzbY5oZpv/hibXTF2h3+s60FRKeT6+3TjMS3nrA3ZFRD8xrfY3ER1kJ+JEdBBwWGKeRAfEH1wS5WFZSDB/AAAAAElFTkSuQmCC'
171
207
  },
172
208
  lastIcon: {
173
209
  // 没有子集的ic
174
210
  type: String,
175
- default: '',
176
- },
211
+ default: ''
212
+ }
177
213
  });
178
214
 
179
215
  const emit = defineEmits(['cancel', 'confirm', 'update:modelValue']);
180
216
 
181
217
  defineExpose({
182
218
  _show,
183
- _hide,
219
+ _hide
184
220
  });
221
+ const paging = ref(null);
185
222
 
186
223
  const showTree = ref(false);
224
+ const dataList = ref([]);
225
+ const searchName = ref('');
187
226
  const treeList = ref([]);
188
227
  const returnedItem = ref([]);
189
228
  const childNums = ref([]);
190
- const curLabel = ref('');
229
+ const curLabel = defineModel('label', { default: '', type: String });
191
230
 
192
231
  let val = [];
232
+ let temporarySelectedValue = [];
193
233
  let label = [];
234
+ let originalTreeList = []; // 保存原始数据
235
+
236
+ const changeSelect = (val) => {
237
+ // 如果没有输入搜索内容,则恢复原始列表
238
+ if (props.request) {
239
+ paging.value && paging.value.reload();
240
+ } else {
241
+ if (!val) {
242
+ treeList.value = JSON.parse(JSON.stringify(originalTreeList));
243
+ // 确保所有节点都正确显示
244
+ treeList.value.forEach((item) => {
245
+ item.show = item.rank === 0 || item.show; // 根节点或原来就显示的节点
246
+ });
247
+ return;
248
+ }
249
+
250
+ treeList.value = originalTreeList.filter(
251
+ (item) => item.name && item.name.toLowerCase().includes(val.toLowerCase())
252
+ );
253
+ }
254
+ };
194
255
 
195
256
  /**
196
257
  * 监听范围数据变化,重新初始化树结构
@@ -198,8 +259,29 @@ let label = [];
198
259
  watch(
199
260
  () => props.range,
200
261
  (newVal) => {
201
- _initTree(newVal);
202
- },
262
+ if (!props.request) {
263
+ _initTree(newVal);
264
+ }
265
+ }
266
+ );
267
+ watch(
268
+ () => dataList.value,
269
+ (newVal) => {
270
+ if (props.request) {
271
+ const data = newVal.map((item) => {
272
+ return {
273
+ ...item,
274
+ label: item.resourceC,
275
+ value: item.resourceC
276
+ };
277
+ });
278
+ if (searchName.value) {
279
+ _initTree([...data]);
280
+ } else {
281
+ _initTree([...props.rangeNext, ...data]);
282
+ }
283
+ }
284
+ }
203
285
  );
204
286
  /**
205
287
  * 监听多选属性变化,重新设置树列表
@@ -208,7 +290,7 @@ watch(
208
290
  () => props.multiple,
209
291
  () => {
210
292
  _reTreeList();
211
- },
293
+ }
212
294
  );
213
295
  /**
214
296
  * 监听父级选择属性变化,重新设置树列表
@@ -217,7 +299,7 @@ watch(
217
299
  () => props.selectParent,
218
300
  () => {
219
301
  _reTreeList();
220
- },
302
+ }
221
303
  );
222
304
  /**
223
305
  * 监听模型值变化,处理默认选中项
@@ -225,24 +307,44 @@ watch(
225
307
  watch(
226
308
  () => props.modelValue,
227
309
  (newVal) => {
228
- if (newVal) {
229
- treeList.value = [];
230
- val = [];
231
- label = [];
310
+ if (!newVal) {
311
+ curLabel.value = '';
312
+ }
313
+ treeList.value = [];
314
+ val = [];
315
+ label = [];
316
+ if (props.request) {
317
+ paging.value && paging.value.reload();
318
+ } else {
232
319
  _renderTreeList(props.range);
233
320
  nextTick().then(() => {
234
321
  _defaultSelect(props.range);
235
322
  });
236
- } else {
237
- curLabel.value = '';
238
323
  }
239
- },
324
+ }
240
325
  );
241
326
 
242
327
  onMounted(() => {
243
- _initTree();
328
+ if (!props.request) {
329
+ _initTree(newVal);
330
+ }
244
331
  });
245
332
 
333
+ function queryList(pageNum, pageSize) {
334
+ props
335
+ .request({
336
+ pageSize,
337
+ pageNum,
338
+ resourceC: searchName.value
339
+ })
340
+ .then((res) => {
341
+ paging.value && paging.value.completeByTotal(res.rows || [], res?.total);
342
+ })
343
+ .catch(() => {
344
+ paging.value && paging.value.complete(false);
345
+ });
346
+ }
347
+
246
348
  /**
247
349
  * 显示树选择器
248
350
  */
@@ -255,6 +357,7 @@ function _show() {
255
357
  * 隐藏树选择器
256
358
  */
257
359
  function _hide() {
360
+ temporarySelectedValue = [];
258
361
  showTree.value = false;
259
362
  }
260
363
 
@@ -262,6 +365,16 @@ function _hide() {
262
365
  * 取消选择操作
263
366
  */
264
367
  function _cancel() {
368
+ if (!props.multiple) {
369
+ curLabel.value = '';
370
+ emit('update:modelValue', '');
371
+ emit('confirm', '');
372
+ } else {
373
+ val = val.filter((v) => !temporarySelectedValue.includes(v));
374
+ curLabel.value = val.join(',');
375
+
376
+ emit('update:modelValue', val);
377
+ }
265
378
  _hide();
266
379
  emit('cancel', []);
267
380
  }
@@ -275,9 +388,10 @@ function _confirm() {
275
388
  treeList.value.forEach((v, i) => {
276
389
  if (treeList.value[i].checked) {
277
390
  rt.push({
391
+ ...treeList.value[i],
278
392
  id: treeList.value[i].id,
279
393
  name: treeList.value[i].name,
280
- value: treeList.value[i].value,
394
+ value: treeList.value[i].value
281
395
  });
282
396
  }
283
397
  });
@@ -317,7 +431,11 @@ function _renderTreeList(list = [], rank = 0, parentId = [], parents = []) {
317
431
  } else {
318
432
  val = props.modelValue ? String(props.modelValue).split(',') : [];
319
433
  }
434
+ val = [...val, ...temporarySelectedValue];
320
435
  list.forEach((item) => {
436
+ if (label.includes(item[props.nameKey])) {
437
+ return;
438
+ }
321
439
  let checked = false;
322
440
  if (val.includes(item[props.allKey]) || val.includes(item[props.nameKey])) {
323
441
  checked = true;
@@ -337,7 +455,7 @@ function _renderTreeList(list = [], rank = 0, parentId = [], parents = []) {
337
455
  hideArr: [],
338
456
  orChecked: item.checked ? item.checked : false,
339
457
  checked: checked,
340
- childNum: 0,
458
+ childNum: 0
341
459
  });
342
460
 
343
461
  if (Array.isArray(item.children) && item.children.length > 0) {
@@ -348,14 +466,14 @@ function _renderTreeList(list = [], rank = 0, parentId = [], parents = []) {
348
466
  parentArr.push({
349
467
  [props.idKey]: item[props.idKey],
350
468
  [props.nameKey]: item[props.nameKey],
351
- [props.allKey]: item[props.allKey],
469
+ [props.allKey]: item[props.allKey]
352
470
  });
353
471
  _renderTreeList(item.children, rank + 1, parentid, parentArr);
354
472
  } else {
355
473
  treeList.value[treeList.value.length - 1].lastRank = true;
356
474
  }
357
475
  });
358
-
476
+ originalTreeList = JSON.parse(JSON.stringify(treeList.value));
359
477
  curLabel.value = label.join(',');
360
478
  }
361
479
  /**
@@ -460,7 +578,7 @@ function _treeItemTap(item, index) {
460
578
  hideArr: [],
461
579
  lastRank: itemC.children && itemC.children.length > 0 ? false : true,
462
580
  orChecked: treeList.value[index].checked,
463
- checked: props.cascade ? treeList.value[index].checked : false,
581
+ checked: props.cascade ? treeList.value[index].checked : false
464
582
  };
465
583
  if (!treeList.value.some((itemT) => itemT.id === itemC[props.idKey])) {
466
584
  treeList.value.splice(nextIndex + 1, 0, childObj);
@@ -565,6 +683,15 @@ function setAncestors(pids, checked) {
565
683
  */
566
684
  function _treeItemSelect(item, index) {
567
685
  treeList.value[index].checked = !treeList.value[index].checked;
686
+
687
+ if (treeList.value[index].checked) {
688
+ val = [...val, item.value];
689
+ temporarySelectedValue = [...temporarySelectedValue, item.value];
690
+ } else {
691
+ val = val.filter((v) => v !== item.value);
692
+ temporarySelectedValue = temporarySelectedValue.filter((v) => v !== item.value);
693
+ }
694
+
568
695
  // 选父级, 子级自动全选
569
696
  if (props.cascade) {
570
697
  syncChecked(treeList.value, item.id, treeList.value[index].checked);
@@ -657,6 +784,7 @@ function _initTree(range = props.range) {
657
784
  white-space: nowrap;
658
785
  overflow: hidden;
659
786
  text-overflow: ellipsis;
787
+ font-size: 34rpx;
660
788
  width: calc(100% - 16rpx);
661
789
  }
662
790
 
@@ -736,7 +864,7 @@ function _initTree(range = props.range) {
736
864
  }
737
865
 
738
866
  .tki-tree-view-sc {
739
- height: 100%;
867
+ height: calc(100% - 70rpx);
740
868
  overflow: hidden;
741
869
  }
742
870
 
@@ -0,0 +1,190 @@
1
+ # HwPickerTree 组件使用说明
2
+
3
+ HwPickerTree 是一个树形选择器组件,支持单选、多选、级联选择、搜索等功能,适用于复杂的层级数据选择场景。
4
+
5
+ ## 功能特性
6
+
7
+ - 支持单选和多选模式
8
+ - 支持级联选择
9
+ - 支持搜索功能
10
+ - 支持选择父级节点
11
+ - 支持自定义图标
12
+ - 支持异步数据加载
13
+ - 支持折叠/展开子节点
14
+
15
+ ## 属性 (Props)
16
+
17
+ | 属性 | 类型 | 默认值 | 说明 |
18
+ |------|------|--------|------|
19
+ | [range](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L14-L20) | Array | [] | 树形数据源 |
20
+ | [rangeNext](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L21-L27) | Array | [] | 额外的数据源(配合异步请求使用) |
21
+ | [request](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L28-L34) | Function | null | 异步请求函数 |
22
+ | [idKey](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L35-L40) | String | 'id' | 数据项的 ID 字段名 |
23
+ | [modelValue](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L43-L47) | String | '' | 绑定值 |
24
+ | [nameKey](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L48-L53) | String | 'name' | 数据项的名称字段名 |
25
+ | [allKey](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L54-L59) | String | 'value' | 数据项的值字段名 |
26
+ | [placeholder](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L60-L65) | String | '请选择' | 占位文本 |
27
+ | [title](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L66-L71) | String | '' | 选择器标题 |
28
+ | [multiple](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L72-L79) | Boolean | true | 是否多选 |
29
+ | [isSearch](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L80-L85) | Boolean | false | 是否显示搜索框 |
30
+ | [disabled](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L86-L91) | Boolean | false | 是否禁用 |
31
+ | [cascade](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L92-L99) | Boolean | false | 是否级联选择 |
32
+ | [isShowLabel](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L100-L105) | Boolean | true | 是否显示标签 |
33
+ | [selectParent](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L106-L111) | Boolean | true | 是否可以选择父级节点 |
34
+ | [foldAll](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L112-L117) | Boolean | false | 折叠时关闭所有子集 |
35
+ | [confirmColor](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L118-L123) | String | '#007aff' | 确认按钮颜色 |
36
+ | [cancelColor](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L124-L129) | String | '#757575' | 取消按钮颜色 |
37
+ | [titleColor](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L130-L135) | String | '#757575' | 标题颜色 |
38
+ | [currentIcon](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L136-L146) | String | 展开图标 | 展开时的图标 |
39
+ | [defaultIcon](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L147-L157) | String | 折叠图标 | 折叠时的图标 |
40
+ | [lastIcon](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L158-L163) | String | '' | 没有子集的图标 |
41
+
42
+ ## 事件 (Events)
43
+
44
+ | 事件名 | 参数 | 说明 |
45
+ |-------|------|------|
46
+ | cancel | - | 取消选择时触发 |
47
+ | confirm | (selectedData: Array) | 确认选择时触发 |
48
+ | update:modelValue | (value: String/Array) | 值变化时触发 |
49
+
50
+ ## 方法 (Methods)
51
+
52
+ | 方法名 | 参数 | 说明 |
53
+ |-------|------|------|
54
+ | _show | - | 显示选择器 |
55
+ | _hide | - | 隐藏选择器 |
56
+
57
+ ## 使用示例
58
+
59
+ ### 基础用法
60
+
61
+ ```vue
62
+ <template>
63
+ <HwPickerTree
64
+ v-model="selectedValue"
65
+ :range="treeData"
66
+ placeholder="请选择"
67
+ title="选择节点"
68
+ />
69
+ </template>
70
+
71
+ <script setup>
72
+ import { ref } from 'vue';
73
+ import HwPickerTree from '@/components/HwPickerTree/HwPickerTree.vue';
74
+
75
+ const selectedValue = ref('');
76
+ const treeData = ref([
77
+ {
78
+ id: 1,
79
+ name: '节点1',
80
+ value: 'node1',
81
+ children: [
82
+ {
83
+ id: 11,
84
+ name: '子节点1-1',
85
+ value: 'node1-1'
86
+ },
87
+ {
88
+ id: 12,
89
+ name: '子节点1-2',
90
+ value: 'node1-2'
91
+ }
92
+ ]
93
+ },
94
+ {
95
+ id: 2,
96
+ name: '节点2',
97
+ value: 'node2'
98
+ }
99
+ ]);
100
+ </script>
101
+ ```
102
+
103
+ ### 多选模式
104
+
105
+ ```vue
106
+ <template>
107
+ <HwPickerTree
108
+ v-model="selectedValue"
109
+ :range="treeData"
110
+ :multiple="true"
111
+ placeholder="请选择"
112
+ title="多选节点"
113
+ />
114
+ </template>
115
+
116
+ <script setup>
117
+ import { ref } from 'vue';
118
+ import HwPickerTree from '@/components/HwPickerTree/HwPickerTree.vue';
119
+
120
+ const selectedValue = ref([]);
121
+ const treeData = ref([
122
+ // ... 同上
123
+ ]);
124
+ </script>
125
+ ```
126
+
127
+ ### 级联选择
128
+
129
+ ```vue
130
+ <template>
131
+ <HwPickerTree
132
+ v-model="selectedValue"
133
+ :range="treeData"
134
+ :multiple="true"
135
+ :cascade="true"
136
+ placeholder="请选择"
137
+ title="级联选择"
138
+ />
139
+ </template>
140
+
141
+ <script setup>
142
+ import { ref } from 'vue';
143
+ import HwPickerTree from '@/components/HwPickerTree/HwPickerTree.vue';
144
+
145
+ const selectedValue = ref([]);
146
+ const treeData = ref([
147
+ // ... 同上
148
+ ]);
149
+ </script>
150
+ ```
151
+
152
+ ### 异步数据加载
153
+
154
+ ```vue
155
+ <template>
156
+ <HwPickerTree
157
+ :request="fetchData"
158
+ placeholder="请选择"
159
+ title="异步数据"
160
+ />
161
+ </template>
162
+
163
+ <script setup>
164
+ import { ref } from 'vue';
165
+ import HwPickerTree from '@/components/HwPickerTree/HwPickerTree.vue';
166
+
167
+ const fetchData = (params) => {
168
+ // 返回一个Promise
169
+ return new Promise((resolve) => {
170
+ setTimeout(() => {
171
+ resolve({
172
+ rows: [
173
+ { id: 1, name: '选项1', value: 'value1' },
174
+ { id: 2, name: '选项2', value: 'value2' }
175
+ ],
176
+ total: 2
177
+ });
178
+ }, 1000);
179
+ });
180
+ };
181
+ </script>
182
+ ```
183
+
184
+ ## 注意事项
185
+
186
+ 1. 树形数据结构需要遵循 `{ id: '唯一标识', name: '显示名称', value: '值', children: [子节点数组] }` 的格式
187
+ 2. 当使用异步数据加载时,需要提供 [request](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L28-L34) 函数
188
+ 3. 级联选择模式下,选择父节点会自动选择所有子节点
189
+ 4. [modelValue](file:///e:/temp/my-components/src/components/HwPickerTree/HwPickerTree.vue#L43-L47) 在多选模式下为数组,在单选模式下为字符串
190
+ 5. 组件内部使用了 z-paging 分页组件来处理大量数据的展示
@@ -0,0 +1,122 @@
1
+ # HwSelect 组件使用说明
2
+
3
+ HwSelect 是一个通用的选择器组件,支持选择和展示选项功能。可以根据传入的数组数据展示选项,并支持表单编辑和详情展示两种模式。
4
+
5
+ ## 功能特性
6
+
7
+ - 支持编辑模式和详情模式
8
+ - 支持自定义选项数据
9
+ - 支持必填标识显示
10
+ - 支持占位符文本
11
+ - 支持自定义背景颜色
12
+ - 支持双向数据绑定
13
+
14
+ ## 属性 (Props)
15
+
16
+ | 属性 | 类型 | 默认值 | 说明 |
17
+ |------|------|--------|------|
18
+ | [rangeKey](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L57-L57) | string | 'label' | 指定选项对象中用于显示的键名 |
19
+ | [rangeValue](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L58-L58) | string | 'value' | 指定选项对象中用于值的键名 |
20
+ | [marginTop](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L59-L59) | string | '20rpx' | 组件上边距 |
21
+ | [label](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L60-L60) | string | - | 选项标签文本 |
22
+ | [placeholder](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L61-L61) | string | '请选择' | 占位符文本 |
23
+ | [types](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L62-L62) | string | 'add' | 组件类型,'add' 为编辑模式,'detail' 为详情模式 |
24
+ | [isDetail](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L63-L63) | boolean | false | 是否为详情模式的标识 |
25
+ | [required](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L64-L64) | boolean | false | 是否必填项标识 |
26
+ | [backgroundColor](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L65-L65) | string | 'transparent' | 背景颜色 |
27
+ | [array](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L66-L66) | Array&lt;any&gt; | - | 选项数据数组 |
28
+
29
+ ## 事件 (Events)
30
+
31
+ | 事件名 | 参数 | 说明 |
32
+ |-------|------|------|
33
+ | change | (selectedItem: any) | 当选择值变化时触发,返回选中的选项对象 |
34
+
35
+ ## 使用示例
36
+
37
+ ### 基础用法
38
+
39
+ ```vue
40
+ <template>
41
+ <HwSelect
42
+ v-model="selectedValue"
43
+ label="选择项"
44
+ :array="options"
45
+ @change="handleChange"
46
+ />
47
+ </template>
48
+
49
+ <script setup>
50
+ import { ref } from 'vue';
51
+ import HwSelect from '@/components/HwSelect/HwSelect.vue';
52
+
53
+ const selectedValue = ref('');
54
+ const options = [
55
+ { label: '选项1', value: '1' },
56
+ { label: '选项2', value: '2' },
57
+ { label: '选项3', value: '3' }
58
+ ];
59
+
60
+ const handleChange = (selectedItem) => {
61
+ console.log('选中的项:', selectedItem);
62
+ };
63
+ </script>
64
+ ```
65
+
66
+ ### 详情模式
67
+
68
+ ```vue
69
+ <template>
70
+ <HwSelect
71
+ v-model="selectedValue"
72
+ label="选择项"
73
+ :array="options"
74
+ types="detail"
75
+ />
76
+ </template>
77
+
78
+ <script setup>
79
+ import { ref } from 'vue';
80
+ import HwSelect from '@/components/HwSelect/HwSelect.vue';
81
+
82
+ const selectedValue = ref('2');
83
+ const options = [
84
+ { label: '选项1', value: '1' },
85
+ { label: '选项2', value: '2' },
86
+ { label: '选项3', value: '3' }
87
+ ];
88
+ </script>
89
+ ```
90
+
91
+ ### 必填项
92
+
93
+ ```vue
94
+ <template>
95
+ <HwSelect
96
+ v-model="selectedValue"
97
+ label="必填选择项"
98
+ :required="true"
99
+ :array="options"
100
+ />
101
+ </template>
102
+
103
+ <script setup>
104
+ import { ref } from 'vue';
105
+ import HwSelect from '@/components/HwSelect/HwSelect.vue';
106
+
107
+ const selectedValue = ref('');
108
+ const options = [
109
+ { label: '选项1', value: '1' },
110
+ { label: '选项2', value: '2' }
111
+ ];
112
+ </script>
113
+ ```
114
+
115
+ ## 注意事项
116
+
117
+ 1. 组件基于 uni-app 的 picker 组件实现
118
+ 2. [array](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L66-L66) 属性需要传入一个对象数组,每个对象需要包含 [rangeKey](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L57-L57) 和 [rangeValue](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L58-L58) 指定的属性
119
+ 3. 在详情模式下,组件只显示选中项的文本,不提供编辑功能
120
+ 4. [types](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L62-L62) 设置为 'detail' 时会显示为只读的详情模式
121
+ 5. [rangeKey](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L57-L57) 默认为 'label',用于显示选项文本
122
+ 6. [rangeValue](file:///e:/temp/my-components/src/components/HwSelect/HwSelect.vue#L58-L58) 默认为 'value',用于存储选项值
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hw-cus-ui",
3
- "version": "1.0.25",
4
- "description": "",
3
+ "version": "1.1.0",
4
+ "description": "使用该组件库需安装uniapp插件@dcloudio/uni-ui",
5
5
  "main": "index.ts",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1"