import { Tag, Image, Button } from 'vant' import { computed, defineComponent, nextTick, onMounted, PropType, reactive, ref } from 'vue' import styles from './index.module.less' import { useRect } from '@vant/use' import { AnswerType, QuestionType } from '../../unit' import AnserTitle from '../anser-title' import AnswerAnalysis from '../answer-analysis' // 单选和多选题 export default defineComponent({ name: 'keep-look-question', props: { value: { type: Array, default: () => [] }, index: { // 题目是第几道 type: Number, default: 1 }, data: { type: Object, default: () => ({}) }, /* 只读 */ readOnly: { type: Boolean, default: false }, showRate: { type: Boolean, default: false }, showAnalysis: { // 是否显示解析 type: Boolean, default: false }, analysis: { type: Object, default: () => ({ message: '', topic: false, // 是否显示结果 userResult: true // 用户答题对错 }) } }, emits: ['update:value'], setup(props, { emit }) { const canvasRef = ref() const state = reactive({ answerDomId: 'answer' + +new Date(), answerRect: {} as any, sortable: null as any, list: [] as any, options: [] as any, drawLineList: [] as any, selectItem: [] as any }) /* drawLineList 下的对象 { startPoint: { x: 0, y: 0 }, endPoint: { x: 0, y: 0}, leftIndex: 1, rightIndex: 2 } */ const onLeftClick = (e: any, item: any) => { // 是否只读 if (props.readOnly) return state.options.forEach((option: any) => { if (!option.leftLocked && item.index !== option.index) { option.left = false } }) const selectd = isDrawLine(item.index, 'left') // 判断当前元素是否已经连线了 if (selectd.status) { state.options.forEach((option: any) => { if (!option.rightLocked) { option.right = false } }) state.selectItem = [] // 如果已经连线了则去掉 state.drawLineList.splice(selectd.selectIndex, 1) // 重新绘制 renderDrawLine(canvasRef.value) state.options.forEach((option: any) => { if (selectd.selectOption.leftIndex === option.index) { option.left = false option.leftLocked = false } if (selectd.selectOption.rightIndex === option.index) { option.right = false option.rightLocked = false } }) } else { item.left = !item.left if (item.left) { // 为true时添加定位 state.selectItem[0] = { index: item.index } } else { state.selectItem[0] = null } // 判断是否有二个的关联 if (state.selectItem[0] && state.selectItem[1]) { const postion = calcPoint() state.drawLineList.push(postion) state.selectItem = [] renderDrawLine(canvasRef.value) } } onSelect() } const onRightClick = (e: any, item: any) => { // 是否只读 if (props.readOnly) return // 去掉 state.options.forEach((option: any) => { if (!option.rightLocked && item.index !== option.index) { option.right = false } }) const selectd = isDrawLine(item.index, 'right') // 判断当前元素是否已经连线了 if (selectd.status) { state.options.forEach((option: any) => { if (!option.leftLocked) { option.left = false } }) state.selectItem = [] // console.log( // selectd, // 'selected', // JSON.stringify(state.drawLineList), // state.drawLineList, // state.drawLineList.length // ) // 如果已经连线了则去掉 state.drawLineList.splice(selectd.selectIndex, 1) // 重新绘制 renderDrawLine(canvasRef.value) state.options.forEach((option: any) => { if (selectd.selectOption.leftIndex === option.index) { option.left = false option.leftLocked = false } if (selectd.selectOption.rightIndex === option.index) { option.right = false option.rightLocked = false } }) } else { item.right = !item.right if (item.right) { // 为true时添加定位 state.selectItem[1] = { index: item.index } } else { state.selectItem[1] = null } // 判断是否有二个的关联 if (state.selectItem[0] && state.selectItem[1]) { const postion = calcPoint() state.drawLineList.push(postion) state.selectItem = [] renderDrawLine(canvasRef.value) } } onSelect() } /** * @description 判断是否已连接 * @param key 选中的选项标识 * @returns status 状态, selectIndex 索引, selectOption 选中选项 */ const isDrawLine = (key: number, type = 'left') => { const drawLineList = state.drawLineList || [] let status = false // true 连,false 没连 let selectIndex = 0 // 连了所在的索引 let selectOption: any = {} drawLineList.forEach((item: any, index: number) => { if (item.leftIndex === key && type === 'left') { selectOption = item status = true selectIndex = index } else if (item.rightIndex === key && type === 'right') { selectOption = item status = true selectIndex = index } }) return { status, selectIndex, selectOption } } /** * @description 计算连线坐标位置及左右关联编号,每次计算坐标的时候都取元素最新位置定位 * @returns 连线的坐标 */ const calcPoint = () => { const canvasPostion = useRect(canvasRef.value) const leftSectionItem = state.selectItem[0] const firstPostion: any = useRect( document.getElementById(leftSectionItem.index + '-left') as any ) firstPostion.index = leftSectionItem.index // console.log('🚀 ~ calcPoint ~ leftObj', leftObj) const rightSectionItem = state.selectItem[1] const secondPostion: any = useRect( document.getElementById(rightSectionItem.index + '-right') as any ) secondPostion.index = rightSectionItem.index console.log(state.selectItem, firstPostion, secondPostion) const startPoint = { x: firstPostion.width, y: firstPostion.top + firstPostion.height / 2 - canvasPostion.top } const endPoint = { x: secondPostion.left - canvasPostion.left, y: secondPostion.top + secondPostion.height / 2 - canvasPostion.top } state.options.forEach((item: any) => { if (item.index === firstPostion.index) { item.leftLocked = true } if (item.index === secondPostion.index) { item.rightLocked = true } }) return { startPoint, endPoint, leftIndex: firstPostion.index, rightIndex: secondPostion.index } } /** * @description 绘制连线 * @param canvasRef 对象 */ const renderDrawLine = (canvasRef: any) => { // 重新画线 if (canvasRef.getContext) { const ctx = canvasRef.getContext('2d') ctx.clearRect(0, 0, state.answerRect.width, state.answerRect.height) state.drawLineList.forEach((item: any) => { drawLine(ctx, item.startPoint, item.endPoint) }) } } /** * @description 连线 * @param ctx canvas 对象 * @param startPoint 开始坐标 * @param endPoint 结束坐标 * @returns void(0) */ const drawLine = (ctx: any, startPoint: any, endPoint: any) => { ctx.beginPath() ctx.moveTo(startPoint.x, startPoint.y) ctx.lineTo(endPoint.x, endPoint.y) ctx.lineWidth = 2 ctx.strokeStyle = '#FF8057' ctx.stroke() } // 返回选中的结果 const onSelect = () => { const options = state.options || [] const drawLineList = state.drawLineList || [] const result: any = [] drawLineList.forEach((item: any) => { const leftOption = options.find((child: any) => child.index === item.leftIndex) const rightOption = options.find((child: any) => child.index === item.rightIndex) result.push({ answerId: leftOption.index, answer: leftOption.leftValue, answerExtra: rightOption.rightValue }) }) emit('update:value', result) } const initOptions = () => { const answers = props.data.answers || [] const userAnswer = props.data.userAnswer || [] // 用户填写的答案 // console.log(answers, '111') answers.forEach((answer: any) => { const tmp = { index: answer.examinationQuestionAnswerId, // 左边的值 leftValue: answer.questionAnswer, // 左边的值 rightValue: answer.questionExtra, // 右边的值 leftType: answer.questionAnswerTypeCode || 'TXT', // 左边类型 rightType: answer.questionExtraTypeCode || 'TXT', // 右边类型 left: false, // 左边是否选中 right: false, // 右边是否选中 leftLocked: false, // 是否已经连线 rightLocked: false // 是否已经连线 } state.options.push(tmp) }) // 反显答案-初始化数据 // console.log(userAnswer) userAnswer.forEach((user: any) => { const temps: any = { startPoint: { x: 0, y: 0 }, endPoint: { x: 0, y: 0 }, leftIndex: 0, rightIndex: 0 } state.options.forEach((option: any) => { // 左边状态 if (option.index === user.answerId) { option.left = true option.leftLocked = true temps.leftIndex = option.index } // 右边状态 // console.log(option, user, '----') if (option.rightValue === user.answerExtra) { option.right = true option.leftLocked = true temps.rightIndex = option.index } }) state.drawLineList.push(temps) }) // console.log(state.drawLineList, state.options) // 反显答案-连线 nextTick(() => { state.drawLineList.forEach((draw: any) => { state.selectItem = [] const leftObj: any = useRect(document.getElementById(draw.leftIndex + '-left') as any) leftObj.index = draw.leftIndex state.selectItem[0] = leftObj const rightObj: any = useRect(document.getElementById(draw.rightIndex + '-right') as any) rightObj.index = draw.rightIndex state.selectItem[1] = rightObj const postion = calcPoint() draw.endPoint = postion.endPoint draw.startPoint = postion.startPoint state.selectItem = [] }) setTimeout(() => { renderDrawLine(canvasRef.value) }, 100) }) } onMounted(() => { initOptions() // 获取canvas 对象 nextTick(() => { // 获取canvas元素定位 const answer: any = document.getElementById(state.answerDomId) const answerRect = useRect(answer) state.answerRect = answerRect }) }) return () => ( <>