zdg #152
|
@ -169,12 +169,12 @@ export class ImChat {
|
||||||
logout() {
|
logout() {
|
||||||
if (!this.timChat) return
|
if (!this.timChat) return
|
||||||
return this.timChat.TIMLogout().then(res => {
|
return this.timChat.TIMLogout().then(res => {
|
||||||
console.log('登出成功', res)
|
this.setConsole('%cim-chat: logout', '登出成功')
|
||||||
this.status.isLogin = false
|
this.status.isLogin = false
|
||||||
this.timChat.TIMUninit() // 反初始化
|
this.timChat.TIMUninit() // 反初始化
|
||||||
return res
|
return res
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.log('登出失败', error)
|
this.setConsole('%cim-chat: logout', '登出失败', error)
|
||||||
return error
|
return error
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ import { getSmarttalkPage, getPrepareById } from '@/api/file'
|
||||||
import SetHomework from '@/views/prepare/container/set-homework.vue'
|
import SetHomework from '@/views/prepare/container/set-homework.vue'
|
||||||
import FileImage from '@/components/file-image/index.vue'
|
import FileImage from '@/components/file-image/index.vue'
|
||||||
import { useGetHomework } from '@/hooks/useGetHomework'
|
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 { useToolState } from '@/store/modules/tool'
|
||||||
import { asyncLocalFile } from '@/utils/talkFile'
|
import { asyncLocalFile } from '@/utils/talkFile'
|
||||||
import Lesson from './lesson.vue';
|
import Lesson from './lesson.vue';
|
||||||
|
@ -113,6 +113,7 @@ const sendHomework = (row) => {
|
||||||
const closeHomework = async() => {
|
const closeHomework = async() => {
|
||||||
ipcMsgSend('tool-sphere:set:ignore', true)
|
ipcMsgSend('tool-sphere:set:ignore', true)
|
||||||
// 发送im消息-推送作业(app|平板)
|
// 发送im消息-推送作业(app|平板)
|
||||||
|
console.log('发送im消息-推送作业(app|平板)', curRow.value.id)
|
||||||
await ipcMsgInvoke('im-chat:msg', curRow.value.id, MsgEnum.HEADS.MSG_0016)
|
await ipcMsgInvoke('im-chat:msg', curRow.value.id, MsgEnum.HEADS.MSG_0016)
|
||||||
setDialog.value = false
|
setDialog.value = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="props.test">
|
<div v-if="props.test">
|
||||||
<el-button type="primary" @click="trigger">点赞</el-button>
|
<el-button type="primary" @click="trigger" v-tap:trigger="">点赞</el-button>
|
||||||
<el-button type="primary" @click="trigger(1, '学生A')">疑惑</el-button>
|
<el-button type="primary" @click="trigger(2, '学生A')" v-tap:trigger="[2,'学生A']">疑惑</el-button>
|
||||||
</div>
|
</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">
|
<template v-for="(item, index) in iconCache.list">
|
||||||
<slot>
|
<slot>
|
||||||
<el-icon v-if="props.def"><Star /></el-icon>
|
<el-icon v-if="props.def"><Star /></el-icon>
|
||||||
|
@ -19,19 +25,35 @@
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
// 功能说明:动画-点赞
|
// 功能说明:动画-点赞
|
||||||
import { ref, nextTick, useSlots, reactive } from 'vue'
|
import { ref, nextTick, useSlots, reactive } from 'vue'
|
||||||
import {Star} from '@element-plus/icons-vue'
|
import {Star} from '@element-plus/icons-vue'
|
||||||
const warpRef = ref(null)
|
const warpRef = ref(null)
|
||||||
const props = defineProps({ test: Boolean, def: Boolean })
|
|
||||||
const slots = useSlots() // 获取插槽
|
const slots = useSlots() // 获取插槽
|
||||||
let iconCache = reactive({list:[]}) // 图标缓存
|
|
||||||
const isSlot = !!Object.keys(slots).length // 是否使用了插槽
|
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) => {
|
const trigger = async (type, name) => {
|
||||||
if (typeof type == 'string') { name = type; type = 1 } // 兼容
|
if (typeof type == 'string') { name = type; type = 1 } // 兼容
|
||||||
|
if (props.type == 2) { // 温度计-模式
|
||||||
|
weatherglassModel(type)
|
||||||
|
} else { // 默认-弹幕模式
|
||||||
iconCache.list.push({
|
iconCache.list.push({
|
||||||
name,
|
name,
|
||||||
icon: isSlot||props.def ? '' : getRandomIcon(type) // 图标
|
icon: isSlot||props.def ? '' : getRandomIcon(type) // 图标
|
||||||
|
@ -43,6 +65,7 @@ const trigger = async (type, name) => {
|
||||||
animInit(el, tipEl, type)
|
animInit(el, tipEl, type)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 初始化动画
|
// 初始化动画
|
||||||
const animInit = (el, tipEl, type) => {
|
const animInit = (el, tipEl, type) => {
|
||||||
const { height, width } = el.getBoundingClientRect()
|
const { height, width } = el.getBoundingClientRect()
|
||||||
|
@ -95,7 +118,31 @@ const animInit = (el, tipEl, type) => {
|
||||||
}
|
}
|
||||||
amFn() // 初次执行
|
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 || '')
|
const setStatic = (el, type, val, end) => el.style[type] = val + (end || '')
|
||||||
// 重置缓存列表
|
// 重置缓存列表
|
||||||
|
@ -107,7 +154,7 @@ const resetIconCache = () => {
|
||||||
const getRandomIcon = (type) => {
|
const getRandomIcon = (type) => {
|
||||||
let iconArr = []
|
let iconArr = []
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 1: // 阿里icon-疑惑
|
case 2: // 阿里icon-疑惑
|
||||||
iconArr = ['yiwen', 'a-yiwen', 'yihuo', 'yiwen-01']
|
iconArr = ['yiwen', 'a-yiwen', 'yihuo', 'yiwen-01']
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
@ -132,11 +179,28 @@ const getRandomBool = () => Math.random() > 0.5
|
||||||
const toNumber = (v, pos = 2) => Number(v.toFixed(pos))
|
const toNumber = (v, pos = 2) => Number(v.toFixed(pos))
|
||||||
// 延时
|
// 延时
|
||||||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
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 })
|
defineExpose({ trigger })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
// 弹幕-模式
|
||||||
.c-warp {
|
.c-warp {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 0;
|
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>
|
</style>
|
|
@ -7,7 +7,7 @@
|
||||||
<side-vue v-ignore @ignore-mounted="sideMouse" @change="sideChange"></side-vue>
|
<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 聊天组件 -->
|
||||||
<im-chat ref="imChatRef" @change="chatChange" />
|
<im-chat ref="imChatRef" @change="chatChange" />
|
||||||
|
@ -67,6 +67,7 @@ const classObj = reactive({ // 课程相关
|
||||||
data: {} // 课程信息
|
data: {} // 课程信息
|
||||||
})
|
})
|
||||||
const msgIds = [] // 消息id
|
const msgIds = [] // 消息id
|
||||||
|
const electron = window.electron // electron
|
||||||
const btnList = [ // 工具栏按钮列表
|
const btnList = [ // 工具栏按钮列表
|
||||||
{ label: '选择', value: 'select', icon: 'icon-mouse' },
|
{ label: '选择', value: 'select', icon: 'icon-mouse' },
|
||||||
{ label: '画笔', value: 'brush', icon: 'icon-huabi' },
|
{ label: '画笔', value: 'brush', icon: 'icon-huabi' },
|
||||||
|
@ -78,6 +79,7 @@ const btnList = [ // 工具栏按钮列表
|
||||||
]
|
]
|
||||||
// === 页面加载完毕 ===
|
// === 页面加载完毕 ===
|
||||||
onMounted(async() => {
|
onMounted(async() => {
|
||||||
|
if (!electron) return // 浏览器端
|
||||||
getClassInfo() // 获取课堂详情 ex3
|
getClassInfo() // 获取课堂详情 ex3
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resetStatus() // 开启重置状态-监听
|
resetStatus() // 开启重置状态-监听
|
||||||
|
@ -130,7 +132,7 @@ const chatChange = (type, data, ...args) => {
|
||||||
case MsgEnum.HEADS.MSG_0002: // 疑惑
|
case MsgEnum.HEADS.MSG_0002: // 疑惑
|
||||||
// console.log('点赞:', data)
|
// console.log('点赞:', data)
|
||||||
if(msgIds.includes(msgId)) return // 忽略重复-点赞消息
|
if(msgIds.includes(msgId)) return // 忽略重复-点赞消息
|
||||||
upvoteRef.value.trigger(data.name) // 触发点赞|疑惑
|
upvoteRef.value.trigger(head, data.name) // 触发点赞|疑惑
|
||||||
if (msgIds.length >= 100) msgIds.shift() // 删除第一个
|
if (msgIds.length >= 100) msgIds.shift() // 删除第一个
|
||||||
msgIds.push(msgId) // 添加到数组
|
msgIds.push(msgId) // 添加到数组
|
||||||
break
|
break
|
||||||
|
|
Loading…
Reference in New Issue