24 changed files with 697 additions and 195 deletions
@ -0,0 +1,74 @@ |
|||
<template> |
|||
<view v-if="list.length" class="flex flex-col"> |
|||
<view |
|||
v-for="(item, index) in list" |
|||
:key="index" |
|||
class="flex flex-nowrap text-center p-2" |
|||
:class="item.checked ? 'bg-blue-500 text-white' : 'bg-gray-100'" |
|||
@click="changeChecked(index)" |
|||
> |
|||
<view class="flex-1 text-left">{{ item.content }} </view> |
|||
<view v-if="item.checked"><u-icon name="checkbox-mark"></u-icon></view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'ChooseTask', |
|||
props: { tasks: { type: Array, default: () => [] }, time: { type: String, default: '' }, defaultId: { type: String, default: '' } }, |
|||
data() { |
|||
return { list: [], ids: [] }; |
|||
}, |
|||
|
|||
watch: { |
|||
tasks(val) { |
|||
if (val) { |
|||
this.list = val; |
|||
} |
|||
}, |
|||
|
|||
defaultId(val) { |
|||
if (val) { |
|||
this.list.forEach(item => { |
|||
if (item.recordId === val) { |
|||
item.checked = true; |
|||
} else { |
|||
item.checked = false; |
|||
} |
|||
}); |
|||
} |
|||
}, |
|||
}, |
|||
|
|||
mounted() { |
|||
this.list = this.tasks; |
|||
this.list.forEach(item => { |
|||
if (item.recordId === this.defaultId) { |
|||
item.checked = true; |
|||
} else { |
|||
item.checked = false; |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
methods: { |
|||
// 选中某个复选框时,由checkbox时触发 |
|||
changeChecked(index) { |
|||
let arr = [...this.list]; |
|||
for (let i = 0; i < arr.length; i++) { |
|||
const item = arr[i]; |
|||
if (i === index) { |
|||
item.checked = true; |
|||
} else { |
|||
item.checked = false; |
|||
} |
|||
} |
|||
this.list = [...arr]; |
|||
this.$emit('changeDefaultId', this.list[index].recordId); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped></style> |
@ -0,0 +1,188 @@ |
|||
<template> |
|||
<view class="canvas-box w-full"> |
|||
<canvas style="width: 100%; height: 450px" canvas-id="firstCanvas"></canvas> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { trainResult: { type: Object, default: () => {} } }, |
|||
|
|||
data() { |
|||
return { |
|||
canvas: null, |
|||
context: null, |
|||
points: [], |
|||
timers: [], |
|||
width: '', |
|||
}; |
|||
}, |
|||
|
|||
watch: { |
|||
trainResult(val) { |
|||
if (val) { |
|||
this.init(); |
|||
} |
|||
}, |
|||
}, |
|||
|
|||
mounted() { |
|||
this.$nextTick(() => { |
|||
const query = uni.createSelectorQuery().in(this); |
|||
query |
|||
.selectAll('.canvas-box') |
|||
.boundingClientRect(data => { |
|||
if (data && data.length) { |
|||
this.width = data[0].width; |
|||
this.init(); |
|||
} |
|||
}) |
|||
.exec(); |
|||
}); |
|||
}, |
|||
|
|||
methods: { |
|||
init() { |
|||
this.clearTimers(); |
|||
this.initCanvas(); |
|||
this.drawImage(this.width); |
|||
}, |
|||
|
|||
// 清除定时器 |
|||
clearTimers() { |
|||
if (this.timers.length) { |
|||
this.timers.forEach(item => { |
|||
item && clearTimeout(item); |
|||
}); |
|||
this.timers = []; |
|||
} |
|||
}, |
|||
|
|||
// 初始化canvas元素 ctx数据 |
|||
initCanvas() { |
|||
this.canvas = this.$refs['reduceCanvas']; |
|||
this.context = uni.createCanvasContext('firstCanvas', this); |
|||
}, |
|||
|
|||
// 绘制图片 |
|||
drawImage(width) { |
|||
const { context } = this; |
|||
const { cardUrl, lines } = this.trainResult; |
|||
let that = this; |
|||
|
|||
uni.downloadFile({ |
|||
url: cardUrl, |
|||
success: function (res) { |
|||
context.drawImage(res.tempFilePath, 0, 0, width, 450); |
|||
context.draw(true); |
|||
// 绘制路径 |
|||
lines.forEach(item => { |
|||
that.drawPath(item, width); |
|||
}); |
|||
}, |
|||
fail(error) { |
|||
console.error('图片加载失败:', error); |
|||
}, |
|||
}); |
|||
}, |
|||
|
|||
/** |
|||
* 绘制路径 |
|||
* 图片坐标是155,194 |
|||
*/ |
|||
drawPath(str, width) { |
|||
const data = this.generatePathData(str); |
|||
const ctx = this.context; |
|||
|
|||
this.timers.push( |
|||
setTimeout(() => { |
|||
if (!this.trainResult.lines || this.trainResult.lines.length === 0) { |
|||
return; |
|||
} |
|||
ctx.beginPath(); |
|||
ctx.setLineWidth(10); |
|||
ctx.setStrokeStyle('#1890ff'); |
|||
const x = (+data[0][0] * width) / 155; |
|||
const y = (+data[0][1] * 450) / 194; |
|||
ctx.moveTo(x, y); |
|||
if (data.length === 1) { |
|||
// 如果只有1个点 就画个小圆圈 |
|||
ctx.arc(x, y, 2, 0, Math.PI * 2, false); |
|||
ctx.fill(); |
|||
} |
|||
}, +data[0][2]), |
|||
); |
|||
|
|||
for (let i = 1, len = data.length; i < len; i++) { |
|||
if (!this.trainResult.lines || this.trainResult.lines.length === 0) return; |
|||
this.timers.push( |
|||
setTimeout(() => { |
|||
if (!this.trainResult.lines || this.trainResult.lines.length === 0) { |
|||
return; |
|||
} |
|||
|
|||
if (i > 1) { |
|||
this.clearAndDraw(data, i, width); |
|||
} else { |
|||
const x = (+data[i][0] * width) / 155; |
|||
const y = (+data[i][1] * 450) / 194; |
|||
ctx.lineTo(x, y); |
|||
ctx.stroke(); |
|||
ctx.draw(true); |
|||
} |
|||
|
|||
ctx.closePath(); |
|||
// }, +data[i][2]), |
|||
}, this.getIntervalTime(+data[i][2], +str.operateTime, +data[0][2])), |
|||
); |
|||
} |
|||
}, |
|||
|
|||
clearAndDraw(data, i, width) { |
|||
if (!data || data.length < 1 || i >= data.length) return; |
|||
const ctx = this.context; |
|||
const x = (+data[0][0] * width) / 155; |
|||
const y = (+data[0][1] * 450) / 194; |
|||
ctx.moveTo(x, y); |
|||
for (let j = 1; j < i + 1; j++) { |
|||
const x = (+data[j][0] * width) / 155; |
|||
const y = (+data[j][1] * 450) / 194; |
|||
ctx.lineTo(x, y); |
|||
if (j === i) { |
|||
ctx.stroke(); |
|||
ctx.draw(true); |
|||
} |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 计算间隔时间 |
|||
* 第一条线的操作时间 |
|||
* 每条线的第一个点的时间 |
|||
* 时间 = (当前线条的操作时间 - 第一条线的操作时间)+ (当前点的时间-同一条线的第一个点的时间) |
|||
*/ |
|||
getIntervalTime(pointTime, operateTime, firstPointTime) { |
|||
const { lines } = this.trainResult; |
|||
let time = 0; |
|||
time = operateTime - lines[0].operateTime + (pointTime - firstPointTime); |
|||
return time; |
|||
}, |
|||
|
|||
/** |
|||
* 生成绘制每段路径的数据 |
|||
* @param {string} str 每条路径的数据"x,y,time;x,y,time" |
|||
* @return {array} result [[x,y,time]] |
|||
*/ |
|||
generatePathData(str) { |
|||
if (!str || !str.line) return; |
|||
let result = []; |
|||
str.line.split(';').forEach(point => { |
|||
result.push(point.split(',')); |
|||
}); |
|||
return result; |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped lang="scss"></style> |
@ -0,0 +1,170 @@ |
|||
<template> |
|||
<view> |
|||
<view class="flex flex-nowrap mt-12 rounded-3xl relative" v-if="finishResult"> |
|||
<view |
|||
class="bubble" |
|||
:style="{ |
|||
left: level.left || 0, |
|||
color: level.color || '#EDEDED', |
|||
backgroundImage: `url(${level.bgPic || 'https://www.tall.wiki/staticrec/yanyuan/bubble-gray.png'})`, |
|||
backgroundSize: '100% 100%', |
|||
backgroundRepeat: 'no-repeat', |
|||
}" |
|||
> |
|||
{{ finishResult === 1 ? '轻松完成' : finishResult === 2 ? '略有困难' : '非常困难' }} |
|||
</view> |
|||
<view v-for="i in levels" :key="i.value" class="flex-1" :class="i.value === 3 ? '' : 'box'"> |
|||
<view class="w-full h-2" :class="i.checked ? level.boxBg : 'bg-gray-100'"></view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { finishResult: { type: Number, default: null } }, |
|||
|
|||
data() { |
|||
return { |
|||
levels: [ |
|||
{ |
|||
value: 0.5, |
|||
checked: false, |
|||
}, |
|||
{ |
|||
value: 1, |
|||
checked: false, |
|||
}, |
|||
{ |
|||
value: 1.5, |
|||
checked: false, |
|||
}, |
|||
{ |
|||
value: 2, |
|||
checked: false, |
|||
}, |
|||
{ |
|||
value: 2.5, |
|||
checked: false, |
|||
}, |
|||
{ |
|||
value: 3, |
|||
checked: false, |
|||
}, |
|||
], |
|||
width: 0, |
|||
level: {}, |
|||
}; |
|||
}, |
|||
|
|||
watch: { |
|||
finishResult(val) { |
|||
if (val) { |
|||
this.setLevel(); |
|||
} |
|||
}, |
|||
}, |
|||
|
|||
mounted() { |
|||
this.$nextTick(() => { |
|||
const query = uni.createSelectorQuery().in(this); |
|||
query |
|||
.selectAll('.box') |
|||
.boundingClientRect(data => { |
|||
if (data && data.length) { |
|||
this.width = data[0].width; |
|||
this.setLevel(); |
|||
} |
|||
}) |
|||
.exec(); |
|||
}); |
|||
}, |
|||
|
|||
methods: { |
|||
setLevel() { |
|||
if (!this.finishResult) return; |
|||
|
|||
if (this.finishResult <= 0.5) { |
|||
this.setParam(1, 0); |
|||
} else if (this.finishResult <= 1) { |
|||
this.setParam(1, 1); |
|||
} else if (this.finishResult <= 1.5) { |
|||
this.setParam(2, 2); |
|||
} else if (this.finishResult <= 2) { |
|||
this.setParam(2, 3); |
|||
} else if (this.finishResult <= 2.5) { |
|||
this.setParam(3, 4); |
|||
} else { |
|||
this.setParam(3, 5); |
|||
} |
|||
this.setBox(); |
|||
}, |
|||
|
|||
setParam(type, value) { |
|||
const { level, width } = this; |
|||
if (type == 1) { |
|||
level.color = '#1890FF'; |
|||
level.value = '轻松完成'; |
|||
level.bgPic = 'https://www.tall.wiki/staticrec/yanyuan/bubble-blue.png'; |
|||
level.boxBg = 'bg-blue-500'; |
|||
} |
|||
if (type == 2) { |
|||
level.color = '#F4C130'; |
|||
level.value = '略有困难'; |
|||
level.bgPic = 'https://www.tall.wiki/staticrec/yanyuan/bubble-yellow.png'; |
|||
level.boxBg = 'bg-yellow-500'; |
|||
} |
|||
if (type == 3) { |
|||
level.color = '#EB5A0D'; |
|||
level.value = '非常困难'; |
|||
level.bgPic = 'https://www.tall.wiki/staticrec/yanyuan/bubble-red.png'; |
|||
level.boxBg = 'bg-red-500'; |
|||
} |
|||
level.left = value * width + 'px'; |
|||
}, |
|||
|
|||
setBox() { |
|||
let index = 0; |
|||
this.levels.forEach((line, i) => { |
|||
if (this.finishResult > i / 2) { |
|||
index = i + 1; |
|||
} |
|||
if (this.finishResult === i / 2) { |
|||
index = i; |
|||
} |
|||
if (i < index) { |
|||
line.checked = true; |
|||
} else { |
|||
line.checked = false; |
|||
} |
|||
}); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.box { |
|||
margin-right: 2px; |
|||
} |
|||
|
|||
.bubble { |
|||
position: absolute; |
|||
top: -40px; |
|||
width: 70px; |
|||
height: 30px; |
|||
line-height: 26px; |
|||
text-align: center; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.num { |
|||
width: 30rpx; |
|||
height: 30rpx; |
|||
text-align: center; |
|||
line-height: 30rpx; |
|||
border-radius: 50%; |
|||
border: 1px solid $uni-bg-color-blue; |
|||
color: $uni-bg-color-blue; |
|||
} |
|||
</style> |
Loading…
Reference in new issue