zdg #144
|
@ -33,6 +33,7 @@
|
||||||
"electron-updater": "^6.1.7",
|
"electron-updater": "^6.1.7",
|
||||||
"element-plus": "^2.7.6",
|
"element-plus": "^2.7.6",
|
||||||
"fabric": "^5.3.0",
|
"fabric": "^5.3.0",
|
||||||
|
"im_electron_sdk": "^8.0.5904",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"jsondiffpatch": "0.6.0",
|
"jsondiffpatch": "0.6.0",
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* @description 腾讯云-即时通讯-sdkID
|
||||||
|
*/
|
||||||
|
// import { ipcMain } from 'electron'
|
||||||
|
// const TimMain = require('im_electron_sdk/dist/main')
|
||||||
|
import TimMain from 'im_electron_sdk/dist/main'
|
||||||
|
// import {TIMErrCode} from 'im_electron_sdk/dist/enumbers'
|
||||||
|
const sdkappidDef = 1600034736 // 可以去腾讯云即时通信IM控制台申请
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
function init(sdkappid = sdkappidDef) {
|
||||||
|
return new TimMain({sdkappid})
|
||||||
|
}
|
||||||
|
export function initialize(){
|
||||||
|
// ipcMain.handle('im-chat:init', (event, sdkappid) => {
|
||||||
|
// return init(sdkappid)
|
||||||
|
// })
|
||||||
|
return init()
|
||||||
|
}
|
||||||
|
export default { initialize, init }
|
|
@ -3,6 +3,7 @@ import { join } from 'path'
|
||||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||||
import icon from '../../resources/icon.png?asset'
|
import icon from '../../resources/icon.png?asset'
|
||||||
import File from './file'
|
import File from './file'
|
||||||
|
import chat from './chat' // chat封装
|
||||||
// 代理 electron/remote
|
// 代理 electron/remote
|
||||||
// 第一步:引入remote
|
// 第一步:引入remote
|
||||||
import remote from '@electron/remote/main'
|
import remote from '@electron/remote/main'
|
||||||
|
@ -227,12 +228,15 @@ app.on('window-all-closed', () => {
|
||||||
|
|
||||||
// 监听全局事件
|
// 监听全局事件
|
||||||
function handleAll() {
|
function handleAll() {
|
||||||
|
// chat.initialize() // im-chat 实例
|
||||||
|
const chatInstance = chat.initialize() // im-chat 实例
|
||||||
// 新窗口创建-监听
|
// 新窗口创建-监听
|
||||||
ipcMain.on('new-window', (e, data) => {
|
ipcMain.on('new-window', (e, data) => {
|
||||||
const { id, type } = data
|
const { id, type } = data
|
||||||
const win = BrowserWindow.fromId(id)
|
const win = BrowserWindow.fromId(id)
|
||||||
win.type = type // 绑定独立标识
|
win.type = type // 绑定独立标识
|
||||||
remote.enable(win.webContents) // 开启远程服务
|
remote.enable(win.webContents) // 开启远程服务
|
||||||
|
chatInstance.enable(win.webContents) // 开启im-chat
|
||||||
})
|
})
|
||||||
// 用于监听-状态管理变化-同步所有窗口
|
// 用于监听-状态管理变化-同步所有窗口
|
||||||
ipcMain.handle('pinia-state-change', (e, storeName, jsonStr) => {
|
ipcMain.handle('pinia-state-change', (e, storeName, jsonStr) => {
|
||||||
|
@ -246,16 +250,8 @@ function handleAll() {
|
||||||
})
|
})
|
||||||
// 用于监听-状态管理变化-初始同步
|
// 用于监听-状态管理变化-初始同步
|
||||||
ipcMain.handle('pinia-state-init', (e, wid, storeName, jsonStr) => {
|
ipcMain.handle('pinia-state-init', (e, wid, storeName, jsonStr) => {
|
||||||
// for(const curWin of BrowserWindow.getAllWindows()){
|
|
||||||
// const id = curWin.webContents.id
|
|
||||||
// const bool = id !== e.sender.id && !curWin.isDestroyed()
|
|
||||||
// if (bool) { // 除了消息发送窗口和销毁的窗口 其他都发送
|
|
||||||
// curWin.webContents.send('pinia-state-set', storeName, jsonStr)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// console.log('pinia-state-init', jsonStr)
|
// console.log('pinia-state-init', jsonStr)
|
||||||
const win = BrowserWindow.fromId(wid)
|
const win = BrowserWindow.fromId(wid)
|
||||||
// console.log(win)
|
|
||||||
win.webContents.send('pinia-state-set', storeName, jsonStr)
|
win.webContents.send('pinia-state-set', storeName, jsonStr)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
/**
|
|
||||||
* @description: electron 封装的工具函数
|
|
||||||
* 消息整理
|
|
||||||
* tool-sphere:create 创建-悬浮球-窗口
|
|
||||||
*/
|
|
||||||
import { app, shell, BrowserWindow, ipcMain } from 'electron'
|
|
||||||
import { is } from '@electron-toolkit/utils'
|
|
||||||
|
|
||||||
// const baseUrl = 'http://localhost:5173/#' // 开发环境使用
|
|
||||||
const baseUrl = process.env['ELECTRON_RENDERER_URL']+'/#' // 开发环境使用
|
|
||||||
// 所有窗口
|
|
||||||
let allWindow = {}
|
|
||||||
// 其他已有窗口 wins
|
|
||||||
export function init() {
|
|
||||||
// 创建工具-悬浮球
|
|
||||||
ipcMain.on('tool-sphere:create', async(e, data) => {
|
|
||||||
// console.log('测试xxxx', data)
|
|
||||||
await createTools(data) // 执行逻辑
|
|
||||||
e.reply('tool-sphere:create-reply', {code: 200, msg: 'success'}) // 返回结果
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @description: 创建工具
|
|
||||||
* @param {*} url 路由地址
|
|
||||||
* @param {number} [width=800] 窗口宽度
|
|
||||||
* @param {number} [height=600] 窗口高度
|
|
||||||
* @param {{}} [option={}] 自定义选项
|
|
||||||
* @author: zdg
|
|
||||||
* @date 2021-07-05 14:07:01
|
|
||||||
*/
|
|
||||||
export function createTools({url, width = 800, height = 600, option={}}) {
|
|
||||||
const { mainWindow } = allWindow||{} // 获取主窗口
|
|
||||||
const devUrl = `${baseUrl}${url}`
|
|
||||||
const buildUrl = `file://${__dirname}/index.html${url}`
|
|
||||||
const urlAll = is.dev ? devUrl : buildUrl
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
let win = new BrowserWindow({
|
|
||||||
width, height,
|
|
||||||
type: 'toolbar', // 创建的窗口类型为工具栏窗口
|
|
||||||
frame: false, // 要创建无边框窗口
|
|
||||||
resizable: false, // 禁止窗口大小缩放
|
|
||||||
transparent: true, // 设置透明
|
|
||||||
alwaysOnTop: true, // 窗口是否总是显示在其他窗口之前
|
|
||||||
|
|
||||||
parent: mainWindow, // 父窗口
|
|
||||||
autoClose: true, // 关闭窗口后自动关闭
|
|
||||||
webPreferences: {
|
|
||||||
nodeIntegration: true, // nodeApi调用
|
|
||||||
contextIsolation: false, // 沙箱取消
|
|
||||||
webSecurity: false // 跨域关闭
|
|
||||||
},
|
|
||||||
...option
|
|
||||||
})
|
|
||||||
// console.log(urlAll)
|
|
||||||
// url = 'https://www.baidu.com'
|
|
||||||
console.log(urlAll)
|
|
||||||
win.loadURL(urlAll)
|
|
||||||
win.setFullScreen(true) // 设置窗口为全屏
|
|
||||||
win.setIgnoreMouseEvents(true) // 忽略鼠标事件|使窗口不可选中
|
|
||||||
win.once('ready-to-show', () => {
|
|
||||||
win.show()
|
|
||||||
resolve(win)
|
|
||||||
})
|
|
||||||
win.on('closed', () => {
|
|
||||||
win = null
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 保存窗口
|
|
||||||
export function setWin(win = {}) {
|
|
||||||
if (win && Object.keys(win).length){
|
|
||||||
Object.keys(win).forEach(key => {
|
|
||||||
if (!allWindow[key]) { // 不存在就保存
|
|
||||||
allWindow[key] = win[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { contextBridge } from 'electron'
|
import { contextBridge } from 'electron'
|
||||||
import { electronAPI } from '@electron-toolkit/preload'
|
import { electronAPI } from '@electron-toolkit/preload'
|
||||||
|
import TimRender from 'im_electron_sdk/dist/renderer' // im渲染部分实例
|
||||||
// Custom APIs for renderer
|
// Custom APIs for renderer
|
||||||
const api = {
|
const api = {
|
||||||
|
preloadPath: __dirname, // 当前preload地址
|
||||||
|
getTimRender: () => new TimRender(), // im渲染部分实例
|
||||||
}
|
}
|
||||||
// Use `contextBridge` APIs to expose Electron APIs to
|
// Use `contextBridge` APIs to expose Electron APIs to
|
||||||
// renderer only if context isolation is enabled, otherwise
|
// renderer only if context isolation is enabled, otherwise
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* @description: 后端接口api
|
||||||
|
* @author zdg
|
||||||
|
* @date 2023-07-03
|
||||||
|
*/
|
||||||
|
import request from '@/utils/request'
|
||||||
|
// /system/user/txCloudSign
|
||||||
|
export class ApiService {
|
||||||
|
// zdg: 公共请求-处理(可进行特殊处理)
|
||||||
|
static publicHttp(url, data, method, option = {}, type) {
|
||||||
|
method = method || 'get' // 默认GET
|
||||||
|
const config = { url, method }
|
||||||
|
if (!!data) basic[method=='get'?'params':'data'] = data
|
||||||
|
if (!!option) Object.assign(config, option)
|
||||||
|
// 特殊格式处理
|
||||||
|
if (type == 'file') config.headers = { 'Content-Type': 'multipart/form-data' }
|
||||||
|
else if (type == 'json') config.headers = { 'Content-Type': 'application/json' }
|
||||||
|
else if (type == 'form') config.headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
|
return request(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// zdg: 腾讯云-即时通讯
|
||||||
|
export class imChat {
|
||||||
|
// 获取腾讯im-chat appid 签名
|
||||||
|
static getTxCloudSign = data => ApiService.publicHttp('/system/user/txCloudSign', data)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,198 @@
|
||||||
|
/**
|
||||||
|
* @description imChat 腾讯-即时通讯(无ui)
|
||||||
|
* 文档地址:https://cloud.tencent.com/document/product/269/75285
|
||||||
|
* 文档地址:https://cloud.tencent.com/document/product/269/63007 (electron)
|
||||||
|
* @author: zdg
|
||||||
|
* @date 2023-07-03
|
||||||
|
*/
|
||||||
|
// const TimRender = require('im_electron_sdk/dist/render')
|
||||||
|
import * as TYPES from './enumbers'
|
||||||
|
const API = window.api
|
||||||
|
// TIM生成签名
|
||||||
|
// import * as GenerateUserSig from './userSig' // 引入签名生成器
|
||||||
|
export class ImChat {
|
||||||
|
timChat // imChat对象
|
||||||
|
SDKAppID // sdkID
|
||||||
|
secretKey // key
|
||||||
|
userID // 用户id
|
||||||
|
timGroupId // 群组id
|
||||||
|
userSig // 签名
|
||||||
|
status = { // 状态
|
||||||
|
isLogin: false, // 是否登录
|
||||||
|
isConnect: false, // 是否连接
|
||||||
|
}
|
||||||
|
defOption = { // 默认配置
|
||||||
|
// 日志等级-全量日志
|
||||||
|
log_level: TYPES.TIMLogLevel.kTIMLog_Test,
|
||||||
|
// 群组类型-会议群(Meeting),成员上限 6000 人
|
||||||
|
group_type: TYPES.TIMGroupType.kTIMGroup_ChatRoom,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor(SDKAppID, userSig, userID, isInit) {
|
||||||
|
this.SDKAppID = SDKAppID
|
||||||
|
// this.userSig = userSig
|
||||||
|
const sig = 'eJwtjN0KgjAYQN9l16Vzcz8I3RhE9J*JV94IW-ZV6nASWfTurfTynAPnjdLNyXvoFkWIeBhN-gxK1x2cYdCMTQnlYmxW3QpjQKEo4BhjGgrKh6KfBlrtPGOMuDTYDqqfE26BWUjEeIHSrW1cL-SulHd5KI7zxDbpdh1cX0nuX7JK7HtroNerZhnnPpYz9PkCe5Mx1w__'
|
||||||
|
this.userSig = sig
|
||||||
|
this.userID = userID
|
||||||
|
window.test = this
|
||||||
|
if (isInit) return this.init()
|
||||||
|
}
|
||||||
|
// 设置配置
|
||||||
|
async setConfig() {
|
||||||
|
await this.timChat.TIMSetConfig({ // TIMSetConfigParam
|
||||||
|
json_config: { // JSONCongfig
|
||||||
|
set_config_log_level: this.defOption.log_level,
|
||||||
|
set_config_callback_log_level: this.defOption.log_level,
|
||||||
|
// set_config_is_log_output_console: true,
|
||||||
|
// set_config_user_config: { // 用户配置
|
||||||
|
// user_config_is_read_receipt: true, // true表示要收已读回执事件
|
||||||
|
// user_config_is_sync_report: true, // true表示服务端要删掉已读状态
|
||||||
|
// user_config_is_ignore_grouptips_unread: true, // true表示群tips不计入群消息已读计数
|
||||||
|
// user_config_is_is_disable_storage: false, // 是否禁用本地数据库,true表示禁用,false表示不禁用。默认是false
|
||||||
|
// user_config_group_getinfo_option // 获取群组信息默认选项
|
||||||
|
// user_config_group_member_getinfo_option // 获取群组成员信息默认选项
|
||||||
|
// },
|
||||||
|
// set_config_user_define_data // 自定义数据,如果需要,初始化前设置
|
||||||
|
},
|
||||||
|
user_data: '',
|
||||||
|
})
|
||||||
|
// 日志监听
|
||||||
|
this.timChat.TIMSetLogCallback({
|
||||||
|
callback: data => {
|
||||||
|
console.log('[im-chat]:', data[1])
|
||||||
|
},
|
||||||
|
user_data: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 初始化-imChat
|
||||||
|
init() {
|
||||||
|
return new Promise(async(resolve, reject) => {
|
||||||
|
try {
|
||||||
|
if(!API) reject('preload api获取失败, 初始化-未完成')
|
||||||
|
this.timChat = await API.getTimRender()
|
||||||
|
await this.timChat.TIMInit()
|
||||||
|
console.log('[im-chat]:初始化成功')
|
||||||
|
this.status.isConnect = true
|
||||||
|
this.setConfig() // 设置日志级别
|
||||||
|
resolve(this)
|
||||||
|
} catch (error) {reject(error)}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 生成签名
|
||||||
|
genTestUserSig() {
|
||||||
|
const options = {
|
||||||
|
SDKAppID: this.SDKAppID,
|
||||||
|
secretKey: this.secretKey,
|
||||||
|
userID: this.userID,
|
||||||
|
}
|
||||||
|
const { userSig } = GenerateUserSig.genTestUserSig(options)
|
||||||
|
this.userSig = userSig
|
||||||
|
}
|
||||||
|
// 监听
|
||||||
|
watch(callback) {
|
||||||
|
this.timChat.TIMAddRecvNewMsgCallback({
|
||||||
|
callback, user_data: {type:'msg'}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 登录
|
||||||
|
login() {
|
||||||
|
const fn = async (resolve, reject) => {
|
||||||
|
const option = {
|
||||||
|
userID: this.userID,
|
||||||
|
userSig: this.userSig,
|
||||||
|
}
|
||||||
|
// 获取登录状态
|
||||||
|
// [1,2,3,4] | [已登陆,登录中,未登录,登出中]
|
||||||
|
console.log('登录', this)
|
||||||
|
const status = await this.timChat.TIMGetLoginStatus()
|
||||||
|
if (status == 3) { // 未登录
|
||||||
|
const res = await this.timChat.TIMLogin(option)
|
||||||
|
if (res && res.code == 0) {
|
||||||
|
console.log('登录成功', res)
|
||||||
|
this.status.isLogin = true
|
||||||
|
resolve({status:0, msg:'登录成功', data:res})
|
||||||
|
} else reject(res)
|
||||||
|
} else {
|
||||||
|
if (status == 1) { // 已登录
|
||||||
|
console.log('已登录')
|
||||||
|
resolve({status, msg:'已登录'})
|
||||||
|
} else if (status == 2) { // 登录中
|
||||||
|
console.log('登录中')
|
||||||
|
resolve({status, msg:'登录中'})
|
||||||
|
} else if (status == 4) { // 登出中
|
||||||
|
console.log('登出中')
|
||||||
|
resolve({status, msg:'登出中'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return new Promise(fn)
|
||||||
|
}
|
||||||
|
// 登出
|
||||||
|
logout() {
|
||||||
|
if (!this.timChat) return
|
||||||
|
return this.timChat.TIMLogout().then(res => {
|
||||||
|
console.log('登出成功', res)
|
||||||
|
this.status.isLogin = false
|
||||||
|
return res
|
||||||
|
}).catch(error => {
|
||||||
|
console.log('登出失败', error)
|
||||||
|
return error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 创建群组 群名和初始成员 userID
|
||||||
|
createGroup(name, memberList=[]) {
|
||||||
|
if (!this.timChat) return
|
||||||
|
if (!!this.timGroupId) return console.log('群组已存在')
|
||||||
|
// 转换初始成员id,userID 转 group_member_info_identifier
|
||||||
|
if (memberList && memberList.length) {
|
||||||
|
memberList = memberList.map(o => ({group_member_info_identifier:o.userID}))
|
||||||
|
}
|
||||||
|
const option = { // CreateGroupParams
|
||||||
|
params: { // GroupParams
|
||||||
|
// create_group_param_group_name: 群组名称(必填)
|
||||||
|
// create_group_param_group_id: 群组ID,不填时创建成功回调会返回一个后台分配的群ID
|
||||||
|
// create_group_param_group_type 群组类型,默认为Public
|
||||||
|
// create_group_param_group_member_array 群组初始成员数组
|
||||||
|
// create_group_param_notification 群组公告
|
||||||
|
// create_group_param_introduction 群组简介
|
||||||
|
// create_group_param_face_url 群组头像URL
|
||||||
|
// create_group_param_add_option 加群选项,默认为Any
|
||||||
|
// create_group_param_max_member_num 群组最大成员数
|
||||||
|
// create_group_param_custom_info 请参考自定义字段
|
||||||
|
create_group_param_group_name: name,
|
||||||
|
create_group_param_group_type: this.defOption.group_type,
|
||||||
|
create_group_param_max_member_num: 200,
|
||||||
|
create_group_param_group_member_array: memberList
|
||||||
|
},
|
||||||
|
data: '', // 用户自定义数据
|
||||||
|
}
|
||||||
|
// @TGS#3XVNI6ZOG
|
||||||
|
return this.timChat.TIMGroupCreate(option).then(res => {
|
||||||
|
if (res && res.code == 0) {
|
||||||
|
const timGroupId = res?.json_param?.create_group_result_groupid
|
||||||
|
if (!!timGroupId) this.timGroupId = timGroupId
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 删除群组
|
||||||
|
deleteGroup() {
|
||||||
|
if (!this.timGroupId) return
|
||||||
|
return this.timChat.TIMGroupDelete({
|
||||||
|
groupId: this.timGroupId,
|
||||||
|
data: '', // 用户自定义数据
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 获取群组列表
|
||||||
|
getGroupList() {
|
||||||
|
return this.timChat.getGroupList().then(res => {
|
||||||
|
console.log('获取群组列表', res)
|
||||||
|
return res
|
||||||
|
}).catch(error => {
|
||||||
|
console.log('获取群组列表失败', error)
|
||||||
|
return error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* @description: 生成签名|客户端计算 UserSig
|
||||||
|
* @author: zdg
|
||||||
|
* @date 2021-07-05 14:07:01
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TIM生成签名
|
||||||
|
import LibGenerateTestUserSig from './lib-generate-test-usersig-es.min.js';
|
||||||
|
/**
|
||||||
|
* Signature expiration time, which should not be too short
|
||||||
|
* Time unit: second
|
||||||
|
* Default time: 7 * 24 * 60 * 60 = 604800 = 7days
|
||||||
|
*/
|
||||||
|
const EXPIRETIME = 604800;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module: GenerateTestUserSig
|
||||||
|
*
|
||||||
|
* Description: Generates UserSig for testing. UserSig is a security signature designed by Tencent Cloud for its cloud services.
|
||||||
|
* It is calculated based on `SDKAppID`, `UserID`, and `EXPIRETIME` using the HMAC-SHA256 encryption algorithm.
|
||||||
|
*
|
||||||
|
* Attention: For the following reasons, do not use the code below in your commercial application.
|
||||||
|
*
|
||||||
|
* The code may be able to calculate UserSig correctly, but it is only for quick testing of the SDK’s basic features, not for commercial applications.
|
||||||
|
* `SECRETKEY` in client code can be easily decompiled and reversed, especially on web.
|
||||||
|
* Once your key is disclosed, attackers will be able to steal your Tencent Cloud traffic.
|
||||||
|
*
|
||||||
|
* The correct method is to deploy the `UserSig` calculation code and encryption key on your project server so that your application can request from your server a `UserSig` that is calculated whenever one is needed.
|
||||||
|
* Given that it is more difficult to hack a server than a client application, server-end calculation can better protect your key.
|
||||||
|
*
|
||||||
|
* Reference: https://cloud.tencent.com/document/product/647/17275#Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
function genTestUserSig(options) {
|
||||||
|
const { SDKAppID, secretKey, userID } = options;
|
||||||
|
const generator = new LibGenerateTestUserSig(SDKAppID, secretKey, EXPIRETIME);
|
||||||
|
const userSig = generator.genTestUserSig(userID);
|
||||||
|
return {
|
||||||
|
SDKAppID,
|
||||||
|
userSig,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { genTestUserSig, EXPIRETIME };
|
|
@ -9,7 +9,7 @@ export function shareStorePlugin({store}) {
|
||||||
// mutation 变量包含了变化前后的状态
|
// mutation 变量包含了变化前后的状态
|
||||||
// mutation.events: key newValue target oldValue oldTarget
|
// mutation.events: key newValue target oldValue oldTarget
|
||||||
// state 是变化后的状态
|
// state 是变化后的状态
|
||||||
console.log('store.$subscribe', mutation)
|
// console.log('store.$subscribe', mutation)
|
||||||
// 在存储变化的时候执行
|
// 在存储变化的时候执行
|
||||||
// const storeName = store.$id
|
// const storeName = store.$id
|
||||||
// const storeName = mutation.storeId
|
// const storeName = mutation.storeId
|
||||||
|
|
|
@ -10,8 +10,8 @@ const isNode = typeof require !== 'undefined' // 是否支持node函数
|
||||||
const path = isNode?require('path'):{}
|
const path = isNode?require('path'):{}
|
||||||
const Remote = isNode?require('@electron/remote'):{}
|
const Remote = isNode?require('@electron/remote'):{}
|
||||||
const { ipcRenderer } = isNode?require('electron'):window.electron || {}
|
const { ipcRenderer } = isNode?require('electron'):window.electron || {}
|
||||||
|
const API = isNode?window.api:{} // preload-api
|
||||||
import { useToolState } from '@/store/modules/tool' // 获取store状态
|
import { useToolState } from '@/store/modules/tool' // 获取store状态
|
||||||
|
|
||||||
// 常用变量
|
// 常用变量
|
||||||
const BaseUrl = isNode?process.env['ELECTRON_RENDERER_URL']+'/#':''
|
const BaseUrl = isNode?process.env['ELECTRON_RENDERER_URL']+'/#':''
|
||||||
const isDev = isNode?process.env.NODE_ENV !== 'production':''
|
const isDev = isNode?process.env.NODE_ENV !== 'production':''
|
||||||
|
@ -84,8 +84,8 @@ export function ipcHandle(fn,key, cb) {
|
||||||
* @param {*} data 参数
|
* @param {*} data 参数
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
let winPdf=null
|
|
||||||
let wins_tool = null
|
let wins_tool = null
|
||||||
|
let winPdf=null
|
||||||
export const createWindow = async (type, data) => {
|
export const createWindow = async (type, data) => {
|
||||||
if (wins_tool) return console.error('createWindow: win is have')
|
if (wins_tool) return console.error('createWindow: win is have')
|
||||||
if (!type) return console.error('createWindow: type is null')
|
if (!type) return console.error('createWindow: type is null')
|
||||||
|
@ -101,6 +101,7 @@ export const createWindow = async (type, data) => {
|
||||||
// autoClose: true, // 关闭窗口后自动关闭
|
// autoClose: true, // 关闭窗口后自动关闭
|
||||||
}
|
}
|
||||||
data.isConsole = true // 是否开启控制台
|
data.isConsole = true // 是否开启控制台
|
||||||
|
data.isWeb = false // 是否开启web安全
|
||||||
data.option = {...defOption, ...option}
|
data.option = {...defOption, ...option}
|
||||||
wins_tool = await toolWindow(data)
|
wins_tool = await toolWindow(data)
|
||||||
wins_tool.type = type // 唯一标识
|
wins_tool.type = type // 唯一标识
|
||||||
|
@ -150,7 +151,7 @@ export const createWindow = async (type, data) => {
|
||||||
* @author: zdg
|
* @author: zdg
|
||||||
* @date 2021-07-05 14:07:01
|
* @date 2021-07-05 14:07:01
|
||||||
*/
|
*/
|
||||||
export function toolWindow({url, isConsole, option={}}) {
|
export function toolWindow({url, isConsole, isWeb=true, option={}}) {
|
||||||
// width = window.screen.width
|
// width = window.screen.width
|
||||||
let width = option?.width || 800
|
let width = option?.width || 800
|
||||||
let height = option?.height || 600
|
let height = option?.height || 600
|
||||||
|
@ -162,14 +163,12 @@ export function toolWindow({url, isConsole, option={}}) {
|
||||||
const config = {
|
const config = {
|
||||||
width, height,
|
width, height,
|
||||||
type: 'toolbar', // 创建的窗口类型为工具栏窗口
|
type: 'toolbar', // 创建的窗口类型为工具栏窗口
|
||||||
icon: path.join(__dirname, '../../resources/logo2.ico'),
|
// icon: path.join(__dirname, '../../resources/logo2.ico'),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
// preload: path.join(__dirname, '../preload/index.js'),
|
preload: path.join(API.preloadPath, '/index.js'),
|
||||||
preload: '@root/src/preload/index.js',
|
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
nodeIntegration: true, // nodeApi调用
|
nodeIntegration: true, // nodeApi调用
|
||||||
contextIsolation: false, // 沙箱取消
|
contextIsolation: false, // 沙箱取消
|
||||||
// webSecurity: false // 跨域关闭
|
|
||||||
},
|
},
|
||||||
...option
|
...option
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,58 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
// 功能说明:im-chat 腾讯im chat聊天室
|
// 功能说明:im-chat 腾讯im chat聊天室
|
||||||
|
import { onMounted, ref, reactive, watchEffect } from 'vue'
|
||||||
|
import { ImChat } from '@/plugins/imChat'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import * as http from '@/api/apiService' // 自定义api service
|
||||||
|
// import { ipcMsgSend, ipcHandle, ipcMain, ipcMsgInvoke } from '@/utils/tool' // 相关工具
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const emits = defineEmits(['change'])
|
||||||
|
let imChat
|
||||||
|
onMounted(() => {
|
||||||
|
// console.log(userStore)
|
||||||
|
initImChat()
|
||||||
|
})
|
||||||
|
// 初始化 im-chat
|
||||||
|
const initImChat = async () => {
|
||||||
|
// console.log('im-chat', userStore.user.timuserid)
|
||||||
|
try {
|
||||||
|
// 获取腾讯云签名
|
||||||
|
const res = await http.imChat.getTxCloudSign()
|
||||||
|
if (res && res.code == 200) {
|
||||||
|
const { sdkAppId, sign } = res.data
|
||||||
|
const { timuserid, deptId, userId } = userStore.user
|
||||||
|
// 群名称
|
||||||
|
const groupName = `${deptId}-classteaching-${userId}`
|
||||||
|
// 注册im-chat
|
||||||
|
// await ipcMsgInvoke('im-chat:init', sdkAppId)
|
||||||
|
imChat = new ImChat(sdkAppId, sign, timuserid)
|
||||||
|
// 初始化 im-chat
|
||||||
|
await imChat.init()
|
||||||
|
// 登录 im-chat
|
||||||
|
await imChat.login()
|
||||||
|
imChat.watch(res => {
|
||||||
|
console.log('im-chat watch: ', res)
|
||||||
|
})
|
||||||
|
// 创建群
|
||||||
|
await createGroup(groupName)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('im-error: ', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 创建群组
|
||||||
|
const createGroup = async (groupName) => {
|
||||||
|
if (!imChat) return
|
||||||
|
await imChat.createGroup(groupName)
|
||||||
|
const params = {type:'createGroup', data: imChat.timGroupId}
|
||||||
|
emits('change', params)
|
||||||
|
}
|
||||||
|
// 退出
|
||||||
|
const logout = () => imChat?.logout()
|
||||||
|
// 解散群
|
||||||
|
const deleteGroup = () => imChat?.deleteGroup()
|
||||||
|
|
||||||
|
defineExpose({ logout, deleteGroup, imChat })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<upvote-vue></upvote-vue>
|
<upvote-vue></upvote-vue>
|
||||||
|
|
||||||
<!-- im-chat 聊天组件 -->
|
<!-- im-chat 聊天组件 -->
|
||||||
<im-chat />
|
<!-- <im-chat ref="imChatRef" @change="chatChange" /> -->
|
||||||
|
|
||||||
<!-- 底部工具栏 -->
|
<!-- 底部工具栏 -->
|
||||||
<div class="tool-bottom-all" @mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
|
<div class="tool-bottom-all" @mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
|
||||||
|
@ -39,7 +39,8 @@
|
||||||
// 功能说明:electron 悬浮球
|
// 功能说明:electron 悬浮球
|
||||||
import { onMounted, ref, reactive, watchEffect } from 'vue'
|
import { onMounted, ref, reactive, watchEffect } from 'vue'
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { endClass } from '@/api/classManage'
|
// import { startClass, endClass } from '@/api/classManage'
|
||||||
|
import * as classManageApi from '@/api/classManage'
|
||||||
import logo from '@root/resources/icon.png' // logo
|
import logo from '@root/resources/icon.png' // logo
|
||||||
import boardVue from './components/board.vue' // 画板-子组件
|
import boardVue from './components/board.vue' // 画板-子组件
|
||||||
import sideVue from './components/side.vue' // 画板-子组件
|
import sideVue from './components/side.vue' // 画板-子组件
|
||||||
|
@ -57,6 +58,11 @@ const dragtime = ref(0) // 拖拽时间-计算点击还是拖动
|
||||||
const isShow = ref(false) // 是否显示-画板
|
const isShow = ref(false) // 是否显示-画板
|
||||||
const toolStore = useToolState() // 状态管理
|
const toolStore = useToolState() // 状态管理
|
||||||
const boardVueRef=ref(null) // 画板ref
|
const boardVueRef=ref(null) // 画板ref
|
||||||
|
const imChatRef = ref(null) // im-chat ref
|
||||||
|
const classObj = reactive({ // 课程相关
|
||||||
|
id: route.query.reservId, // 课程id
|
||||||
|
data: {} // 课程信息
|
||||||
|
})
|
||||||
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' },
|
||||||
|
@ -69,6 +75,7 @@ const btnList = [ // 工具栏按钮列表
|
||||||
// === 页面加载完毕 ===
|
// === 页面加载完毕 ===
|
||||||
onMounted(async() => {
|
onMounted(async() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
console.log(classObj)
|
||||||
resetStatus() // 开启重置状态-监听
|
resetStatus() // 开启重置状态-监听
|
||||||
}, 200);
|
}, 200);
|
||||||
})
|
})
|
||||||
|
@ -96,6 +103,13 @@ const mouseChange = (bool) => {
|
||||||
if (!isShow.value) resBool = !!bool
|
if (!isShow.value) resBool = !!bool
|
||||||
setIgnore(resBool)
|
setIgnore(resBool)
|
||||||
}
|
}
|
||||||
|
// im-chat: 聊天事件
|
||||||
|
const chatChange = ({type, data}) => {
|
||||||
|
if (type == 'createGroup') { // 创建群
|
||||||
|
// classManageApi.startClass()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 忽略鼠标穿透
|
// 忽略鼠标穿透
|
||||||
const setIgnore = (bool) => {ipcMsgSend('tool-sphere:set:ignore', bool)}
|
const setIgnore = (bool) => {ipcMsgSend('tool-sphere:set:ignore', bool)}
|
||||||
// 重置状态: 鼠标|画板
|
// 重置状态: 鼠标|画板
|
||||||
|
@ -127,8 +141,10 @@ const sideChange = async o => {
|
||||||
break
|
break
|
||||||
case 'over': // 下课
|
case 'over': // 下课
|
||||||
toolStore.isToolWin = false
|
toolStore.isToolWin = false
|
||||||
await endClass(route.query.reservId)
|
await classManageApi.endClass(route.query.reservId)
|
||||||
ipcMsgSend('tool-sphere:close')
|
await imChatRef.value?.deleteGroup() // 解散群
|
||||||
|
await imChatRef.value?.logout() // 退出im
|
||||||
|
ipcMsgSend('tool-sphere:close') // 关闭窗口
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue