刚才的Echarts图表组件非常简单,下面我们开发相对复杂的自动打卡组件
向友空间发送自动打卡请求
如果打卡成功,返回
打卡成功,考勤时间为YYYY-MM-DD HH:mm:ss
。
如果打卡失败,返回打卡失败的原因
如果打卡失败,询问是否需要帮忙打开考勤应用或使用外勤打卡
如果需要,打开考勤应用/帮助使用外勤打卡
如果不需要,返回
好的,您可以稍后在 考勤应用/外勤打卡 中进行打卡。
定义一个状态,用来表示签到信息:
const [signData, setSignData] = useState<SignData>({signType: '',message: ''})
定义一个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) // 可能内容会比较多,手动滚动到底部
})
}
使用模拟数据测试组件的渲染效果
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
}
引入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: '您好,当前地点不在内勤的范围区间内,您需要在指定的范围内才能打内勤卡,您是否需要使用外勤打卡?' }} />
根据不同的签到信息,返回不同的组件
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
}
下面我们开始编写第二阶段的需求:如果打卡失败,询问是否需要帮忙打开考勤应用或使用外勤打卡
如果用户发送
是|需要|好
等意图,则打开考勤应用或外勤打卡
如果用户发送
不|不需要|不用
等意图,则返回好的,您可以稍后在'考勤应用'/'外勤打卡'中进行打卡。
其他情况,返回
您好,我不太明白您的意思,请重新选择是否需要我为您打开考勤应用|外勤打卡?
下面我们开始实战
在打卡失败处使用 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} />
}
添加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' ? '考勤应用' : '外勤打卡'}。`
}} />
}
}
组件中判断是否是在上一次会话中,如果是,则返回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' ? '考勤应用' : '外勤打卡'}。`
}} />
}
}
此处我们已经完成了自动打卡组件。