|
@@ -1,9 +1,6 @@
|
|
|
import { Tag, Image, Button } from 'vant'
|
|
|
-import { defineComponent, nextTick, onMounted, PropType, reactive } from 'vue'
|
|
|
-import { labelOptions } from '../../unit'
|
|
|
+import { defineComponent, nextTick, onMounted, PropType, reactive, ref } from 'vue'
|
|
|
import styles from './index.module.less'
|
|
|
-import Sortable from 'sortablejs'
|
|
|
-import deepClone from '@/helpers/deep-clone'
|
|
|
import { useRect } from '@vant/use'
|
|
|
|
|
|
// 单选和多选题
|
|
@@ -25,18 +22,16 @@ export default defineComponent({
|
|
|
/* 只读 */
|
|
|
readOnly: {
|
|
|
type: Boolean,
|
|
|
- default: true
|
|
|
+ default: false
|
|
|
}
|
|
|
},
|
|
|
emits: ['update:value'],
|
|
|
setup(props, { emit }) {
|
|
|
+ const canvasRef = ref()
|
|
|
const state = reactive({
|
|
|
answerDomId: 'answer' + +new Date(),
|
|
|
- answerObj: {
|
|
|
- height: 100,
|
|
|
- width: 100
|
|
|
- },
|
|
|
- canvasDomId: 'canvas' + +new Date(),
|
|
|
+ answerRect: {} as any,
|
|
|
+
|
|
|
sortable: null as any,
|
|
|
list: [] as any,
|
|
|
options: [
|
|
@@ -45,70 +40,198 @@ export default defineComponent({
|
|
|
value: 'Sol',
|
|
|
left: false,
|
|
|
right: false,
|
|
|
- locked: false // 是否已经连线
|
|
|
+ leftLocked: false, // 是否已经连线
|
|
|
+ rightLocked: false // 是否已经连线
|
|
|
},
|
|
|
{
|
|
|
index: 2,
|
|
|
value: 'Sal',
|
|
|
left: false,
|
|
|
right: false,
|
|
|
- locked: false
|
|
|
+ leftLocked: false, // 是否已经连线
|
|
|
+ rightLocked: false // 是否已经连线
|
|
|
},
|
|
|
{
|
|
|
index: 3,
|
|
|
value: 'La',
|
|
|
left: false,
|
|
|
right: false,
|
|
|
- locked: false
|
|
|
+ leftLocked: false, // 是否已经连线
|
|
|
+ rightLocked: false // 是否已经连线
|
|
|
},
|
|
|
{
|
|
|
index: 4,
|
|
|
value: 'Si',
|
|
|
left: false,
|
|
|
right: false,
|
|
|
- locked: false
|
|
|
+ leftLocked: false, // 是否已经连线
|
|
|
+ rightLocked: false // 是否已经连线
|
|
|
}
|
|
|
],
|
|
|
+ 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) => {
|
|
|
- const obj = useRect(e.target)
|
|
|
- console.log(e, obj)
|
|
|
- state.options.forEach((item: any) => {
|
|
|
- item.left = false
|
|
|
+ // 是否只读
|
|
|
+ if (props.readOnly) return
|
|
|
+ state.options.forEach((option: any) => {
|
|
|
+ if (!option.leftLocked && item.index !== option.index) {
|
|
|
+ option.left = false
|
|
|
+ }
|
|
|
})
|
|
|
- item.left = !item.left
|
|
|
|
|
|
- // 为true时添加定位
|
|
|
- if (item.left) {
|
|
|
- state.selectItem[0] = obj
|
|
|
+ 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时添加定位
|
|
|
+ const obj: any = useRect(e.target)
|
|
|
+ obj.index = item.index
|
|
|
+ state.selectItem[0] = obj
|
|
|
+ } else {
|
|
|
+ state.selectItem[0] = null
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断是否有二个的关联
|
|
|
+ if (state.selectItem[0] && state.selectItem[1]) {
|
|
|
+ const postion = calcPoint()
|
|
|
+ state.drawLineList.push(postion)
|
|
|
+ state.selectItem = []
|
|
|
+
|
|
|
+ renderDrawLine(canvasRef.value)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const onRightClick = (e: any, item: any) => {
|
|
|
- const obj = useRect(e.target)
|
|
|
- console.log(e, obj)
|
|
|
- state.options.forEach((item: any) => {
|
|
|
- item.right = false
|
|
|
+ // 是否只读
|
|
|
+ if (props.readOnly) return
|
|
|
+ // 去掉
|
|
|
+ state.options.forEach((option: any) => {
|
|
|
+ if (!option.rightLocked && item.index !== option.index) {
|
|
|
+ option.right = false
|
|
|
+ }
|
|
|
})
|
|
|
- item.right = !item.right
|
|
|
+ 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)
|
|
|
|
|
|
- // 为true时添加定位
|
|
|
- if (item.right) {
|
|
|
- state.selectItem[1] = obj
|
|
|
+ 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时添加定位
|
|
|
+ const obj: any = useRect(e.target)
|
|
|
+ obj.index = item.index
|
|
|
+ state.selectItem[1] = obj
|
|
|
+ } else {
|
|
|
+ state.selectItem[1] = null
|
|
|
+ }
|
|
|
|
|
|
- if (state.selectItem.length >= 2) {
|
|
|
- drawLine()
|
|
|
+ // 判断是否有二个的关联
|
|
|
+ if (state.selectItem[0] && state.selectItem[1]) {
|
|
|
+ const postion = calcPoint()
|
|
|
+ state.drawLineList.push(postion)
|
|
|
+ state.selectItem = []
|
|
|
+ renderDrawLine(canvasRef.value)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- const drawLine = () => {
|
|
|
- const canvas: any = document.getElementById(state.canvasDomId)
|
|
|
- const canvasPostion = useRect(canvas)
|
|
|
- console.log(canvasPostion, 'canvasPostion')
|
|
|
+ /**
|
|
|
+ * @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 firstPostion = state.selectItem[0]
|
|
|
const secondPostion = state.selectItem[1]
|
|
|
|
|
@@ -122,9 +245,45 @@ export default defineComponent({
|
|
|
y: secondPostion.top + secondPostion.height / 2 - canvasPostion.top
|
|
|
}
|
|
|
|
|
|
- const ctx = canvas.getContext('2d')
|
|
|
- console.log(startPoint, endPoint, ctx)
|
|
|
+ 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)
|
|
@@ -150,13 +309,13 @@ export default defineComponent({
|
|
|
// }
|
|
|
|
|
|
onMounted(() => {
|
|
|
- const answer: any = document.getElementById(state.answerDomId)
|
|
|
- const { width, height } = useRect(answer)
|
|
|
- console.log(width, height)
|
|
|
- state.answerObj = {
|
|
|
- height,
|
|
|
- width
|
|
|
- }
|
|
|
+ // 获取canvas 对象
|
|
|
+ nextTick(() => {
|
|
|
+ // 获取canvas元素定位
|
|
|
+ const answer: any = document.getElementById(state.answerDomId)
|
|
|
+ const answerRect = useRect(answer)
|
|
|
+ state.answerRect = answerRect
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
return () => (
|
|
@@ -172,8 +331,14 @@ export default defineComponent({
|
|
|
/>
|
|
|
|
|
|
<div class={[styles.unitAnswers]} id={state.answerDomId}>
|
|
|
- <div class={styles.leftSection}>
|
|
|
- {state.options.map((item: any) => (
|
|
|
+ <canvas
|
|
|
+ ref={canvasRef}
|
|
|
+ class={styles.canvasSection}
|
|
|
+ width={state.answerRect.width || 0}
|
|
|
+ height={state.answerRect.height || 0}
|
|
|
+ ></canvas>
|
|
|
+ {state.options.map((item: any) => (
|
|
|
+ <div class={styles.answerItem}>
|
|
|
<div
|
|
|
class={[styles.unitItem, item.left && styles.active]}
|
|
|
onClick={(e: any) => onLeftClick(e, item)}
|
|
@@ -183,25 +348,38 @@ export default defineComponent({
|
|
|
class={styles.img}
|
|
|
/>
|
|
|
</div>
|
|
|
- ))}
|
|
|
- </div>
|
|
|
- <div class={styles.rightSection}>
|
|
|
- {state.options.map((item: any) => (
|
|
|
<div
|
|
|
class={[styles.unitItem, item.right && styles.active]}
|
|
|
onClick={(e: any) => onRightClick(e, item)}
|
|
|
>
|
|
|
- Re{item.index}
|
|
|
+ {item.value}
|
|
|
+ {item.index}
|
|
|
</div>
|
|
|
- ))}
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ <div class={styles.resetBtnGroup}>
|
|
|
+ <Button
|
|
|
+ round
|
|
|
+ type="primary"
|
|
|
+ disabled={props.readOnly}
|
|
|
+ onClick={() => {
|
|
|
+ state.drawLineList = []
|
|
|
+ renderDrawLine(canvasRef.value)
|
|
|
|
|
|
- <canvas
|
|
|
- id={state.canvasDomId}
|
|
|
- class={styles.canvasSection}
|
|
|
- width={state.answerObj.width}
|
|
|
- height={state.answerObj.height}
|
|
|
- ></canvas>
|
|
|
+ // 清除所有的选中状态
|
|
|
+ state.options.forEach((item: any) => {
|
|
|
+ item.left = false
|
|
|
+ item.right = false
|
|
|
+ item.leftLocked = false
|
|
|
+ item.rightLocked = false
|
|
|
+ })
|
|
|
+ // 清除所有的选择的内容
|
|
|
+ state.selectItem = []
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ 重置
|
|
|
+ </Button>
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|