|
@@ -19,6 +19,7 @@
|
|
|
:type="i.itemType ? i.itemType : 'text'"
|
|
|
:placeholder="i.placeholder ? i.placeholder : '请输入'"
|
|
|
:clearable="i.clearable ? i.clearable : false"
|
|
|
+ :rules="getRules(i.prop)"
|
|
|
>
|
|
|
</van-field>
|
|
|
<van-field v-if="i.type == 'switch'" :label="i.label" :name="i.prop">
|
|
@@ -26,10 +27,12 @@
|
|
|
<van-switch v-model="formData[i.prop]" />
|
|
|
</template>
|
|
|
</van-field>
|
|
|
+ <!-- 多选checkbox -->
|
|
|
<van-field
|
|
|
v-if="i.type == 'checkbox'"
|
|
|
:label="i.label"
|
|
|
:name="i.prop"
|
|
|
+ :rules="getRules(i.prop)"
|
|
|
>
|
|
|
<template #input>
|
|
|
<van-checkbox-group
|
|
@@ -46,7 +49,13 @@
|
|
|
</van-checkbox-group>
|
|
|
</template>
|
|
|
</van-field>
|
|
|
- <van-field v-if="i.type == 'radio'" :label="i.label" :name="i.prop">
|
|
|
+ <!-- 单选radio -->
|
|
|
+ <van-field
|
|
|
+ v-if="i.type == 'radio'"
|
|
|
+ :label="i.label"
|
|
|
+ :name="i.prop"
|
|
|
+ :rules="getRules(i.prop)"
|
|
|
+ >
|
|
|
<template #input>
|
|
|
<van-radio-group
|
|
|
v-model="formData[i.prop]"
|
|
@@ -61,6 +70,7 @@
|
|
|
</van-radio-group>
|
|
|
</template>
|
|
|
</van-field>
|
|
|
+ <!-- 单选 -->
|
|
|
<van-field
|
|
|
v-if="i.type == 'picker' && i.itemType == 'onePicker'"
|
|
|
:label="i.label"
|
|
@@ -69,6 +79,7 @@
|
|
|
is-link
|
|
|
readonly
|
|
|
@click="i.showPicker = true"
|
|
|
+ :rules="getRules(i.prop)"
|
|
|
>
|
|
|
</van-field>
|
|
|
<van-popup
|
|
@@ -83,7 +94,7 @@
|
|
|
@confirm="(option) => onConfirmPicker(option, i, index)"
|
|
|
/>
|
|
|
</van-popup>
|
|
|
-
|
|
|
+ <!-- 时间选择器 -->
|
|
|
<van-field
|
|
|
v-if="i.type == 'picker' && i.itemType == 'datePicker'"
|
|
|
:label="i.label"
|
|
@@ -92,6 +103,7 @@
|
|
|
is-link
|
|
|
readonly
|
|
|
@click="i.showPicker = true"
|
|
|
+ :rules="getRules(i.prop)"
|
|
|
>
|
|
|
</van-field>
|
|
|
<van-popup
|
|
@@ -108,6 +120,70 @@
|
|
|
:columns-type="i.columnsType"
|
|
|
/>
|
|
|
</van-popup>
|
|
|
+ <!-- 级联 城市 -->
|
|
|
+ <van-field
|
|
|
+ v-if="i.type == 'cascader' && i.itemType == 'city'"
|
|
|
+ :label="i.label"
|
|
|
+ :name="i.prop"
|
|
|
+ v-model="formData[i.prop + 'Name']"
|
|
|
+ is-link
|
|
|
+ readonly
|
|
|
+ @click="i.showPicker = true"
|
|
|
+ :rules="getRules(i.prop)"
|
|
|
+ />
|
|
|
+ <van-popup
|
|
|
+ v-if="i.type == 'cascader' && i.itemType == 'city'"
|
|
|
+ v-model:show="i.showPicker"
|
|
|
+ round
|
|
|
+ position="bottom"
|
|
|
+ >
|
|
|
+ <van-cascader
|
|
|
+ v-model="formData[i.prop]"
|
|
|
+ :title="i.title ? i.title : '请选择'"
|
|
|
+ :options="cityOption"
|
|
|
+ @close="i.showPicker = false"
|
|
|
+ @change="(option) => cityOnChange(option, i, index)"
|
|
|
+ @finish="(option) => (i.finishFn ? i.finishFn(option) : () => {})"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+ <!-- 级联 公共 -->
|
|
|
+ <van-field
|
|
|
+ v-if="i.type == 'cascader' && i.itemType == 'common'"
|
|
|
+ :label="i.label"
|
|
|
+ :name="i.prop"
|
|
|
+ v-model="formData[i.prop + 'Name']"
|
|
|
+ is-link
|
|
|
+ readonly
|
|
|
+ @click="i.showPicker = true"
|
|
|
+ :rules="getRules(i.prop)"
|
|
|
+ />
|
|
|
+ <van-popup
|
|
|
+ v-if="i.type == 'cascader' && i.itemType == 'common'"
|
|
|
+ v-model:show="i.showPicker"
|
|
|
+ round
|
|
|
+ position="bottom"
|
|
|
+ >
|
|
|
+ <van-cascader
|
|
|
+ v-model="formData[i.prop]"
|
|
|
+ :title="i.title ? i.title : '请选择'"
|
|
|
+ :options="i.options"
|
|
|
+ :field-names="i.fieldNames ? i.fieldNames : fieldNames"
|
|
|
+ @close="i.showPicker = false"
|
|
|
+ @change="(option) => commonOnChange(option, i, index)"
|
|
|
+ @finish="
|
|
|
+ (option) => (i.finishFn ? i.finishFn(i, option) : () => {})
|
|
|
+ "
|
|
|
+ />
|
|
|
+ <!-- @change="
|
|
|
+ (option) => (i.onChangeFn ? i.onChangeFn(i, option) : () => {})
|
|
|
+ "
|
|
|
+ @finish="
|
|
|
+ (option) => (i.finishFn ? i.finishFn(i, option) : () => {})
|
|
|
+ " -->
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+ <!-- 插槽 -->
|
|
|
<van-field v-if="i.type == 'slot'" :label="i.label">
|
|
|
<template #input>
|
|
|
<div>
|
|
@@ -116,62 +192,117 @@
|
|
|
</template>
|
|
|
</van-field>
|
|
|
</div>
|
|
|
- <!-- <van-field
|
|
|
- v-for="i in formConfig"
|
|
|
- v-model="formData[i.prop]"
|
|
|
- :name="i.prop"
|
|
|
- :label="i.label"
|
|
|
- :type="i.itemType ? i.itemType : 'text'"
|
|
|
- :readonly="getReadonly(i)"
|
|
|
- :placeholder="i.placeholder"
|
|
|
- :rules="getRules(i.prop)"
|
|
|
- :is-link="i.isLink ? i.isLink : i.name == 'picker' ? true : false"
|
|
|
- :clearable="i.clearable ? i.clearable : false"
|
|
|
- @click="handleClickFiel(i)"
|
|
|
+ </van-cell-group>
|
|
|
+ <!-- 循环业务数据 -->
|
|
|
+ <van-cell-group
|
|
|
+ inset
|
|
|
+ v-for="(item, index) in formData[btnConfigCopy.prop]"
|
|
|
+ :key="index"
|
|
|
+ style="margin-top: 10px !important"
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ 明细{{ index + 1 }} <span @click="handleRemove(index)">删除</span>
|
|
|
+ </div>
|
|
|
+ <!-- 循环表单数据 -->
|
|
|
+ <div v-for="(i, sonIndex) in btnConfigCopy.listConfig" :key="i.prop">
|
|
|
+ <van-field
|
|
|
+ v-if="i.type == 'input'"
|
|
|
+ v-model="formData[btnConfigCopy.prop][index][i.prop]"
|
|
|
+ :label="i.label"
|
|
|
+ :name="i.prop"
|
|
|
+ :type="i.itemType ? i.itemType : 'text'"
|
|
|
+ :placeholder="i.placeholder ? i.placeholder : '请输入'"
|
|
|
+ :clearable="i.clearable ? i.clearable : false"
|
|
|
+ :rules="getRules(i.prop)"
|
|
|
+ >
|
|
|
+ </van-field>
|
|
|
+ <!-- 单选 -->
|
|
|
+ <van-field
|
|
|
+ v-if="i.type == 'picker' && i.itemType == 'onePicker'"
|
|
|
+ :label="i.label"
|
|
|
+ :name="i.prop"
|
|
|
+ v-model="formData[btnConfigCopy.prop][index][i.prop + 'Name']"
|
|
|
+ is-link
|
|
|
+ readonly
|
|
|
+ @click="handleListItemClick(i, index, sonIndex)"
|
|
|
+ :rules="getRules(i.prop)"
|
|
|
+ >
|
|
|
+ </van-field>
|
|
|
+ <!-- 时间选择器 -->
|
|
|
+ <van-field
|
|
|
+ v-if="i.type == 'picker' && i.itemType == 'datePicker'"
|
|
|
+ :label="i.label"
|
|
|
+ :name="i.prop"
|
|
|
+ v-model="formData[btnConfigCopy.prop][index][i.prop]"
|
|
|
+ is-link
|
|
|
+ readonly
|
|
|
+ @click="handleListItemClick(i, index, sonIndex)"
|
|
|
+ :rules="getRules(i.prop)"
|
|
|
+ >
|
|
|
+ </van-field>
|
|
|
+ </div>
|
|
|
+ </van-cell-group>
|
|
|
+ <!-- 单独写个循环,保证弹窗唯一 -->
|
|
|
+ <div v-for="(item, index) in btnConfigCopy.listConfig" :key="index">
|
|
|
+ <van-popup
|
|
|
+ v-model:show="item.showPicker"
|
|
|
+ round
|
|
|
+ position="bottom"
|
|
|
+ v-if="item.type == 'picker' && item.itemType == 'onePicker'"
|
|
|
>
|
|
|
- <template #input v-if="i.type == 'switch'">
|
|
|
- <van-switch v-model="formData[i.prop]" />
|
|
|
- </template>
|
|
|
-
|
|
|
- <template #input v-else-if="i.type == 'checkbox'">
|
|
|
- <van-checkbox-group
|
|
|
- v-model="formData[i.prop]"
|
|
|
- direction="horizontal"
|
|
|
- >
|
|
|
- <van-checkbox
|
|
|
- shape="square"
|
|
|
- v-for="j in i.data"
|
|
|
- :key="j.value"
|
|
|
- :name="j.value"
|
|
|
- >{{ j.text }}</van-checkbox
|
|
|
- >
|
|
|
- </van-checkbox-group>
|
|
|
- </template>
|
|
|
+ <van-picker
|
|
|
+ :columns="item.pickerOption.columns"
|
|
|
+ @cancel="item.showPicker = false"
|
|
|
+ @confirm="(option) => onConfirmListPicker(option, item)"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+ <van-popup
|
|
|
+ v-model:show="item.showPicker"
|
|
|
+ round
|
|
|
+ position="bottom"
|
|
|
+ v-if="item.type == 'picker' && item.itemType == 'datePicker'"
|
|
|
+ >
|
|
|
+ <van-date-picker
|
|
|
+ @confirm="(option) => onConfirmListPicker(option, item)"
|
|
|
+ @cancel="item.showPicker = false"
|
|
|
+ :min-date="item.minDate"
|
|
|
+ :max-date="item.maxDate"
|
|
|
+ :columns-type="item.columnsType"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+ </div>
|
|
|
|
|
|
- <template #input v-else-if="i.type == 'radio'">
|
|
|
- <van-radio-group v-model="formData[i.prop]" direction="horizontal">
|
|
|
- <van-radio
|
|
|
- v-for="j in i.data"
|
|
|
- :key="j.value"
|
|
|
- :name="j.value || j.id"
|
|
|
- >{{ j.label || j.title }}</van-radio
|
|
|
- >
|
|
|
- </van-radio-group>
|
|
|
+ <!-- 按钮 -->
|
|
|
+ <div class="btn-box">
|
|
|
+ <van-button
|
|
|
+ :plain="btnConfigCopy.plain ? btnConfigCopy.plain : false"
|
|
|
+ :type="btnConfigCopy.itemType ? btnConfigCopy.itemType : 'primary'"
|
|
|
+ :size="btnConfigCopy.size ? btnConfigCopy.size : 'small'"
|
|
|
+ style="width: 100%; border: none"
|
|
|
+ @click="handlePush()"
|
|
|
+ >
|
|
|
+ <template #icon>
|
|
|
+ <van-icon
|
|
|
+ :name="btnConfigCopy.icon ? btnConfigCopy.icon : 'plus'"
|
|
|
+ :size="12"
|
|
|
+ />
|
|
|
</template>
|
|
|
- </van-field> -->
|
|
|
- </van-cell-group>
|
|
|
+ {{
|
|
|
+ btnConfigCopy.btnName ? btnConfigCopy.btnName : "添加"
|
|
|
+ }}</van-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
<div style="margin: 16px">
|
|
|
<van-button round block type="primary" native-type="submit">
|
|
|
提交
|
|
|
</van-button>
|
|
|
</div>
|
|
|
</van-form>
|
|
|
- <!-- <div>{{ num }}</div> -->
|
|
|
- <div v-for="i in pickerData">{{ i.label }}</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
+import { showLoadingToast, closeToast, showNotify } from "vant";
|
|
|
import {
|
|
|
ref,
|
|
|
getCurrentInstance,
|
|
@@ -202,14 +333,16 @@ const props = defineProps({
|
|
|
});
|
|
|
const proxy = getCurrentInstance().proxy;
|
|
|
const { formConfig, formOption, rules } = toRefs(props);
|
|
|
-
|
|
|
const formData = computed(() => {
|
|
|
return proxy.modelValue;
|
|
|
});
|
|
|
+//
|
|
|
+const fieldNames = { text: "label", value: "id" };
|
|
|
const emit = defineEmits(["update:modelValue"]);
|
|
|
|
|
|
-const onSubmit = () => {};
|
|
|
-//根据resurl获取数据
|
|
|
+const onSubmit = () => {
|
|
|
+ emit("onSubmit");
|
|
|
+};
|
|
|
|
|
|
// 获取验证规则
|
|
|
const getRules = (prop) => {
|
|
@@ -220,37 +353,57 @@ const getRules = (prop) => {
|
|
|
const getReadonly = (i) => {
|
|
|
return i.readonly ? i.readonly : i.name == "picker" ? true : false;
|
|
|
};
|
|
|
-//
|
|
|
-const pickerData = ref([]);
|
|
|
-const handleClickFiel = (index) => {
|
|
|
- formConfig.value[index].showPicker = true;
|
|
|
- console.log(formConfig.value[index].showPicker, "ass");
|
|
|
-};
|
|
|
-
|
|
|
-// onMounted(() => {
|
|
|
-// pickerData.value = formConfig.value.filter((x) => x.name == "picker");
|
|
|
-// console.log(pickerData.value, "wss");
|
|
|
-// });
|
|
|
|
|
|
-const testForm = ref(null); // 延迟使用,因为还没有返回跟挂载
|
|
|
+// 国家初始化
|
|
|
+const cityOption = ref([]);
|
|
|
+const cityOptionInit = () => {
|
|
|
+ proxy.post("/areaInfo/list", formData.value).then((res) => {
|
|
|
+ cityOption.value = res.data.map((item, index) => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ index: index,
|
|
|
+ text: item.chineseName,
|
|
|
+ value: item.id,
|
|
|
+ children: [],
|
|
|
+ };
|
|
|
+ });
|
|
|
+ });
|
|
|
+};
|
|
|
|
|
|
+let btnConfigCopy = {};
|
|
|
+const btnPickerList = ref([]);
|
|
|
const formDataInit = () => {
|
|
|
- const formConfigCopy = {};
|
|
|
var map = {
|
|
|
input: "",
|
|
|
radio: "",
|
|
|
switch: false,
|
|
|
checkbox: [],
|
|
|
date: "",
|
|
|
- datetime: "",
|
|
|
picker: "",
|
|
|
+ cascader: "",
|
|
|
};
|
|
|
+ // 判断是否需要按钮
|
|
|
+ if (formOption.value.btnConfig && formOption.value.btnConfig.isNeed) {
|
|
|
+ formData.value[formOption.value.btnConfig.prop] = [];
|
|
|
+ btnConfigCopy = { ...formOption.value.btnConfig };
|
|
|
+ if (
|
|
|
+ formOption.value.btnConfig.listConfig &&
|
|
|
+ formOption.value.btnConfig.listConfig.length > 0
|
|
|
+ ) {
|
|
|
+ btnPickerList.value = formOption.value.btnConfig.listConfig
|
|
|
+ .filter((x) => x.type === "picker")
|
|
|
+ .map((x) => x.itemType);
|
|
|
+ }
|
|
|
+ }
|
|
|
for (let i = 0; i < formConfig.value.length; i++) {
|
|
|
const element = formConfig.value[i];
|
|
|
if (element.type === "slot") {
|
|
|
continue;
|
|
|
}
|
|
|
- if (element.type === "picker") {
|
|
|
+ if (element.type === "cascader" && element.itemType === "city") {
|
|
|
+ cityOptionInit();
|
|
|
+ }
|
|
|
+ if (element.type === "picker" || element.type === "cascader") {
|
|
|
formData.value[element.prop] = map[element.type];
|
|
|
formData.value[element.prop + "Name"] = map[element.type];
|
|
|
}
|
|
@@ -259,11 +412,11 @@ const formDataInit = () => {
|
|
|
}
|
|
|
}
|
|
|
emit("update:modelValue", formData.value);
|
|
|
- console.log(formData.value, "wass");
|
|
|
+ console.log(formData.value, "aws");
|
|
|
};
|
|
|
|
|
|
formDataInit();
|
|
|
-
|
|
|
+// 选择框 确定事件
|
|
|
const onConfirmPicker = (option, item, index) => {
|
|
|
switch (item.itemType) {
|
|
|
case "onePicker": {
|
|
@@ -279,7 +432,121 @@ const onConfirmPicker = (option, item, index) => {
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
+const currentIndex = ref(-1);
|
|
|
+const currentSonIndex = ref(-1);
|
|
|
+const handleListItemClick = (i, index, sonIndex) => {
|
|
|
+ currentIndex.value = index;
|
|
|
+ currentSonIndex.value = sonIndex;
|
|
|
+ btnConfigCopy.listConfig[sonIndex].showPicker = true;
|
|
|
+};
|
|
|
+const onConfirmListPicker = (option, item) => {
|
|
|
+ switch (item.itemType) {
|
|
|
+ case "onePicker": {
|
|
|
+ formData.value[btnConfigCopy.prop][currentIndex.value][
|
|
|
+ item.prop + "Name"
|
|
|
+ ] = option.selectedOptions[0].text;
|
|
|
+ formData.value[btnConfigCopy.prop][currentIndex.value][item.prop] =
|
|
|
+ option.selectedOptions[0].value;
|
|
|
+ btnConfigCopy.listConfig[currentSonIndex.value].showPicker = false;
|
|
|
+ }
|
|
|
+ case "datePicker": {
|
|
|
+ formData.value[btnConfigCopy.prop][currentIndex.value][item.prop] =
|
|
|
+ option.selectedValues.join(item.split ? item.split : "-");
|
|
|
+ btnConfigCopy.listConfig[currentSonIndex.value].showPicker = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
+// push事件
|
|
|
+const handlePush = () => {
|
|
|
+ if (btnConfigCopy.clickFn && typeof btnConfigCopy.clickFn == "function") {
|
|
|
+ btnConfigCopy.clickFn();
|
|
|
+ }
|
|
|
+};
|
|
|
+// remove
|
|
|
+const handleRemove = (index) => {
|
|
|
+ formData.value[btnConfigCopy.prop].splice(index, 1);
|
|
|
+};
|
|
|
+// 拉去城市最近数据及处理
|
|
|
+const getAreaInfo = (selectedOptions, item, index) => {
|
|
|
+ showLoadingToast("加载中...");
|
|
|
+ proxy
|
|
|
+ .post("/areaInfo/list", { parentId: selectedOptions.value })
|
|
|
+ .then((res) => {
|
|
|
+ let countryIndex = selectedOptions.selectedOptions[0].index;
|
|
|
+ let provinceIndex =
|
|
|
+ selectedOptions.tabIndex === 1
|
|
|
+ ? selectedOptions.selectedOptions[1].index
|
|
|
+ : null;
|
|
|
+ let cityIndex =
|
|
|
+ selectedOptions.tabIndex === 2
|
|
|
+ ? selectedOptions.selectedOptions[2].index
|
|
|
+ : null;
|
|
|
+ //已经没有下级数据
|
|
|
+ if (res.data.length === 0) {
|
|
|
+ if (selectedOptions.tabIndex === 1) {
|
|
|
+ formData.value[item.prop + "Name"] = selectedOptions.selectedOptions
|
|
|
+ .map((item) => item.text)
|
|
|
+ .join(" ");
|
|
|
+ formConfig.value[index].showPicker = false;
|
|
|
+ formData.value.selectedOptions = selectedOptions;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (selectedOptions.tabIndex === 2) {
|
|
|
+ formData.value[item.prop + "Name"] = selectedOptions.selectedOptions
|
|
|
+ .map((item) => item.text)
|
|
|
+ .join(" ");
|
|
|
+ formConfig.value[index].showPicker = false;
|
|
|
+ formData.value.selectedOptions = selectedOptions;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (selectedOptions.tabIndex === 0) {
|
|
|
+ cityOption.value[countryIndex].children = res.data.map(
|
|
|
+ (item, index) => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ index: index,
|
|
|
+ text: item.name,
|
|
|
+ value: item.id,
|
|
|
+ };
|
|
|
+ }
|
|
|
+ );
|
|
|
+ } else if (selectedOptions.tabIndex === 1) {
|
|
|
+ cityOption.value[countryIndex].children[provinceIndex].children =
|
|
|
+ res.data.map((item, index) => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ index: index,
|
|
|
+ text: item.name,
|
|
|
+ value: item.id,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ } else if (selectedOptions.tabIndex === 2) {
|
|
|
+ cityOption.value[countryIndex].children[provinceIndex].children[
|
|
|
+ cityIndex
|
|
|
+ ].children = res.data.map((item, index) => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ index: index,
|
|
|
+ text: item.name,
|
|
|
+ value: item.id,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ closeToast();
|
|
|
+ });
|
|
|
+};
|
|
|
+// 城市变动事件
|
|
|
+const cityOnChange = (selectedOptions, item, index) => {
|
|
|
+ getAreaInfo(selectedOptions, item, index);
|
|
|
+};
|
|
|
+
|
|
|
+const commonOnChange = ({ selectedOptions }, item, index) => {
|
|
|
+ formData.value[item.prop + "Name"] =
|
|
|
+ selectedOptions[selectedOptions.length - 1].label;
|
|
|
+};
|
|
|
+const testForm = ref(null); // 延迟使用,因为还没有返回跟挂载
|
|
|
watch(
|
|
|
formData.value,
|
|
|
(val) => {
|
|
@@ -292,4 +559,9 @@ watch(
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
+.btn-box {
|
|
|
+ margin-top: 10px;
|
|
|
+ width: 100%;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
</style>
|