14. Ant-Design-Vue Form校验自界说组件校验办法

首先在Form.Item中界说name特点,然后写一个表单项自界说校验函数, 这里有一点要注意一下,假如自界说校验函数的校验值与表单数据某个呼应式key值相对应,自界说表单项校验时机是实时的,表单项呼应式数据发生改变,校验成果也会随之改变,假如自界说表单项校验函数中的校验值不是表单数据的某个呼应式值,则需求用watch函数,手动触发表单项实时校验。

          <!-- 群聊 -->
          <Form.Item
            v-else-if="formData.pushType === 2"
            label=""
            name="groupChatList"
            :rules="[{ required: true, validator: validateGroupChatList }]"
          >
            <FormSelectStaffGroupButton
              v-model:value="formData.groupChatList"
              :maxCount="200"
              :disabled="isOnlyRead"
              btnClassName="select-group-btn"
            >
              <template #button> 挑选群聊</template>
              <template #list>
                <div v-if="formData.groupChatList?.length" style="margin: 10px 0 10px 0">
                  <div><span class="text">已选职工:</span>{{ Object.keys(staffGroupChatList.groups).length }}个</div>
                  <div><span class="text">已选群聊:</span>{{ formData.groupChatList.length }}个</div>
                  <div
                    v-for="(chatNames, groupOwnerName) in staffGroupChatList.groups"
                    :key="groupOwnerName"
                    class="staff-group-chat-list"
                  >
                    <span class="name">{{ groupOwnerName }}: </span>
                    <div>{{ chatNames.join('、') }}</div>
                  </div>
                </div>
              </template>
            </FormSelectStaffGroupButton>
          </Form.Item>
  <script>
  // 挑选群聊校验
  const validateGroupChatList = (rule: any, value: any, callback: any) => {
    if (formData.groupChatList.length === 0) {
      return Promise.reject(new Error('请至少挑选一个谈天群'));
    } else {
      return Promise.resolve();
    }
  };
  </script>

手动触发表单项实时校验的写法

  watch(
    staffGroupChatList,
    () => {
      eventFormRef.value.validateFields(['groupChatList']);
    },
    { deep: true }
  );
  // 挑选群聊校验
  const validateGroupChatList = (rule: any, value: any, callback: any) => {
    if (Object.keys(staffGroupChatList.value.groups).length === 0) {
      return Promise.reject(new Error('请至少挑选一个谈天群'));
    } else {
      return Promise.resolve();
    }
  };

13. 一些时刻禁选函数

  // 禁选某个时刻段规模之外的日期
  const disableOutsideRanage = (current: Dayjs) => {
    const {startDate,endDate}=props;
    return (
      (current && current < dayjs(startDate).startOf('day')) ||
      current > dayjs(.endDate).endOf('day')
    );
  };
  // 只能挑选未来的时刻 且 结束与开端时刻跨度不能超越100天
   const disabledPastDate = (current: Dayjs) => {
    const [startDate] = formData.strategyRange;
    if (
      current < dayjs().startOf('day') ||
      current.endOf('day').diff(startDate.startOf('day'), 'day') > 99
    ) {
      return true;
    }
    return false;
  };
  // 查找某个时刻段内是否存包含每周某一天
  function findDayOfWeek(start: Dayjs, end: Dayjs, findDayOfWeek: number) {
    const dayOfWeek = findDayOfWeek === 7 ? 0 : findDayOfWeek;
    let current = start;
    while (current <= end) {
      if (current.day() === dayOfWeek) {
        return true;
      }
      current = current.add(1, 'day');
    }
    return false;
  }
  // 查找某个时刻段内是否存包含每月某一天
  function findDayOfMonth(start: Dayjs, end: Dayjs, findDay: number) {
    let current = start;
    while (current <= end) {
      if (current.date() === findDay) {
        return true;
      }
      current = current.add(1, 'day');
    }
    return false;
  }
// 禁选当时时刻10分钟之后的小时
const disabledHours = () => {
  const today = dayjs();
  const selectDay = formData.date;
  // 假如是当天
  if (dayjs(selectDay).isSame(today, 'day')) {
    let baseMin = dayjs().minute();
    // 预留10分钟缓冲时刻
    baseMin = dayjs().add(10, 'minute').minute();
    // 计算出来的当时小时分钟数超越当时的50分钟时,禁选当时小时
    let curHour = dayjs().hour();
    if (baseMin > 50) {
      curHour = curHour + 1;
    }
    // 禁止当时小时之前的小时
    return range(0, 24).filter((hour) => hour < curHour);
  }
  return [];
};
 // 禁选当时时刻10分钟之后的分钟挑选
  const disabledMinutes = (selectedHour: number) => {
    const selectDay = dayjs(formData.date);
    // 假如是当天
    if (selectDay.isSame(dayjs(), 'day')) {
      let baseMin = dayjs().minute();
      // 预留10分钟缓冲时刻
      baseMin = dayjs().add(10, 'minute').minute();
      // 禁选当时小时之前分钟
      if (selectedHour < dayjs().hour()) {
        return range(0, 59);
      } else if (selectedHour === dayjs().hour()) {
        // 禁选当时小时当时分钟之前的分钟
        return range(0, 59).filter((minute) => minute < baseMin);
      }
    }
    // 假如是当天之后的时刻,每小时的0-59都可挑选
    return [];
  };
  // 生成时刻规模
  const range = (start: number, end: number) => {
    const result = [];
    for (let i = start; i < end; i++) {
      result.push(i);
    }
    return result;
  };

12. 四个交互优化点

  • 表单元素的placeholder写一些套话,不如把校验规则写出来,减少用户犯错,更好一些
  • 动态展现图标时,让图标和文字在垂直方向对齐,更漂亮。
  • 二次确认弹窗, 非强制用户挑选的,假如用户点击了页面上别的地方, 要自行进行关闭,不要阻碍用户的视线。
  • 灵敏的设置弹窗方位,有些弹窗方位合适固定,有些弹窗方位,现在在浏览器窗口的固定方位,操作更便利

11. Ant-Design-Vue RangePicker组件将值设置成字符串类型,取用更便利

假如不需求设置初始时刻值的话,引荐加上valueFormat="YYYY-MM-DD"特点,

     <RangePicker
      format="YYYY-MM-DD"
      valueFormat="YYYY-MM-DD"
      :placeholder="['开端时刻', '结束时刻']"
      v-model:value="formData.taskClientRule.addTime"
   />

界说的时分能够直接赋空字符串

const formData = reactive({
    taskClientRule: {
      // ...
      addTime: ['', ''],
    },
}

提交数据的时分, 能够直接取出YYYY-MM-DD字符串类型的日期数值提交给后端服务器, 省去了用dayjs().format('YYYY-MM-DD')来反转。

10. 前端要引导后端,界说合理的接口数据类型

至于为什么要这么做,请看下面的反例展现,excludeTags本来应该界说成一个string[], materials也应该界说成一个Item[], 而前端未告知后端,后端也没仔细看需求文档,界说接口数据类型怎么省事怎么来,把这两个字段都界说成了字符串,成果便是:

// 在编辑和概况页面,需求把字符串转成数组
formData.taskClientRule.excludeTags =
      excludeTags === '' ? [] : excludeTags?.split(',').map((item) => Number(item)) || [];
formData.materials = isValidJson(materialsStr) ? JSON.parse(materialsStr) : [];
// 在提交数据时, 又得将数组转化成字符串
params.excludeTags= formData.taskClientRule.excludeTags.join(','),
params.materialsStr = JSON.stringify(materialContent.materials || []);

不合理的数据类型设计, 既多了一些额外的转化,也增加了发生bug的概率,上例中的Number(item)isValidJson(materialsStr)都是因为自测出bug,才添加上去的。

9. Vue3 watch监听ref界说的目标的两种写法不同

let obj = ref({
 name:'juejin',
 age:19
})
// 带.value的写法监视的是RefImpl目标,不用写{deep:true}
watch(obj.value,(new,old)=>{
    console.log('obj发生了改变',new,old)
})
// 不带.value的写法,需求敞开{deep:true}参数
watch(obj,(new,old)=>{
    console.log('obj发生了改变',new,old)
},{deep:true})

在这里特别阐明一下RefImpl,RefImpl是 reference+implement,翻译为引证完结。也便是说在vue3里边,假如想把数据变成呼应式的,应该用ref函数包裹,也便是把该数据变成引证完结的实例目标。

8. 空值合并运算符一个需求注意的小点

let obj={};
// 假如a是一个特点,下面的写法没问题
obj?.a
// 假如a是一个函数,下面的写法就会报错
obj?.a()
// 正确的写法是
obj?.a?.()
// 如下的写法也不会报错
obj?.a?.b()

也便是说判别a是否存在和运行a()是同时执行的, 遇到这种情况,要进行两次判空。

7.简单发生bug的地方?

  • 反常流程处理欠周 比如说上传清单文件,遗漏了上传文件失利清空数据
  • 接口数据界说不合理,比如说前端的多选组件,值的类型为数组,数组中的每个值为数字,后端将接纳多选数据的字段界说成字符串,在处理字符串转数组的过程中,有可能忽略数据类型的转化
  • 联动逻辑,比如说要判别某个时刻点是否已过,要结合挑选的日期和小时分钟做判别,假如改变了 小时分钟的禁选逻辑,那么就要考虑一下,挑选日期的逻辑是否也会引起改变。
  • 遗漏了一些场景的测试,比如说有多个tab, 某个tab与其它的tab展现逻辑不一样, 只看了一下展现相同的tab的逻辑,遗漏了展现不同的tab逻辑。
  • 合并代码处理抵触时, 当文件中的代码呈现大段的抵触时, 可能会因为不仔细,发生代码合并错误,造成bug。

6.企业微信中external_userid,userid和unionId三者的联系?

企业微信下的corpid, userid, external_userid的概念可查询此文

  • 其间企业微信中的账号ID(即userid)假如是由系统主动生成的,将答应被修改一次。
  • external_userid由企业微信系统生成,企业无法在管理端页面中查看,只能经过 API 获取到,同一个客户在企业的不同自建运用,获取到的external_userid是相同的。
  • unionid的用途是微信中为了区分同一个用户在不同运用下的唯一身份。能够用unionid查询出客户的external_userid.

5.eslint在package.json中设置忽略对某个文件校验的办法

比如说想设置对src/jimo-tracker-sdk/lib/index.js,语法如下:

  "lint-staged": {
    "src/**/*.{js,ts,jsx,tsx}": [
      "eslint --fix --ignore-pattern 'src/jimo-tracker-sdk/lib/index.js'"
    ],
    "**/*.{js,jsx,ts,json,html,css,less,scss,md}": [
      "prettier --write"
    ]
  },

4.postman中的Authorization字段,对应的是fetch请求中哪个字段?

每月前进一点点--202401

对应联系如下:

fetch('https://example.com/data', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer ' + token
  }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));

3.数组快速填充办法

// 办法一
let obj = Array(10).fill({name:'王五',age:18});
// 办法二 用于只想生成一个用来遍历10次,执行其它逻辑的数,组
const arr = [...Array(10).keys()];
//[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
// 办法三
const arr = Array.from(Array(10), (item, index)=>({name:'王五',age:18}));
// 办法四
const arr = Array.from({length:10},(item,index)=>({name:'王五',age:18}));

2.Vue3对标React-DnD的库是哪个?

试了好几个vue3的拖拽库,不是运用文档不翔实,便是只支撑vue2

  • vue-draggable
  • vue3-dnd
  • vue-smooth-dnd
  • vue-easy-dnd
  • v-drag

最终找个这个库vue-draggable-plus,按照文章描述,很Easy的就把拖拽效果完结了,假如我们在vue3项目需求用到拖拽功用的话,引荐我们运用这个库。

1.企业微信的大坑

首先说一下Webview的概念。webview 是一个根据webkit的引擎,能够解析DOM 元素,展现html页面的控件,它和浏览器展现页面的原理是相同的,类似浏览器。webview通常用于原生运用加载html页面,这样做的优点是:运用内容假如选用的是webview+从服务器端获取html+css+js文件的话, 更新运用内容无需用户下载安装最新版别的运用安装包,只要新功用部署完结,用户重新进入运用之后,运用会主动改写,就能够看到最新的功用。企业微信内置浏览器内核在IOS是webkit,在Android上以前是x5,现在是Chromium。

企业微信App v4.1.6版别,内置的WebView版别是107, 自建H5运用能够正常翻开,而v4.1.6以上版别,内置的WebView版别是110,自建H5运用无法翻开。我从v4.1.16逐个版别往下降,降到v4.1.6才正常了。还有一点比较诡异,便是v4.1.6以上的企业微信版别,刚开端安装完,内置的WebView显示的版别是70,自建H5运用翻开正常,过个十几分钟后,内置的WebView版别会变成110,这个时分自建H5运用就打不开了。

每月前进一点点--202401