|
@@ -27,6 +27,11 @@ export const useApiSelect = (option: ApiSelectProps) => {
|
|
|
type: String,
|
|
|
default: 'GET'
|
|
|
},
|
|
|
+ // 选项解析函数
|
|
|
+ parseFunc: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
// 请求参数
|
|
|
data: {
|
|
|
type: String,
|
|
@@ -41,35 +46,121 @@ export const useApiSelect = (option: ApiSelectProps) => {
|
|
|
multiple: {
|
|
|
type: Boolean,
|
|
|
default: false
|
|
|
+ },
|
|
|
+ // 是否远程搜索
|
|
|
+ remote: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ // 远程搜索时携带的参数
|
|
|
+ remoteField: {
|
|
|
+ type: String,
|
|
|
+ default: 'label'
|
|
|
}
|
|
|
},
|
|
|
setup(props) {
|
|
|
const attrs = useAttrs()
|
|
|
const options = ref<any[]>([]) // 下拉数据
|
|
|
+ const loading = ref(false) // 是否正在从远程获取数据
|
|
|
+ const queryParam = ref<any>() // 当前输入的值
|
|
|
const getOptions = async () => {
|
|
|
options.value = []
|
|
|
// 接口选择器
|
|
|
if (isEmpty(props.url)) {
|
|
|
return
|
|
|
}
|
|
|
- let data = []
|
|
|
switch (props.method) {
|
|
|
case 'GET':
|
|
|
- data = await request.get({ url: props.url })
|
|
|
+ let url: string = props.url
|
|
|
+ if (props.remote) {
|
|
|
+ url = `${url}?${props.remoteField}=${queryParam.value}`
|
|
|
+ }
|
|
|
+ parseOptions(await request.get({ url: url }))
|
|
|
break
|
|
|
case 'POST':
|
|
|
- data = await request.post({ url: props.url, data: jsonParse(props.data) })
|
|
|
+ const data: any = jsonParse(props.data)
|
|
|
+ if (props.remote) {
|
|
|
+ data[props.remoteField] = queryParam.value
|
|
|
+ }
|
|
|
+ parseOptions(await request.post({ url: props.url, data: data }))
|
|
|
break
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseOptions(data: any) {
|
|
|
+ // 情况一:如果有自定义解析函数优先使用自定义解析
|
|
|
+ if (!isEmpty(props.parseFunc)) {
|
|
|
+ options.value = parseFunc()?.(data)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 情况二:返回的直接是一个列表
|
|
|
+ if (Array.isArray(data)) {
|
|
|
+ parseOptions0(data)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 情况二:返回的是分页数据,尝试读取 list
|
|
|
+ data = data.list
|
|
|
+ if (!!data && Array.isArray(data)) {
|
|
|
+ parseOptions0(data)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 情况三:不是 yudao-vue-pro 标准返回
|
|
|
+ console.warn(
|
|
|
+ `接口[${props.url}] 返回结果不是 yudao-vue-pro 标准返回建议采用自定义解析函数处理`
|
|
|
+ )
|
|
|
+ }
|
|
|
|
|
|
+ function parseOptions0(data: any[]) {
|
|
|
if (Array.isArray(data)) {
|
|
|
options.value = data.map((item: any) => ({
|
|
|
- label: item[props.labelField],
|
|
|
- value: item[props.valueField]
|
|
|
+ label: parseExpression(item, props.labelField),
|
|
|
+ value: parseExpression(item, props.valueField)
|
|
|
}))
|
|
|
return
|
|
|
}
|
|
|
- console.error(`接口[${props.url}] 返回结果不是一个数组`)
|
|
|
+ console.warn(`接口[${props.url}] 返回结果不是一个数组`)
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseFunc() {
|
|
|
+ let parse: any = null
|
|
|
+ if (!!props.parseFunc) {
|
|
|
+ // 解析字符串函数
|
|
|
+ parse = new Function(`return ${props.parseFunc}`)()
|
|
|
+ }
|
|
|
+ return parse
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseExpression(data: any, template: string) {
|
|
|
+ // 检测是否使用了表达式
|
|
|
+ if (template.indexOf('${') === -1) {
|
|
|
+ return data[template]
|
|
|
+ }
|
|
|
+ // 正则表达式匹配模板字符串中的 ${...}
|
|
|
+ const pattern = /\$\{([^}]*)}/g
|
|
|
+ // 使用replace函数配合正则表达式和回调函数来进行替换
|
|
|
+ return template.replace(pattern, (_, expr) => {
|
|
|
+ // expr 是匹配到的 ${} 内的表达式(这里是属性名),从 data 中获取对应的值
|
|
|
+ const result = data[expr.trim()] // 去除前后空白,以防用户输入带空格的属性名
|
|
|
+ if (!result) {
|
|
|
+ console.warn(
|
|
|
+ `接口选择器选项模版[${template}][${expr.trim()}] 解析值失败结果为[${result}], 请检查属性名称是否存在于接口返回值中,存在则忽略此条!!!`
|
|
|
+ )
|
|
|
+ }
|
|
|
+ return result
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const remoteMethod = async (query: any) => {
|
|
|
+ if (!query) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ queryParam.value = query
|
|
|
+ await getOptions()
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
onMounted(async () => {
|
|
@@ -80,15 +171,29 @@ export const useApiSelect = (option: ApiSelectProps) => {
|
|
|
if (props.multiple) {
|
|
|
// fix:多写此步是为了解决 multiple 属性问题
|
|
|
return (
|
|
|
- <el-select class="w-1/1" {...attrs} multiple>
|
|
|
+ <el-select
|
|
|
+ class="w-1/1"
|
|
|
+ multiple
|
|
|
+ loading={loading.value}
|
|
|
+ {...attrs}
|
|
|
+ remote={props.remote}
|
|
|
+ {...(props.remote && { remoteMethod: remoteMethod })}
|
|
|
+ >
|
|
|
{options.value.map((item, index) => (
|
|
|
<el-option key={index} label={item.label} value={item.value} />
|
|
|
))}
|
|
|
</el-select>
|
|
|
)
|
|
|
}
|
|
|
+ debugger
|
|
|
return (
|
|
|
- <el-select class="w-1/1" {...attrs}>
|
|
|
+ <el-select
|
|
|
+ class="w-1/1"
|
|
|
+ loading={loading.value}
|
|
|
+ {...attrs}
|
|
|
+ remote={props.remote}
|
|
|
+ {...(props.remote && { remoteMethod: remoteMethod })}
|
|
|
+ >
|
|
|
{options.value.map((item, index) => (
|
|
|
<el-option key={index} label={item.label} value={item.value} />
|
|
|
))}
|