3.5.4代码实战3:SmartSignIn

自动打卡组件实战

刚才的Echarts图表组件非常简单,下面我们开发相对复杂的自动打卡组件

需求分析

  1. 向友空间发送自动打卡请求

    如果打卡成功,返回 打卡成功,考勤时间为YYYY-MM-DD HH:mm:ss

    如果打卡失败,返回打卡失败的原因

  2. 如果打卡失败,询问是否需要帮忙打开考勤应用或使用外勤打卡

    如果需要,打开考勤应用/帮助使用外勤打卡

    如果不需要,返回好的,您可以稍后在 考勤应用/外勤打卡 中进行打卡。

开始实战

  1. 定义一个状态,用来表示签到信息:

    const [signData, setSignData] = useState<SignData>({signType: '',message: ''})
    
  2. 定义一个doSignIn方法,用于在组件渲染完成后向友空间发送自动签到请求,并设置签到信息signData

    import { useEffect } from 'react'
    useEffect(() => {
        console.log('SmartSignIn')
        doSignIn()
    }, [])
    
    function doSignIn () {
        vpa.notify({
          action: 'robot_open_AutomaticSign',
          data: {
            signType: 'startDiworkSign'
          }
        }, (ret: SignData) => {
          console.log('签到结果', ret)
          setSignData(ret)
          vpa.util.scrollToBottom(0) // 可能内容会比较多,手动滚动到底部
        })
    }
    
  3. 使用模拟数据测试组件的渲染效果

    vpa.notify方法用于向友空间发送请求,第一个参数为请求的数据,第二个参数为请求的回调函数。由于在开发环境中无法向友空间发送请求,所以我们可以返回一个模拟的签到信息,用于测试组件的渲染效果。

    .config/mock目录下新建notify.js文件,用于模拟vpa.notify方法的返回值

    module.exports = function (option) {
    
    const ret = {
      signType: 'diworkSignSucess',
      message: '2020-10-10 10:10:10'
    }
    
    const ret2 = {
      signType: 'diworkSignError',
      message: '外勤'
    }
    
    return Math.random() > 0.5 ? ret : ret2
    }
    
  4. 引入SimpleText组件,用于返回不同结果的组件

    const { SimpleText } = vpa.__shared.cards
    
    const SignInSuccess = () => <SimpleText modelID={1} data={{ text: `打卡成功,考勤时间为:【${signData.message}】` }} />
    
    const SignInFail = () => <SimpleText modelID={1} data={{ text: `您好,内勤打卡失败,失败原因为:【${signData.message}】,请到考勤设置模块查看具体原因或联系考勤管理员反馈,是否需要帮忙打开考勤应用?` }} />
    
    const OutDiworkSign = () => <SimpleText modelID={1} data={{ text: '您好,当前地点不在内勤的范围区间内,您需要在指定的范围内才能打内勤卡,您是否需要使用外勤打卡?' }} />
    
  5. 根据不同的签到信息,返回不同的组件

    let ui
    switch (signData.signType) {
      case 'diworkSignSucess':
        ui = <SignInSuccess />
        break
      case 'diworkSignError':
        ui = <SignInFail />
        break
      case 'outDiworkSign':
        ui = <OutDiworkSign />
        break
      default:
        ui = <SimpleText modelID={0} />
    }
    
    return ui
    

到此处为止,我们已经完成了第一阶段需求的编写。

此时你的代码应该是这样子:

import { useEffect, useState } from 'react'

const { SimpleText } = vpa.__shared.cards

interface SignData {
  /**
   * 签到结果类型
   */
  signType: '' | 'diworkSignSucess' | 'diworkSignError' | 'outDiworkSign'
  message: string
}

export default function SmartSignIn (data) {

  const [signData, setSignData] = useState<SignData>({
    signType: '',
    message: ''
  })

  useEffect(() => {
    console.log('SmartSignIn')
    doSignIn()
  }, [])

  function doSignIn () {
    vpa.notify({
      action: 'robot_open_AutomaticSign',
      data: {
        signType: 'startDiworkSign'
      }
    }, (ret: SignData) => {
      console.log('签到结果', ret)
      setSignData(ret)
      vpa.util.scrollToBottom(0) // 可能内容会比较多,手动滚动到底部
    })
  }

  const SignInSuccess = () => <SimpleText modelID={1}
    data={{ text: `打卡成功,考勤时间为:【${signData.message}】` }} />

  const SignInFail = () => <SimpleText modelID={1}
    data={{ text: `您好,内勤打卡失败,失败原因为:【${signData.message}】,请到考勤设置模块查看具体原因或联系考勤管理员反馈,是否需要帮忙打开考勤应用?` }} />

  const OutDiworkSign = () => <SimpleText modelID={1}
    data={{ text: '您好,当前地点不在内勤的范围区间内,您需要在指定的范围内才能打内勤卡,您是否需要使用外勤打卡?' }} />

  let ui

  switch (signData.signType) {
  case 'diworkSignSucess':
    ui = <SignInSuccess />
    break
  case 'diworkSignError':
    ui = <SignInFail />
    break
  case 'outDiworkSign':
    ui = <OutDiworkSign />
    break
  default:
    ui = <SimpleText modelID={0} />
  }

  return ui
}

下面我们开始编写第二阶段的需求:如果打卡失败,询问是否需要帮忙打开考勤应用或使用外勤打卡

如果用户发送 是|需要|好等意图,则打开考勤应用或外勤打卡

如果用户发送 不|不需要|不用等意图,则返回好的,您可以稍后在'考勤应用'/'外勤打卡'中进行打卡。

其他情况,返回您好,我不太明白您的意思,请重新选择是否需要我为您打开考勤应用|外勤打卡?

下面我们开始实战

  1. 在打卡失败处使用 vpa.keepNext()方法 控制下一次会话的控制权,避免将用户的意图发送给智能中台,而是直接在前端进行处理

      switch (signData.signType) {
        case 'diworkSignSucess':
       ui = <SignInSuccess />
       break
      case 'diworkSignError':
       ui = <SignInFail />
       vpa.keepNext(signData)
       break
      case 'outDiworkSign':
        ui = <OutDiworkSign />
        vpa.keepNext(signData)
        break
      default:
        ui = <SimpleText modelID={0} />
      }
    
  2. 添加NextAction组件,用来处理用户第二次发送的意图

    组件中我们要判断用户是否需要我们打开考勤应用外勤打卡

    function NextAction ({
      text,
      data
    }) {
      console.log('NextAction', {
        text,
        data
      })
    
      if (
        text === '是' ||
        text === '确定' ||
        text.includes('好') ||
        text.includes('打开') ||
        text.includes('帮')
      ) {
        vpa.disposeNext()
    
        let text
    
        if (data.signType === 'diworkSignError') { // 打卡失败,打开考勤
          text = '已帮您打开考勤应用,请在考勤应用中进行打卡。'
    
          vpa.notify({
            action: 'opensignin'
          })
        } else if (data.signType === 'outDiworkSign') { // 外勤打卡
          text = '已帮您打开外勤打卡,请在外勤打卡中进行打卡。'
          vpa.notify({
            action: 'robot_open_AutomaticSign',
            data: {
              signType: 'startOutDiworkSign'
            }
          })
        }
    
        return <SimpleText modelID={1} data={{
          text
        }} />
      } else if (text === '否' || text === '取消' || text.includes('不')) {
        vpa.disposeNext()
        return <SimpleText modelID={1} data={{
          text: `好的,您可以稍后在${data.signType === 'diworkSignError' ? '考勤应用' : '外勤打卡'}中进行打卡。`
        }} />
      } else {
        return <SimpleText modelID={1} data={{
          text: `您好,我不太明白您的意思,请重新选择是否需要我为您打开${data.signType === 'diworkSignError' ? '考勤应用' : '外勤打卡'}。`
        }} />
      }
    }
    
  3. 组件中判断是否是在上一次会话中,如果是,则返回NextAction组件

  if (data._keepNext) return <NextAction {...data} />

完整代码如下

import { useEffect, useState } from 'react'

const { SimpleText } = vpa.__shared.cards

interface SignData {
  /**
   * 签到结果类型
   */
  signType: '' | 'diworkSignSucess' | 'diworkSignError' | 'outDiworkSign'
  message: string
}

export default function SmartSignIn (data) {
  if (data._keepNext) return <NextAction {...data} />

  const [signData, setSignData] = useState<SignData>({
    signType: '',
    message: ''
  })

  useEffect(() => {
    console.log('SmartSignIn')
    doSignIn()
  }, [])

  function doSignIn () {
    vpa.notify({
      action: 'robot_open_AutomaticSign',
      data: {
        signType: 'startDiworkSign'
      }
    }, (ret: SignData) => {
      console.log('签到结果', ret)
      setSignData(ret)
      vpa.util.scrollToBottom(0) // 可能内容会比较多,手动滚动到底部
    })
  }

  const SignInSuccess = () => <SimpleText modelID={1}
    data={{ text: `打卡成功,考勤时间为:【${signData.message}】` }} />

  const SignInFail = () => <SimpleText modelID={1}
    data={{ text: `您好,内勤打卡失败,失败原因为:【${signData.message}】,请到考勤设置模块查看具体原因或联系考勤管理员反馈,是否需要帮忙打开考勤应用?` }} />

  const OutDiworkSign = () => <SimpleText modelID={1}
    data={{ text: '您好,当前地点不在内勤的范围区间内,您需要在指定的范围内才能打内勤卡,您是否需要使用外勤打卡?' }} />

  let ui

  switch (signData.signType) {
  case 'diworkSignSucess':
    ui = <SignInSuccess />
    break
  case 'diworkSignError':
    ui = <SignInFail />
    vpa.keepNext(signData)
    break
  case 'outDiworkSign':
    ui = <OutDiworkSign />
    vpa.keepNext(signData)
    break
  default:
    ui = <SimpleText modelID={0} />
  }

  return ui
}

function NextAction ({
  text,
  data
}) {
  console.log('NextAction', {
    text,
    data
  })

  if (
    text === '是' ||
    text === '确定' ||
    text.includes('好') ||
    text.includes('打开') ||
    text.includes('帮')
  ) {
    vpa.disposeNext()

    let text

    if (data.signType === 'diworkSignError') { // 打卡失败,打开考勤
      text = '已帮您打开考勤应用,请在考勤应用中进行打卡。'

      vpa.notify({
        action: 'opensignin'
      })
    } else if (data.signType === 'outDiworkSign') { // 外勤打卡
      text = '已帮您打开外勤打卡,请在外勤打卡中进行打卡。'
      vpa.notify({
        action: 'robot_open_AutomaticSign',
        data: {
          signType: 'startOutDiworkSign'
        }
      })
    }

    return <SimpleText modelID={1} data={{
      text
    }} />
  } else if (text === '否' || text === '取消' || text.includes('不')) {
    vpa.disposeNext()
    return <SimpleText modelID={1} data={{
      text: `好的,您可以稍后在${data.signType === 'diworkSignError' ? '考勤应用' : '外勤打卡'}中进行打卡。`
    }} />
  } else {
    return <SimpleText modelID={1} data={{
      text: `您好,我不太明白您的意思,请重新选择是否需要我为您打开${data.signType === 'diworkSignError' ? '考勤应用' : '外勤打卡'}。`
    }} />
  }
}

此处我们已经完成了自动打卡组件。

是否仍需要帮助? 请保持联络!
最后更新于 2024/05/31