提交 | 用户 | 时间
820397 1 import request from '@/config/axios'
H 2 import { isEmpty } from '@/utils/is'
3 import { ApiSelectProps } from '@/components/FormCreate/src/type'
4 import { jsonParse } from '@/utils'
5
6 export const useApiSelect = (option: ApiSelectProps) => {
7   return defineComponent({
8     name: option.name,
9     props: {
10       // 选项标签
11       labelField: {
12         type: String,
13         default: () => option.labelField ?? 'label'
14       },
15       // 选项的值
16       valueField: {
17         type: String,
18         default: () => option.valueField ?? 'value'
19       },
20       // api 接口
21       url: {
22         type: String,
23         default: () => option.url ?? ''
24       },
25       // 请求类型
26       method: {
27         type: String,
28         default: 'GET'
29       },
30       // 选项解析函数
31       parseFunc: {
32         type: String,
33         default: ''
34       },
35       // 请求参数
36       data: {
37         type: String,
38         default: ''
39       },
40       // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio
41       selectType: {
42         type: String,
43         default: 'select'
44       },
45       // 是否多选
46       multiple: {
47         type: Boolean,
48         default: false
49       },
50       // 是否远程搜索
51       remote: {
52         type: Boolean,
53         default: false
54       },
55       // 远程搜索时携带的参数
56       remoteField: {
57         type: String,
58         default: 'label'
59       }
60     },
61     setup(props) {
62       const attrs = useAttrs()
63       const options = ref<any[]>([]) // 下拉数据
64       const loading = ref(false) // 是否正在从远程获取数据
65       const queryParam = ref<any>() // 当前输入的值
66       const getOptions = async () => {
67         options.value = []
68         // 接口选择器
69         if (isEmpty(props.url)) {
70           return
71         }
72         switch (props.method) {
73           case 'GET':
74             let url: string = props.url
75             if (props.remote) {
76               url = `${url}?${props.remoteField}=${queryParam.value}`
77             }
78             parseOptions(await request.get({ url: url }))
79             break
80           case 'POST':
81             const data: any = jsonParse(props.data)
82             if (props.remote) {
83               data[props.remoteField] = queryParam.value
84             }
85             parseOptions(await request.post({ url: props.url, data: data }))
86             break
87         }
88       }
89
90       function parseOptions(data: any) {
91         //  情况一:如果有自定义解析函数优先使用自定义解析
92         if (!isEmpty(props.parseFunc)) {
93           options.value = parseFunc()?.(data)
94           return
95         }
96         // 情况二:返回的直接是一个列表
97         if (Array.isArray(data)) {
98           parseOptions0(data)
99           return
100         }
101         // 情况二:返回的是分页数据,尝试读取 list
102         data = data.list
103         if (!!data && Array.isArray(data)) {
104           parseOptions0(data)
105           return
106         }
3e359e 107         // 情况三:不是 yudao-vue-pro 标准返回
820397 108         console.warn(
3e359e 109           `接口[${props.url}] 返回结果不是 yudao-vue-pro 标准返回建议采用自定义解析函数处理`
820397 110         )
H 111       }
112
113       function parseOptions0(data: any[]) {
114         if (Array.isArray(data)) {
115           options.value = data.map((item: any) => ({
116             label: parseExpression(item, props.labelField),
117             value: parseExpression(item, props.valueField)
118           }))
119           return
120         }
121         console.warn(`接口[${props.url}] 返回结果不是一个数组`)
122       }
123
124       function parseFunc() {
125         let parse: any = null
126         if (!!props.parseFunc) {
127           // 解析字符串函数
128           parse = new Function(`return ${props.parseFunc}`)()
129         }
130         return parse
131       }
132
133       function parseExpression(data: any, template: string) {
134         // 检测是否使用了表达式
135         if (template.indexOf('${') === -1) {
136           return data[template]
137         }
138         // 正则表达式匹配模板字符串中的 ${...}
139         const pattern = /\$\{([^}]*)}/g
140         // 使用replace函数配合正则表达式和回调函数来进行替换
141         return template.replace(pattern, (_, expr) => {
142           // expr 是匹配到的 ${} 内的表达式(这里是属性名),从 data 中获取对应的值
143           const result = data[expr.trim()] // 去除前后空白,以防用户输入带空格的属性名
144           if (!result) {
145             console.warn(
146               `接口选择器选项模版[${template}][${expr.trim()}] 解析值失败结果为[${result}], 请检查属性名称是否存在于接口返回值中,存在则忽略此条!!!`
147             )
148           }
149           return result
150         })
151       }
152
153       const remoteMethod = async (query: any) => {
154         if (!query) {
155           return
156         }
157         loading.value = true
158         try {
159           queryParam.value = query
160           await getOptions()
161         } finally {
162           loading.value = false
163         }
164       }
165
166       onMounted(async () => {
167         await getOptions()
168       })
169
170       const buildSelect = () => {
171         if (props.multiple) {
172           // fix:多写此步是为了解决 multiple 属性问题
173           return (
174             <el-select
175               class="w-1/1"
176               multiple
177               loading={loading.value}
178               {...attrs}
179               remote={props.remote}
180               {...(props.remote && { remoteMethod: remoteMethod })}
181             >
182               {options.value.map((item, index) => (
183                 <el-option key={index} label={item.label} value={item.value} />
184               ))}
185             </el-select>
186           )
187         }
188         return (
189           <el-select
190             class="w-1/1"
191             loading={loading.value}
192             {...attrs}
193             remote={props.remote}
194             {...(props.remote && { remoteMethod: remoteMethod })}
195           >
196             {options.value.map((item, index) => (
197               <el-option key={index} label={item.label} value={item.value} />
198             ))}
199           </el-select>
200         )
201       }
202       const buildCheckbox = () => {
203         if (isEmpty(options.value)) {
204           options.value = [
205             { label: '选项1', value: '选项1' },
206             { label: '选项2', value: '选项2' }
207           ]
208         }
209         return (
210           <el-checkbox-group class="w-1/1" {...attrs}>
211             {options.value.map((item, index) => (
212               <el-checkbox key={index} label={item.label} value={item.value} />
213             ))}
214           </el-checkbox-group>
215         )
216       }
217       const buildRadio = () => {
218         if (isEmpty(options.value)) {
219           options.value = [
220             { label: '选项1', value: '选项1' },
221             { label: '选项2', value: '选项2' }
222           ]
223         }
224         return (
225           <el-radio-group class="w-1/1" {...attrs}>
226             {options.value.map((item, index) => (
227               <el-radio key={index} value={item.value}>
228                 {item.label}
229               </el-radio>
230             ))}
231           </el-radio-group>
232         )
233       }
234       return () => (
235         <>
236           {props.selectType === 'select'
237             ? buildSelect()
238             : props.selectType === 'radio'
239               ? buildRadio()
240               : props.selectType === 'checkbox'
241                 ? buildCheckbox()
242                 : buildSelect()}
243         </>
244       )
245     }
246   })
247 }