lyc-dev #153
|
@ -169,12 +169,12 @@ export class ImChat {
|
|||
logout() {
|
||||
if (!this.timChat) return
|
||||
return this.timChat.TIMLogout().then(res => {
|
||||
console.log('登出成功', res)
|
||||
this.setConsole('%cim-chat: logout', '登出成功')
|
||||
this.status.isLogin = false
|
||||
this.timChat.TIMUninit() // 反初始化
|
||||
return res
|
||||
}).catch(error => {
|
||||
console.log('登出失败', error)
|
||||
this.setConsole('%cim-chat: logout', '登出失败', error)
|
||||
return error
|
||||
})
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ import { getSmarttalkPage } from '@/api/file'
|
|||
import SetHomework from '@/components/set-homework/index.vue'
|
||||
import FileImage from '@/components/file-image/index.vue'
|
||||
import { useGetHomework } from '@/hooks/useGetHomework'
|
||||
import { ipcMsgSend, ipcMsgInvoke } from '@/utils/tool'
|
||||
import { ipcMsgSend, ipcMsgSend2, ipcMsgInvoke } from '@/utils/tool'
|
||||
import { useToolState } from '@/store/modules/tool'
|
||||
import Lesson from './lesson.vue';
|
||||
import { parseCataByNode } from '@/utils/talkFile'
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
<template>
|
||||
<div v-if="props.test">
|
||||
<el-button type="primary" @click="trigger">点赞</el-button>
|
||||
<el-button type="primary" @click="trigger(1, '学生A')">疑惑</el-button>
|
||||
<el-button type="primary" @click="trigger" v-tap:trigger="">点赞</el-button>
|
||||
<el-button type="primary" @click="trigger(2, '学生A')" v-tap:trigger="[2,'学生A']">疑惑</el-button>
|
||||
</div>
|
||||
<div ref="warpRef" class="c-warp">
|
||||
<!-- 温度计-模式 -->
|
||||
<div v-if="props.type == 2" class="c-anim">
|
||||
<div class="item like" :style="`height:${wtData.like*5}px;`"></div>
|
||||
<div class="item doubt" :style="`height:${wtData.doubt*5}px;`"></div>
|
||||
</div>
|
||||
<!-- (默认)弹幕-模式 -->
|
||||
<div v-else ref="warpRef" class="c-warp">
|
||||
<template v-for="(item, index) in iconCache.list">
|
||||
<slot>
|
||||
<el-icon v-if="props.def"><Star /></el-icon>
|
||||
|
@ -19,19 +25,35 @@
|
|||
</slot>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script setup>
|
||||
// 功能说明:动画-点赞
|
||||
import { ref, nextTick, useSlots, reactive } from 'vue'
|
||||
import {Star} from '@element-plus/icons-vue'
|
||||
const warpRef = ref(null)
|
||||
const props = defineProps({ test: Boolean, def: Boolean })
|
||||
const slots = useSlots() // 获取插槽
|
||||
let iconCache = reactive({list:[]}) // 图标缓存
|
||||
const isSlot = !!Object.keys(slots).length // 是否使用了插槽
|
||||
// === 触发 type 1 疑惑 def 点赞 ===
|
||||
const props = defineProps({
|
||||
test: Boolean, // 是否开启-测试按钮
|
||||
def: Boolean, // 是否使用默认图标
|
||||
type: String, // 模式 1-弹幕 2-温度计
|
||||
tback: Number, // 模式:温度计-退回速度
|
||||
tsleep: Number, // 模式:温度计-执行退回间隔
|
||||
})
|
||||
let iconCache = reactive({list:[]}) // 图标缓存
|
||||
let wtData = reactive({ // 温度计模式-计数
|
||||
like: 0, doubt: 0,
|
||||
timeout: {like: null, doubt: null}, // 定时器-单次
|
||||
interval: {like: null, doubt: null}, // 定时器-循环
|
||||
})
|
||||
|
||||
// === 触发 type 1: 点赞 2: 疑惑 ===
|
||||
const trigger = async (type, name) => {
|
||||
if (typeof type == 'string') { name = type; type = 1 } // 兼容
|
||||
if (props.type == 2) { // 温度计-模式
|
||||
weatherglassModel(type)
|
||||
} else { // 默认-弹幕模式
|
||||
iconCache.list.push({
|
||||
name,
|
||||
icon: isSlot||props.def ? '' : getRandomIcon(type) // 图标
|
||||
|
@ -43,6 +65,7 @@ const trigger = async (type, name) => {
|
|||
animInit(el, tipEl, type)
|
||||
})
|
||||
}
|
||||
}
|
||||
// 初始化动画
|
||||
const animInit = (el, tipEl, type) => {
|
||||
const { height, width } = el.getBoundingClientRect()
|
||||
|
@ -95,7 +118,31 @@ const animInit = (el, tipEl, type) => {
|
|||
}
|
||||
amFn() // 初次执行
|
||||
}
|
||||
|
||||
// 温度计-模式触发 1: 点赞 2: 疑惑
|
||||
const weatherglassModel = async(type) => {
|
||||
const field = type == 2 ? 'doubt' : 'like'
|
||||
wtData[field]++
|
||||
autoReduce(type)
|
||||
}
|
||||
// 温度计-自动恢复初始
|
||||
const autoReduce = type => {
|
||||
const field = type == 2 ? 'doubt' : 'like'
|
||||
const reduceFn = () => { // 该定时器: 会一直执行间隔1s
|
||||
wtData.interval[field] = setInterval(() => {
|
||||
wtData[field]--
|
||||
// 停止-定时器
|
||||
if (wtData[field] <= 0) {
|
||||
clearInterval(wtData.interval[field])
|
||||
wtData[field] = 0
|
||||
}
|
||||
}, (props.tback||100))
|
||||
}
|
||||
if (!!wtData.timeout[field]) clearInterval(wtData.timeout[field]) // 清除定时器:上一次
|
||||
if (!!wtData.interval[field]) clearInterval(wtData.interval[field]) // 清除定时器:上一次
|
||||
wtData.timeout[field] = setTimeout(() => {
|
||||
reduceFn()
|
||||
}, (props.tsleep||20) * 1000)
|
||||
}
|
||||
// 设置-静态样式
|
||||
const setStatic = (el, type, val, end) => el.style[type] = val + (end || '')
|
||||
// 重置缓存列表
|
||||
|
@ -107,7 +154,7 @@ const resetIconCache = () => {
|
|||
const getRandomIcon = (type) => {
|
||||
let iconArr = []
|
||||
switch (type) {
|
||||
case 1: // 阿里icon-疑惑
|
||||
case 2: // 阿里icon-疑惑
|
||||
iconArr = ['yiwen', 'a-yiwen', 'yihuo', 'yiwen-01']
|
||||
break
|
||||
default:
|
||||
|
@ -132,11 +179,28 @@ const getRandomBool = () => Math.random() > 0.5
|
|||
const toNumber = (v, pos = 2) => Number(v.toFixed(pos))
|
||||
// 延时
|
||||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||
// 自定义-局部指令
|
||||
const vTap = {
|
||||
mounted(el, binding) {
|
||||
const instance = binding.instance
|
||||
const fn = binding.arg
|
||||
const args = binding.value||[]
|
||||
if (!fn) return
|
||||
let cleId = null
|
||||
el.addEventListener('mousedown', () => {
|
||||
cleId = setInterval(() => {instance[fn](...args)}, 10);
|
||||
})
|
||||
el.addEventListener('mouseup', () => {
|
||||
!!cleId && clearInterval(cleId)
|
||||
})
|
||||
}
|
||||
}
|
||||
// 暴露方法
|
||||
defineExpose({ trigger })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 弹幕-模式
|
||||
.c-warp {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
|
@ -179,4 +243,31 @@ defineExpose({ trigger })
|
|||
}
|
||||
}
|
||||
}
|
||||
// 温度计-模式
|
||||
.c-anim {
|
||||
position: fixed;
|
||||
// height: 90vh;
|
||||
// border: 1px solid;
|
||||
inset: auto auto 3em 1em;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-end;
|
||||
.item{
|
||||
flex: 1;
|
||||
border-radius: 3px;
|
||||
min-width: 15px;
|
||||
// height: 500px;
|
||||
&.like{
|
||||
background-image: linear-gradient(to top, #fef0f0, #f56c6c);
|
||||
// animation: striped-flow 5s linear infinite;
|
||||
}
|
||||
&.doubt{
|
||||
background-image: linear-gradient(to top, #fdf6ec, #e6a23c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes striped-flow {
|
||||
0%{background-position: 0 500px;}
|
||||
100%{background-position: 0 0;}
|
||||
}
|
||||
</style>
|
|
@ -7,7 +7,7 @@
|
|||
<side-vue v-ignore @ignore-mounted="sideMouse" @change="sideChange"></side-vue>
|
||||
|
||||
<!-- 点赞组件 -->
|
||||
<upvote-vue ref="upvoteRef"></upvote-vue>
|
||||
<upvote-vue ref="upvoteRef" type="2"></upvote-vue>
|
||||
|
||||
<!-- im-chat 聊天组件 -->
|
||||
<im-chat ref="imChatRef" @change="chatChange" />
|
||||
|
@ -67,6 +67,7 @@ const classObj = reactive({ // 课程相关
|
|||
data: {} // 课程信息
|
||||
})
|
||||
const msgIds = [] // 消息id
|
||||
const electron = window.electron // electron
|
||||
const btnList = [ // 工具栏按钮列表
|
||||
{ label: '选择', value: 'select', icon: 'icon-mouse' },
|
||||
{ label: '画笔', value: 'brush', icon: 'icon-huabi' },
|
||||
|
@ -78,6 +79,7 @@ const btnList = [ // 工具栏按钮列表
|
|||
]
|
||||
// === 页面加载完毕 ===
|
||||
onMounted(async() => {
|
||||
if (!electron) return // 浏览器端
|
||||
getClassInfo() // 获取课堂详情 ex3
|
||||
setTimeout(() => {
|
||||
resetStatus() // 开启重置状态-监听
|
||||
|
@ -130,7 +132,7 @@ const chatChange = (type, data, ...args) => {
|
|||
case MsgEnum.HEADS.MSG_0002: // 疑惑
|
||||
// console.log('点赞:', data)
|
||||
if(msgIds.includes(msgId)) return // 忽略重复-点赞消息
|
||||
upvoteRef.value.trigger(data.name) // 触发点赞|疑惑
|
||||
upvoteRef.value.trigger(head, data.name) // 触发点赞|疑惑
|
||||
if (msgIds.length >= 100) msgIds.shift() // 删除第一个
|
||||
msgIds.push(msgId) // 添加到数组
|
||||
break
|
||||
|
|
Loading…
Reference in New Issue