Vue3 is a relatively complete form component

form.view

<template>
  <div>
    <a-form
      :model="form"
      ref="formRef"
      :label-col="labelCol"
      :wrapper-col="wrapperCol"
       :rules="rules"
    >
      <a-form-item
        v-for="(item, index) in formData"
        :key="index"
        v-show="item.show"
        :label="item.label"
        :name="item.key"
      >
        <a-input
          v-model:value.trim="form[item.key]"
          v-if="item.type == 'input'"
          :disabled="item.disabled"
        ></a-input>
        <!-- 编码查重开始 -->
        <a-input
          v-if="item.type == 'inputRepeat'"
          @blur="blurRepeatEvent(item)"
          @change="changeInputRepeat(item)"
          v-model:value.trim="form[item.key]"
          :disabled="formTitle.indexOf('修改') > -1"
        ></a-input>
        <div
          class="errortip"
          v-if="item.type == 'inputRepeat' && item.repeatObj.totalCount > 0"
        >
          {
   
   { item.repeatObj.errorMessage }}
        </div>
        <div
          class="helptip"
          v-if="item.type == 'inputRepeat' && item.repeatObj?.checked"
        >
          查重已通过
        </div>
        <!-- 编码查重结束 -->
        <a-textarea
          v-model:value.trim="form[item.key]"
          autoSize
          v-if="item.type == 'textarea'"
          :disabled="item.disabled"
          @blur="
            item.delSpaces
              ? blurEvent(`${item.key}`)
              : () => {
                  return true;
                }
          "
        ></a-textarea>
        <!-- 籍贯 -->
        <v-distpicker
          v-if="item.type == 'space'"
          v-model="form[item.key]"
          @selected="selected"
          hide-area
          :province="nativePlace.province"
          :city="nativePlace.city"
        ></v-distpicker>

        <a-switch
          v-model:checked="form[item.key]"
          v-if="item.type == 'switch'"
          :disabled="item.disabled"
        ></a-switch>
        <a-date-picker
          v-if="item.type == 'datepicker'"
          :disabled="item.disabled"
          v-model:value="form[item.key]"
        />
        <a-input-search
          v-if="item.type == 'secretInput'"
          v-model:value="form[item.key]"
          @search="changeRandom(item.key)"
          :disabled="item.disabled"
        >
          <template #enterButton>
            <a-button :disabled="item.disabled">随机生成</a-button>
          </template></a-input-search
        >
        <a-select
          v-model:value="form[item.key]"
          v-if="item.type == 'select'"
          :disabled="item.disabled"
          :allowClear="item.allowClear"
          :getPopupContainer="(triggerNode) => triggerNode.parentNode"
        >
          <a-select-option
            v-for="selectOption in item.selectList"
            :key="selectOption.id"
            :value="selectOption.id"
            >{
   
   {
              selectOption.displayName || selectOption.name
            }}</a-select-option
          >
        </a-select>
        <Select
          ref="selectRef"
          v-if="item.type == 'requestSelect'"
          :objectName="item.key"
          :popupScroll="item.requestApi"
          :watchParams="item.watchParams"
          :params="item.requestParams"
          :isAffect="item.isAffect"
          :allowClear="item.requestAllowClear"
          @selectedValue="
            (value) => selectedValue(value, item.isAffect, item.key)
          "
        ></Select>
        <!-- 用户声明 -->
        <div v-if="item.type=='claims'">
                  <div @click="selectDeclare">
              <a-input  v-model:value="selectedDeclare"></a-input>
            </div>
                <div class="modal">
      <a-modal
        v-model:visible="visibleDeclare"
        :centered="true"
        :width="700"
        :closable="false"
      >
        <div class="modalBox">
          <div class="left">
            <div class="leftTitle">选择</div>
            <div class="leftList" v-if="leftList.length">
              <div
                v-for="(item, index) in leftList"
                :key="index"
                @click="selectEvent(item)"
              >
                {
   
   { item.name }}
              </div>
            </div>
            <div class="leftList" v-else>
              <!-- <a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" /> -->
            </div>
          </div>
          <div class="right">
            <div class="leftTitle">已选</div>
            <div class="leftList" v-if="selectedList.length">
              <div v-for="(item, index) in selectedList" :key="index">
                {
   
   { item.name }}
                <close-outlined
                  @click="delSelectEvent(item)"
                  style="font-size: 12px; float: right; line-height: 24px"
                />
              </div>
            </div>
            <div class="leftList" v-else>
              <!-- <a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" /> -->
            </div>
          </div>
        </div>
        <template #footer>
          <div style="text-align: center">
            <a-button @click="() => (visibleDeclare = false)">取消</a-button>
            <a-button type="primary" @click="ok">确定</a-button>
          </div>
        </template>
      </a-modal>
    </div>
            <!-- <selectModal
              :handleDelSelect="handleDelSelect"
              :selected="Selected"
              :visible="visibleDeclare"
              :cancel="cancelDeclare"
              :ok="ok"
            >
              <a-list bordered :data-source="selectList" >
                <a-list-item
                  @click="getDeclareData(item)"
                  slot="renderItem"
                  slot-scope="item"
                  style="padding-left: 20px;"
                >{
   
   { item.name }}
                </a-list-item>
              </a-list>
            </selectModal> -->
        </div>

      </a-form-item>
      <div class="btn_box" v-if="showBtnGroup.submitBtn||showBtnGroup.cancelBtn">
        <div class="btnGroup">
          <a-button
            type="primary"
            v-show="showBtnGroup.submitBtn"
            :loading="loading"
            @click="submit"
            >提交</a-button
          >
          <a-button v-show="showBtnGroup.cancelBtn" @click="cancel"
            >取消</a-button
          >
        </div>
      </div>
    </a-form>
  </div>
</template>
<script setup>
import moment from "moment";
import selectModal from '@/components/selectModal/index.vue'
import { getResource, getCurResource } from "@/api/resourceApi";
import { getUserfield } from "@/api/userfieldApi";
import VDistpicker from "v-distpicker";
import { reactive, ref, watch, onMounted, nextTick, computed } from "vue";
import { message } from "ant-design-vue";
import Select from "@/components/Select/index.vue";
import { useRoute } from "vue-router";
import {getRandom}from '@/utils/globalFunc'
const route = useRoute();
// 表单
let form = reactive({});
// 表单的ref
const formRef = ref();
// 表单样式
const labelCol = { span: 6 };
const wrapperCol = { span: 14 };
// 父组件传递的参数
const props = defineProps({
  curRoleID: {
    type: String,
    default: "",
  },
  roleId: {
    type: String,
    default: "",
  },
  // 传递表单数据
  formData: {
    type: Array,
    default: () => {
      return [
        {
          key: "name", // 字段名(必填)
          value: "", //字段默认值(必填)
          label: "日志集名称", // 字段label名(必填)
                   repeatObj: {
            validatorPass: false,
            checked: false,
            totalCount: 0,
            errorMessage: "",
          }, //查重必传属性
          rulesObj: {
            rule: [{ required: true, message: "名称为必填项" }],
            validate: null, //() => {}
            validataTrigger: "", //change/blur
          },
          type: "input", // 输入类型(必填)('input','select','inputRepeat(需要查重时使用)','textarea','switch','requestSelect(下拉列表需要滚动请求)',)
          delSpaces: false, // 'input', 'textarea'时,如果值中包含空格,失去焦点时自动去除
          show: true, // 该输入框是否展示(必填)
          disabled: false, // 输入框是否禁用(必填)
          selectList: [], // 类型为'select'必填
          allowClear: false, // 类型为'select'时,是否允许清除
          requestApi: () => {}, // 'requestSelect'时,请求接口
          requestAllowClear: false, // 'requestSelect'时,是否允许清除
          watchParams: true, // 'requestSelect'时,是否请求的监听参数
          requestParams: {}, // 'requestSelect'时,请求接口参数
          isAffect: true, // 'requestSelect'时,选值时是否影响其他requestSelect的选择
        },
      ];
    },
  },
  showBtnGroup: {
    type: Object,
    default: {
      submitBtn: true, // 提交按钮是否展示
      cancelBtn: true, // 取消按钮是否展示
    },
  },
  // 添加调用的接口
  addApi: {
    type: Function,
    default: () => {},
  },
  // 添加权限组调用的接口
  addGroupApi: {
    type: Function,
    default: () => {},
  },
  // 修改调用的接口closeForm
  editApi: {
    type: Function,
    default: () => {},
  },
  // 提交成功后刷新页面方法
  refreshFunc: {
    type: Function,
    default: () => {},
  },
  // 表单标题
  formTitle: {
    type: String,
    default: "",
  },
  // 关闭表单方法
  closeForm: {
    type: Function,
    default: () => {},
  },
  // 添加权限组成功后的回调
  childrenTable: {
    type: Function,
    default: () => {},
  },
  // 监听参数(是否打开表单)
  visible: {
    type: Boolean,
    default: false,
  },
  // 被控制/被影响的其他请求下拉框
  controlledSelect: {
    type: Function,
    default: () => {},
  },
  //用户管理列表修改、添加时,自定义字段的接口
  addCustomizeApi: {
    type: Function,
    default: () => {},
  },
  editCustomizeApi: {
    type: Function,
    default: () => {},
  },
  selectResourceCount: {
    type: Number,
    default: 0,
  },
  selectScopeCount: {
    type: Number,
    default: 0,
  },
});
onMounted(() => {
  // console.log('1',props.selectResourceCount)
});
// 请求下拉框的ref
const selectRef = ref(null);
// 防抖
let loading = ref(false);
let rules = ref({});
const getRules = () => {
  props.formData.forEach((item) => {
    rules.value[item.key] = item.rulesObj
      ? [
          ...item.rulesObj.rule,
          {
            validator: async (rule, value) => {
              if (value) {
                if (item.rulesObj.validate) {
                  const { success, message } = await item.rulesObj.validate(
                    value,
                    item
                  );
                  if (success) {
                    return Promise.resolve();
                  } else {
                    return Promise.reject(message);
                  }
                } else {
                  return Promise.resolve();
                }
              }
            },
            trigger: item.rulesObj.validataTrigger
              ? item.rulesObj.validataTrigger
              : ["change", "blur"],
          },
        ]
      : [];
  });
};

// 查重
const blurRepeatEvent = (item) => {
  if (item.repeatObj.validatorPass) {
    item
      .requestApi({ ...item.requestParams, code: form[item.key] })
      .then((res) => {
        if (res.totalCount > 0) {
          if (
            props.formTitle.indexOf("修改") > -1 &&
            item.value == form[item.key]
          ) {
            item.repeatObj.checked = true;
          } else {
            item.repeatObj.checked = false;
            item.repeatObj.totalCount = res.totalCount;
          }
        } else {
          item.repeatObj.checked = true;
          console.log('value',form[item.key])
        }
      })
      .catch((err) => {
        console.log("err", err);
      });
  }
};
function changeInputRepeat(item) {
  item.repeatObj.totalCount = 0;
  item.repeatObj.checked = false;
}

// 查重校验是否可提交
let isSubmit = ref(true);

// 校验
// 查重校验
async function checkCode(_rule, value) {
  const reg = /[^\x21-\x7E]+/g;
  isTrue.value = false;
  // showhelp.value = false;
  if (codeDuplicate.value == 0) {
    if (value && reg.test(value)) {
      return Promise.reject(
        "只允许大写字母、小写字母、数字和特殊符号(除空格)"
      );
    } else if (value && value.length > 18) {
      return Promise.reject(`输入字符不能超过18`);
    } else {
      isSubmit.value = true;
      isTrue.value = true;
      return Promise.resolve();
    }
  } else {
    isSubmit.value = false;
    codeDuplicate.value = 0;
    return Promise.reject(`该编码已被占用,请重新输入`);
  }
}
// input框失去焦点时,去除空格
function blurEvent(params) {
  // var reg = /\s+/g;
  form[params] = form[params].trim();
  formRef.value.validate(`${params}`);
}
// checkName校验
async function checkName(_rule, value) {
  var reg = /\s+/g;
  const maxLength = _rule.field == "code" ? 18 : 20;
  if (value && value.length > maxLength) {
    return Promise.reject(`输入字符不能超过${maxLength}`);
  } else if (reg.test(value)) {
    return Promise.reject("不允许输入空格");
  } else {
    return Promise.resolve();
  }
}
// 不做校验
async function checkNo() {
  return Promise.resolve();
}
// 随机生成按钮
function changeRandom(value) {
  form[value] = getRandom();
  formRef.value.validate(`${value}`);
}
// 校验密码
async function checkSecret(_rule, value) {
  const rules = /^[0-9a-zA-Z]*$/g;
  if (rules.test(value)) {
    return Promise.resolve();
  }
  return Promise.reject("只允许输入数字和字母");
}
let nativePlace = reactive({
  provice: "",
  city: "",
});
// 提交
function submit() {
  // 是否做查重:isRepeatInput为true需要做查重,否则不做
  let isRepeatInput = true;
  props.formData.forEach((item) => {
     if (item.repeatObj && item.repeatObj.totalCount > 0) {
      repeat = false;
    }
  });
  if (isRepeatInput) {
    formRef.value
      .validate()
      .then((res) => {
         loading.value = true;
        if (props.formTitle.indexOf("添加权限组用户") > -1) {
          props
            .addApi({
              roleGroupId: props.curRoleID,
              userId: form.userId,
              type: form.type,
            })
            .then((res) => {
              message.success("添加权限组用户成功");
              props.refreshFunc();
              cancel();
              props.closeForm();
            })
            .catch((err) => {
              loading.value = false;
            });
        } 
                else if (props.formTitle.indexOf("添加权限用户") > -1) {
          props
            .addApi({
              roleId: props.roleId,
              ordinal: 0,
              // ...form,
              type:form.type,
              userId:form.userId
            })
            .then((res) => {
              message.success("添加权限用户成功");
              props.refreshFunc();
              cancel();
              props.closeForm();
            })
            .catch((err) => {
              loading.value = false;
            });
        } 
        else if (props.formTitle.indexOf("添加权限组") > -1) {
          props
            .addGroupApi({
              roleGroupId: form.roleGroupId,
              roleId: form.roleID,
              ordinal: 0,
              ...form,
            })
            .then((res) => {
              message.success("添加权限组成功");
              props.refreshFunc();
              cancel();
              props.closeForm();
            })
            .catch((err) => {
              loading.value = false;
            });
        } 
        else if (props.formTitle.indexOf("添加权限") > -1) {
          if (route.fullPath == "/appConfig/powerManage/powerAssign"){
                      props
            .addApi({
              roleGroupId: props.curRoleID,
              roleId: form.name,
              ordinal: 0,
            })
            .then((res) => {
              message.success("添加权限成功");
              props.refreshFunc();
              cancel();
              props.closeForm();
            })
            .catch((err) => {
              loading.value = false;
            });
          }
         else if(route.fullPath == "/appConfig/powerManage/powerList"){
            props
            .addApi({ ...form })
             .then((res) => {
              message.success("添加成功");
              props.refreshFunc();
              cancel();
              props.closeForm();
            })
            .catch((err) => {
              loading.value = false;
            });
         }
        } 
        else if (props.formTitle.indexOf("添加") > -1) {
           const apiScopeClaims = selectedList.value.map(item => {
              delete item.name
              return item
            })
          props
            .addApi({ ...form,apiScopeClaims: JSON.parse(JSON.stringify(apiScopeClaims).replace(/value/g, 'type')) })
            .then((res) => {          
              message.success("添加成功");
              props.refreshFunc();
              cancel();
              props.closeForm();
            })
            .catch((err) => {
              loading.value = false;
            });
        } else if (props.formTitle.indexOf("修改") > -1) {
          props
            .editApi({ ...form }, form.id)
            .then((res) => {     
                props.refreshFunc();
                cancel();
                props.closeForm();
            })
            .catch((err) => {
              loading.value = false;
            });
        }
      })
      .catch((err) => {
        message.warning("请输入/选择必填项");
      });
  } 
}
// 取消
function cancel() {
  loading.value = false;
  formRef.value.resetFields();
  props.closeForm();
  nativePlace = {
    provice: "",
    city: "",
  };
}
// select组件调用的方法(params子组件中传递的值,isAffect是否影响其他请求下拉框)
function selectedValue(params, isAffect, key) {
  // form[params.key] = params.value;
  // nextTick(() => {
  //   formRef.value.validate(`${params.key}`);
  // });
  // if (isAffect) {
  //   // 影响作用请求下拉框选中数据改变,被影响请求下拉框值清空
  //   props.controlledSelect(form);
  //   if (selectRef.value[1]) {
  //     selectRef.value[1].delSelectedData();
  //   }
  // }

  form[params.key] = params.value;
  nextTick(() => {
    formRef.value.validate(`${params.key}`);
  });
  if (props.formTitle == "添加权限" && isAffect) {
    if (key == "scopeId") {
      getResource({
        Enabled: true,
        MaxResultCount: 15,
        PageNumber: 1,
        ApiScopeId: form.scopeId,
      }).then((res) => {
        if (res.items) {
          selectRef.value[1].selectList = res.items;
          selectRef.value[1].selectedData = "";
        }
      });
      props.formData[2].selectList.splice(0);
      form.name = "";
      form.apiResourceId = "";
    } else if (key == "apiResourceId") {
      getCurResource({ id: form.apiResourceId }).then((res) => {
        if (res.roles) {
          props.formData[2].selectList = res.roles;
          form.name = "";
        }
      });
    }
  }
}
//用户声明相关的方法和变量
const Selected=ref([])
const selectedData1=ref([])
const selectList=ref([])
const oldName=ref('')
// 用户声明的input框
const  selectedDeclare1=ref('')
 // 用户声明的弹窗框的显示
const visibleDeclare=ref(false)
 // 用户声明的弹出框的可选择的列表数据
const selectDeclareList=ref([
        { name: '用户资料', value: 'profile' },
        { name: '唯一标识', value: 'sub' },
        { name: '姓名', value: 'name' },
        { name: '昵称', value: 'nickname' },
        { name: '性别', value: 'gender' },
        { name: '头像', value: 'picture' },
        { name: '生日', value: 'birthdate' },
        { name: '时区', value: 'zoneinfo' },
        { name: '区域', value: 'locale' },
        { name: '地址', value: 'address' },
        { name: '权限', value: 'role' },
        { name: '邮箱', value: 'email' },
        { name: '邮箱验证', value: 'email_verified' },
        { name: '电话号', value: 'phone_number' },
        { name: '电话号验证', value: 'phone_number_verified' },
        { name: '企业ID', value: 'EnterpriseId' },
        { name: '企业名称', value: 'EnterpriseName' },
        { name: '地区', value: 'NativePlace' }
      ])
    // 弹出框显示出来
function selectDeclare1() {
      // Selected.value = selectedData.value
      // const selected = Selected.value.map(item => item.value)
      // selectList.value = selectDeclareList.value.filter(item => !selected.includes(item.value))
      visibleDeclare.value = true
    }
        // 删除弹窗框中选中的值
  function handleDelSelect(index){
      Selected.value.splice(index, 1)
      const selected = selected.value.map(item => item.value)
      selectList.value = selectDeclareList.value.filter(item => !selected.includes(item.value))
    }
      // 关闭弹出框
   function cancelDeclare(){
      // this.selected = []
      visibleDeclare.value = false
    }
    // 选中数据值关闭弹出框
    function ok1(){
      visibleDeclare.value = false
      selectedData.value = Selected.value
      if (selectedData.value.length > 0) {
        for (var i = 0; i < selectedData.value.length; i++) {
          if (i == 0) {
            selectedDeclare.value = selectedData.value[i].name
          } else {
            selectedDeclare.value = selectedDeclare.value + ',' + selectedData.value[i].name
          }
        }
      } else {
        selectedDeclare.value = ''
      }
    }
    function getData(){
      detailApiScope(this.id).then(res => {
        // 用户声明
        for (var i = 0; i < res.apiScopeClaims.length; i++) {
          if (i == 0) {
            selectedDeclare.value = selectList.value.find(item => {
              return item.value == res.apiScopeClaims[0].type
            }).name
          } else {
           selectedDeclare.value =
              selectedDeclare.value +
              ',' +
              selectList.value.find(item => {
                return item.value == res.apiScopeClaims[i].type
              }).name
          }
          selectedData.value[i] = {
            value: res.apiScopeClaims[i].type,
            name: selectList.value.find(item => {
              return item.value == res.apiScopeClaims[i].type
            }).name
          }
        }
        Selected.value = [...selectedData.value]
        oldName.value = res.name
        const apiScopeClaims = res.apiScopeClaims.map(item => item.type)
        selectList.value = selectList.value.filter(item => !apiScopeClaims.includes(item.value))
        apiScopeForm.value = {
          id: this.id,
          name: res.name,
          displayName: res.displayName,
          enabled: res.enabled,
          showInDiscoveryDocument: res.showInDiscoveryDocument,
          required: res.required,
          emphasize: res.emphasize,
          description: res.description
        }
      })
    }


// 后来换的
const selectedData=ref([])
const selectedList = ref([]);
// 用户声明的input框
const  selectedDeclare=ref('')
function selectDeclare() {
      // Selected.value = selectedData.value
      // const selected = Selected.value.map(item => item.value)
      // selectList.value = selectDeclareList.value.filter(item => !selected.includes(item.value))
      visibleDeclare.value = true
    }
const leftList = ref([
     { name: '用户资料', value: 'profile' },
        { name: '唯一标识', value: 'sub' },
        { name: '姓名', value: 'name' },
        { name: '昵称', value: 'nickname' },
        { name: '性别', value: 'gender' },
        { name: '头像', value: 'picture' },
        { name: '生日', value: 'birthdate' },
        { name: '时区', value: 'zoneinfo' },
        { name: '区域', value: 'locale' },
        { name: '地址', value: 'address' },
        { name: '权限', value: 'role' },
        { name: '邮箱', value: 'email' },
        { name: '邮箱验证', value: 'email_verified' },
        { name: '电话号', value: 'phone_number' },
        { name: '电话号验证', value: 'phone_number_verified' },
        { name: '企业ID', value: 'EnterpriseId' },
        { name: '企业名称', value: 'EnterpriseName' },
        { name: '地区', value: 'NativePlace' }
]);
const selectEvent = (value) => {
  const index = leftList.value.findIndex((item) => item.name == value.name);
  leftList.value.splice(index, 1);
  selectedList.value.push({ name: value.name,type:value.value });
};
const delSelectEvent = (value) => {
  const index = selectedList.value.findIndex(
    (item) => item.name == value.name
  );
  selectedList.value.splice(index, 1);
  leftList.value.push({ name: value.name,type:value.value });
};
 // 选中数据值关闭弹出框
function ok(){
      visibleDeclare.value = false
      selectedData.value = selectedList.value
      if (selectedData.value.length > 0) {
        for (var i = 0; i < selectedData.value.length; i++) {
          if (i == 0) {
            selectedDeclare.value = selectedData.value[i].name
          } else {
            selectedDeclare.value = selectedDeclare.value + ',' + selectedData.value[i].name
          }
        }
      } else {
        selectedDeclare.value = ''
      }
    }

// 暴露给父组件的数据
defineExpose({
  form,
  cancel,
});
// 监听visible
// watch(
//    () => visibleDeclare.value,
//     (newVal, oldVal) => {
//     if(newVal){
//         // selectList.value = selectDeclareList.value
//         // // console.log('list',selectList.value)
//         // if (props.curRoleID) {
//         //   getData()
//         // }
//     }
//     },
//      { deep: true, immediate: true }
// )
watch(
  () => props.visible,
  (newVal, oldVal) => {
    // showhelp.value = false;
    
    // 展示表单时
    if (newVal) {
        getRules();
    } else {
        // formRef.value.resetFields();
      // 关闭表单时
      if (formRef.value) {
        formRef.value.resetFields();
        props.closeForm();
        nativePlace = {
          provice: "",
          city: "",
        };
        if (selectRef.value) {
          selectRef.value.forEach((item) => {
            item.delSelectedData();
          });
        }
      }
    }
  },
  { deep: true, immediate: true }
);
watch(
  () => props.formData,
  (newVal, oldVal) => {
    // 展示表单时
    if (newVal) {
      newVal.map((item) => {
        if(item.key=='nativePlace'&&item.value){
            nativePlace.province = item.value.split(",")[0];
        nativePlace.city = item.value.split(",")[1];
        form[item.key] = item.value
        }
        else{
           form[item.key] = item.value;
        }
      });
    } 
  },
  { deep: true, immediate: true }
);
// 籍贯
function selected(info) {
  form.nativePlace = info.province.value + "," + info.city.value;
  nativePlace = {
    province: info.province.value,
    city: info.city.value,
  };
}
watch(
  () => props.roleId,
  (newVal, oldVal) => {
   props.roleId=newVal
  },
  )
</script>
<style lang="less" scoped>
.btn_box {
  width: 650px;
  padding-top: 20px;
  padding-bottom: 10px;
  border-top: solid 1px #e8e8e8;
  position: absolute;
  bottom: 0;
  right: 0;
  text-align: center;
  z-index: 2;
  background: #fff;
}
.btnGroup {
  width: 146px;
  display: flex;
  justify-content: space-between;
  margin: 0 auto;
}

// 查重样式
.checkDuplicate {
  position: absolute;
  width: 30px;
  font-size: 12px;
  margin-left: 5px;
  line-height: 32px;
}
.helptip {
  color: #52c41a;
  font-size: 13px;
}
</style>

<style lang="less" scoped>
:deep(.ant-list-items){
  height: 376px;
  overflow-y: scroll;
  cursor: pointer;
}
.add-form-warp {
  background-color: #fff;
  .addSource {
    border: solid 1px #ccc;
    border-radius: 5px;
    width: 1000px;
    margin-left: auto;
    margin-right: auto;
  }
  .showSource {
    margin-top: 20px;
    width: 1000px;
    margin-left: auto;
    margin-right: auto;
    border: solid 1px #ccc;
    border-radius: 5px;
    :deep(.ant-table-pagination.ant-pagination) {
      margin-right: 20px;
    }
  }
  .title {
    line-height: 50px;
    border-bottom: solid 1px #ccc;
    background-color: rgba(0, 0, 0, 0.03);
    margin-bottom: 20px;
    div {
      margin-left: 20px;
    }
  }
  .picker {
    margin-top: 20px;
  }
}
</style>
<style lang="less" scoped>
.clientPage {
  background-color: #fff;
  padding: 24px;
  min-height: calc(100vh - 100px);
  .basicBox {
    margin: 20px auto;
  }
  .appAuthorBox {
    margin: 20px auto;
    display: flex;
    justify-content: space-between;
    .sourceBox {
      width: 48%;
      :deep(.ant-btn) {
        width: 100%;
      }
    }
    .empowerBox {
      width: 48%;
      :deep(.ant-btn) {
        width: 100%;
      }
    }
  }
  .secretBox {
    width: 80%;
    margin: 20px auto;
    button {
      width: 100%;
    }
  }
  .tokenBox {
    width: 80%;
    margin: 20px auto;
    // text-align: center;
    .declareBox {
      margin: 20px auto;
      .declareBtn {
        width: 100%;
      }
    }
  }
}
.modalBox {
  display: flex;
  justify-content: space-between;
  // height: 480px;
  .leftTitle {
    font-size: 16px;
    color: #363636;
    padding-bottom: 14px;
  }
  .leftList {
    width: 300px;
    height: 380px;
    background-color: #f4f6f8;
    border: 1px solid #dedede;
    border-radius: 5px;
    overflow-y: auto;
    list-style: none;
    div {
      width: 100%;
      padding: 10px 20px;
      border-bottom: 1px solid #e8e8e8;
      cursor: pointer;
      &:last-child {
        border-bottom: none;
      }
    }
  }
}
:deep(.ant-modal-footer) {
  text-align: center !important;
}
</style>

select component

<template>
  <div class="selectBox">
    <a-select
      @change="changeSelect"
      :getPopupContainer="(triggerNode) => triggerNode.parentNode"
      @popupScroll="popupScrollEvent"
      v-model:value="selectedData"
      @click="focusSelect"
    >
      <a-select-option
        v-for="(item, index) in selectList"
        :key="index"
        :value="item.id ? item.id : item.logSetId"
        >{
   
   { item.name }}</a-select-option
      >
      <template #dropdownRender="{ menuNode: menu }">
        <v-nodes :vnodes="menu" />
        <div v-if="isLoading">
          <span><a-spin /></span>
        </div>
        <div v-else-if="!isNewData" class="noMore">
          没有更多数据了
          <!-- <template #description> 暂无数据 </template> -->
        </div>
        <!--显示结果-->
        <!--v-nodes必须要写在最后面,这个代表要渲染的下拉列表数据-->
        <!-- <template v-else>
          <v-nodes :vnodes="menu" />
        </template> -->
      </template>
    </a-select>
  </div>
</template>
<script setup>
import { onMounted, ref, watch } from "vue";
import { message } from "ant-design-vue";
import { useRoute } from "vue-router";
const emit = defineEmits();
//必须要写
const VNodes = (_, { attrs }) => {
  return attrs.vnodes;
};
const props = defineProps({
  // 默认选中数据
  value: {
    type: String,
    default: "",
  },
  // 监听参数
  watchParams: {
    type: Boolean,
    default: false,
  },
  // 滚动触底请求接口
  popupScroll: {
    type: Function,
    default: () => {},
  },
  // 对象键名
  objectName: {
    type: String,
    default: "",
  },
  // 请求参数
  params: {
    type: Object,
    default: () => {
      return {};
    },
  },
    // 表单标题  下拉框内容有时候需要特殊判断特殊处理
  formTitle: {
    type: String,
    default: "",
  },
});
let selectList = ref([]);
let PageNumber = ref(1);
const MaxResultCount = ref(10);
let totalPages = ref(1);
// 选中的值
let selectedData = ref("");
onMounted(() => {});
// 是否正在请求中
let isLoading = ref(true);
// 接口请求的新数据
let isNewData = ref(true);
// 滚动事件
function popupScrollEvent(e) {
  const { target } = e;
  const scrllHeight = target.scrollHeight - target.scrollTop;
  const clientHeight = target.clientHeight;
  isNewData.value = true;
  // 下拉框不下拉的时候
  if (scrllHeight === 0 && clientHeight === 0) {
    PageNumber.value = 1;
  } else if (scrllHeight - clientHeight == 0) {
    // 下拉到底部时
    if (PageNumber.value < totalPages.value) {
      // 如果滑到底部,则加载下一页
      PageNumber.value++;
      getListDataCode();
    } else {
      // message.warn("没有更多数据了");
      isNewData.value = false;
    }
  }
}
// 选中的select的值,调用父组件中selectedValue方法
function changeSelect(val) {
  const params = {
    key: props.objectName,
    value: val,
  };
  selectedData.value = val;
  emit("selectedValue", params);
}
// 清空select中的数据
const delSelectedData = () => {
  selectedData.value = undefined;
  selectList.value = []
};
// 请求方法
function getListDataCode() {
  isLoading.value = true;
props
    .popupScroll({
      PageNumber: PageNumber.value,
      MaxResultCount: MaxResultCount.value,
      ...props.params,
    })
    .then((res) => {
      isLoading.value = false;
      //需要特殊显示形势的判断 不需要就删除此判断
      if(route.fullPath == "/appConfig/powerManage/powerAssign"){
        if(props.formTitle=='添加权限用户'||props.formTitle=='添加权限组用户')
        res.items.forEach((item)=>{
    item.name=item.disPlayName + '(' + item.name + ')'
  })
      }
            if(route.fullPath == "/organizationManage/archite"){
        if(props.formTitle=='添加成员')
        res.items.forEach((item)=>{
    item.name=item.disPlayName + '(' + item.name + ')'
  })
      }
      selectList.value = selectList.value.concat(res.items ? res.items : res);
      totalPages.value = Math.ceil(res.totalCount / MaxResultCount.value);
    })
    .catch((err) => {
      isLoading.value = false;
    });
}
// 获取焦点时滚动条回到顶部
function focusSelect() {
  if (!selectedData.value) {
    document.getElementsByClassName("rc-virtual-list-holder")[0].scrollTop = 0;
  }
}
const route = useRoute();
defineExpose({
  delSelectedData,
  selectedData,
  selectList
});
watch(
  () => props.watchParams,
  (newVal, oldVal) => {
                     PageNumber.value = 1;
    selectList.value = [];
    getListDataCode();
    selectedData.value = props.value;
    // if(props.formTitle=='添加成员'&&props.objectName=='code'){
    //   PageNumber.value = 1;
    // }
    // else{
    //        PageNumber.value = 1;
    // selectList.value = [];
    // getListDataCode();
    // selectedData.value = props.value;
    // }
   
  },
  { deep: true, immediate: true }
);
</script>
<style lang="less">
.noMore {
  text-align: center;
}
</style>

golalFuc

export function getRandom(length) {
    var str = "",
        range = length?length: 16,
        arr = [
            "0",
            "1",
            "2",
            "3",
            "4",
            "5",
            "6",
            "7",
            "8",
            "9",
            "a",
            "b",
            "c",
            "d",
            "e",
            "f",
            "g",
            "h",
            "i",
            "j",
            "k",
            "l",
            "m",
            "n",
            "o",
            "p",
            "q",
            "r",
            "s",
            "t",
            "u",
            "v",
            "w",
            "x",
            "y",
            "z",
            "A",
            "B",
            "C",
            "D",
            "E",
            "F",
            "G",
            "H",
            "I",
            "J",
            "K",
            "L",
            "M",
            "N",
            "O",
            "P",
            "Q",
            "R",
            "S",
            "T",
            "U",
            "V",
            "W",
            "X",
            "Y",
            "Z",
        ];
    // 随机产生
    for (var i = 0; i < range; i++) {
        str += arr[Math.round(Math.random() * (arr.length - 1))];
    }
    return str
}

Guess you like

Origin blog.csdn.net/qq_46617584/article/details/130400257