You need to sign in or sign up before continuing.
hookehuyr

新增树形选择组件

...@@ -7,11 +7,13 @@ export {} ...@@ -7,11 +7,13 @@ export {}
7 7
8 declare module '@vue/runtime-core' { 8 declare module '@vue/runtime-core' {
9 export interface GlobalComponents { 9 export interface GlobalComponents {
10 + _index: typeof import('./src/components/TreeField/_index.vue')['default']
10 AppointmentField: typeof import('./src/components/AppointmentField/index.vue')['default'] 11 AppointmentField: typeof import('./src/components/AppointmentField/index.vue')['default']
11 AreaPickerField: typeof import('./src/components/AreaPickerField/index.vue')['default'] 12 AreaPickerField: typeof import('./src/components/AreaPickerField/index.vue')['default']
12 ButtonField: typeof import('./src/components/ButtonField/index.vue')['default'] 13 ButtonField: typeof import('./src/components/ButtonField/index.vue')['default']
13 CalendarField: typeof import('./src/components/CalendarField/index.vue')['default'] 14 CalendarField: typeof import('./src/components/CalendarField/index.vue')['default']
14 CheckboxField: typeof import('./src/components/CheckboxField/index.vue')['default'] 15 CheckboxField: typeof import('./src/components/CheckboxField/index.vue')['default']
16 + Children: typeof import('./src/components/TreeField/children.vue')['default']
15 ContactField: typeof import('./src/components/ContactField/index.vue')['default'] 17 ContactField: typeof import('./src/components/ContactField/index.vue')['default']
16 CustomField: typeof import('./src/components/CustomField/index.vue')['default'] 18 CustomField: typeof import('./src/components/CustomField/index.vue')['default']
17 DatePickerField: typeof import('./src/components/DatePickerField/index.vue')['default'] 19 DatePickerField: typeof import('./src/components/DatePickerField/index.vue')['default']
...@@ -43,6 +45,9 @@ declare module '@vue/runtime-core' { ...@@ -43,6 +45,9 @@ declare module '@vue/runtime-core' {
43 TextareaField: typeof import('./src/components/TextareaField/index.vue')['default'] 45 TextareaField: typeof import('./src/components/TextareaField/index.vue')['default']
44 TextField: typeof import('./src/components/TextField/index.vue')['default'] 46 TextField: typeof import('./src/components/TextField/index.vue')['default']
45 TimePickerField: typeof import('./src/components/TimePickerField/index.vue')['default'] 47 TimePickerField: typeof import('./src/components/TimePickerField/index.vue')['default']
48 + Tree: typeof import('./src/components/TreeField/tree.vue')['default']
49 + TreeField: typeof import('./src/components/TreeField/index.vue')['default']
50 + TreeSelect: typeof import('./src/components/TreeField/treeSelect.vue')['default']
46 VanArea: typeof import('vant/es')['Area'] 51 VanArea: typeof import('vant/es')['Area']
47 VanButton: typeof import('vant/es')['Button'] 52 VanButton: typeof import('vant/es')['Button']
48 VanCalendar: typeof import('vant/es')['Calendar'] 53 VanCalendar: typeof import('vant/es')['Calendar']
...@@ -69,8 +74,11 @@ declare module '@vue/runtime-core' { ...@@ -69,8 +74,11 @@ declare module '@vue/runtime-core' {
69 VanRadioGroup: typeof import('vant/es')['RadioGroup'] 74 VanRadioGroup: typeof import('vant/es')['RadioGroup']
70 VanRate: typeof import('vant/es')['Rate'] 75 VanRate: typeof import('vant/es')['Rate']
71 VanRow: typeof import('vant/es')['Row'] 76 VanRow: typeof import('vant/es')['Row']
77 + VanSearch: typeof import('vant/es')['Search']
72 VanSwipe: typeof import('vant/es')['Swipe'] 78 VanSwipe: typeof import('vant/es')['Swipe']
73 VanSwipeItem: typeof import('vant/es')['SwipeItem'] 79 VanSwipeItem: typeof import('vant/es')['SwipeItem']
80 + VanTab: typeof import('vant/es')['Tab']
81 + VanTabs: typeof import('vant/es')['Tabs']
74 VanTimePicker: typeof import('vant/es')['TimePicker'] 82 VanTimePicker: typeof import('vant/es')['TimePicker']
75 VanUploader: typeof import('vant/es')['Uploader'] 83 VanUploader: typeof import('vant/es')['Uploader']
76 VideoField: typeof import('./src/components/VideoField/index.vue')['default'] 84 VideoField: typeof import('./src/components/VideoField/index.vue')['default']
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
39 "@vant/touch-emulator": "^1.4.0", 39 "@vant/touch-emulator": "^1.4.0",
40 "@vitejs/plugin-legacy": "^1.8.2", 40 "@vitejs/plugin-legacy": "^1.8.2",
41 "@vueuse/core": "^8.5.0", 41 "@vueuse/core": "^8.5.0",
42 + "@wsfe/vue-tree": "^3.2.0",
42 "animate.css": "^4.1.1", 43 "animate.css": "^4.1.1",
43 "browser-md5-file": "^1.1.1", 44 "browser-md5-file": "^1.1.1",
44 "dayjs": "^1.11.3", 45 "dayjs": "^1.11.3",
...@@ -55,7 +56,7 @@ ...@@ -55,7 +56,7 @@
55 "sha1": "^1.1.1", 56 "sha1": "^1.1.1",
56 "typescript": "^4.7.3", 57 "typescript": "^4.7.3",
57 "uuid": "^8.3.2", 58 "uuid": "^8.3.2",
58 - "vant": "^4.1.1", 59 + "vant": "^4.9.0",
59 "vconsole": "^3.14.6", 60 "vconsole": "^3.14.6",
60 "vite-plugin-dynamic-import": "^0.9.6", 61 "vite-plugin-dynamic-import": "^0.9.6",
61 "vite-plugin-mp": "^1.6.1", 62 "vite-plugin-mp": "^1.6.1",
......
1 +<!--
2 + * @Date: 2022-08-29 14:31:20
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2024-05-29 14:19:52
5 + * @FilePath: /data-table/src/components/TreeField/index.vue
6 + * @Description: 树形组件
7 +-->
8 +<template>
9 + <div v-if="HideShow" class="name-field-page">
10 + <div :class="[isGroup ? 'group-label' : 'label']">
11 + <span v-if="item.component_props.required">&nbsp;*</span>
12 + {{ item.component_props.label }}
13 + </div>
14 +
15 + <van-popup
16 + v-model:show="showBottom"
17 + position="bottom"
18 + :style="{ height: '80vh' }"
19 + >
20 + <div v-if="!is_search" class="search-box" @click="onSearchFocus">
21 + <van-icon name="search" size="1.1rem" />&nbsp;点击搜索
22 + </div>
23 + <van-field ref="searchInputRef" v-else v-model="search_value" label="" placeholder="可通过名称,手机号或邮箱查询" :border="false" @blur="onSearchBlur" @focus="onSearchFocus">
24 + <template #button>
25 + <van-button size="small" type="primary" @click="onCloseSearch">关闭</van-button>
26 + </template>
27 + </van-field>
28 +
29 + <div>
30 + {{ value }}
31 + </div>
32 +
33 + <div v-if="!is_search" class="tab-tree-container">
34 + <van-tabs :color="styleColor.baseColor" v-model:active="tabActive" @click-tab="onClickTab" style="margin-bottom: 1rem;">
35 + <van-tab title="组织结构"></van-tab>
36 + <van-tab title="角色"></van-tab>
37 + <van-tab title="成员"></van-tab>
38 + </van-tabs>
39 +
40 + <div v-if="tabActive === 0" style="padding: 0 0 1rem 1rem;">
41 + <Vtree
42 + ref="orgTreeRef"
43 + v-model="select_org_value"
44 + checkable
45 + titleField="name"
46 + keyField="id"
47 + :expandOnFilter="false"
48 + :showCheckedButton="false"
49 + :showFooter="false"
50 + :cascade="false"
51 + :defaultExpandAll="false"
52 + @search="searchMethod"
53 + @checked-change="checkedChangeMethod"
54 + >
55 + <span slot="empty">暂无数据</span>
56 + </Vtree>
57 + </div>
58 +
59 + <div v-if="tabActive === 1" style="padding: 0 0 1rem 1rem;">
60 + <van-checkbox-group
61 + v-model="role_checked"
62 + >
63 + <van-checkbox v-for="(role, index) in roleList" :key="index" :name="role.id" shape="square" icon-size="13px" :checked-color="styleColor.baseColor" style="margin-bottom: 0.5rem;">{{ role.name }}</van-checkbox>
64 + </van-checkbox-group>
65 + </div>
66 +
67 + <div v-if="tabActive === 2" style="padding: 0 0 0 1rem;">
68 + <van-row gutter="">
69 + <van-col span="10" style="border-right: 1px solid #eee; height: 70vh; max-height: 60vh; overflow: scroll;">
70 + <Vtree
71 + ref="memberTreeRef"
72 + v-model="select_member_value"
73 + selectable
74 + titleField="name"
75 + keyField="id"
76 + :expandOnFilter="false"
77 + :showCheckedButton="false"
78 + @update:modelValue="() => {}"
79 + >
80 + <span slot="empty">暂无数据</span>
81 + </Vtree>
82 + </van-col>
83 + <van-col span="14">
84 + <!-- {{ select_member_value }} -->
85 + <van-checkbox-group
86 + v-model="member_checked"
87 + style="padding: 0 0 1rem 1rem;"
88 + >
89 + <van-checkbox v-for="(member, index) in memberList" :key="index" :name="member.id" shape="square" icon-size="13px" :checked-color="styleColor.baseColor" style="margin-bottom: 0.5rem;">{{ member.name }}</van-checkbox>
90 + </van-checkbox-group>
91 + </van-col>
92 + </van-row>
93 + </div>
94 + </div>
95 +
96 + <div v-else class="search-container">
97 + <div>
98 + <p>部门</p>
99 + <div>1</div>
100 + </div>
101 + <div>
102 + <p>角色</p>
103 + <div>2</div>
104 + </div>
105 + <div>
106 + <p>成员</p>
107 + <div>3</div>
108 + </div>
109 + </div>
110 +
111 + </van-popup>
112 +
113 + </div>
114 +</template>
115 +
116 +<script setup>
117 +import { styleColor } from "@/constant.js";
118 +// 大家可以根据需要是否引入VTreeNode, VTreeSearch, VTreeDrop
119 +import Vtree, { VTreeNode, VTreeSearch, VTreeDrop } from '@wsfe/vue-tree'
120 +import '@wsfe/vue-tree/style.css';
121 +
122 +const props = defineProps({
123 + item: Object,
124 +});
125 +
126 +// 隐藏显示
127 +const HideShow = computed(() => {
128 + return !props.item.component_props.disabled
129 +});
130 +
131 +// 集合组标识
132 +const isGroup = computed(() => {
133 + return props.item.component_props.is_field_group
134 +});
135 +
136 +const showBottom = ref(true);
137 +
138 +const search_value = ref('');
139 +const onSearch = (val) => {
140 + console.log(val);
141 +};
142 +const onCancel = () => {
143 + search_value.value = '';
144 +};
145 +
146 +const tabActive = ref(2);
147 +const select_org_value = ref();
148 +const orgTreeRef = ref();
149 +const role_checked = ref([]);
150 +const roleList = ref([{
151 + id: 'a',
152 + name: '法务组长',
153 +}, {
154 + id: 'b',
155 + name: '接待组长',
156 +}, {
157 + id: 'c',
158 + name: '场地管理',
159 +}]);
160 +const select_member_value = ref();
161 +const memberTreeRef = ref();
162 +const member_checked = ref([]);
163 +const memberList = ref([{
164 + id: 'a',
165 + name: '法务组长',
166 +}, {
167 + id: 'b',
168 + name: '接待组长',
169 +}, {
170 + id: 'c',
171 + name: '场地管理',
172 +}]);
173 +
174 +const onClickTab = ({ title }) => { // tab点击事件
175 + if (title === '组织结构') {
176 + nextTick(() => {
177 + orgListReset();
178 + });
179 + }
180 + if (title === '角色') {
181 + nextTick(() => {
182 + roleListReset();
183 + });
184 + }
185 + if (title === '成员') {
186 + nextTick(() => {
187 + memberListReset();
188 + });
189 + }
190 +};
191 +
192 +const testData = [{
193 + id: 1,
194 + name: '西园寺',
195 + children: [{
196 + name: '保安',
197 + id: 2,
198 + children: [{
199 + name: '1号',
200 + id: 3
201 + }]
202 + }, {
203 + name: '餐饮',
204 + id: 4,
205 + children: [{
206 + name: '5号',
207 + id: 5
208 + }]
209 + }, {
210 + name: '保安',
211 + id: 21,
212 + children: [{
213 + name: '1号',
214 + id: 31
215 + }]
216 + }, {
217 + name: '餐饮',
218 + id: 41,
219 + children: [{
220 + name: '5号',
221 + id: 51
222 + }]
223 + }, {
224 + name: '保安',
225 + id: 22,
226 + children: [{
227 + name: '1号',
228 + id: 32
229 + }]
230 + }, {
231 + name: '餐饮',
232 + id: 42,
233 + children: [{
234 + name: '5号',
235 + id: 52
236 + }]
237 + }]
238 +}]
239 +
240 +onMounted(() => {
241 + props.item.value = props.item.component_props.default;
242 +
243 + // TODO:获取数据
244 + memberTreeRef.value.setData(testData);
245 + // 默认展开第一个
246 + memberTreeRef.value.setExpand(1, true)
247 +});
248 +
249 +const searchMethod = (value) => {
250 + console.log(value)
251 +}
252 +
253 +const checkedChangeMethod = (value) => {
254 + console.log(value)
255 + // console.log(orgTreeRef.value.getCheckedNodes())
256 +}
257 +
258 +const is_search = ref(false); // 默认不显示搜索框
259 +
260 +const onSearchBlur = () => { // 搜索框失去焦点
261 +
262 +}
263 +const onSearchFocus = () => { // 搜索框获取焦点
264 + is_search.value = true;
265 + nextTick(() => {
266 + searchInputRef.value.focus()
267 + })
268 +}
269 +
270 +const onCloseSearch = () => {
271 + is_search.value = false;
272 +}
273 +
274 +const searchInputRef = ref(null);
275 +
276 +const orgListReset = () => { // 组织重置列表
277 + orgTreeRef.value.setData(testData);
278 +
279 + orgTreeRef.value.setExpand(1, true)
280 +}
281 +const roleListReset = () => {
282 +
283 +}
284 +const memberListReset = () => {
285 + memberTreeRef.value.setData(testData);
286 +
287 + memberTreeRef.value.setExpand(1, true)
288 +}
289 +</script>
290 +
291 +<style lang="less" scoped>
292 +.name-field-page {
293 + .label {
294 + padding: 1rem 1rem 0 1rem;
295 + font-size: 0.9rem;
296 + font-weight: bold;
297 + span {
298 + color: red;
299 + }
300 + }
301 +
302 + .group-label {
303 + padding: 0.75rem 0 0.75rem 1rem;
304 + font-size: 0.9rem;
305 + font-weight: bold;
306 + background-color: #f9f9f9;
307 + color: #666;
308 + border-top: 1px solid #eee;
309 + border-bottom: 1px solid #eee;
310 +
311 + span {
312 + color: red;
313 + }
314 + }
315 +}
316 +
317 +:deep(.van-field__body) {
318 + border: 1px solid #eaeaea;
319 + border-radius: 0.25rem;
320 + padding: 0.25rem 0.5rem;
321 +}
322 +
323 +.search-box {
324 + display: flex;
325 + align-items: center;
326 + justify-content: center;
327 + background-color: #eee;
328 + margin: 1rem;
329 + border-radius: 3px;
330 + padding: 0.6rem;
331 + font-size: 0.9rem;
332 +}
333 +
334 +:deep(.ctree-tree-node__checkbox_checked) {
335 + border-color: #C2915F;
336 + background-color: #C2915F;
337 +}
338 +
339 +:deep(.ctree-tree-node__title_selected) {
340 + background-color: #f8e2cb;
341 +}
342 +</style>
...@@ -32,6 +32,7 @@ import GenderField from '@/components/GenderField/index.vue'; ...@@ -32,6 +32,7 @@ import GenderField from '@/components/GenderField/index.vue';
32 import AppointmentField from '@/components/AppointmentField/index.vue'; 32 import AppointmentField from '@/components/AppointmentField/index.vue';
33 import CustomField from '@/components/CustomField/index.vue'; 33 import CustomField from '@/components/CustomField/index.vue';
34 import GroupField from '@/components/GroupField/index.vue'; 34 import GroupField from '@/components/GroupField/index.vue';
35 +import TreeField from '@/components/TreeField/index.vue';
35 36
36 /** 37 /**
37 * 生成自定义组件类型 38 * 生成自定义组件类型
...@@ -63,6 +64,7 @@ import GroupField from '@/components/GroupField/index.vue'; ...@@ -63,6 +64,7 @@ import GroupField from '@/components/GroupField/index.vue';
63 * @type gender 性别控件 GenderField 64 * @type gender 性别控件 GenderField
64 * @type appointment 预约控件 AppointmentField 65 * @type appointment 预约控件 AppointmentField
65 * @type group 组集合输入控件 GroupField 66 * @type group 组集合输入控件 GroupField
67 + * @type tree 树形选择控件 TreeField
66 */ 68 */
67 export function createComponentType(data) { 69 export function createComponentType(data) {
68 // 判断类型和使用组件 70 // 判断类型和使用组件
...@@ -195,5 +197,9 @@ export function createComponentType(data) { ...@@ -195,5 +197,9 @@ export function createComponentType(data) {
195 item.name = item.key; 197 item.name = item.key;
196 item.component = GroupField; 198 item.component = GroupField;
197 } 199 }
200 + if (item.component_props.tag === 'tree') {
201 + item.name = item.key;
202 + item.component = TreeField;
203 + }
198 }) 204 })
199 } 205 }
......
1 <!-- 1 <!--
2 * @Date: 2022-07-18 10:22:22 2 * @Date: 2022-07-18 10:22:22
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2024-05-28 10:39:05 4 + * @LastEditTime: 2024-05-29 14:28:37
5 * @FilePath: /data-table/src/views/index.vue 5 * @FilePath: /data-table/src/views/index.vue
6 * @Description: 首页 6 * @Description: 首页
7 --> 7 -->
...@@ -249,6 +249,23 @@ onMounted(async () => { ...@@ -249,6 +249,23 @@ onMounted(async () => {
249 // field_name : "field_4", 249 // field_name : "field_4",
250 // index : 41, 250 // index : 41,
251 // interaction_type : "h5edit", 251 // interaction_type : "h5edit",
252 + // label : "树形选择器",
253 + // name : "name_41",
254 + // placeholder : "请输入",
255 + // readonly : false,
256 + // required : false,
257 + // tag : "tree",
258 + // unique : false,
259 + // });
260 +
261 + // page_form.unshift({
262 + // data_type : "text",
263 + // default : "",
264 + // disabled : false,
265 + // field_id : 1414832,
266 + // field_name : "field_4",
267 + // index : 41,
268 + // interaction_type : "h5edit",
252 // label : "物品详情", 269 // label : "物品详情",
253 // name : "name_41", 270 // name : "name_41",
254 // placeholder : "请输入", 271 // placeholder : "请输入",
......
This diff could not be displayed because it is too large.