Skip to content

Commit c29d747

Browse files
authored
feat:selec重构为组件selectWrapper (#3862)
1 parent 4047778 commit c29d747

File tree

18 files changed

+1416
-418
lines changed

18 files changed

+1416
-418
lines changed

packages/modules.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2174,6 +2174,14 @@
21742174
"pc"
21752175
]
21762176
},
2177+
"SelectWrapper": {
2178+
"path": "vue/src/select-wrapper/index.ts",
2179+
"type": "component",
2180+
"exclude": false,
2181+
"mode": [
2182+
"pc"
2183+
]
2184+
},
21772185
"SelectDropdown": {
21782186
"path": "vue/src/select-dropdown/index.ts",
21792187
"type": "component",
@@ -2211,6 +2219,11 @@
22112219
"type": "template",
22122220
"exclude": false
22132221
},
2222+
"SelectWrapperPc": {
2223+
"path": "vue/src/select-wrapper/src/pc.vue",
2224+
"type": "template",
2225+
"exclude": false
2226+
},
22142227
"SelectView": {
22152228
"path": "vue/src/select-view/index.ts",
22162229
"type": "component",
@@ -3039,4 +3052,4 @@
30393052
"type": "template",
30403053
"exclude": false
30413054
}
3042-
}
3055+
}

packages/renderless/src/base-select/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2007,7 +2007,7 @@ export const computedGetIcon =
20072007
return props.dropdownIcon
20082008
? { icon: props.dropdownIcon }
20092009
: {
2010-
icon: designConfig?.icons.dropdownIcon || 'icon-delta-down',
2010+
icon: designConfig?.icons.dropdownIcon || 'icon-down-ward',
20112011
isDefault: true
20122012
}
20132013
}

packages/renderless/src/grid-select/index.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,31 @@ export const syncGridSelection =
6868
}
6969

7070
export const handleVisibleChange =
71-
({ api, state }) =>
71+
({ api, state, props }) =>
7272
(visible: boolean) => {
7373
// 面板打开时,同步表格选中状态
7474
if (visible && state.isMounted) {
7575
api.syncGridSelection()
76+
77+
// 如果启用了自动搜索且是远程搜索,触发初始查询
78+
if (props.remote && props.remoteConfig?.autoSearch && state.firstAutoSearch !== false) {
79+
// 触发远程搜索,使用空字符串作为初始查询
80+
api.filter('')
81+
state.firstAutoSearch = false
82+
}
7683
}
7784
}
7885

7986
export const buildSelectConfig =
8087
({ props, state }) =>
8188
() => {
82-
const checkRowKeys = state.gridCheckedData
8389
const selectConfig = props.selectConfig
90+
const rawCheckRowKeys = state.gridCheckedData
91+
const checkRowKeys = Array.isArray(rawCheckRowKeys)
92+
? rawCheckRowKeys
93+
: rawCheckRowKeys && Array.isArray(rawCheckRowKeys.value)
94+
? rawCheckRowKeys.value
95+
: []
8496

8597
return Object.assign({}, selectConfig, { checkRowKeys })
8698
}

packages/renderless/src/grid-select/vue.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,14 @@ export const api = [
2222
'selectChange',
2323
'getcheckedData',
2424
'getPluginOption',
25-
'initQuery',
2625
'mounted',
2726
'syncGridSelection',
2827
'watchValue',
2928
'handleVisibleChange'
3029
]
3130

3231
export const renderless = (props, { reactive, computed, watch, onMounted, nextTick }, { vm, emit }) => {
33-
const api = {}
32+
const api: any = {}
3433

3534
// 初始化 gridData,支持 { data: [], columns: [] } 格式
3635
const initGridData = () => {
@@ -54,7 +53,8 @@ export const renderless = (props, { reactive, computed, watch, onMounted, nextTi
5453
currentKey: props.multiple ? '' : props.modelValue,
5554
previousQuery: null,
5655
modelValue: props.multiple ? (Array.isArray(props.modelValue) ? [...props.modelValue] : []) : props.modelValue,
57-
isMounted: false
56+
isMounted: false,
57+
firstAutoSearch: props.remoteConfig?.autoSearch || false
5858
})
5959

6060
Object.assign(api, {
@@ -69,7 +69,7 @@ export const renderless = (props, { reactive, computed, watch, onMounted, nextTi
6969
radioChange: radioChange({ props, vm, emit, state }),
7070
selectChange: selectChange({ props, vm, emit, state, nextTick }),
7171
syncGridSelection: syncGridSelection({ props, vm, state, nextTick }),
72-
handleVisibleChange: handleVisibleChange({ api, state }),
72+
handleVisibleChange: handleVisibleChange({ api, state, props }),
7373
watchValue: watchValue({ api, props, vm, state })
7474
})
7575

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
export const api = [
2+
'state',
3+
'resolvedComponent',
4+
'mergedProps',
5+
'listeners',
6+
'slotNames',
7+
'hasScopedDefault',
8+
'focus',
9+
'blur'
10+
]
11+
12+
export const renderless = (props, { reactive, computed, useAttrs }, { constants, vm, components }) => {
13+
const api = {}
14+
15+
const resolvedComponent = computed(() => computedResolvedComponent({ props, constants, vm, components }))
16+
17+
const mergedProps = computed(() => {
18+
const runtimeAttrs = typeof useAttrs === 'function' ? useAttrs() : null
19+
const attrs = runtimeAttrs || vm.$attrs || {}
20+
const className = attrs.class
21+
const classArray = Array.isArray(className)
22+
? ['tiny-select', ...className]
23+
: className
24+
? ['tiny-select', className]
25+
: ['tiny-select']
26+
27+
const { class: _omitClass, ...restAttrs } = attrs
28+
29+
return {
30+
...props,
31+
...restAttrs,
32+
class: classArray
33+
}
34+
})
35+
36+
const slotNames = computed(() => Object.keys(vm.$slots || {}).filter((name) => name && name !== 'default'))
37+
38+
const hasScopedDefault = computed(() => {
39+
const scoped = vm.$scopedSlots?.default
40+
if (scoped && scoped.length) {
41+
return true
42+
}
43+
const slot = vm.$slots?.default
44+
return typeof slot === 'function' && slot.length > 0
45+
})
46+
47+
const listeners = computed(() => {
48+
if (vm.$listeners) {
49+
return vm.$listeners
50+
}
51+
return {}
52+
})
53+
54+
const getChildComponent = () => vm.$refs?.childComponent
55+
56+
// 暴露子组件的 state,让用户可以通过 ref.state 访问子组件的状态(如 cachedOptions)
57+
// 使用 Proxy 代理子组件的 state,实现动态访问
58+
const state = new Proxy(
59+
{},
60+
{
61+
get(target, prop) {
62+
const child = getChildComponent()
63+
return child?.state?.[prop]
64+
},
65+
set(target, prop, value) {
66+
const child = getChildComponent()
67+
if (child?.state) {
68+
child.state[prop] = value
69+
return true
70+
}
71+
return false
72+
},
73+
has(target, prop) {
74+
const child = getChildComponent()
75+
return prop in (child?.state || {})
76+
},
77+
ownKeys(target) {
78+
const child = getChildComponent()
79+
return Object.keys(child?.state || {})
80+
},
81+
getOwnPropertyDescriptor(target, prop) {
82+
const child = getChildComponent()
83+
const childState = child?.state || {}
84+
if (prop in childState) {
85+
return {
86+
enumerable: true,
87+
configurable: true,
88+
value: childState[prop]
89+
}
90+
}
91+
return undefined
92+
}
93+
}
94+
)
95+
96+
const focus = () => {
97+
const child = getChildComponent()
98+
child && typeof child.focus === 'function' && child.focus()
99+
}
100+
101+
const blur = () => {
102+
const child = getChildComponent()
103+
child && typeof child.blur === 'function' && child.blur()
104+
}
105+
106+
const hasData = (value: any) => {
107+
if (!value) {
108+
return false
109+
}
110+
if (Array.isArray(value)) {
111+
return value.length > 0
112+
}
113+
if (typeof value === 'object') {
114+
return Object.keys(value).length > 0
115+
}
116+
return true
117+
}
118+
119+
const computedResolvedComponent = ({ props, constants, vm, components }) => {
120+
// 优先使用传入的 components,否则从 vm.$options.components 获取
121+
const comps = components || vm.$options?.components || {}
122+
123+
if (props.renderType === constants.TYPE.Tree || hasData(props.treeOp)) {
124+
return comps.TinyTreeSelect || 'TinyTreeSelect'
125+
}
126+
if (props.renderType === constants.TYPE.Grid || hasData(props.gridOp)) {
127+
return comps.TinyGridSelect || 'TinyGridSelect'
128+
}
129+
return comps.TinyBaseSelect || 'TinyBaseSelect'
130+
}
131+
Object.assign(api, {
132+
state,
133+
resolvedComponent,
134+
mergedProps,
135+
listeners,
136+
slotNames,
137+
hasScopedDefault,
138+
focus,
139+
blur
140+
})
141+
142+
return api
143+
}

packages/renderless/src/tree-select/index.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,17 +156,22 @@ export const getChildValue = () => (childNodes, key) => {
156156
export const mounted =
157157
({ api, state, props, vm }) =>
158158
() => {
159-
if (!state.modelValue || state.modelValue.length === 0) return
159+
if (!state.modelValue || (Array.isArray(state.modelValue) && state.modelValue.length === 0)) return
160160

161161
if (props.multiple) {
162162
let initialNodes = []
163163
if (Array.isArray(state.modelValue)) {
164164
state.modelValue.forEach((value) => {
165165
const option = api.getPluginOption(value)
166-
initialNodes = initialNodes.concat(option)
166+
if (option && option.length > 0) {
167+
initialNodes = initialNodes.concat(option)
168+
}
167169
})
168170
}
169171

172+
// 如果没有找到任何节点(例如懒加载场景),直接返回
173+
if (initialNodes.length === 0) return
174+
170175
const selected = initialNodes.map((node) => {
171176
return {
172177
...node,
@@ -179,7 +184,12 @@ export const mounted =
179184

180185
state.defaultCheckedKeys = api.getCheckedData(selected)
181186
} else {
182-
const data = api.getPluginOption(state.modelValue)[0]
187+
const options = api.getPluginOption(state.modelValue)
188+
const data = options && options.length > 0 ? options[0] : null
189+
190+
// 如果没有找到节点(例如懒加载场景),直接返回
191+
if (!data) return
192+
183193
vm.$refs.baseSelectRef.updateSelectedData({
184194
...data,
185195
currentLabel: data[props.textField],
@@ -224,7 +234,9 @@ export const watchValue =
224234
if (Array.isArray(checkedKeys)) {
225235
checkedKeys.forEach((value) => {
226236
const option = api.getPluginOption(value)
227-
initialNodes = initialNodes.concat(option)
237+
if (option && option.length > 0) {
238+
initialNodes = initialNodes.concat(option)
239+
}
228240
})
229241
}
230242

packages/renderless/src/tree-select/vue.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,22 @@ export const api = ['state', 'check', 'filter', 'nodeClick']
1515
export const renderless = (props, { reactive, computed, watch, onMounted }, { vm, emit }) => {
1616
const api = {}
1717

18+
const resolveTreeData = () => {
19+
if (Array.isArray(props.treeOp)) {
20+
return props.treeOp
21+
}
22+
if (props.treeOp && Array.isArray(props.treeOp.data)) {
23+
return props.treeOp.data
24+
}
25+
return []
26+
}
27+
1828
const state = reactive({
1929
childrenName: computed(() => (props.treeOp.props && props.treeOp.props.children) || 'children'),
2030
currentKey: props.modelValue,
2131
defaultCheckedKeys: [],
2232
remoteData: [],
23-
treeData: props.treeOp.data,
33+
treeData: resolveTreeData(),
2434
modelValue: []
2535
})
2636

@@ -38,8 +48,12 @@ export const renderless = (props, { reactive, computed, watch, onMounted }, { vm
3848
})
3949

4050
watch(
41-
() => props.treeOp.data,
42-
(data) => data && (state.treeData = data),
51+
() => (Array.isArray(props.treeOp) ? props.treeOp : props.treeOp?.data),
52+
(data) => {
53+
if (Array.isArray(data)) {
54+
state.treeData = data
55+
}
56+
},
4357
{ immediate: true, deep: true }
4458
)
4559

0 commit comments

Comments
 (0)