261 lines
6.5 KiB
Vue
261 lines
6.5 KiB
Vue
<template>
|
|
<div class="iview-form-checkbox-container">
|
|
<a-input
|
|
type="text"
|
|
style="display: none"
|
|
:id="'form_item_' + formatFieldName(field.name)"
|
|
/>
|
|
<a-form-item-rest>
|
|
<div v-if="supportedRemoteSearch" style="margin-bottom: 8px">
|
|
<a-input-search
|
|
v-model:value="searchInputValue"
|
|
:placeholder="field.searchPlaceholder || '输入关键字搜索'"
|
|
:loading="remoteSearchLoading"
|
|
allow-clear
|
|
@search="handleSearch"
|
|
@input="handleSearchInput"
|
|
/>
|
|
</div>
|
|
<div v-if="remoteSearchLoading || remoteOptionsLoading" style="padding: 8px 0">
|
|
<a-spin size="small" />
|
|
</div>
|
|
<div
|
|
v-else-if="remoteSearchErrorMsg || remoteOptionsErrorMsg"
|
|
style="padding: 8px 0; color: #ff4d4f"
|
|
>
|
|
{{ remoteSearchErrorMsg || remoteOptionsErrorMsg }}
|
|
<a-button
|
|
size="small"
|
|
type="default"
|
|
style="margin-left: 8px"
|
|
@click="handleRefresh"
|
|
>
|
|
刷新
|
|
</a-button>
|
|
</div>
|
|
<a-checkbox-group
|
|
v-else
|
|
v-model:value="vModel[formatFieldName(field.name)]"
|
|
:disabled="readOnly"
|
|
>
|
|
<a-checkbox
|
|
v-for="option in parseOptions(field.options)"
|
|
:key="option.value"
|
|
:value="option.value"
|
|
>
|
|
{{ readLabel(option.label) }}
|
|
<template v-if="showValueTag">
|
|
<span style="color: #999; font-size: 12px"> {{ option.value }} </span>
|
|
</template>
|
|
<template v-else>
|
|
<span v-if="!!readTag(option.label)" style="color: #999; font-size: 12px">
|
|
{{ readTag(option.label) }}
|
|
</span>
|
|
</template>
|
|
</a-checkbox>
|
|
</a-checkbox-group>
|
|
</a-form-item-rest>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted } from "vue";
|
|
import { useVModel } from "@/iview/utils/useModel";
|
|
import { delayCall } from "@/iview/utils/delayCall";
|
|
import { callFunc } from "@/iview/utils/func";
|
|
|
|
const props = defineProps({
|
|
field: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
fieldPrefix: {
|
|
type: String,
|
|
default: "",
|
|
},
|
|
modelValue: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
readOnly: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
rawFormData: {
|
|
type: Object,
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(["update:modelValue"]);
|
|
const vModel = useVModel(props, "modelValue", emit);
|
|
|
|
const remoteOptionsData = ref([]);
|
|
const remoteOptionsLoading = ref(false);
|
|
const remoteOptionsErrorMsg = ref(null);
|
|
|
|
const remoteSearchValue = ref("");
|
|
const remoteSearchData = ref([]);
|
|
const remoteSearchLoading = ref(false);
|
|
const remoteSearchErrorMsg = ref(null);
|
|
const remoteSearchId = ref(0);
|
|
const searchInputValue = ref("");
|
|
|
|
const supportedRemoteSearch = computed(() => {
|
|
return (
|
|
!!props.field.remoteSearchMethod &&
|
|
typeof props.field.remoteSearchMethod === "function"
|
|
);
|
|
});
|
|
|
|
const supportedRemoteOptions = computed(() => {
|
|
return (
|
|
!!props.field.remoteOptionsMethod &&
|
|
typeof props.field.remoteOptionsMethod === "function"
|
|
);
|
|
});
|
|
|
|
const showValueTag = computed(() => {
|
|
return !!props.field.showValueTag;
|
|
});
|
|
|
|
// 解析选项字符串或数组
|
|
const parseOptions = (options) => {
|
|
if (remoteSearchData.value && remoteSearchData.value.length > 0) {
|
|
return remoteSearchData.value;
|
|
}
|
|
if (remoteOptionsData.value && remoteOptionsData.value.length > 0) {
|
|
return remoteOptionsData.value;
|
|
}
|
|
if (!options) return [];
|
|
|
|
if (Array.isArray(options)) {
|
|
return options;
|
|
}
|
|
|
|
if (typeof options === "string") {
|
|
return options
|
|
.split("\n")
|
|
.filter((line) => line.trim() !== "")
|
|
.map((line) => {
|
|
const parts = line.split("|");
|
|
return {
|
|
label: parts[0]?.trim() || "",
|
|
value: parts[1]?.trim() || parts[0]?.trim() || "",
|
|
};
|
|
});
|
|
}
|
|
|
|
return [];
|
|
};
|
|
|
|
const formatFieldName = (name) => {
|
|
if (!props.fieldPrefix) {
|
|
return name;
|
|
}
|
|
return props.fieldPrefix + "-" + name;
|
|
};
|
|
|
|
const readLabel = (value) => {
|
|
return `${value}`.split("#")[0];
|
|
};
|
|
|
|
const readTag = (value) => {
|
|
return `${value}`.split("#")[1];
|
|
};
|
|
|
|
const handleRemoteOptions = () => {
|
|
if (!supportedRemoteOptions.value) {
|
|
return;
|
|
}
|
|
remoteOptionsLoading.value = true;
|
|
remoteOptionsErrorMsg.value = null;
|
|
Promise.resolve()
|
|
.then(() => {
|
|
let remoteOptionsMethod = props.field.remoteOptionsMethod;
|
|
return callFunc([], [], remoteOptionsMethod, []);
|
|
})
|
|
.then((data) => {
|
|
remoteOptionsData.value = data || [];
|
|
remoteOptionsLoading.value = false;
|
|
})
|
|
.catch((err) => {
|
|
remoteOptionsErrorMsg.value =
|
|
(err && err.msg) || (err && err.message) || "连接服务器失败,请检查网络";
|
|
remoteOptionsLoading.value = false;
|
|
});
|
|
};
|
|
|
|
const handleRemoteSearchValue = delayCall((value) => {
|
|
remoteSearchId.value += 1;
|
|
remoteSearchValue.value = value;
|
|
const currentRemoteSearchId = remoteSearchId.value;
|
|
remoteSearchLoading.value = true;
|
|
remoteSearchErrorMsg.value = null;
|
|
remoteSearchData.value = [];
|
|
Promise.resolve()
|
|
.then(() => {
|
|
let remoteSearchMethod = props.field.remoteSearchMethod;
|
|
return callFunc(["value"], [value], remoteSearchMethod, []);
|
|
})
|
|
.then((data) => {
|
|
if (currentRemoteSearchId !== remoteSearchId.value) {
|
|
return;
|
|
}
|
|
remoteSearchData.value = data || [];
|
|
remoteSearchLoading.value = false;
|
|
remoteSearchValue.value = null;
|
|
})
|
|
.catch((err) => {
|
|
remoteSearchErrorMsg.value =
|
|
(err && err.msg) || (err && err.message) || "连接服务器失败,请检查网络";
|
|
remoteSearchLoading.value = false;
|
|
});
|
|
}, 300);
|
|
|
|
const handleSearch = (value) => {
|
|
if (supportedRemoteSearch.value && value) {
|
|
handleRemoteSearchValue(value);
|
|
}
|
|
};
|
|
|
|
const handleSearchInput = (e) => {
|
|
const value = e.target.value;
|
|
if (supportedRemoteSearch.value) {
|
|
if (value) {
|
|
handleRemoteSearchValue(value);
|
|
} else {
|
|
remoteSearchData.value = [];
|
|
remoteSearchErrorMsg.value = null;
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleRefresh = () => {
|
|
if (supportedRemoteSearch.value && !!remoteSearchValue.value) {
|
|
handleRemoteSearchValue(remoteSearchValue.value);
|
|
}
|
|
if (supportedRemoteOptions.value) {
|
|
handleRemoteOptions();
|
|
}
|
|
};
|
|
|
|
onMounted(() => {
|
|
handleRemoteOptions();
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
:deep(.ant-checkbox-group) {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: flex-start;
|
|
text-align: left;
|
|
}
|
|
|
|
:deep(.ant-checkbox-wrapper) {
|
|
margin-left: 0;
|
|
margin-right: 16px;
|
|
padding: 4px 0;
|
|
}
|
|
</style>
|