diff --git a/.env.development b/.env.development
index b10b50c..df0b713 100644
--- a/.env.development
+++ b/.env.development
@@ -9,7 +9,8 @@ VITE_APP_BASE_API = '/dev-api'
VITE_APP_DOMAIN = 'file.ysaix.com'
-VITE_APP_UPLOAD_API = 'http://192.168.2.52:7863'
+VITE_APP_UPLOAD_API = 'https://file.ysaix.com:7868/prod-api'
+#VITE_APP_UPLOAD_API = 'http://192.168.2.52:7863'
VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/'
diff --git a/.env.test b/.env.test
index 993cace..3d671e1 100644
--- a/.env.test
+++ b/.env.test
@@ -1,5 +1,5 @@
# 页面标题
-VITE_APP_TITLE = AIx数字平台
+VITE_APP_TITLE = AIx数字平台(测试版)
# 生产环境配置
VITE_APP_ENV = 'production'
diff --git a/electron-builder-prod.yml b/electron-builder-prod.yml
index 29c65e8..9dfb51e 100644
--- a/electron-builder-prod.yml
+++ b/electron-builder-prod.yml
@@ -1,7 +1,11 @@
appId: com.electron.app
productName: AIx
directories:
+ output: dist
buildResources: build
+win:
+ executableName: AIx
+ icon: resources/logo2.ico
files:
- '!**/.vscode/*'
- '!src/*'
@@ -10,9 +14,6 @@ files:
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
asarUnpack:
- resources/**
-win:
- executableName: AIx
- icon: resources/logo2.ico
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
@@ -45,3 +46,9 @@ publish:
url: https://prev.ysaix.com:7868/src/assets/smarttalk/
electronDownload:
mirror: https://npmmirror.com/mirrors/electron/
+# 额外依赖打包到输出目录
+extraFiles:
+ - from: ./node_modules/im_electron_sdk/lib/
+ to: ./resources
+ filter:
+ - '**/*'
diff --git a/electron-builder-test.yml b/electron-builder-test.yml
index ed4e1e2..6159547 100644
--- a/electron-builder-test.yml
+++ b/electron-builder-test.yml
@@ -13,7 +13,7 @@ asarUnpack:
win:
executableName: AIx
icon: resources/logo2.ico
-nsis:
+nsis:
oneClick: false
allowToChangeInstallationDirectory: true
artifactName: ${name}-${version}-setup.${ext}
@@ -45,3 +45,9 @@ publish:
url: http://localhost:3000
electronDownload:
mirror: https://npmmirror.com/mirrors/electron/
+# 额外依赖打包到输出目录
+extraFiles:
+ - from: ./node_modules/im_electron_sdk/lib/
+ to: ./resources
+ filter:
+ - '**/*'
diff --git a/electron-builder.yml b/electron-builder.yml
index 911fb1c..46e484d 100644
--- a/electron-builder.yml
+++ b/electron-builder.yml
@@ -13,7 +13,7 @@ asarUnpack:
win:
executableName: AIx
icon: resources/logo2.ico
-nsis:
+nsis:
oneClick: false
allowToChangeInstallationDirectory: true
artifactName: ${name}-${version}-setup.${ext}
@@ -45,3 +45,9 @@ publish:
url: https://file.ysaix.com:7868/src/assets/smarttalk/
electronDownload:
mirror: https://npmmirror.com/mirrors/electron/
+# 额外依赖打包到输出目录
+extraFiles:
+ - from: ./node_modules/im_electron_sdk/lib/
+ to: ./resources
+ filter:
+ - '**/*'
diff --git a/package.json b/package.json
index 5c5797a..946990a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "aix-win",
- "version": "1.0.7",
+ "version": "1.1.8",
"description": "An Electron application with Vue",
"main": "./out/main/index.js",
"author": "example.com",
@@ -30,18 +30,21 @@
"crypto-js": "^4.2.0",
"electron-dl-manager": "^3.0.0",
"electron-log": "^5.1.7",
+ "electron-store": "8.0.0",
"electron-updater": "^6.1.7",
"element-plus": "^2.7.6",
"fabric": "^5.3.0",
"js-cookie": "^3.0.5",
"jsencrypt": "^3.3.2",
"jsondiffpatch": "0.6.0",
+ "im_electron_sdk": "^8.0.5904",
"lodash": "^4.17.21",
"pdfjs-dist": "4.4.168",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"spark-md5": "^3.0.2",
"vue-router": "^4.4.0",
+ "xgplayer": "^3.0.19",
"xlsx": "^0.18.5"
},
"devDependencies": {
diff --git a/resources/logo.png b/resources/logo.png
new file mode 100644
index 0000000..45f6e65
Binary files /dev/null and b/resources/logo.png differ
diff --git a/src/main/chat.js b/src/main/chat.js
new file mode 100644
index 0000000..b6700f2
--- /dev/null
+++ b/src/main/chat.js
@@ -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 }
diff --git a/src/main/file.js b/src/main/file.js
index d3eede2..ddabb2c 100644
--- a/src/main/file.js
+++ b/src/main/file.js
@@ -42,19 +42,23 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
let filePath = appRootFilePath + fileNewName
let uploadId = null
let isOn = false
+ let lastMTime = fs.statSync(filePath).mtime.getTime()
+ console.log(lastMTime)
setInterval(() => {
- getFileMD5(filePath).then((md5New) => {
- if (md5New !== md5) {
- md5 = md5New
+ getFileMsg(filePath).then((msg) => {
+ if (msg !== lastMTime) {
+ lastMTime = msg
if (uploadId) {
clearTimeout(uploadId)
}
if (isOn === false) {
+ console.log(fileNewName)
e.reply('listen-file-change-on' + fileNewName)
isOn = true
}
//倒数十秒提交更改,十秒之内有继续修改则重置倒数
uploadId = setTimeout(() => {
+ console.log(223)
//执行更新,上传文件
let formData = new FormData()
formData.append('id', id)
@@ -77,12 +81,19 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
console.error('Error uploading file:', err)
}
})
- }, 20000)
+ }, 5000)
}
})
- }, 10000)
+ }, 1000)
})
+ function getFileMsg(path) {
+ return new Promise((resolve, reject) => {
+ const stats = fs.statSync(path)
+ return resolve(stats.mtime.getTime())
+ })
+ }
+
function getFileMD5(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, dataFile) => {
@@ -121,13 +132,14 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'down' })
return
}
- getFileMD5(filePath).then((localMd5) => {
- if (localMd5 === md5) {
+ getFileMsg(filePath).then((msg) => {
+ let time = new Date(lastModifyTime).getTime();
+ msg = parseInt(msg/1000)*1000;
+ if (msg == time) {
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: false, type: '' })
} else {
const stats = fs.statSync(filePath)
//如果线上时间大于线下时间,就需要从线上下载,否则则需要上传
- let time = new Date(lastModifyTime)
if (time > stats.mtime.getTime()) {
e.reply('is-async-local-file-reply' + fileNewName, { isAsync: true, type: 'down' })
} else if (time < stats.mtime.getTime()) {
@@ -146,7 +158,7 @@ export default async function ({ app, shell, BrowserWindow, ipcMain }) {
//使用默认应用打开本地文件
ipcMain.on('open-path-app', (e, destination) => {
let path = appRootFilePath + destination
- shell.openExternal(path).catch((error) => {
+ shell.openPath(path).catch((error) => {
console.log(error)
})
})
diff --git a/src/main/index.js b/src/main/index.js
index 441bf06..e4559b2 100644
--- a/src/main/index.js
+++ b/src/main/index.js
@@ -3,12 +3,19 @@ import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset'
import File from './file'
+import Logger from './logger' // 日志封装
+import chat from './chat' // chat封装
+import Store from './store' // Store封装
+import updateInit from './update'
// 代理 electron/remote
// 第一步:引入remote
import remote from '@electron/remote/main'
// 第二步: 初始化remote
remote.initialize()
-import updateInit from './update'
+// 日志配置-初始化(日志直接绑定到console上)
+if(!is.dev) Logger.initialize()
+// 持久化数据-初始化
+Store.initialize()
File({ app, shell, BrowserWindow, ipcMain })
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
@@ -28,6 +35,7 @@ function createLoginWindow() {
icon: join(__dirname, '../../resources/logo2.ico'),
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
+ defaultEncoding: 'utf-8',
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
nodeIntegration: true,
@@ -60,13 +68,16 @@ function createLoginWindow() {
function createMainWindow() {
mainWindow = new BrowserWindow({
width: 1200,
+ minWidth: 1200,
height: 700,
show: false,
frame: false, // 无边框
autoHideMenuBar: true,
+ maximizable: false,
icon: join(__dirname, '../../resources/logo2.ico'),
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
+ defaultEncoding: 'utf-8',
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
// nodeIntegration: true,
@@ -115,6 +126,7 @@ async function createLinkWin(data) {
autoHideMenuBar: true,
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
+ defaultEncoding: 'utf-8',
sandbox: false,
nodeIntegration: true,
worldSafeExecuteJavaScript: true,
@@ -126,13 +138,14 @@ async function createLinkWin(data) {
let cookieDetails = { ...data.cookieData }
await linkWin[data.key].webContents.session.cookies
.set(cookieDetails)
- .then(() => {
- console.log('Cookie is successful')
- })
- .catch((error) => {
- console.error('Cookie is error', error)
- })
+ .then(() => {})
+ .catch((error) => {})
data.fullPath = data.fullPath.replaceAll('//', '/')
+ if (data.fullPath.indexOf('?') !== -1) {
+ data.fullPath += '&urlSource=smarttalk'
+ }else {
+ data.fullPath += '?urlSource=smarttalk'
+ }
linkWin[data.key].loadURL(data.fullPath)
linkWin[data.key].once('ready-to-show', () => {
@@ -147,6 +160,8 @@ async function createLinkWin(data) {
// 初始化完成
app.on('ready', () => {
+ appWatchError() // 监听app错误
+ process.env.LANG = 'en_US.UTF-8'
// 设置应用程序用户模型标识符
electronApp.setAppUserModelId('com.electron')
@@ -226,12 +241,14 @@ app.on('window-all-closed', () => {
// 监听全局事件
function handleAll() {
+ const chatInstance = chat.initialize() // im-chat 实例
// 新窗口创建-监听
ipcMain.on('new-window', (e, data) => {
const { id, type } = data
const win = BrowserWindow.fromId(id)
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) => {
@@ -243,4 +260,41 @@ function handleAll() {
}
}
})
+ // 用于监听-状态管理变化-初始同步
+ ipcMain.handle('pinia-state-init', (e, wid, storeName, jsonStr) => {
+ // console.log('pinia-state-init', jsonStr)
+ const win = BrowserWindow.fromId(wid)
+ win.webContents.send('pinia-state-set', storeName, jsonStr)
+ })
}
+
+// app 崩溃监听器
+function appWatchError() {
+ // 渲染进程崩溃
+ app.on('renderer-process-crashed', (event, webContents, killed) => {
+ console.error(
+ `APP-ERROR:renderer-process-crashed; event: ${JSON.stringify(event)}; webContents:${JSON.stringify(
+ webContents
+ )}; killed:${JSON.stringify(killed)}`
+ )
+ })
+
+ // GPU进程崩溃
+ app.on('gpu-process-crashed', (event, killed) => {
+ console.error(`APP-ERROR:gpu-process-crashed; event: ${JSON.stringify(event)}; killed: ${JSON.stringify(killed)}`)
+ })
+
+ // 渲染进程结束
+ app.on('render-process-gone', async (event, webContents, details) => {
+ console.error(
+ `APP-ERROR:render-process-gone; event: ${JSON.stringify(event)}; webContents:${JSON.stringify(
+ webContents
+ )}; details:${JSON.stringify(details)}`
+ )
+ })
+
+ // 子进程结束
+ app.on('child-process-gone', async (event, details) => {
+ console.error(`APP-ERROR:child-process-gone; event: ${JSON.stringify(event)}; details:${JSON.stringify(details)}`)
+ })
+}
\ No newline at end of file
diff --git a/src/main/logger.js b/src/main/logger.js
new file mode 100644
index 0000000..03b6db3
--- /dev/null
+++ b/src/main/logger.js
@@ -0,0 +1,52 @@
+/**
+ * @description 日志配置
+ * @author zdg
+ * @date 2021-07-05 14:07:01
+ */
+// import log from 'electron-log'
+import log from 'electron-log/main'
+import { app } from 'electron'
+import path from 'path'
+
+// 关闭控制台打印
+// 日志控制台等级,默认值:false
+log.transports.console.level = false
+// log.transports.console.level = 'info'
+// 日志文件等级,默认值:false
+log.transports.file.level = 'info'
+// 日志文件名,默认:main.log
+// log.transports.file.fileName = 'main.log';
+// 日志大小,默认:1048576(1M),达到最大上限后,备份文件并重命名为:main.old.log,有且仅有一个备份文件
+log.transports.file.maxSize = 10 * 1024 * 1024; // 文件最大不超过 10M
+// 自定义日志文件滚动策略
+log.transports.file.rollSize = 10 * 1024 * 1024; // 10MB
+// 日志格式,默认:[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}]{scope} {text}
+log.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}]{scope} {text}'
+let date = new Date()
+let dateStr = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate()
+// 文件位置及命名方式
+// 默认位置为:C:\Users\[user]\AppData\Roaming\[appname]\electron_log\
+// 文件名为:年-月-日.log
+// 自定义文件保存位置为安装目录下 \log\年-月-日.log
+// log.transports.file.resolvePathFn = () => 'logs\\' + dateStr+ '.log';
+log.transports.file.resolvePathFn = () => path.join(app.getPath('userData'), `logs/${dateStr}.log`)
+
+// 有六个日志级别error, warn, info, verbose, debug, silly。默认是silly
+export const logger = {
+ error: (...args) => log.error(...args),
+ warn: (...args) => log.warn(...args),
+ info: (...args) => log.info(...args),
+ verbose: (...args) => log.verbose(...args),
+ debug: (...args) => log.debug(...args),
+ silly: (...args) => log.silly(...args)
+}
+export function initialize(bool = true, type = 'all') {
+ log.initialize() // 为渲染器进行初始化
+ if (bool) { // 是否替换默认的console
+ if (type == 'all') Object.assign(console, log.functions)
+ else { // 替换指定类型
+ console[type] = log[type]
+ }
+ }
+}
+export default { initialize }
\ No newline at end of file
diff --git a/src/main/store.js b/src/main/store.js
new file mode 100644
index 0000000..c37f8f5
--- /dev/null
+++ b/src/main/store.js
@@ -0,0 +1,61 @@
+/**
+ * @description 解决 主进程|渲染进程 数据共享
+ */
+import Store from 'electron-store' // 持久化存储
+
+// 设置ipc与渲染器通信
+Store.initRenderer()
+
+// 默认共享数据
+const defaultData = {
+ session: { // 缓存(临时sessionStorage)
+ model: 'select', // 悬浮球-当前模式
+ showBoardAll: false, // 全屏画板-是否显示
+ isPdfWin: false, // pdf窗口是否打开
+ isToolWin: false, // 工具窗口是否打开
+ curSubjectNode: {
+ data: {}, // 当前教材节点 (包含当前教材 单元)
+ querySearch: {} // 查询资源所需参数
+ }
+ },
+ local: { // 本地(永久localStorage)
+ },
+}
+
+// 初始化
+export function initialize(){
+ // 缓存数据-sessionStore
+ const sessionStore = new Store({
+ name: 'session-store', // 存储文件名
+ fileExtension: 'ini', // 文件后缀名
+ encryptionKey: 'BvPLmgCC4DSIG0KkTec5', // 数据加密-防止用户直接改配置
+ beforeEachMigration: (store, context) => { // 版本迁移回调
+ console.log(`[session-store] 迁移从 ${context.fromVersion} → ${context.toVersion}`);
+ },
+ migrations: { // 版本变化
+ '0.0.0': store => {
+ // store.set('debugPhase', true);
+ }
+ }
+ })
+ sessionStore.clear() // 先清除-所有缓存数据
+ sessionStore.set(defaultData.session) // 初始化-默认数据
+
+ // 缓存数据-localStore
+ const localStore = new Store({
+ name: 'local-store', // 存储文件名
+ fileExtension: 'ini', // 文件后缀名
+ encryptionKey: '6CyoHQmUaPmLzvVsh', // 数据加密-防止用户直接改配置
+ beforeEachMigration: (store, context) => { // 版本迁移回调
+ console.log(`[local-store] 迁移从 ${context.fromVersion} → ${context.toVersion}`);
+ },
+ migrations: { // 版本变化
+ '0.0.0': store => {
+ // store.set('debugPhase', true);
+ }
+ }
+ })
+ localStore.set(defaultData.local) // 初始化-默认数据
+ return {sessionStore, localStore}
+}
+export default { initialize }
\ No newline at end of file
diff --git a/src/main/tool.js b/src/main/tool.js
deleted file mode 100644
index 8be3a66..0000000
--- a/src/main/tool.js
+++ /dev/null
@@ -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]
- }
- })
- }
-}
diff --git a/src/preload/index.js b/src/preload/index.js
index 9da4869..ffdb7a0 100644
--- a/src/preload/index.js
+++ b/src/preload/index.js
@@ -1,8 +1,10 @@
import { contextBridge } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
-
+import TimRender from 'im_electron_sdk/dist/renderer' // im渲染部分实例
// Custom APIs for renderer
const api = {
+ preloadPath: __dirname, // 当前preload地址
+ getTimRender: () => new TimRender(), // im渲染部分实例
}
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
diff --git a/src/renderer/index.html b/src/renderer/index.html
index 3441296..1de810c 100644
--- a/src/renderer/index.html
+++ b/src/renderer/index.html
@@ -2,13 +2,13 @@
- AIx智慧教育
+ %VITE_APP_TITLE%
-
+
diff --git a/src/renderer/src/api/apiService.js b/src/renderer/src/api/apiService.js
new file mode 100644
index 0000000..0b28e0d
--- /dev/null
+++ b/src/renderer/src/api/apiService.js
@@ -0,0 +1,28 @@
+/**
+ * @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) config[method=='get'?'params':'data'] = data
+ if (!!option) Object.assign(config, option)
+ // 特殊格式处理
+ let headers
+ if (type == 'file') headers = { 'Content-Type': 'multipart/form-data' }
+ else if (type == 'json') headers = { 'Content-Type': 'application/json' }
+ else if (type == 'form') headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
+ headers && (config.headers = { ...config.headers, ...headers })
+ return request(config)
+ }
+}
+// zdg: 腾讯云-即时通讯
+export class imChat {
+ // 获取腾讯im-chat appid 签名
+ static getTxCloudSign = data => ApiService.publicHttp('/system/user/txCloudSign', data)
+}
diff --git a/src/renderer/src/api/classManage/index.js b/src/renderer/src/api/classManage/index.js
index 6db5913..14471ee 100644
--- a/src/renderer/src/api/classManage/index.js
+++ b/src/renderer/src/api/classManage/index.js
@@ -50,7 +50,7 @@ export function getClassmain(id) {
// 获取小组列表
export function listClassgroup(query) {
return request({
- url: '/education/classgroup/list',
+ url: '/education/classgroup/new/list',
method: 'get',
params: query
})
@@ -159,11 +159,13 @@ export function deleteSmartReserv(id) {
method: 'delete'
})
}
-export function startClass(id) {
+export function startClass(id, ex3) {
+ const params = {id}
+ !!ex3 && (params.ex3 = ex3)
return request({
url: '/smarttalk/classReserv/startClass',
method: 'get',
- params: {id}
+ params
})
}
export function endClass(id) {
@@ -173,3 +175,15 @@ export function endClass(id) {
params: {id}
})
}
+/**
+ * @description 获取课堂信息
+ * @param {*} id
+ * @returns
+ */
+export function getClassInfo(id) {
+ return request({
+ url: '/smarttalk/classReserv/selectById',
+ method: 'get',
+ params: {id}
+ })
+}
diff --git a/src/renderer/src/api/classTask/index.js b/src/renderer/src/api/classTask/index.js
new file mode 100644
index 0000000..f441957
--- /dev/null
+++ b/src/renderer/src/api/classTask/index.js
@@ -0,0 +1,237 @@
+// 查询evaluation列表
+import request from '@/utils/request'
+
+// 查询反馈列表
+
+// 查询classworkdata列表 班级作业列表
+export function listClassworkdata(query) {
+ return request({
+ url: '/education/classworkdata/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询entpcoursework列表 课程作业列表
+export function listEntpcoursework(query) {
+ return request({
+ url: '/education/entpcoursework/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询classworkeval列表 课堂作业列表
+export function listClassworkeval(query) {
+ return request({
+ url: '/education/classworkeval/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 修改classworkeval
+export function updateClassworkeval(data) {
+ return request({
+ url: '/education/classworkeval',
+ method: 'put',
+ data: data
+ })
+}
+
+
+
+
+
+
+
+
+
+// 0------------------------
+// 查询班级列表
+export function listClassmain(query) {
+ return request({
+ url: '/education/classmain/list',
+ method: 'get',
+ params: query
+ })
+}
+// 查询学生列表
+export function listClassuser(query) {
+ return request({
+ url: '/education/classuser/list',
+ method: 'get',
+ params: query
+ })
+}
+// 新增班级
+export function addClassmain(data) {
+ return request({
+ url: '/education/classmain',
+ method: 'post',
+ data: data
+ })
+}
+// 查询所有学科的列表
+export function listEvaluation(query) {
+ return request({
+ url: '/education/evaluation/list',
+ method: 'get',
+ params: query
+ })
+}
+// 新增小组
+export function addClassgroup(data) {
+ return request({
+ url: '/education/classgroup',
+ method: 'post',
+ data: data
+ })
+}
+//班级详情
+export function getClassmain(id) {
+ return request({
+ url: '/education/classmain/' + id,
+ method: 'get'
+ })
+}
+// 获取小组列表
+export function listClassgroup(query) {
+ return request({
+ url: '/education/classgroup/new/list',
+ method: 'get',
+ params: query
+ })
+}
+//删除小组
+export function delClassgroup(id) {
+ return request({
+ url: '/education/classgroup/' + id,
+ method: 'delete'
+ })
+}
+//查询小组信息
+export function getClassgroup(id) {
+ return request({
+ url: '/education/classgroup/' + id,
+ method: 'get'
+ })
+}
+//修改小组信息
+export function updateClassgroup(data) {
+ return request({
+ url: '/education/classgroup',
+ method: 'put',
+ data: data
+ })
+}
+//新增学生
+export function addStudentmain(data) {
+ return request({
+ url: '/education/studentmain',
+ method: 'post',
+ data: data
+ })
+}
+//修改学生信息
+export function updateStudentmain(data) {
+ return request({
+ url: '/education/studentmain',
+ method: 'put',
+ data: data
+ })
+}
+//获取学生信息
+export function getStudentmain(id) {
+ return request({
+ url: '/education/studentmain/' + id,
+ method: 'get'
+ })
+}
+//删除学生
+export function leaveClass(data) {
+ return request({
+ url: '/education/classuser/leaveClass',
+ method: 'post',
+ data: data
+ })
+}
+//删除学生所有数据
+export function removeStudentDataAll(id) {
+ return request({
+ url: '/education/studentmain/removeStudent/' + id,
+ method: 'post'
+ })
+}
+//删除教室
+export function delClassroom(id) {
+ return request({
+ url: '/education/classroom/' + id,
+ method: 'delete'
+ })
+}
+//导入学生
+export function addStudentmainByNameArray(data) {
+ return request({
+ url: '/education/studentmain/addByNameArray',
+ method: 'post',
+ data: data
+ })
+}
+//新增课程预约
+export function addSmartClassReserv(data) {
+ return request({
+ url: '/smarttalk/classReserv/addSmartClassReserv',
+ method: 'post',
+ data: data
+ })
+}
+//修改课程预约
+export function updateSmartClassReserv(data) {
+ return request({
+ url: '/smarttalk/classReserv/updateSmartClassReserv',
+ method: 'post',
+ data: data
+ })
+}
+//查询课程预约
+export function getSelfReserv() {
+ return request({
+ url: '/smarttalk/classReserv/getSelfReserv',
+ method: 'get'
+ })
+}
+export function deleteSmartReserv(id) {
+ return request({
+ url: '/smarttalk/classReserv/' + id,
+ method: 'delete'
+ })
+}
+export function startClass(id, ex3) {
+ const params = {id}
+ !!ex3 && (params.ex3 = ex3)
+ return request({
+ url: '/smarttalk/classReserv/startClass',
+ method: 'get',
+ params
+ })
+}
+export function endClass(id) {
+ return request({
+ url: '/smarttalk/classReserv/endClass',
+ method: 'get',
+ params: {id}
+ })
+}
+/**
+ * @description 获取课堂信息
+ * @param {*} id
+ * @returns
+ */
+export function getClassInfo(id) {
+ return request({
+ url: '/smarttalk/classReserv/selectById',
+ method: 'get',
+ params: {id}
+ })
+}
diff --git a/src/renderer/src/api/file/third.js b/src/renderer/src/api/file/third.js
new file mode 100644
index 0000000..5019721
--- /dev/null
+++ b/src/renderer/src/api/file/third.js
@@ -0,0 +1,64 @@
+//查询第三方课件的接口
+import request from '@/utils/request'
+//获取学科
+export const getSubjects = (params) => {
+ return request({
+ url: '/smarttalk/cnjy/getSubjects',
+ method: 'get',
+ params
+ })
+}
+//获取教材版本
+export const getTextbookVersion = (params) => {
+ return request({
+ url: '/smarttalk/cnjy/getVersions',
+ method: 'get',
+ params
+ })
+}
+//获得书籍
+export const getTextbook = (params) => {
+ return request({
+ url: '/smarttalk/cnjy/getBooks',
+ method: 'get',
+ params
+ })
+}
+//获取书籍章节
+export const getBook = (params) => {
+ return request({
+ url: '/smarttalk/cnjy/getChapters',
+ method: 'get',
+ params
+ })
+}
+//获取知识点信息
+export const getKnowledge = (params) => {
+ return request({
+ url: '/smarttalk/cnjy/getKnowledgePoints',
+ method: 'get',
+ params
+ })
+}
+//查询列表资源
+export const getBookList = (params) => {
+ return request({
+ url: '/smarttalk/cnjy/getDocuments',
+ method: 'post',
+ params
+ })
+}
+//获取图片路径
+export const getImgPath = (params) => {
+ return request({
+ url: '/smarttalk/cnjy/getPreview',
+ method: 'get',
+ params
+ })
+}
+
+
+
+
+
+
diff --git a/src/renderer/src/api/system/user.js b/src/renderer/src/api/system/user.js
index b7e9e23..f8b74f0 100644
--- a/src/renderer/src/api/system/user.js
+++ b/src/renderer/src/api/system/user.js
@@ -50,3 +50,9 @@ export function updateUserInfo(data) {
data: data
})
}
+export function getUserInfo(userId) {
+ return request({
+ url: '/system/user/' + userId,
+ method: 'get'
+ })
+}
diff --git a/src/renderer/src/api/teaching/classwork.js b/src/renderer/src/api/teaching/classwork.js
index 57da30d..3173201 100644
--- a/src/renderer/src/api/teaching/classwork.js
+++ b/src/renderer/src/api/teaching/classwork.js
@@ -42,4 +42,13 @@ export function delClasswork(id) {
url: '/education/classwork/' + id,
method: 'delete'
})
+}
+
+// 新增classwork
+export function addClassworkReturnId(data) {
+ return request({
+ url: '/education/classwork/saveAndReturnId',
+ method: 'post',
+ data: data
+ })
}
\ No newline at end of file
diff --git a/src/renderer/src/assets/iconfont/iconfont.css b/src/renderer/src/assets/iconfont/iconfont.css
index fd25490..c1fab69 100644
--- a/src/renderer/src/assets/iconfont/iconfont.css
+++ b/src/renderer/src/assets/iconfont/iconfont.css
@@ -1,9 +1,9 @@
@font-face {
font-family: "iconfont"; /* Project id 2794390 */
- src: url('iconfont.woff2?t=1723452423265') format('woff2'),
- url('iconfont.woff?t=1723452423265') format('woff'),
- url('iconfont.ttf?t=1723452423265') format('truetype'),
- url('iconfont.svg?t=1723452423265#iconfont') format('svg');
+ src: url('iconfont.woff2?t=1724212790213') format('woff2'),
+ url('iconfont.woff?t=1724212790213') format('woff'),
+ url('iconfont.ttf?t=1724212790213') format('truetype'),
+ url('iconfont.svg?t=1724212790213#iconfont') format('svg');
}
.iconfont {
@@ -14,6 +14,94 @@
-moz-osx-font-smoothing: grayscale;
}
+.icon-yiwen:before {
+ content: "\e687";
+}
+
+.icon-yiwen-01:before {
+ content: "\e688";
+}
+
+.icon-yihuo:before {
+ content: "\e689";
+}
+
+.icon-a-yiwen:before {
+ content: "\e6b1";
+}
+
+.icon-zan:before {
+ content: "\e658";
+}
+
+.icon-zan1:before {
+ content: "\e659";
+}
+
+.icon-zan2:before {
+ content: "\e65a";
+}
+
+.icon-zan3:before {
+ content: "\e65c";
+}
+
+.icon-zan4:before {
+ content: "\e67c";
+}
+
+.icon-yizan:before {
+ content: "\e67e";
+}
+
+.icon-zan5:before {
+ content: "\e67f";
+}
+
+.icon-zan-yizan:before {
+ content: "\e680";
+}
+
+.icon-zan6:before {
+ content: "\e681";
+}
+
+.icon-MBEfenggeduosetubiao-xihuan:before {
+ content: "\e682";
+}
+
+.icon-zan7:before {
+ content: "\e683";
+}
+
+.icon-zan11:before {
+ content: "\e6ff";
+}
+
+.icon-zan8:before {
+ content: "\e684";
+}
+
+.icon-dianzan-red:before {
+ content: "\e685";
+}
+
+.icon-zan9:before {
+ content: "\e69e";
+}
+
+.icon-zanping:before {
+ content: "\100ae";
+}
+
+.icon-zan10:before {
+ content: "\e686";
+}
+
+.icon-arrangement:before {
+ content: "\e656";
+}
+
.icon-zanwushuju:before {
content: "\e655";
}
diff --git a/src/renderer/src/assets/iconfont/iconfont.js b/src/renderer/src/assets/iconfont/iconfont.js
index ed5d6de..2ad9df4 100644
--- a/src/renderer/src/assets/iconfont/iconfont.js
+++ b/src/renderer/src/assets/iconfont/iconfont.js
@@ -1 +1 @@
-window._iconfont_svg_string_2794390=' ',function(l){var h=(h=document.getElementsByTagName("script"))[h.length-1],c=h.getAttribute("data-injectcss"),h=h.getAttribute("data-disable-injectsvg");if(!h){var a,v,t,z,i,p=function(h,c){c.parentNode.insertBefore(h,c)};if(c&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(h){console&&console.log(h)}}a=function(){var h,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_2794390,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(h=document.body).firstChild?p(c,h.firstChild):h.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),a()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(t=a,z=l.document,i=!1,o(),z.onreadystatechange=function(){"complete"==z.readyState&&(z.onreadystatechange=null,M())})}function M(){i||(i=!0,t())}function o(){try{z.documentElement.doScroll("left")}catch(h){return void setTimeout(o,50)}M()}}(window);
\ No newline at end of file
+window._iconfont_svg_string_2794390=' ',(c=>{var h=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var a,t,z,p,i,v=function(h,l){l.parentNode.insertBefore(h,l)};if(h&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(h){console&&console.log(h)}}a=function(){var h,l=document.createElement("div");l.innerHTML=c._iconfont_svg_string_2794390,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(h=document.body).firstChild?v(l,h.firstChild):h.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),a()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(z=a,p=c.document,i=!1,d(),p.onreadystatechange=function(){"complete"==p.readyState&&(p.onreadystatechange=null,M())})}function M(){i||(i=!0,z())}function d(){try{p.documentElement.doScroll("left")}catch(h){return void setTimeout(d,50)}M()}})(window);
\ No newline at end of file
diff --git a/src/renderer/src/assets/iconfont/iconfont.json b/src/renderer/src/assets/iconfont/iconfont.json
index fd3dafa..30d5f43 100644
--- a/src/renderer/src/assets/iconfont/iconfont.json
+++ b/src/renderer/src/assets/iconfont/iconfont.json
@@ -5,6 +5,160 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
+ {
+ "icon_id": "20574719",
+ "name": "疑问",
+ "font_class": "yiwen",
+ "unicode": "e687",
+ "unicode_decimal": 59015
+ },
+ {
+ "icon_id": "21052326",
+ "name": "yiwen-01",
+ "font_class": "yiwen-01",
+ "unicode": "e688",
+ "unicode_decimal": 59016
+ },
+ {
+ "icon_id": "30456317",
+ "name": "疑惑",
+ "font_class": "yihuo",
+ "unicode": "e689",
+ "unicode_decimal": 59017
+ },
+ {
+ "icon_id": "33439935",
+ "name": "[疑问]",
+ "font_class": "a-yiwen",
+ "unicode": "e6b1",
+ "unicode_decimal": 59057
+ },
+ {
+ "icon_id": "1242129",
+ "name": "赞",
+ "font_class": "zan",
+ "unicode": "e658",
+ "unicode_decimal": 58968
+ },
+ {
+ "icon_id": "1741390",
+ "name": "赞",
+ "font_class": "zan1",
+ "unicode": "e659",
+ "unicode_decimal": 58969
+ },
+ {
+ "icon_id": "3159200",
+ "name": "赞",
+ "font_class": "zan2",
+ "unicode": "e65a",
+ "unicode_decimal": 58970
+ },
+ {
+ "icon_id": "3402139",
+ "name": "赞",
+ "font_class": "zan3",
+ "unicode": "e65c",
+ "unicode_decimal": 58972
+ },
+ {
+ "icon_id": "4931286",
+ "name": "赞 (1)",
+ "font_class": "zan4",
+ "unicode": "e67c",
+ "unicode_decimal": 59004
+ },
+ {
+ "icon_id": "4942300",
+ "name": "已赞",
+ "font_class": "yizan",
+ "unicode": "e67e",
+ "unicode_decimal": 59006
+ },
+ {
+ "icon_id": "5806181",
+ "name": "赞",
+ "font_class": "zan5",
+ "unicode": "e67f",
+ "unicode_decimal": 59007
+ },
+ {
+ "icon_id": "7172310",
+ "name": "赞-已赞",
+ "font_class": "zan-yizan",
+ "unicode": "e680",
+ "unicode_decimal": 59008
+ },
+ {
+ "icon_id": "7293361",
+ "name": "赞2",
+ "font_class": "zan6",
+ "unicode": "e681",
+ "unicode_decimal": 59009
+ },
+ {
+ "icon_id": "8705087",
+ "name": "MBE风格多色图标-喜欢",
+ "font_class": "MBEfenggeduosetubiao-xihuan",
+ "unicode": "e682",
+ "unicode_decimal": 59010
+ },
+ {
+ "icon_id": "10024138",
+ "name": "赞",
+ "font_class": "zan7",
+ "unicode": "e683",
+ "unicode_decimal": 59011
+ },
+ {
+ "icon_id": "11055391",
+ "name": "赞",
+ "font_class": "zan11",
+ "unicode": "e6ff",
+ "unicode_decimal": 59135
+ },
+ {
+ "icon_id": "11086734",
+ "name": "赞",
+ "font_class": "zan8",
+ "unicode": "e684",
+ "unicode_decimal": 59012
+ },
+ {
+ "icon_id": "23592614",
+ "name": "点赞",
+ "font_class": "dianzan-red",
+ "unicode": "e685",
+ "unicode_decimal": 59013
+ },
+ {
+ "icon_id": "26327261",
+ "name": "赞",
+ "font_class": "zan9",
+ "unicode": "e69e",
+ "unicode_decimal": 59038
+ },
+ {
+ "icon_id": "27804883",
+ "name": "赞评",
+ "font_class": "zanping",
+ "unicode": "100ae",
+ "unicode_decimal": 65710
+ },
+ {
+ "icon_id": "29252894",
+ "name": "赞",
+ "font_class": "zan10",
+ "unicode": "e686",
+ "unicode_decimal": 59014
+ },
+ {
+ "icon_id": "4978988",
+ "name": "作业-布置作业",
+ "font_class": "arrangement",
+ "unicode": "e656",
+ "unicode_decimal": 58966
+ },
{
"icon_id": "9689424",
"name": "暂无数据",
diff --git a/src/renderer/src/assets/iconfont/iconfont.svg b/src/renderer/src/assets/iconfont/iconfont.svg
index 99a0abf..8201a34 100644
--- a/src/renderer/src/assets/iconfont/iconfont.svg
+++ b/src/renderer/src/assets/iconfont/iconfont.svg
@@ -14,6 +14,50 @@
/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/src/assets/iconfont/iconfont.ttf b/src/renderer/src/assets/iconfont/iconfont.ttf
index 477afd5..43f8dc3 100644
Binary files a/src/renderer/src/assets/iconfont/iconfont.ttf and b/src/renderer/src/assets/iconfont/iconfont.ttf differ
diff --git a/src/renderer/src/assets/iconfont/iconfont.woff b/src/renderer/src/assets/iconfont/iconfont.woff
index 397925d..9fc1445 100644
Binary files a/src/renderer/src/assets/iconfont/iconfont.woff and b/src/renderer/src/assets/iconfont/iconfont.woff differ
diff --git a/src/renderer/src/assets/iconfont/iconfont.woff2 b/src/renderer/src/assets/iconfont/iconfont.woff2
index 7f2c06e..e1759ea 100644
Binary files a/src/renderer/src/assets/iconfont/iconfont.woff2 and b/src/renderer/src/assets/iconfont/iconfont.woff2 differ
diff --git a/src/renderer/src/components/choose-textbook/index.vue b/src/renderer/src/components/choose-textbook/index.vue
index bdd14b9..38fd5e1 100644
--- a/src/renderer/src/components/choose-textbook/index.vue
+++ b/src/renderer/src/components/choose-textbook/index.vue
@@ -42,11 +42,11 @@
+
+
diff --git a/src/renderer/src/components/choose-textbook/third/index.vue b/src/renderer/src/components/choose-textbook/third/index.vue
new file mode 100644
index 0000000..2b7a6a2
--- /dev/null
+++ b/src/renderer/src/components/choose-textbook/third/index.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/src/components/choose-textbook/third/selectSubject.vue b/src/renderer/src/components/choose-textbook/third/selectSubject.vue
new file mode 100644
index 0000000..af4b57b
--- /dev/null
+++ b/src/renderer/src/components/choose-textbook/third/selectSubject.vue
@@ -0,0 +1,35 @@
+
+
+
+ {{ item.subjectName }}
+
+
+
+
+
+
+
diff --git a/src/renderer/src/components/classManage/select/index.vue b/src/renderer/src/components/classManage/select/index.vue
index b8959c3..6a11142 100644
--- a/src/renderer/src/components/classManage/select/index.vue
+++ b/src/renderer/src/components/classManage/select/index.vue
@@ -5,7 +5,7 @@
+
+
\ No newline at end of file
diff --git a/src/renderer/src/components/move-file/index.vue b/src/renderer/src/components/move-file/index.vue
index cb9d2b0..dfa7911 100644
--- a/src/renderer/src/components/move-file/index.vue
+++ b/src/renderer/src/components/move-file/index.vue
@@ -88,8 +88,6 @@ const curBookId = ref(-1)
const curBookName = ref('')
// 上册
const volumeOne = ref([])
-// 下册
-const volumeTwo = ref([])
// 当前节点
const currentNode = reactive({
data: {}
@@ -115,7 +113,8 @@ const getSubjectContent = async () => {
const params = {
edusubject,
edustage,
- entpcourseedituserid: userId,
+ // entpcourseedituserid: userId,
+ itemgroup: 'textbook',
pageSize: 500
}
let data;
@@ -127,9 +126,12 @@ const getSubjectContent = async () => {
//获取教材版本
getSubject()
//上册
- volumeOne.value = data.filter(item => item.level == 1 && item.semester == '上册')
- //下册
- volumeTwo.value = data.filter(item => item.level == 1 && item.semester == '下册')
+ /**
+ * 不区分上下册
+ * 2024/08/20调整
+ */
+ // volumeOne.value = data.filter(item => item.level == 1)
+
getTreeData()
}
@@ -140,8 +142,8 @@ const getSubject = async () => {
subjectList.value = JSON.parse(localStorage.getItem('subjectList'))
}
else {
- const { rows } = await listEvaluation({ itemkey: "version", pageSize: 500 })
- subjectList.value = rows.filter(item => item.edustage == edustage && item.edusubject == edusubject && isHaveUnit(item.id))
+ const { rows } = await listEvaluation({ itemkey: "version", edusubject, edustage, pageSize: 500 })
+ subjectList.value = rows
localStorage.setItem('subjectList', JSON.stringify(subjectList.value))
}
@@ -159,14 +161,9 @@ const isHaveUnit = (id) => {
const getTreeData = () => {
//数据过滤
- let upData = transData(volumeOne.value)
- let downData = transData(volumeTwo.value)
-
- if(upData.length && downData.length){
- treeData.value = [...upData,...downData]
- }
- else if(upData.length || downData.length){
- treeData.value = upData.length ? upData : downData
+ let upData = transData(evaluationList.value)
+ if(upData.length){
+ treeData.value = [...upData]
}
else{
treeData.value = []
@@ -252,26 +249,35 @@ const handleNodeClick = (data, node) => {
const transData = (data) => {
let ary = []
-
data.forEach(item => {
let obj = {}
-
+ // 根据当前教材ID 过滤出对应的单元、章节
if (item.rootid == curBookId.value) {
- obj.label = item.itemtitle
- obj.id = item.id
- let ary2 = []
- evaluationList.value.forEach(el => {
- let obj2 = {}
- if (item.id == el.parentid) {
- obj2 = {
- label: el.itemtitle,
- id: el.id
+ if(item.level == 1){
+ obj.label = item.itemtitle
+ obj.id = item.id
+ obj.itemtitle = item.itemtitle
+ obj.edudegree = item.edudegree
+ obj.edustage = item.edustage
+ obj.edusubject = item.edusubject
+ let ary2 = []
+ evaluationList.value.forEach(el => {
+ let obj2 = {}
+ if (item.id == el.parentid) {
+ obj2 = {
+ label: el.itemtitle,
+ id: el.id,
+ itemtitle : el.itemtitle,
+ edudegree : el.edudegree,
+ edustage : el.edustage,
+ edusubject : el.edusubject,
+ }
+ ary2.push(obj2)
}
- ary2.push(obj2)
- }
- obj.children = ary2
- })
- ary.push(obj)
+ obj.children = ary2
+ })
+ ary.push(obj)
+ }
}
})
return ary
diff --git a/src/renderer/src/components/pdf/index copy.vue b/src/renderer/src/components/pdf/index copy.vue
index 48d93e3..a1c3487 100644
--- a/src/renderer/src/components/pdf/index copy.vue
+++ b/src/renderer/src/components/pdf/index copy.vue
@@ -1,8 +1,12 @@
-
+pdfAdnFabric
-
@@ -13,12 +17,20 @@ import {
onMounted,
watch,
reactive,
+ defineProps,
+ defineExpose,
nextTick,
+ defineEmits,watchEffect
} from 'vue'
-import { fabric } from 'fabric'
+// import { fabric } from 'fabric'
import { ElMessage } from 'element-plus'
import { handleevent, savecanvsStore, initcanvasdata, displayData } from '@/utils/pdfAndFabric'
+import { fabricVue, TYPES } from '@/plugins/fabric'
+import { updateSmartBookMarkContent, addsmartBookMark,getBookMarkById } from '@/api/eTextbook/index'
+import {useToolState} from '@/store/modules/tool'
+const { ipcRenderer } = require('electron')
+const toolState = useToolState();
const props = defineProps({
pdfObj: {
type: Object,
@@ -29,13 +41,16 @@ const props = defineProps({
}
}
})
+const ispointer = ref(false) //是否画笔模式
// canvas的所有数据
const canvsStore = reactive({
- id: 'xxxx',
pageArr: []
})
const fabriccanvas = ref(null)
const fabriccanvas1 = ref(null)
+
+const canvasFabricVue = ref(null)
+const canvas1FabricVue = ref(null)
// 页面总数
const numPagesTotal = ref(0)
const imgarr = ref([])
@@ -47,10 +62,10 @@ const renderPage = async (canvasobj) => {
const pdf = await pdfjsLib.getDocument(props.pdfObj.pdfUrl).promise
// 渲染当前页
const page = await pdf.getPage(canvasobj.page)
- var screenWidth = window.innerWidth/2-100;
- var screenHeight = window.innerHeight;
- const viewport = page.getViewport({ scale:2})
-
+ var screenWidth = window.innerWidth / 2 - 100
+ var screenHeight = window.innerHeight
+ const viewport = page.getViewport({ scale: 2 })
+
const canvasElement = canvasobj.canvas
canvasElement.width = viewport.width
canvasElement.height = viewport.height
@@ -58,6 +73,11 @@ const renderPage = async (canvasobj) => {
canvasContext: canvasobj.context,
viewport: viewport
}
+ // console.log(renderContext,22222222222222222222)
+ // const textContent = await page.getTextContent();
+ // console.log(textContent);
+ // const annotations = await page.getAnnotations();
+ // console.log(annotations);
page.render(renderContext).promise.then((res) => {
const img = document.createElement('img')
img.src = canvasobj.canvas.toDataURL('image/png')
@@ -68,34 +88,66 @@ const renderPage = async (canvasobj) => {
// 根据传过来的pdf对象 判断改渲染哪一个fabric
if (props.pdfObj.numberOfPdf == 2) {
if (canvasobj.index == 0) {
-
- fabriccanvas.value.setWidth(screenWidth)
- fabriccanvas.value.setHeight(screenHeight)
- displayData(fabriccanvas, canvsStore, canvasobj, fabric, img)
+ canvasFabricVue.value.canvas.setWidth(screenWidth)
+ canvasFabricVue.value.canvas.setHeight(screenHeight)
+ // updateCanvasBackgroundImage(canvasFabricVue,img)
+ displayData(canvasFabricVue, canvsStore, canvasobj, fabric, img)
} else {
- fabriccanvas1.value.setWidth(screenWidth)
- fabriccanvas1.value.setHeight(screenHeight)
- displayData(fabriccanvas1, canvsStore, canvasobj, fabric, img)
+ canvas1FabricVue.value.canvas.setWidth(screenWidth)
+ canvas1FabricVue.value.canvas.setHeight(screenHeight)
+ displayData(canvas1FabricVue, canvsStore, canvasobj, fabric, img)
}
} else {
- fabriccanvas.value.setWidth(screenWidth)
- fabriccanvas.value.setHeight(screenHeight)
- displayData(fabriccanvas, canvsStore, canvasobj, fabric, img)
+ canvasFabricVue.value.canvas.setHeight(screenHeight)
+ displayData(canvasFabricVue, canvsStore, canvasobj, fabric, img)
}
- // console.log(imgarr.value)
img.remove()
}
- // 判断imgarr的JSONdata在canvsStore.pageArr有没有
- canvsStore.pageArr.forEach((item) => {
- if (item.page == canvasobj.page) {
- imgarr.value.forEach((img) => {
- if (img.page == canvasobj.page) {
- img.JSONdata = item.JSONdata
- }
- })
+ })
+}
+// 保存数据
+const savaDataStore = () => {
+ if(!toolState.isToolWin){
+ toolState.isPdfWin=false
+ toolState.showBoardAll=true //恢复默认值
+ // ipcRenderer.invoke('tool-sphere:reset') //重置tool状态
+ ipcRenderer.send('open-PDF:minimize')
+ return
+ }
+ imgarr.value.forEach((a) => {
+ if (a.index == 0) {
+ a.JSONdata = canvasFabricVue.value.canvas.toJSON()
+ } else {
+ a.JSONdata = canvas1FabricVue.value.canvas.toJSON()
+ }
+ })
+ const nameMap = new Map(canvsStore.pageArr.map((item) => [item.page, item.id]))
+ // 创建一个用于存储所有异步操作的数组
+ let promises = []
+ imgarr.value.forEach((item) => {
+ if (nameMap.has(item.page)) {
+ const params = {
+ id: nameMap.get(item.page),
+ contentData: JSON.stringify(item.JSONdata.objects)
+ }
+ promises.push(updateSmartBookMarkContent([params]))
+ } else {
+ promises.push(addsmartBookMark({
+ pageNum: item.page,
+ contentData: JSON.stringify(item.JSONdata.objects),
+ bookId: props.pdfObj.bookId,
+ type: '教材',
+ source: 'smarttalk'
+ }))
+
}
})
- })
+ Promise.all(promises).then(res=>{
+ toolState.isPdfWin=false
+ toolState.showBoardAll=true //恢复默认值
+ // ipcRenderer.invoke('tool-sphere:reset') //重置tool状态
+ ipcRenderer.send('open-PDF:minimize')
+ })
}
const updatePage = (canvasobj) => {
renderPage(canvasobj)
@@ -105,16 +157,55 @@ const loadPdf = async (canvasobj) => {
}
const initPdf = async (type = 'default') => {
+ imgarr.value.forEach((a) => {
+ if (a.index == 0) {
+ a.JSONdata = canvasFabricVue.value.canvas.toJSON()
+ } else {
+ a.JSONdata = canvas1FabricVue.value.canvas.toJSON()
+ }
+ })
+ // 判断是否翻页以及工具窗口是否打开 满足 翻页+打开工具才能保存数据
+ if (type != 'default' && toolState.isToolWin) {
+ const nameMap = new Map(canvsStore.pageArr.map((item) => [item.page, item.id]))
+ // 创建一个用于存储所有异步操作的数组
+ let promises = []
+ imgarr.value.forEach((item) => {
+ if (nameMap.has(item.page)) {
+ const params = {
+ id: nameMap.get(item.page),
+ contentData: JSON.stringify(item.JSONdata.objects)
+ }
+ promises.push(updateSmartBookMarkContent([params]))
+ } else {
+ promises.push(addsmartBookMark({
+ pageNum: item.page,
+ contentData: JSON.stringify(item.JSONdata.objects),
+ bookId: props.pdfObj.bookId,
+ type: '教材',
+ source: 'smarttalk'
+ }))
+
+ }
+ })
+ Promise.all(promises).then(res=>{
+ getBookMarkById(props.pdfObj.bookId).then(res=>{
+ const pageArr=getUniqueArrayByLastOccurrence(res.data)
+ canvsStore.pageArr=[]
+ pageArr.forEach((a) => {
+ canvsStore.pageArr.push({ page: a.pageNum, id: a.id, JSONdata: a.contentData })
+ })
+ })
+ })
+ }
// 保存数据
- savecanvsStore(imgarr, canvsStore)
- // initcanvasdata(fabriccanvas)
- // initcanvasdata(fabriccanvas1)
- // 单页模式
- if (type == 'restone') {
- // 清除 canvas 上的所有对象
- fabriccanvas1.value.clear()
- // 释放 canvas 的资源
- fabriccanvas1.value.dispose()
+ // savecanvsStore(imgarr, canvsStore)
+
+ if (props.pdfObj.numberOfPdf == 1) {
+ // imgarr.value[0]
+ canvasFabricVue.value.history.clean()
+ } else {
+ canvasFabricVue.value.history.clean()
+ canvas1FabricVue.value.history.clean()
}
// 清除现有 canvas 元素的内容
canvasNumbsValue.value.forEach((canvasObj) => {
@@ -146,7 +237,6 @@ const initPdf = async (type = 'default') => {
} else {
canvasNumbsValue.value[i].page = props.pdfObj.numPages + 1
}
-
canvasNumbsValue.value[i].index = i
// 加载FabricVue
await loadPdf(canvasNumbsValue.value[i])
@@ -154,47 +244,154 @@ const initPdf = async (type = 'default') => {
}
}
}
+//根据page去重
+const getUniqueArrayByLastOccurrence=(array)=> {
+ const uniqueItems = array.reduce((acc, current) => {
+ // 使用 Map 来跟踪最后一个出现的 pageNum 和它的对象
+ acc.set(current.pageNum, current);
+ return acc;
+ }, new Map());
+
+ // 将 Map 的值转换回数组
+ const resultArray = Array.from(uniqueItems.values());
+
+ return resultArray;
+}
const initPdfone = async () => {
- setTimeout(() => {
- fabriccanvas1.value = new fabric.Canvas('pdf-fabric1')
- fabriccanvas1.value.isDrawingMode = true
- fabriccanvas1.value.freeDrawingBrush.color = '#A33AFE'
- fabriccanvas1.value.freeDrawingCursor = 'default'
- fabriccanvas1.value.setWidth(860)
- handleevent(fabriccanvas1.value, imgarr, 'two')
- }, 0)
- initPdf('addOnePage')
+ setTimeout(async () => {
+ const option = { freeDrawingCursor: 'default' }
+ const canvas2 = new fabricVue()
+ await canvas2.initCanvas(fabriccanvas1.value, option)
+ canvas2.canvas.setWidth(window.innerWidth / 2 - 100)
+ canvas1FabricVue.value = canvas2
+ await initPdf('addOnePage')
+ }, 0)
}
onMounted(async () => {
try {
+ // 创建canvas转化成图片
const pdf = await pdfjsLib.getDocument(props.pdfObj.pdfUrl).promise
numPagesTotal.value = pdf.numPages
- // console.log(pdf)
// 初始化fabriccanvas
- fabriccanvas.value = new fabric.Canvas('pdf-fabric')
- fabriccanvas.value.setWidth(860)
- fabriccanvas.value.isDrawingMode = true
- fabriccanvas.value.freeDrawingBrush.color = '#A33AFE'
- fabriccanvas.value.freeDrawingCursor = 'default'
-
- fabriccanvas1.value = new fabric.Canvas('pdf-fabric1')
- fabriccanvas1.value.isDrawingMode = true
- fabriccanvas1.value.freeDrawingBrush.color = '#A33AFE'
- fabriccanvas1.value.freeDrawingCursor = 'default'
- fabriccanvas1.value.setWidth(860)
+ const option = { freeDrawingCursor: 'default' }
+ const canvas1 = new fabricVue()
+ // canvas1.boardConfig.mode= TYPES.ActionMode.OTHER
+ // canvas1.boardConfig.mode= TYPES.ActionMode.ERASE
+ await canvas1.initCanvas(fabriccanvas.value, option)
+ canvas1.canvas.setWidth(window.innerWidth / 2 - 100)
+ canvasFabricVue.value = canvas1
+ const canvas2 = new fabricVue()
+ await canvas2.initCanvas(fabriccanvas1.value, option)
+ canvas2.canvas.setWidth(window.innerWidth / 2 - 100)
+ // canvas2.canvas.isDrawingMode=false
+ canvas1FabricVue.value = canvas2
+ window.test = { canvas1, canvas2 }
emit('update:numPagesTotal', pdf.numPages)
- initPdf()
+
+ if (props.pdfObj.allPageData.length) {
+ props.pdfObj.allPageData.forEach((a) => {
+ if (a.pageNum == 1 || a.pageNum == 2) {
+ canvsStore.pageArr.push({ page: a.pageNum, id: a.id, JSONdata: a.contentData })
+ }
+ })
+ }
+ await initPdf()
} catch (error) {
console.log(error)
ElMessage.error('pdf文件错误')
}
+ setToolStatus()
// 监听2个canvas事件
- handleevent(fabriccanvas.value, imgarr)
- handleevent(fabriccanvas1.value, imgarr, 'two')
+ // handleevent(fabriccanvas.value, imgarr)
+ // handleevent(fabriccanvas1.value, imgarr, 'two')
})
+// zdg: 设置-底部工具栏-状态
+const setToolStatus = () => {
+ toolState.showBoardAll = false
+}
+// 判断元素是否加载完成
+const handleMode = (vale,type)=>{
+ if(vale=='select'){
+ ispointer.value=true
+ }else{
+ ispointer.value=false
+ }
+ switch(vale) {
+ case 'select': // 选择模式
+ canvasFabricVue.value?.handleMode(TYPES.ActionMode.OTHER)
+ canvas1FabricVue.value?.handleMode(TYPES.ActionMode.OTHER)
+ break
+ case 'brush': // 画笔模式
+ canvasFabricVue.value?.handleMode(TYPES.ActionMode.DRAW)
+ canvasFabricVue.value.canvas.freeDrawingCursor = 'default'
+ canvas1FabricVue.value?.handleMode(TYPES.ActionMode.DRAW)
+ canvas1FabricVue.value.canvas.freeDrawingCursor = 'default'
+ break
+ case 'erase': // 板擦模式
+ canvasFabricVue.value?.handleMode(TYPES.ActionMode.ERASE)
+ canvas1FabricVue.value?.handleMode(TYPES.ActionMode.ERASE)
+ break
+ case 'clear': // 清空画布
+ clearCanvas()
+ // canvas1FabricVue.value.history?.clean()
+ break
+ }
+}
+// 清空canvas
+const clearCanvas=()=>{
+ if(canvasFabricVue.value){
+ const objects = canvasFabricVue.value.canvas.getObjects();
+ objects.forEach((obj) => {
+ // 检查对象是否是背景
+ if (obj !== canvasFabricVue.value.canvas.backgroundImage) {
+ // 删除背景之外的对象
+ canvasFabricVue.value.canvas.remove(obj);
+ }
+ });
+
+ canvasFabricVue.value.canvas.renderAll();
+ }
+ if(canvas1FabricVue.value){
+ const objects = canvas1FabricVue.value.canvas.getObjects();
+ objects.forEach((obj) => {
+ // 检查对象是否是背景
+ if (obj !== canvas1FabricVue.value.canvas.backgroundImage) {
+ // 删除背景之外的对象
+ canvas1FabricVue.value.canvas.remove(obj);
+ }
+ });
+
+ canvas1FabricVue.value.canvas.renderAll();
+ }
+}
+const watchToolState=()=>{
+ if(toolState.showBoardAll){
+ setTimeout(() => {
+ toolState.showBoardAll=false
+ }, 200);
+ }
+ // 加载工具
+ handleMode(toolState.model)
+
+}
defineExpose({
initPdf,
- initPdfone
+ initPdfone,
+ savaDataStore
+})
+watchEffect(() => {
+ setTimeout(() => {
+ console.log(toolState,'监听')
+
+ }, 300)
+ if(toolState.isPdfWin){
+ // if(toolState.isToolWin){
+ // ispointer.value=false
+ // }else{
+ // ispointer.value=true
+ // }
+ watchToolState() //监听工具栏
+ }
})
@@ -204,6 +401,8 @@ defineExpose({
flex-wrap: wrap;
width: 100%;
justify-content: center;
+ overflow: hidden;
+ max-height: 100vh;
}
.pdfAdnFabric {
position: relative;
@@ -214,4 +413,7 @@ defineExpose({
margin-right: 10px;
}
}
+.ispointer {
+ pointer-events: none;
+}
\ No newline at end of file
diff --git a/src/renderer/src/components/pdf/index.vue b/src/renderer/src/components/pdf/index.vue
index c686d14..2ac6582 100644
--- a/src/renderer/src/components/pdf/index.vue
+++ b/src/renderer/src/components/pdf/index.vue
@@ -1,15 +1,9 @@
pdfAdnFabric
-
-
@@ -79,8 +73,6 @@ const renderPage = async (canvasobj) => {
canvasContext: canvasobj.context,
viewport: viewport
}
- // console.log(renderContext,22222222222222222222)
-
page.render(renderContext).promise.then((res) => {
const img = document.createElement('img')
img.src = canvasobj.canvas.toDataURL('image/png')
@@ -93,7 +85,6 @@ const renderPage = async (canvasobj) => {
if (canvasobj.index == 0) {
canvasFabricVue.value.canvas.setWidth(screenWidth)
canvasFabricVue.value.canvas.setHeight(screenHeight)
- // updateCanvasBackgroundImage(canvasFabricVue,img)
displayData(canvasFabricVue, canvsStore, canvasobj, fabric, img)
} else {
canvas1FabricVue.value.canvas.setWidth(screenWidth)
@@ -106,25 +97,22 @@ const renderPage = async (canvasobj) => {
}
img.remove()
}
- // 判断imgarr的JSONdata在canvsStore.pageArr有没有
- // canvsStore.pageArr.forEach((item) => {
- // if (item.page == canvasobj.page) {
- // imgarr.value.forEach((img) => {
- // if (img.page == canvasobj.page) {
- // img.JSONdata = item.JSONdata
- // }
- // })
- // }
- // })
})
}
// 保存数据
-const savaDataStore = () => {
+const savaDataStore = async (type) => {
if(!toolState.isToolWin){
- toolState.isPdfWin=false
- toolState.showBoardAll=true //恢复默认值
- ipcRenderer.invoke('tool-sphere:reset') //重置tool状态
- ipcRenderer.send('open-PDF:minimize')
+ toolState.isPdfWin = false
+ await sleep(20) // 延时
+ toolState.showBoardAll = true //恢复默认值
+ await sleep(50) // 延时
+ if(type=='rest'){
+ // ipcRenderer.invoke('tool-sphere:reset') //重置tool状态-废弃
+ ipcRenderer.send('open-PDF:close')
+ }else{
+ ipcRenderer.invoke('open-PDF:minimize')
+ }
+
return
}
imgarr.value.forEach((a) => {
@@ -155,13 +143,24 @@ const savaDataStore = () => {
}
})
- Promise.all(promises).then(res=>{
+ Promise.all(promises).then(async res=>{
toolState.isPdfWin=false
+ await sleep(20) // 延时
toolState.showBoardAll=true //恢复默认值
- ipcRenderer.invoke('tool-sphere:reset') //重置tool状态
- ipcRenderer.send('open-PDF:minimize')
+ await sleep(50) // 延时
+ // ipcRenderer.send('open-PDF:minimize')
+ if(type=='rest'){
+ // ipcRenderer.invoke('tool-sphere:reset') //重置tool状态-废弃
+ ipcRenderer.send('open-PDF:close')
+ }else{
+ ipcRenderer.invoke('open-PDF:minimize')
+ }
+ // ipcRenderer.send('open-PDF:close')
+
})
}
+// 延时
+const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const updatePage = (canvasobj) => {
renderPage(canvasobj)
}
@@ -211,10 +210,7 @@ const initPdf = async (type = 'default') => {
})
}
// 保存数据
- // savecanvsStore(imgarr, canvsStore)
-
if (props.pdfObj.numberOfPdf == 1) {
- // imgarr.value[0]
canvasFabricVue.value.history.clean()
} else {
canvasFabricVue.value.history.clean()
@@ -393,8 +389,9 @@ defineExpose({
savaDataStore
})
watchEffect(() => {
- console.log(toolState.model,'监听')
- watchToolState() //监听工具栏
+ if(toolState.isPdfWin){
+ watchToolState() //监听工具栏
+ }
})
diff --git a/src/renderer/src/components/select-subject/index.vue b/src/renderer/src/components/select-subject/index.vue
index 52f306a..4c7cd97 100644
--- a/src/renderer/src/components/select-subject/index.vue
+++ b/src/renderer/src/components/select-subject/index.vue
@@ -1,5 +1,5 @@
-
import { ref, watch } from 'vue'
+import { ElMessage } from 'element-plus'
import { listEvaluation } from '@/api/subject'
import { updateUserInfo } from '@/api/system/user'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore()
const { userId, userName } = userStore.user
+const { ipcRenderer } = window.electron || {}
const props = defineProps({
loginData: {
@@ -48,17 +50,11 @@ const props = defineProps({
return {}
}
},
- modelValue: {
- type: Boolean,
- default: false
- },
})
+const model = defineModel();
const subjectLoading = ref(false)
-
-// 定义要发送的emit事件
-const emit = defineEmits(['update:modelValue', 'onSuccess'])
-
+// 默认学段 前端写死 参照web AIx
const gradeList = ref([
{
label: '高中',
@@ -85,7 +81,6 @@ gradeVal.value = gradeList.value[0].value
//学科列表数据
const subjectList = ref([])
const allSubject = ref([])
-const dialogVisible = ref(false)
//切换年级
const changeGrade = ()=>{
@@ -98,8 +93,8 @@ const changeGrade = ()=>{
// 默认选中第一个学科
subjectVal.value = subjectList.value[0].itemtitle
}
-// 获取学科数据
+// 获取学科数据
const getSubject = async ()=>{
const { rows } = await listEvaluation({ itemkey: "subject", pageSize: 500 })
// 所有学科
@@ -119,7 +114,6 @@ const editUserInfo = async () =>{
edustage: gradeVal.value,
edusubject: subjectVal.value
}
-
// 修改之后需要重新登录 查询用户信息,否则不登录 查询的用户信息是未修改之前的
// 接口如此,我也不知道为啥要这样
subjectLoading.value = true
@@ -128,14 +122,15 @@ const editUserInfo = async () =>{
await updateUserInfo(data)
await userStore.login(props.loginData)
await userStore.getInfo()
+ ElMessage.success('登录成功')
+ model.value = false
+ ipcRenderer && ipcRenderer.send('openMainWindow')
} finally {
subjectLoading.value = false
}
- emit('onSuccess')
}
-watch(() => props.modelValue, (newVal) => {
- dialogVisible.value = newVal
+watch(() => model.value, (newVal) => {
if(newVal){
getSubject()
}
diff --git a/src/renderer/src/components/set-homework/index.vue b/src/renderer/src/components/set-homework/index.vue
new file mode 100644
index 0000000..7518cf2
--- /dev/null
+++ b/src/renderer/src/components/set-homework/index.vue
@@ -0,0 +1,323 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ tag.name }}
+
+
+
+
+
+ 必做
+ 选做
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/src/hooks/useGetHomework.js b/src/renderer/src/hooks/useGetHomework.js
index c881f94..ebdabf3 100644
--- a/src/renderer/src/hooks/useGetHomework.js
+++ b/src/renderer/src/hooks/useGetHomework.js
@@ -11,6 +11,7 @@ let chapterId = null
export const useGetHomework = async (node) => {
/**
* node
+ * 左侧选择章节的节点数据
*/
if (!node) return
curNode = node
@@ -26,7 +27,10 @@ export const useGetHomework = async (node) => {
chapterId = rows[0].id
}
- return await getHomeWorkList()
+ return new Promise(async (resolve) =>{
+ const res = await getHomeWorkList()
+ resolve({res,chapterId})
+ })
}
diff --git a/src/renderer/src/hooks/useProcessList.js b/src/renderer/src/hooks/useProcessList.js
new file mode 100644
index 0000000..d53f73e
--- /dev/null
+++ b/src/renderer/src/hooks/useProcessList.js
@@ -0,0 +1,514 @@
+export const isJson = (str) => {
+ if (typeof str == 'string') {
+ try {
+ let obj = JSON.parse(str)
+ if (typeof obj == 'object' && obj) {
+ return true
+ } else {
+ return false
+ }
+ } catch (e) {
+ return false
+ }
+ }
+}
+
+/**
+ * @description processList 格式化试题
+ * @param {*} row
+ */
+export const processList = (row) => {
+ for (var i = 0; i < row.length; i++) {
+ if (isJson(row[i].workanalysis)) {
+ //1、先默认格式化 格式化各项内容(待优化, 后续界面显示的为format的值)
+ row[i].titleFormat = row[i].title // 题目
+ row[i].examdateFormat = row[i].examdate // ?考试日期 eg: 2024-07-11 14:39:27"
+ row[i].workdescFormat = row[i].workdesc // 题目选项
+ row[i].workanswerFormat = row[i].workanswer // 题目正确答案
+ if (row[i].workanswerFormat == null || row[i].workanswerFormat == '') {
+ row[i].workanswerFormat = '见试题解答内容'
+ }
+
+ // workanalysis 解析内容(analyse:解答; method:分析; discuss:点评; )
+ var jjj = JSON.parse(row[i].workanalysis)
+ row[i].analyse = jjj.analyse
+ row[i].method = jjj.method
+ row[i].discuss = jjj.discuss
+ //row[i].discusscollapse = false;
+ if (row[i].examdate !== null && row[i].examdate !== undefined) {
+ row[i].examdate = row[i].examdate.substring(0, 10)
+ }
+
+ // 具体题型数据结构处理
+ if (row[i].worktype == '复合题') {
+ // 旧类型
+ if (row[i].title.indexOf('!@#$%') !== -1) {
+ // 1.选项解析替换
+ const options = JSON.parse(row[i].workdesc)
+ // 题目(背景材料+复合题目)
+ const bjTitle = row[i].title.split('!@#$%')[0]
+ const tmTitles = row[i].title.split('!@#$%').filter((it, ix) => ix > 0)
+ // console.log(bjTitle,'背景标题');
+ // console.log(tmTitles,'复合题目');
+ let titls = []
+ options.forEach((element, index1) => {
+ const workDescArr = element.split('#&')
+ let tmp = ''
+ let j = 0
+ for (; j < workDescArr.length; j++) {
+ if (j % 2 == 0) {
+ tmp += ``
+ }
+ const char = String.fromCharCode(65 + j)
+ tmp += `
${char}.${workDescArr[j]}
`
+ if (j % 2 == 1) {
+ tmp += '
'
+ }
+ }
+ // j此刻已自增1, 故当选项为单数时, 需要补充结束标签
+ if (j % 2 == 1) {
+ tmp += ' '
+ }
+
+ // workDescArr为 [''] 表示为 判断题或者填空题,这里不需要选项
+ if (workDescArr[0] != '') {
+ titls.splice(index1, 1, tmp)
+ } else {
+ titls.splice(index1, 1, '')
+ }
+ })
+ const s = []
+ tmTitles.map((it, ix) => {
+ s.push(it)
+ titls.map((it2, ix2) => {
+ if (ix == ix2) {
+ s.push(it2)
+ }
+ })
+ })
+ // console.log(s,'?????????????????')
+
+ row[i].titleFormat = bjTitle + s.join('')
+ row[i].workdescFormat = ''
+
+ //2.答案 - 数字转为ABCD
+ const answerArr = JSON.parse(row[i].workanswer)
+ let indexLabel = 1
+ let arr = []
+ answerArr.forEach((item) => {
+ const arrTmp = item.answer.split('#&')
+ let value = `(${indexLabel})`
+ arrTmp.forEach((element, i) => {
+ if (item.type == '单选题' || item.type == '多选题') {
+ value += `${String.fromCharCode(65 + Number(element))}`
+ }
+ if (item.type == '判断题' || item.type == '填空题') {
+ // 去除下 html标签
+ value += `${element.replace(/<[^>]+>/g, '')}` + (i == arrTmp.length - 1 ? '' : '、')
+ }
+ if (item.type == '主观题') {
+ if (element) {
+ console.log(element, 'element')
+ value += item.answer
+ } else {
+ value += '答案不唯一,请参考分析解答点评!'
+ }
+ }
+ })
+ arr.push(value)
+ indexLabel++
+ })
+ const answer = arr.join('
')
+
+ row[i].workanswerFormat = answer
+ } else {
+ // 处理[题干显示] - 不再需要处理
+ // row[i].titleFormat = row[i].title; // 仅占位提示
+
+ /**
+ * 处理[选项显示] - 特殊结构
+ * [
+ * {type: '单选题', title: '题目1', options: ['ABC123','ABC123']},
+ * {type: '多选题', title: '题目1', options: ['ABC123','ABC123']},
+ * {type: '填空题', title: '题目1', options: []},
+ * {type: '判断题', title: '题目1', options: []},
+ * {type: '主观题', title: '题目1', options: []},
+ * ]
+ */
+ let workDescArr = JSON.parse(row[i].workdesc)
+ let workDescHtml = `
${index + 1}. ${item.title}
`
+ let tmp = ''
+ let j = 0
+ let optionsArr = item.options
+ for (; j < optionsArr.length; j++) {
+ if (j % 2 == 0) {
+ tmp += `
`
+ }
+ const char = String.fromCharCode(65 + j)
+ tmp += `
${char}.${optionsArr[j]}
`
+ if (j % 2 == 1) {
+ tmp += '
'
+ }
+ }
+ // j此刻已自增1, 故当选项为单数时, 需要补充结束标签
+ if (j % 2 == 1) {
+ tmp += '
'
+ }
+
+ workDescHtml += tmp
+ } else if (item.type == '填空题' || item.type == '判断题' || item.type == '主观题') {
+ workDescHtml += `${index + 1}. ${item.title}
`
+ }
+ })
+ workDescHtml += ' '
+ row[i].workdescFormat = workDescHtml
+
+ /**
+ * 处理[答案显示] - 特殊结构
+ * [
+ * {type: '单选题', answer: ['0']},
+ * {type: '多选题', answer: ['0','1']},
+ * {type: '填空题', answer: ['填空1','填空2']},
+ * {type: '判断题', answer: ['0'/'1']},
+ * {type: '主观题', answer: [xxxx]},
+ * ]
+ */
+ let workAnswerArr = JSON.parse(row[i].workanswer)
+ let workAnswerHtml = ``
+ workAnswerArr.map((item, index) => {
+ const answerArr = item.answer //JSON.parse(item.answer);
+ if (item.type == '单选题' || item.type == '多选题') {
+ const answer = answerArr
+ .map((item) => {
+ return String.fromCharCode(65 + Number(item))
+ })
+ .join('')
+ workAnswerHtml += `${index + 1}. ${answer}
`
+ } else if (item.type == '填空题') {
+ const answer = answerArr.join('、')
+ workAnswerHtml += `${index + 1}. ${answer}
`
+ } else if (item.type == '判断题') {
+ const answer = answerArr
+ .map((item) => {
+ return item === '1' ? '正确' : '错误'
+ })
+ .join('、')
+ workAnswerHtml += `${index + 1}. ${answer}
`
+ } else if (item.type == '主观题') {
+ // 复合题里面的主观题只有一个答案,或没填
+ const answer = answerArr.join('、')
+ if (answerArr[0]) {
+ workAnswerHtml += `${index + 1}. ${answer}
`
+ } else {
+ workAnswerHtml += `${index + 1}. ${answer}答案不唯一,请参考分析解答点评!
`
+ }
+ }
+ })
+ row[i].workanswerFormat = workAnswerHtml
+ }
+ } else if (
+ row[i].worktype == '主观题' ||
+ (row[i].worktype !== '单选题' &&
+ row[i].worktype !== '多选题' &&
+ row[i].worktype !== '填空题' &&
+ row[i].worktype !== '判断题')
+ ) {
+ // 处理[选项显示] - 主观题中无选项, 故置空
+ row[i].workdescFormat = ''
+ row[i].workanswerFormat = ''
+ // 答案处理- eg: "\"不唯一的答案,参考\""
+ if (row[i].workanswer && row[i].workanswer != '') {
+ row[i].workanswerFormat = JSON.parse(row[i].workanswer)
+ }
+ } else {
+ // 单选题|多选题|填空题|判断题|主观题?(待确认是否归在这里)
+ // 通用选项结构 ['ABC123','ABC123'] | ['ABC123','ABC123'] | [](填空题无选项) | [](判断题无选项)
+ let workDescArr = []
+ if (
+ row[i].workdesc.charAt(0) === '[' &&
+ row[i].workdesc.charAt(row[i].workdesc.length - 1) === ']'
+ ) {
+ //123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
+ workDescArr = JSON.parse(row[i].workdesc)
+ } else if (row[i].workdesc.indexOf('#&') !== -1) {
+ workDescArr = row[i].workdesc.split('#&')
+ } else if (row[i].workdesc.indexOf(',') !== -1) {
+ workDescArr = row[i].workdesc.split(',')
+ } else {
+ // 单字符串直接添加至空数组(待考虑确认)
+ workDescArr.push(row[i].workdesc)
+ }
+
+ // 单选题|多选题|填空题|判断题|主观题?(待确认是否归在这里)
+ // 通用答案结构 ['0'] | ['0','1'] | ['填空1','填空2'] | ['0'/'1']
+ let workAnswerArr = []
+ if (
+ row[i].workanswer.charAt(0) === '[' &&
+ row[i].workanswer.charAt(row[i].workanswer.length - 1) === ']'
+ ) {
+ // 123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
+ workAnswerArr = JSON.parse(row[i].workanswer)
+ } else if (row[i].workanswer.indexOf('#&') !== -1) {
+ workAnswerArr = row[i].workanswer.split('#&')
+ } else if (row[i].workanswer.indexOf(',') !== -1) {
+ workAnswerArr = row[i].workanswer.split(',')
+ } else {
+ // 单字符串直接添加至空数组(待考虑确认)
+ workAnswerArr.push(row[i].workanswer)
+ }
+
+ // 具体题型处理
+ if (row[i].worktype == '单选题' || row[i].worktype == '多选题') {
+ // 处理[选项显示] - 拼接ABCD首序号
+ let tmp = ''
+ let j = 0
+ for (; j < workDescArr.length; j++) {
+ if (j % 2 == 0) {
+ tmp += ``
+ }
+ const char = String.fromCharCode(65 + j)
+ tmp += `
${char}.${workDescArr[j]}
`
+ if (j % 2 == 1) {
+ tmp += '
'
+ }
+ }
+ if (j % 2 == 0) {
+ tmp += ''
+ }
+ row[i].workdescFormat = tmp
+
+ // 处理[答案显示] - 转换ABCD
+ let arr2Char = workAnswerArr
+ .map((item) => {
+ return String.fromCharCode(65 + Number(item))
+ })
+ .join('')
+ row[i].workanswerFormat = arr2Char
+ } else if (row[i].worktype == '填空题') {
+ // 处理[选项显示] - 填空题中无选项, 故置空
+ row[i].workdescFormat = ''
+
+ // 处理[答案显示] - 逗号连接
+ row[i].workanswerFormat = workAnswerArr.join('、')
+ } else if (row[i].worktype == '判断题') {
+ // 处理[选项显示] - 判断题中无选项, 故置空
+ row[i].workdescFormat = ''
+
+ // 处理[答案显示] - 1-正常 0-错误
+ const answer = workAnswerArr
+ .map((item) => {
+ return item === '1' ? '正确' : '错误'
+ })
+ .join('、')
+ row[i].workanswerFormat = answer
+ }
+ }
+
+ /*
+ //2、处理单选题
+ if(row[i].worktype == '单选题' || row[i].worktype == '多选题' ){
+ //1.选项前增加ABCD workdesc: "①②#&①③#&②④#&③④" || "为了活着
#&为了填报肚子
#&为了吃饭而吃饭
"
+ let workDescArr = [];
+ if(row[i].workdesc.indexOf('[')!==-1 && row[i].workdesc.indexOf(']')!==-1) {
+ //123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
+ workDescArr = JSON.parse(row[i].workdesc);
+ }
+ else if(row[i].workdesc.indexOf('#&')) {
+ workDescArr = row[i].workdesc.split('#&');
+ }
+ else if(row[i].workdesc.indexOf(',')){
+ workDescArr = row[i].workdesc.split(',');
+ }
+ else {
+ // 待考虑
+ workDescArr.push(item.workdesc)
+ }
+
+
+
+ //2.答案 - 数字转为ABCD
+ if(row[i].worktype == '单选题') {
+ const str2Char = String.fromCharCode(65+Number(row[i].workanswer));
+ row[i].workanswerFormat = str2Char;
+ } else if (row[i].worktype == '多选题') {
+ const answerArr = row[i].workanswer.split('#&');
+ let arr2Char = '';
+ for(let k=0; k]*>/g, "").split('#&'),'????')
+ // 填空题答案
+ row[i].workanswerFormat = row[i].workanswer.replace(/#&/g,", ");
+ // 填空选项不需要展示,
+ row[i].workdescFormat = '';
+ }
+ else if(row[i].worktype == '判断题'){
+ // console.log(row[i].workanswer.replace(/<[^>]*>/g, "").split('#&'),'????')
+ // 判断题答案
+ row[i].workanswerFormat = row[i].workanswer.replace(/#&/g,", ");
+ // 判断选项不需要展示,
+ row[i].workdescFormat = '';
+ }
+ else if(row[i].worktype == '复合题') {
+ // 1.选项解析替换
+ const options = JSON.parse(row[i].workdesc);
+ // 题目(背景材料+复合题目)
+ const bjTitle = row[i].title.split('!@#$%')[0];
+ const tmTitles = row[i].title.split('!@#$%').filter((it,ix)=>ix>0);
+ // console.log(bjTitle,'背景标题');
+ // console.log(tmTitles,'复合题目');
+ let titls = [];
+ options.forEach((element,index1) => {
+ const workDescArr = element.split('#&');
+ let tmp = '';
+ let j=0;
+ for(; j`;
+ }
+ const char = String.fromCharCode(65+j);
+ tmp += `${char}.${jsonArr[j]}
`;
+ if(j%2 == 1){
+ tmp += '';
+ }
+ }
+
+ if(j%2== 0){
+ tmp += '';
+ }
+ workdesc = tmp;
+ }
+
+ row[i].workdescFormat = workdesc; // 题目选项
+
+
+ // 答案处理
+ let workanswer = '';
+ if(row[i].workanswer && row[i].workanswer != '') {
+ // 因答案内容存在多种格式: 1.["123","1234"] 2.123#&1234 3.123
+ if(row[i].workanswer.indexOf('[')!==-1 && row[i].workanswer.indexOf(']')!==-1) {
+ //123会直接被转换, 且不是数组对象, 故手动判断是否有[和]两个字符
+ let json = JSON.parse(row[i].workanswer);
+ // 单选、多选 需要 数字转为ABCD
+ if(row[i].worktype == '单选题') {
+ const str2Char = String.fromCharCode(65+Number(json[0]));
+ workanswer = str2Char;
+ } else if (row[i].worktype == '多选题') {
+ // const answerArr = row[i].workanswer.split('#&');
+ let arr2Char = '';
+ for(let k=0; k ';
+ }
+ workanswer = arr2Char;
+ row[i].titleFormat = row[i].titleFormat.replace(/!@#\$%/g, '');
+ } else {
+ workanswer = json.join('、');
+ }
+ } else if(row[i].workanswer.indexOf('#&')) {
+ // 意味着多个答案或者填空内容
+ let workanswerList = row[i].workanswer.split('#&');
+ if(row[i].worktype == '多选题') {
+ // 数字转为ABCD
+ let arr2Char = '';
+ for(let k=0; k {
+ const arrTmp = item.answer.split('#&');
+ let value = `(${indexLabel})`;
+ arrTmp.forEach((element,i) => {
+ if(item.type == '单选题' || item.type == '多选题'){
+ value += `${String.fromCharCode(65+Number(element))}`;
+ }
+ if(item.type == '判断题' || item.type == '填空题'){
+ // 去除下 html标签
+ value += `${element.replace(/<[^>]+>/g, '')}`+ (i==arrTmp.length-1?'':'、');
+ }
+ })
+ arr.push(value);
+ indexLabel++;
+ })
+ const answer = arr.join(' ');
+
+ row[i].workanswerFormat = answer;
+ }
+ else if(row[i].worktype == '主观题') {
+ // 1.选项解析替换---主观题没选项
+ // 题目(背景材料+主观题目)
+ const bjTitle = row[i].title.split('!@#$%')[0];
+ const tmTitles = row[i].title.split('!@#$%').filter((it,ix)=>ix>0);
+ // console.log(bjTitle,'背景标题');
+ // console.log(tmTitles,'主观题目');
+ let titls = [];
+ const s = [];
+ tmTitles.map((it,ix)=>{
+ s.push(it);
+ })
+ // console.log(s,'?????????????????')
+
+ row[i].titleFormat = bjTitle + s.join('');
+ // 填空选项不需要展示,
+ row[i].workdescFormat = '';
+
+ //2.答案
+ // 填空题答案
+ const workanswerList = JSON.parse(row[i].workanswer);
+ let tmp='';
+ workanswerList&&workanswerList.map((item,index)=>{
+ tmp += ''+(index+1)+'.'+item.replace(/#&/g, ',')+'
';
+ })
+ row[i].workanswerFormat = tmp;
+
+ }
+ else {
+ //处理答案
+ row[i].workanswerFormat = '见试题解答内容';
+ }
+ */
+ }
+ }
+}
diff --git a/src/renderer/src/layout/components/Header.vue b/src/renderer/src/layout/components/Header.vue
index 799bf33..e151832 100644
--- a/src/renderer/src/layout/components/Header.vue
+++ b/src/renderer/src/layout/components/Header.vue
@@ -10,7 +10,7 @@
class="flex"
:style="{'color' : item.color}"
:class="currentRoute === item.url ? 'active-li' : ''"
- @click="handleOutLink(item.url,item.type)"
+ @click="handleOutLink(item.url,item.type, item.name)"
>
{{ item.name }}
@@ -29,7 +29,7 @@
@command="handleCommand"
>
-
+
{{ userStore.user.nickName }}
@@ -37,6 +37,7 @@
个人中心
课程预约
班级中心
+ 作业批改
退出登录
@@ -57,13 +58,15 @@ import WindowTools from '@/components/window-tools/index.vue'
import useUserStore from '@/store/modules/user'
import routerStore from '@/store/modules/route'
import outLink from '@/utils/linkConfig'
+
const routeHeader = routerStore()
const { ipcRenderer } = window.electron || {}
const userStore = useUserStore()
const router = useRouter()
const currentRoute = ref('')
+const dev_api = ref(import.meta.env.VITE_APP_BASE_API)
-const handleOutLink = (path, type) => {
+const handleOutLink = (path, type, name) => {
if (!path) return
if (type === 'hash') {
router.push(path)
@@ -72,6 +75,11 @@ const handleOutLink = (path, type) => {
let configObj = outLink().getBaseData()
let fullPath = configObj.fullPath + path
fullPath = fullPath.replaceAll('//', '/')
+ const { levelFirstId, levelSecondId } = JSON.parse(localStorage.getItem('unitId'))
+ let unitId = levelSecondId ? levelSecondId :levelFirstId
+ if(name == '教材分析' || name == '高考研究'){
+ fullPath += `?unitId=${unitId}`
+ }
// 通知主进程
ipcRenderer.send('openWindow', {
key: path,
diff --git a/src/renderer/src/main.js b/src/renderer/src/main.js
index f3a9a67..da34555 100644
--- a/src/renderer/src/main.js
+++ b/src/renderer/src/main.js
@@ -12,10 +12,14 @@ import 'virtual:windi.css'
import { store } from '@/store'
import App from './App.vue'
import router from './router'
+import log from 'electron-log/renderer' // 渲染进程日志-文件记录
+
+if(process.env.NODE_ENV != 'development') { // 非开发环境,将日志打印到日志文件
+ Object.assign(console, log.functions) // 渲染进程日志-控制台替换
+}
const app = createApp(App)
-
app.use(router)
.use(store)
.use(ElementPlus, { locale: zhLocale }).mount('#app')
\ No newline at end of file
diff --git a/src/renderer/src/plugins/imChat/enumbers.js b/src/renderer/src/plugins/imChat/enumbers.js
new file mode 100644
index 0000000..05d12c3
--- /dev/null
+++ b/src/renderer/src/plugins/imChat/enumbers.js
@@ -0,0 +1,1267 @@
+/**
+ * @brief 错误码
+ * 详细 [错误码](https://cloud.tencent.com/document/product/269/1671),请您移步官网查看
+ */
+const TIMErrCode= {
+ ERR_SUCC : 0,
+ ERR_IN_PROGESS : 6015,
+ ERR_INVALID_PARAMETERS : 6017,
+ ERR_IO_OPERATION_FAILED : 6022,
+ ERR_INVALID_JSON : 6027,
+ ERR_OUT_OF_MEMORY : 6028,
+ ERR_PARSE_RESPONSE_FAILED : 6001,
+ ERR_SERIALIZE_REQ_FAILED : 6002,
+ ERR_SDK_NOT_INITIALIZED : 6013,
+ ERR_LOADMSG_FAILED : 6005,
+ ERR_DATABASE_OPERATE_FAILED : 6019,
+ ERR_SDK_COMM_CROSS_THREAD : 7001,
+ ERR_SDK_COMM_TINYID_EMPTY : 7002,
+ ERR_SDK_COMM_INVALID_IDENTIFIER : 7003,
+ ERR_SDK_COMM_FILE_NOT_FOUND : 7004,
+ ERR_SDK_COMM_FILE_TOO_LARGE : 7005,
+ ERR_SDK_COMM_FILE_SIZE_EMPTY : 7006,
+ ERR_SDK_COMM_FILE_OPEN_FAILED : 7007,
+ ERR_SDK_COMM_API_CALL_FREQUENCY_LIMIT : 7008,
+ ERR_SDK_COMM_INTERRUPT : 7009,
+ ERR_SDK_COMM_DATABASE_FAILURE : 7010,
+ ERR_SDK_COMM_DATABASE_NOTFOUND : 7011,
+ ERR_SDK_INTERNAL_ERROR : 7012,
+ ERR_SDK_NOT_LOGGED_IN : 6014,
+ ERR_NO_PREVIOUS_LOGIN : 6026,
+ ERR_USER_SIG_EXPIRED : 6206,
+ ERR_LOGIN_KICKED_OFF_BY_OTHER : 6208,
+ ERR_SDK_ACCOUNT_LOGIN_IN_PROCESS : 7501,
+ ERR_SDK_ACCOUNT_LOGOUT_IN_PROCESS : 7502,
+ ERR_SDK_ACCOUNT_TLS_INIT_FAILED : 7503,
+ ERR_SDK_ACCOUNT_TLS_NOT_INITIALIZED : 7504,
+ ERR_SDK_ACCOUNT_TLS_TRANSPKG_ERROR : 7505,
+ ERR_SDK_ACCOUNT_TLS_DECRYPT_FAILED : 7506,
+ ERR_SDK_ACCOUNT_TLS_REQUEST_FAILED : 7507,
+ ERR_SDK_ACCOUNT_TLS_REQUEST_TIMEOUT : 7508,
+ ERR_INVALID_CONVERSATION : 6004,
+ ERR_FILE_TRANS_AUTH_FAILED : 6006,
+ ERR_FILE_TRANS_NO_SERVER : 6007,
+ ERR_FILE_TRANS_UPLOAD_FAILED : 6008,
+ ERR_IMAGE_UPLOAD_FAILED_NOTIMAGE : 6031,
+ ERR_FILE_TRANS_DOWNLOAD_FAILED : 6009,
+ ERR_HTTP_REQ_FAILED : 6010,
+ ERR_INVALID_MSG_ELEM : 6016,
+ ERR_INVALID_SDK_OBJECT : 6021,
+ ERR_SDK_MSG_BODY_SIZE_LIMIT : 8001,
+ ERR_SDK_MSG_KEY_REQ_DIFFER_RSP : 8002,
+ ERR_SDK_IMAGE_CONVERT_ERROR : 8003,
+ ERR_SDK_IMAGE_CI_BLOCK : 8004,
+ ERR_MERGER_MSG_LAYERS_OVER_LIMIT : 8005,
+ ERR_SDK_SIGNALING_INVALID_INVITE_ID : 8010,
+ ERR_SDK_SIGNALING_NO_PERMISSION : 8011,
+ ERR_SDK_INVALID_CANCEL_MESSAGE : 8020,
+ ERR_SDK_SEND_MESSAGE_FAILED_WITH_CANCEL : 8021,
+ ERR_SDK_GROUP_INVALID_ID : 8501,
+ ERR_SDK_GROUP_INVALID_NAME : 8502,
+ ERR_SDK_GROUP_INVALID_INTRODUCTION : 8503,
+ ERR_SDK_GROUP_INVALID_NOTIFICATION : 8504,
+ ERR_SDK_GROUP_INVALID_FACE_URL : 8505,
+ ERR_SDK_GROUP_INVALID_NAME_CARD : 8506,
+ ERR_SDK_GROUP_MEMBER_COUNT_LIMIT : 8507,
+ ERR_SDK_GROUP_JOIN_PRIVATE_GROUP_DENY : 8508,
+ ERR_SDK_GROUP_INVITE_SUPER_DENY : 8509,
+ ERR_SDK_GROUP_INVITE_NO_MEMBER : 8510,
+ ERR_SDK_GROUP_ATTR_FREQUENCY_LIMIT : 8511,
+ ERR_SDK_GROUP_GET_ONLINE_MEMBER_COUNT_LIMIT : 8512,
+ ERR_SDK_GROUP_GET_GROUPS_INFO_LIMIT : 8513,
+ ERR_SDK_GROUP_GET_JOINED_GROUP_LIMIT : 8514,
+ ERR_SDK_FRIENDSHIP_INVALID_PROFILE_KEY : 9001,
+ ERR_SDK_FRIENDSHIP_INVALID_ADD_REMARK : 9002,
+ ERR_SDK_FRIENDSHIP_INVALID_ADD_WORDING : 9003,
+ ERR_SDK_FRIENDSHIP_INVALID_ADD_SOURCE : 9004,
+ ERR_SDK_FRIENDSHIP_FRIEND_GROUP_EMPTY : 9005,
+ ERR_SDK_FRIENDSHIP_EXCEED_THE_LIMIT : 9006,
+ ERR_SDK_NET_ENCODE_FAILED : 9501,
+ ERR_SDK_NET_DECODE_FAILED : 9502,
+ ERR_SDK_NET_AUTH_INVALID : 9503,
+ ERR_SDK_NET_COMPRESS_FAILED : 9504,
+ ERR_SDK_NET_UNCOMPRESS_FAILED : 9505,
+ ERR_SDK_NET_FREQ_LIMIT : 9506,
+ ERR_SDK_NET_REQ_COUNT_LIMIT : 9507,
+ ERR_SDK_NET_DISCONNECT : 9508,
+ ERR_SDK_NET_ALLREADY_CONN : 9509,
+ ERR_SDK_NET_CONN_TIMEOUT : 9510,
+ ERR_SDK_NET_CONN_REFUSE : 9511,
+ ERR_SDK_NET_NET_UNREACH : 9512,
+ ERR_SDK_NET_SOCKET_NO_BUFF : 9513,
+ ERR_SDK_NET_RESET_BY_PEER : 9514,
+ ERR_SDK_NET_SOCKET_INVALID : 9515,
+ ERR_SDK_NET_HOST_GETADDRINFO_FAILED : 9516,
+ ERR_SDK_NET_CONNECT_RESET : 9517,
+ ERR_SDK_NET_WAIT_INQUEUE_TIMEOUT : 9518,
+ ERR_SDK_NET_WAIT_SEND_TIMEOUT : 9519,
+ ERR_SDK_NET_WAIT_ACK_TIMEOUT : 9520,
+ ERR_SDK_NET_WAIT_SEND_REMAINING_TIMEOUT : 9521,
+ ERR_SDK_NET_PKG_SIZE_LIMIT : 9522,
+ ERR_SDK_NET_WAIT_SEND_TIMEOUT_NO_NETWORK : 9523,
+ ERR_SDK_NET_WAIT_ACK_TIMEOUT_NO_NETWORK : 9524,
+ ERR_SDK_NET_SEND_REMAINING_TIMEOUT_NO_NETWORK : 9525,
+ ERR_SVR_SSO_CONNECT_LIMIT : -302,
+ ERR_SVR_SSO_VCODE : -10000,
+ ERR_SVR_SSO_D2_EXPIRED : -10001,
+ ERR_SVR_SSO_A2_UP_INVALID : -10003,
+ ERR_SVR_SSO_A2_DOWN_INVALID : -10004,
+ ERR_SVR_SSO_EMPTY_KEY : -10005,
+ ERR_SVR_SSO_UIN_INVALID : -10006,
+ ERR_SVR_SSO_VCODE_TIMEOUT : -10007,
+ ERR_SVR_SSO_NO_IMEI_AND_A2 : -10008,
+ ERR_SVR_SSO_COOKIE_INVALID : -10009,
+ ERR_SVR_SSO_DOWN_TIP : -10101,
+ ERR_SVR_SSO_DISCONNECT : -10102,
+ ERR_SVR_SSO_IDENTIFIER_INVALID : -10103,
+ ERR_SVR_SSO_CLIENT_CLOSE : -10104,
+ ERR_SVR_SSO_MSFSDK_QUIT : -10105,
+ ERR_SVR_SSO_D2KEY_WRONG : -10106,
+ ERR_SVR_SSO_UNSURPPORT : -10107,
+ ERR_SVR_SSO_PREPAID_ARREARS : -10108,
+ ERR_SVR_SSO_PACKET_WRONG : -10109,
+ ERR_SVR_SSO_APPID_BLACK_LIST : -10110,
+ ERR_SVR_SSO_CMD_BLACK_LIST : -10111,
+ ERR_SVR_SSO_APPID_WITHOUT_USING : -10112,
+ ERR_SVR_SSO_FREQ_LIMIT : -10113,
+ ERR_SVR_SSO_OVERLOAD : -10114,
+ ERR_SVR_RES_NOT_FOUND : 114000,
+ ERR_SVR_RES_ACCESS_DENY : 114001,
+ ERR_SVR_RES_SIZE_LIMIT : 114002,
+ ERR_SVR_RES_SEND_CANCEL : 114003,
+ ERR_SVR_RES_READ_FAILED : 114004,
+ ERR_SVR_RES_TRANSFER_TIMEOUT : 114005,
+ ERR_SVR_RES_INVALID_PARAMETERS : 114011,
+ ERR_SVR_RES_INVALID_FILE_MD5 : 115066,
+ ERR_SVR_RES_INVALID_PART_MD5 : 115068,
+ ERR_SVR_COMM_INVALID_HTTP_URL : 60002,
+ ERR_SVR_COMM_REQ_JSON_PARSE_FAILED : 60003,
+ ERR_SVR_COMM_INVALID_ACCOUNT : 60004,
+ ERR_SVR_COMM_INVALID_ACCOUNT_EX : 60005,
+ ERR_SVR_COMM_INVALID_SDKAPPID : 60006,
+ ERR_SVR_COMM_REST_FREQ_LIMIT : 60007,
+ ERR_SVR_COMM_REQUEST_TIMEOUT : 60008,
+ ERR_SVR_COMM_INVALID_RES : 60009,
+ ERR_SVR_COMM_ID_NOT_ADMIN : 60010,
+ ERR_SVR_COMM_SDKAPPID_FREQ_LIMIT : 60011,
+ ERR_SVR_COMM_SDKAPPID_MISS : 60012,
+ ERR_SVR_COMM_RSP_JSON_PARSE_FAILED : 60013,
+ ERR_SVR_COMM_EXCHANGE_ACCOUNT_TIMEUT : 60014,
+ ERR_SVR_COMM_INVALID_ID_FORMAT : 60015,
+ ERR_SVR_COMM_SDKAPPID_FORBIDDEN : 60016,
+ ERR_SVR_COMM_REQ_FORBIDDEN : 60017,
+ ERR_SVR_COMM_REQ_FREQ_LIMIT : 60018,
+ ERR_SVR_COMM_REQ_FREQ_LIMIT_EX : 60019,
+ ERR_SVR_COMM_INVALID_SERVICE : 60020,
+ ERR_SVR_COMM_SENSITIVE_TEXT : 80001,
+ ERR_SVR_COMM_BODY_SIZE_LIMIT : 80002,
+ ERR_SVR_ACCOUNT_USERSIG_EXPIRED : 70001,
+ ERR_SVR_ACCOUNT_USERSIG_EMPTY : 70002,
+ ERR_SVR_ACCOUNT_USERSIG_CHECK_FAILED : 70003,
+ ERR_SVR_ACCOUNT_USERSIG_CHECK_FAILED_EX : 70005,
+ ERR_SVR_ACCOUNT_USERSIG_MISMATCH_PUBLICKEY : 70009,
+ ERR_SVR_ACCOUNT_USERSIG_MISMATCH_ID : 70013,
+ ERR_SVR_ACCOUNT_USERSIG_MISMATCH_SDKAPPID : 70014,
+ ERR_SVR_ACCOUNT_USERSIG_PUBLICKEY_NOT_FOUND : 70016,
+ ERR_SVR_ACCOUNT_SDKAPPID_NOT_FOUND : 70020,
+ ERR_SVR_ACCOUNT_INVALID_USERSIG : 70052,
+ ERR_SVR_ACCOUNT_NOT_FOUND : 70107,
+ ERR_SVR_ACCOUNT_SEC_RSTR : 70114,
+ ERR_SVR_ACCOUNT_INTERNAL_TIMEOUT : 70169,
+ ERR_SVR_ACCOUNT_INVALID_COUNT : 70206,
+ ERR_SVR_ACCOUNT_INVALID_PARAMETERS : 70402,
+ ERR_SVR_ACCOUNT_ADMIN_REQUIRED : 70403,
+ ERR_SVR_ACCOUNT_FREQ_LIMIT : 70050,
+ ERR_SVR_ACCOUNT_BLACKLIST : 70051,
+ ERR_SVR_ACCOUNT_COUNT_LIMIT : 70398,
+ ERR_SVR_ACCOUNT_INTERNAL_ERROR : 70500,
+ ERR_SVR_PROFILE_INVALID_PARAMETERS : 40001,
+ ERR_SVR_PROFILE_ACCOUNT_MISS : 40002,
+ ERR_SVR_PROFILE_ACCOUNT_NOT_FOUND : 40003,
+ ERR_SVR_PROFILE_ADMIN_REQUIRED : 40004,
+ ERR_SVR_PROFILE_SENSITIVE_TEXT : 40005,
+ ERR_SVR_PROFILE_INTERNAL_ERROR : 40006,
+ ERR_SVR_PROFILE_READ_PERMISSION_REQUIRED : 40007,
+ ERR_SVR_PROFILE_WRITE_PERMISSION_REQUIRED : 40008,
+ ERR_SVR_PROFILE_TAG_NOT_FOUND : 40009,
+ ERR_SVR_PROFILE_SIZE_LIMIT : 40601,
+ ERR_SVR_PROFILE_VALUE_ERROR : 40605,
+ ERR_SVR_PROFILE_INVALID_VALUE_FORMAT : 40610,
+ ERR_SVR_FRIENDSHIP_INVALID_PARAMETERS : 30001,
+ ERR_SVR_FRIENDSHIP_INVALID_SDKAPPID : 30002,
+ ERR_SVR_FRIENDSHIP_ACCOUNT_NOT_FOUND : 30003,
+ ERR_SVR_FRIENDSHIP_ADMIN_REQUIRED : 30004,
+ ERR_SVR_FRIENDSHIP_SENSITIVE_TEXT : 30005,
+ ERR_SVR_FRIENDSHIP_INTERNAL_ERROR : 30006,
+ ERR_SVR_FRIENDSHIP_NET_TIMEOUT : 30007,
+ ERR_SVR_FRIENDSHIP_WRITE_CONFLICT : 30008,
+ ERR_SVR_FRIENDSHIP_ADD_FRIEND_DENY : 30009,
+ ERR_SVR_FRIENDSHIP_COUNT_LIMIT : 30010,
+ ERR_SVR_FRIENDSHIP_GROUP_COUNT_LIMIT : 30011,
+ ERR_SVR_FRIENDSHIP_PENDENCY_LIMIT : 30012,
+ ERR_SVR_FRIENDSHIP_BLACKLIST_LIMIT : 30013,
+ ERR_SVR_FRIENDSHIP_PEER_FRIEND_LIMIT : 30014,
+ ERR_SVR_FRIENDSHIP_IN_SELF_BLACKLIST : 30515,
+ ERR_SVR_FRIENDSHIP_ALLOW_TYPE_DENY_ANY : 30516,
+ ERR_SVR_FRIENDSHIP_IN_PEER_BLACKLIST : 30525,
+ ERR_SVR_FRIENDSHIP_ALLOW_TYPE_NEED_CONFIRM : 30539,
+ ERR_SVR_FRIENDSHIP_ADD_FRIEND_SEC_RSTR : 30540,
+ ERR_SVR_FRIENDSHIP_PENDENCY_NOT_FOUND : 30614,
+ ERR_SVR_FRIENDSHIP_DEL_NONFRIEND : 31704,
+ ERR_SVR_FRIENDSHIP_DEL_FRIEND_SEC_RSTR : 31707,
+ ERR_SVR_FRIENDSHIP_ACCOUNT_NOT_FOUND_EX : 31804,
+ ERR_SVR_CONV_ACCOUNT_NOT_FOUND : 50001,
+ ERR_SVR_CONV_INVALID_PARAMETERS : 50002,
+ ERR_SVR_CONV_ADMIN_REQUIRED : 50003,
+ ERR_SVR_CONV_INTERNAL_ERROR : 50004,
+ ERR_SVR_CONV_NET_TIMEOUT : 50005,
+ ERR_SVR_MSG_PKG_PARSE_FAILED : 20001,
+ ERR_SVR_MSG_INTERNAL_AUTH_FAILED : 20002,
+ ERR_SVR_MSG_INVALID_ID : 20003,
+ ERR_SVR_MSG_NET_ERROR : 20004,
+ ERR_SVR_MSG_INTERNAL_ERROR1 : 20005,
+ ERR_SVR_MSG_PUSH_DENY : 20006,
+ ERR_SVR_MSG_IN_PEER_BLACKLIST : 20007,
+ ERR_SVR_MSG_BOTH_NOT_FRIEND : 20009,
+ ERR_SVR_MSG_NOT_PEER_FRIEND : 20010,
+ ERR_SVR_MSG_NOT_SELF_FRIEND : 20011,
+ ERR_SVR_MSG_SHUTUP_DENY : 20012,
+ ERR_SVR_MSG_REVOKE_TIME_LIMIT : 20016,
+ ERR_SVR_MSG_DEL_RAMBLE_INTERNAL_ERROR : 20018,
+ ERR_SVR_MSG_JSON_PARSE_FAILED : 90001,
+ ERR_SVR_MSG_INVALID_JSON_BODY_FORMAT : 90002,
+ ERR_SVR_MSG_INVALID_TO_ACCOUNT : 90003,
+ ERR_SVR_MSG_INVALID_RAND : 90005,
+ ERR_SVR_MSG_INVALID_TIMESTAMP : 90006,
+ ERR_SVR_MSG_BODY_NOT_ARRAY : 90007,
+ ERR_SVR_MSG_ADMIN_REQUIRED : 90009,
+ ERR_SVR_MSG_INVALID_JSON_FORMAT : 90010,
+ ERR_SVR_MSG_TO_ACCOUNT_COUNT_LIMIT : 90011,
+ ERR_SVR_MSG_TO_ACCOUNT_NOT_FOUND : 90012,
+ ERR_SVR_MSG_TIME_LIMIT : 90026,
+ ERR_SVR_MSG_INVALID_SYNCOTHERMACHINE : 90031,
+ ERR_SVR_MSG_INVALID_MSGLIFETIME : 90044,
+ ERR_SVR_MSG_ACCOUNT_NOT_FOUND : 90048,
+ ERR_SVR_MSG_INTERNAL_ERROR2 : 90994,
+ ERR_SVR_MSG_INTERNAL_ERROR3 : 90995,
+ ERR_SVR_MSG_INTERNAL_ERROR4 : 91000,
+ ERR_SVR_MSG_INTERNAL_ERROR5 : 90992,
+ ERR_SVR_MSG_BODY_SIZE_LIMIT : 93000,
+ ERR_SVR_MSG_LONGPOLLING_COUNT_LIMIT : 91101,
+ ERR_SVR_GROUP_INTERNAL_ERROR : 10002,
+ ERR_SVR_GROUP_API_NAME_ERROR : 10003,
+ ERR_SVR_GROUP_INVALID_PARAMETERS : 10004,
+ ERR_SVR_GROUP_ACOUNT_COUNT_LIMIT : 10005,
+ ERR_SVR_GROUP_FREQ_LIMIT : 10006,
+ ERR_SVR_GROUP_PERMISSION_DENY : 10007,
+ ERR_SVR_GROUP_INVALID_REQ : 10008,
+ ERR_SVR_GROUP_SUPER_NOT_ALLOW_QUIT : 10009,
+ ERR_SVR_GROUP_NOT_FOUND : 10010,
+ ERR_SVR_GROUP_JSON_PARSE_FAILED : 10011,
+ ERR_SVR_GROUP_INVALID_ID : 10012,
+ ERR_SVR_GROUP_ALLREADY_MEMBER : 10013,
+ ERR_SVR_GROUP_FULL_MEMBER_COUNT : 10014,
+ ERR_SVR_GROUP_INVALID_GROUPID : 10015,
+ ERR_SVR_GROUP_REJECT_FROM_THIRDPARTY : 10016,
+ ERR_SVR_GROUP_SHUTUP_DENY : 10017,
+ ERR_SVR_GROUP_RSP_SIZE_LIMIT : 10018,
+ ERR_SVR_GROUP_ACCOUNT_NOT_FOUND : 10019,
+ ERR_SVR_GROUP_GROUPID_IN_USED : 10021,
+ ERR_SVR_GROUP_SEND_MSG_FREQ_LIMIT : 10023,
+ ERR_SVR_GROUP_REQ_ALLREADY_BEEN_PROCESSED : 10024,
+ ERR_SVR_GROUP_GROUPID_IN_USED_FOR_SUPER : 10025,
+ ERR_SVR_GROUP_SDKAPPID_DENY : 10026,
+ ERR_SVR_GROUP_REVOKE_MSG_NOT_FOUND : 10030,
+ ERR_SVR_GROUP_REVOKE_MSG_TIME_LIMIT : 10031,
+ ERR_SVR_GROUP_REVOKE_MSG_DENY : 10032,
+ ERR_SVR_GROUP_NOT_ALLOW_REVOKE_MSG : 10033,
+ ERR_SVR_GROUP_REMOVE_MSG_DENY : 10034,
+ ERR_SVR_GROUP_NOT_ALLOW_REMOVE_MSG : 10035,
+ ERR_SVR_GROUP_AVCHATROOM_COUNT_LIMIT : 10036,
+ ERR_SVR_GROUP_COUNT_LIMIT : 10037,
+ ERR_SVR_GROUP_MEMBER_COUNT_LIMIT : 10038,
+ ERR_NO_SUCC_RESULT : 6003,
+ ERR_TO_USER_INVALID : 6011,
+ ERR_REQUEST_TIMEOUT : 6012,
+ ERR_INIT_CORE_FAIL : 6018,
+ ERR_EXPIRED_SESSION_NODE : 6020,
+ ERR_LOGGED_OUT_BEFORE_LOGIN_FINISHED : 6023,
+ ERR_TLSSDK_NOT_INITIALIZED : 6024,
+ ERR_TLSSDK_USER_NOT_FOUND : 6025,
+ ERR_BIND_FAIL_UNKNOWN : 6100,
+ ERR_BIND_FAIL_NO_SSOTICKET : 6101,
+ ERR_BIND_FAIL_REPEATD_BIND : 6102,
+ ERR_BIND_FAIL_TINYID_NULL : 6103,
+ ERR_BIND_FAIL_GUID_NULL : 6104,
+ ERR_BIND_FAIL_UNPACK_REGPACK_FAILED : 6105,
+ ERR_BIND_FAIL_REG_TIMEOUT : 6106,
+ ERR_BIND_FAIL_ISBINDING : 6107,
+ ERR_PACKET_FAIL_UNKNOWN : 6120,
+ ERR_PACKET_FAIL_REQ_NO_NET : 6121,
+ ERR_PACKET_FAIL_RESP_NO_NET : 6122,
+ ERR_PACKET_FAIL_REQ_NO_AUTH : 6123,
+ ERR_PACKET_FAIL_SSO_ERR : 6124,
+ ERR_PACKET_FAIL_REQ_TIMEOUT : 6125,
+ ERR_PACKET_FAIL_RESP_TIMEOUT : 6126,
+ ERR_PACKET_FAIL_REQ_ON_RESEND : 6127,
+ ERR_PACKET_FAIL_RESP_NO_RESEND : 6128,
+ ERR_PACKET_FAIL_FLOW_SAVE_FILTERED : 6129,
+ ERR_PACKET_FAIL_REQ_OVER_LOAD : 6130,
+ ERR_PACKET_FAIL_LOGIC_ERR : 6131,
+ ERR_FRIENDSHIP_PROXY_NOT_SYNCED : 6150,
+ ERR_FRIENDSHIP_PROXY_SYNCING : 6151,
+ ERR_FRIENDSHIP_PROXY_SYNCED_FAIL : 6152,
+ ERR_FRIENDSHIP_PROXY_LOCAL_CHECK_ERR : 6153,
+ ERR_GROUP_INVALID_FIELD : 6160,
+ ERR_GROUP_STORAGE_DISABLED : 6161,
+ ERR_LOADGRPINFO_FAILED : 6162,
+ ERR_REQ_NO_NET_ON_REQ : 6200,
+ ERR_REQ_NO_NET_ON_RSP : 6201,
+ ERR_SERIVCE_NOT_READY : 6205,
+ ERR_LOGIN_AUTH_FAILED : 6207,
+ ERR_NEVER_CONNECT_AFTER_LAUNCH : 6209,
+ ERR_REQ_FAILED : 6210,
+ ERR_REQ_INVALID_REQ : 6211,
+ ERR_REQ_OVERLOADED : 6212,
+ ERR_REQ_KICK_OFF : 6213,
+ ERR_REQ_SERVICE_SUSPEND : 6214,
+ ERR_REQ_INVALID_SIGN : 6215,
+ ERR_REQ_INVALID_COOKIE : 6216,
+ ERR_LOGIN_TLS_RSP_PARSE_FAILED : 6217,
+ ERR_LOGIN_OPENMSG_TIMEOUT : 6218,
+ ERR_LOGIN_OPENMSG_RSP_PARSE_FAILED : 6219,
+ ERR_LOGIN_TLS_DECRYPT_FAILED : 6220,
+ ERR_WIFI_NEED_AUTH : 6221,
+ ERR_USER_CANCELED : 6222,
+ ERR_REVOKE_TIME_LIMIT_EXCEED : 6223,
+ ERR_LACK_UGC_EXT : 6224,
+ ERR_AUTOLOGIN_NEED_USERSIG : 6226,
+ ERR_QAL_NO_SHORT_CONN_AVAILABLE : 6300,
+ ERR_REQ_CONTENT_ATTACK : 80101,
+ ERR_LOGIN_SIG_EXPIRE : 70101,
+ ERR_SDK_HAD_INITIALIZED : 90101,
+ ERR_OPENBDH_BASE : 115000,
+ ERR_REQUEST_NO_NET_ONREQ : 6250,
+ ERR_REQUEST_NO_NET_ONRSP : 6251,
+ ERR_REQUEST_FAILED : 6252,
+ ERR_REQUEST_INVALID_REQ : 6253,
+ ERR_REQUEST_OVERLOADED : 6254,
+ ERR_REQUEST_KICK_OFF : 6255,
+ ERR_REQUEST_SERVICE_SUSPEND : 6256,
+ ERR_REQUEST_INVALID_SIGN : 6257,
+ ERR_REQUEST_INVALID_COOKIE : 6258
+}
+/**
+* @brief 调用接口的返回值
+
+* @note
+* 若接口参数中有回调,只有当接口返回TIM_SUCC时,回调才会被调用
+*
+* | 名称 | 含义 | 值 |
+* | ---- | ---- | ---- |
+* | TIM_SUCC | 接口调用成功 | 0 |
+* | TIM_ERR_SDKUNINIT | 失败,IM SDK未初始化 | -1 |
+* | TIM_ERR_NOTLOGIN | 失败,IM SDK未初始化 | -2 |
+* | TIM_ERR_JSON | 接口调用失败,错误的Json格式或Json Key | -3 |
+* | TIM_ERR_PARAM | 接口调用失败,参数错误 | -4 |
+* | TIM_ERR_CONV | 接口调用失败,无效的会话 | -5 |
+* | TIM_ERR_GROUP | 接口调用失败,无效的群组 | -6 |
+*/
+const TIMResult= {
+ TIM_SUCC : 0,
+ TIM_ERR_SDKUNINIT : -1,
+ TIM_ERR_NOTLOGIN : -2,
+ TIM_ERR_JSON : -3,
+ TIM_ERR_PARAM : -4,
+ TIM_ERR_CONV : -5,
+ TIM_ERR_GROUP : -6
+}
+/**
+* @brief 日志级别
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMLog_Off | 关闭日志输出 | 0 |
+* | kTIMLog_Test | 全量日志 | 1 |
+* | kTIMLog_Verbose| 开发调试过程中一些详细信息日志 | 2 |
+* | kTIMLog_Debug | 调试日志 | 3 |
+* | kTIMLog_Info | 信息日志 | 4 |
+* | kTIMLog_Warn | 警告日志 | 5 |
+* | kTIMLog_Error | 错误日志 | 6 |
+* | kTIMLog_Assert | 断言日志 | 7 |
+*/
+const TIMLogLevel= {
+ kTIMLog_Off : 0,
+ kTIMLog_Test : 1,
+ kTIMLog_Verbose : 2,
+ kTIMLog_Debug : 3,
+ kTIMLog_Info : 4,
+ kTIMLog_Warn : 5,
+ kTIMLog_Error : 6,
+ kTIMLog_Assert : 7
+}
+/**
+* @brief 登陆状态
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMLoginStatus_Logined | 已登陆 | 1 |
+* | kTIMLoginStatus_Logining | 登陆中 | 2 |
+* | kTIMLoginStatus_UnLogined | 未登陆 | 3 |
+* | kTIMLoginStatus_Logouting | 登出中 | 4 |
+*/
+const TIMLoginStatus= {
+ kTIMLoginStatus_Logined : 1,
+ kTIMLoginStatus_Logining : 2,
+ kTIMLoginStatus_UnLogined : 3,
+ kTIMLoginStatus_Logouting : 4
+}
+/**
+* @brief 连接事件类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMConnected | 已连接 | kTIMConnected |
+* | kTIMDisconnected | 失去连接 | kTIMDisconnected |
+* | kTIMConnecting | 正在连接 | kTIMConnecting |
+* | kTIMConnectFailed | 连接失败 | kTIMConnectFailed |
+*/
+const TIMNetworkStatus= {
+ kTIMConnected : 0,
+ kTIMDisconnected : 1,
+ kTIMConnecting : 2,
+ kTIMConnectFailed : 3
+}
+/**
+* @brief 会话事件类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMConvEvent_Add | 会话新增,例如收到一条新消息,产生一个新的会话是事件触发 | kTIMConvEvent_Add |
+* | kTIMConvEvent_Del | 会话删除,例如自己删除某会话时会触发 | kTIMConvEvent_Del |
+* | kTIMConvEvent_Update | 会话更新,会话内消息的未读计数变化和收到新消息时触发 | kTIMConvEvent_Update |
+* | kTIMConvEvent_Start | 会话开始 | kTIMConvEvent_Start |
+* | kTIMConvEvent_Finish | 会话结束 | kTIMConvEvent_Finish |
+*/
+const TIMConvEvent= {
+ kTIMConvEvent_Add : 0,
+ kTIMConvEvent_Del : 1,
+ kTIMConvEvent_Update : 2,
+ kTIMConvEvent_Start : 3,
+ kTIMConvEvent_Finish : 4
+}
+/**
+* @brief 会话类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMConv_Invalid | 无效会话 | kTIMConv_Invalid |
+* | kTIMConv_C2C | 个人会话 | kTIMConv_C2C |
+* | kTIMConv_Group | 群组会话 | kTIMConv_Group |
+* | kTIMConv_System | 系统会话 | kTIMConv_System |
+*/
+const TIMConvType= {
+ kTIMConv_Invalid : 0,
+ kTIMConv_C2C : 1,
+ kTIMConv_Group : 2,
+ kTIMConv_System : 3
+}
+/**
+* @brief 平台信息
+*
+* | 名称 | 含义 | 值(enum/number) |
+* | ---- | ---- | ---- |
+* | kTIMPlatform_Other | 未知平台 | 0(number) |
+* | kTIMPlatform_Windows | Windows平台 | kTIMPlatform_Windows |
+* | kTIMPlatform_Android | Android平台 | kTIMPlatform_Android |
+* | kTIMPlatform_IOS | iOS平台 | kTIMPlatform_IOS |
+* | kTIMPlatform_Mac | MacOS平台 | kTIMPlatform_Mac |
+* | kTIMPlatform_Simulator | iOS模拟器平台 | kTIMPlatform_Simulator |
+*/
+const TIMPlatform= {
+ kTIMPlatform_Other : 0,
+ kTIMPlatform_Windows : 1,
+ kTIMPlatform_Android : 2,
+ kTIMPlatform_IOS : 3,
+ kTIMPlatform_Mac : 4,
+ kTIMPlatform_Simulator : 5
+}
+/**
+* @brief 群组成员信息标识
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMPlatform_Other | 无 | 0x00 |
+* | kTIMPlatform_Windows | 加入时间 | 0x01 |
+* | kTIMPlatform_Android | 群消息接收选项 | 0x01 << 1 |
+* | kTIMPlatform_IOS | 成员已读消息seq | 0x01 << 2 |
+* | kTIMPlatform_IOS | 成员角色 | 0x01 << 3 |
+* | kTIMPlatform_Mac | 禁言时间。当该值为0时表示没有被禁言 | 0x01 << 4, |
+* | kTIMPlatform_Simulator | iOS模拟器平台 | 0x01 << 5 |
+*/
+const TIMGroupMemberInfoFlag= {
+ kTIMGroupMemberInfoFlag_None : 0,
+ kTIMGroupMemberInfoFlag_JoinTime : 1,
+ kTIMGroupMemberInfoFlag_MsgFlag : 2,
+ kTIMGroupMemberInfoFlag_MsgSeq : 4,
+ kTIMGroupMemberInfoFlag_MemberRole : 8,
+ kTIMGroupMemberInfoFlag_ShutupUntill : 16,
+ kTIMGroupMemberInfoFlag_NameCard : 32
+}
+/**
+* @brief 群组成员角色标识
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMGroupMemberRoleFlag_All | 获取全部角色类型 | 0x00 |
+* | kTIMGroupMemberRoleFlag_Owner | 获取所有者(群主) | 0x01 |
+* | kTIMGroupMemberRoleFlag_Admin | 获取管理员,不包括群主 | 0x01 << 1 |
+* | kTIMGroupMemberRoleFlag_Member | 获取普通群成员,不包括群主和管理员 | 0x01 << 2 |
+*/
+const TIMGroupMemberRoleFlag= {
+ kTIMGroupMemberRoleFlag_All : 0,
+ kTIMGroupMemberRoleFlag_Owner : 1,
+ kTIMGroupMemberRoleFlag_Admin : 2,
+ kTIMGroupMemberRoleFlag_Member : 4
+}
+/**
+* @brief 群组成员信息标识
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMGroupInfoFlag_None | 无 | 0x00 |
+* | kTIMGroupInfoFlag_Name | 群组名称 | 0x01 |
+* | kTIMGroupInfoFlag_CreateTime | 群组创建时间 | 0x01 << 1 |
+* | kTIMGroupInfoFlag_OwnerUin | 群组创建者帐号 | 0x01 << 2 |
+* | kTIMGroupInfoFlag_Seq | seq | 0x01 << 3 |
+* | kTIMGroupInfoFlag_LastTime | 群组信息最后修改时间 | 0x01 << 4 |
+* | kTIMGroupInfoFlag_NextMsgSeq | 下一个消息的seq | 0x01 << 5 |
+* | kTIMGroupInfoFlag_LastMsgTime | 最新群组消息时间 | 0x01 << 6 |
+* | kTIMGroupInfoFlag_AppId | AppId | 0x01 << 7 |
+* | kTIMGroupInfoFlag_MemberNum | 群组成员数量 | 0x01 << 8 |
+* | kTIMGroupInfoFlag_MaxMemberNum | 群组成员最大数量 | 0x01 << 9 |
+* | kTIMGroupInfoFlag_Notification | 群公告内容 | 0x01 << 10 |
+* | kTIMGroupInfoFlag_Introduction | 群简介内容 | 0x01 << 11|
+* | kTIMGroupInfoFlag_FaceUrl | 群头像URL | 0x01 << 12 |
+* | kTIMGroupInfoFlag_AddOption | 加群选项 | 0x01 << 13 |
+* | kTIMGroupInfoFlag_GroupType | 群类型 | 0x01 << 14 |
+* | kTIMGroupInfoFlag_LastMsg | 群组内最新一条消息 | 0x01 << 15 |
+* | kTIMGroupInfoFlag_OnlineNum | 群组在线成员数 | 0x01 << 16 |
+* | kTIMGroupInfoFlag_Visible | 群组是否可见 | 0x01 << 17 |
+* | kTIMGroupInfoFlag_Searchable | 群组是否可以搜索 | 0x01 << 18 |
+* | kTIMGroupInfoFlag_ShutupAll | 群组是否全禁言 | 0x01 << 19 |
+*/
+const TIMGroupGetInfoFlag= {
+ kTIMGroupInfoFlag_None : 0,
+ kTIMGroupInfoFlag_Name : 1,
+ kTIMGroupInfoFlag_CreateTime : 2,
+ kTIMGroupInfoFlag_OwnerUin : 4,
+ kTIMGroupInfoFlag_Seq : 8,
+ kTIMGroupInfoFlag_LastTime : 16,
+ kTIMGroupInfoFlag_NextMsgSeq : 32,
+ kTIMGroupInfoFlag_LastMsgTime : 64,
+ kTIMGroupInfoFlag_AppId : 128,
+ kTIMGroupInfoFlag_MemberNum : 256,
+ kTIMGroupInfoFlag_MaxMemberNum : 512,
+ kTIMGroupInfoFlag_Notification : 1024,
+ kTIMGroupInfoFlag_Introduction : 2048,
+ kTIMGroupInfoFlag_FaceUrl : 4096,
+ kTIMGroupInfoFlag_AddOption : 8192,
+ kTIMGroupInfoFlag_GroupType : 16384,
+ kTIMGroupInfoFlag_LastMsg : 32768,
+ kTIMGroupInfoFlag_OnlineNum : 65536,
+ kTIMGroupInfoFlag_Visible : 131072,
+ kTIMGroupInfoFlag_Searchable : 262144,
+ kTIMGroupInfoFlag_ShutupAll : 524288
+}
+/**
+* @brief Android离线推送模式
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMAndroidOfflinePushNotifyMode_Normal | 普通通知栏消息模式,离线消息下发后,点击通知栏消息直接启动应用,不会给应用进行回调 | kTIMAndroidOfflinePushNotifyMode_Normal |
+* | kTIMAndroidOfflinePushNotifyMode_Custom | 自定义消息模式,离线消息下发后,点击通知栏消息会给应用进行回调 | kTIMAndroidOfflinePushNotifyMode_Custom |
+*/
+const TIMAndroidOfflinePushNotifyMode= {
+ kTIMAndroidOfflinePushNotifyMode_Normal : 0,
+ kTIMAndroidOfflinePushNotifyMode_Custom : 1
+}
+/**
+* @brief 推送规则
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMOfflinePushFlag_Default | 按照默认规则进行推送 | kTIMOfflinePushFlag_Default |
+* | kTIMOfflinePushFlag_NoPush | 不进行推送 | kTIMOfflinePushFlag_NoPush |
+*/
+const TIMOfflinePushFlag= {
+ kTIMOfflinePushFlag_Default : 0,
+ kTIMOfflinePushFlag_NoPush : 1
+}
+/**
+* @brief 消息当前状态定义
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMMsg_Sending | 消息正在发送 | 1 |
+* | kTIMMsg_SendSucc | 消息发送成功 | 2 |
+* | kTIMMsg_SendFail | 消息发送失败 | 3 |
+* | kTIMMsg_Deleted | 消息已删除 | 4 |
+* | kTIMMsg_LocalImported | 消息导入状态 | 5 |
+* | kTIMMsg_Revoked | 消息撤回状态 | 6 |
+* | kTIMMsg_Cancel | 消息取消 | 7 |
+*/
+const TIMMsgStatus= {
+ kTIMMsg_Sending : 1,
+ kTIMMsg_SendSucc : 2,
+ kTIMMsg_SendFail : 3,
+ kTIMMsg_Deleted : 4,
+ kTIMMsg_LocalImported : 5,
+ kTIMMsg_Revoked : 6,
+ kTIMMsg_Cancel : 7
+}
+/**
+* @brief 标识消息的优先级,数字越大优先级越低
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMMsgPriority_High | 优先级最高,一般为红包或者礼物消息 | kTIMMsgPriority_High |
+* | kTIMMsgPriority_Normal | 表示优先级次之,建议为普通消息 | kTIMMsgPriority_Normal |
+* | kTIMMsgPriority_Low | 建议为点赞消息等 | kTIMMsgPriority_Low |
+* | kTIMMsgPriority_Lowest | 消息发送失败 | kTIMMsgPriority_Lowest |
+*/
+const TIMMsgPriority= {
+ kTIMMsgPriority_High : 0,
+ kTIMMsgPriority_Normal : 1,
+ kTIMMsgPriority_Low : 2,
+ kTIMMsgPriority_Lowest : 3
+}
+/**
+* @brief 元素的类型
+*
+* | 名称 | 含义 | 值(enum/number) |
+* | ---- | ---- | ---- |
+* | kTIMElem_Text | 文本元素 | kTIMElem_Text |
+* | kTIMElem_Image | 图片元素 | kTIMElem_Image |
+* | kTIMElem_Sound | 声音元素 | kTIMElem_Sound |
+* | kTIMElem_Custom | 自定义元素 | kTIMElem_Custom |
+* | kTIMElem_File | 文件元素 | kTIMElem_File |
+* | kTIMElem_GroupTips | 群组系统消息元素 | kTIMElem_GroupTips |
+* | kTIMElem_Face | 表情元素 | kTIMElem_Face |
+* | kTIMElem_Location | 位置元素 | kTIMElem_Location |
+* | kTIMElem_GroupReport | 群组系统通知元素 | kTIMElem_GroupReport |
+* | kTIMElem_Video | 视频元素 | kTIMElem_Video |
+* | kTIMElem_FriendChange | 关系链变更消息元素 | kTIMElem_FriendChange |
+* | kTIMElem_ProfileChange | 资料变更消息元素 | kTIMElem_ProfileChange |
+* | kTIMElem_Merge | 合并消息素 | kTIMElem_Merge |
+* | kTIMElem_Invalid | 未知元素类型 | -1 |
+*/
+const TIMElemType= {
+ kTIMElem_Text : 0,
+ kTIMElem_Image : 1,
+ kTIMElem_Sound : 2,
+ kTIMElem_Custom : 3,
+ kTIMElem_File : 4,
+ kTIMElem_GroupTips : 5,
+ kTIMElem_Face : 6,
+ kTIMElem_Location : 7,
+ kTIMElem_GroupReport : 8,
+ kTIMElem_Video : 9,
+ kTIMElem_FriendChange : 10,
+ kTIMElem_ProfileChange : 11,
+ kTIMElem_Merge : 12,
+ kTIMElem_Invalid : -1
+}
+/**
+* @brief 图片质量级别
+*
+* | 名称 | 含义 | 值(enum/number) |
+* | ---- | ---- | ---- |
+* | kTIMImageLevel_Orig | 原图发送 | kTIMImageLevel_Orig |
+* | kTIMImageLevel_Compression | 高压缩率图发送(图片较小,默认值) | kTIMImageLevel_Compression |
+* | kTIMImageLevel_HD | 高清图发送(图片较大) | kTIMImageLevel_HD |
+*/
+const TIMImageLevel= {
+ kTIMImageLevel_Orig : 0,
+ kTIMImageLevel_Compression : 1,
+ kTIMImageLevel_HD : 2
+}
+/**
+* @brief 群组信息修改的类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMGroupTipChangeFlag_Unknown | 未知的修改 | kTIMGroupTipChangeFlag_Unknown |
+* | kTIMGroupTipChangeFlag_Name | 修改群组名称 | kTIMGroupTipChangeFlag_Name |
+* | kTIMGroupTipChangeFlag_Introduction | 修改群简介 | kTIMGroupTipChangeFlag_Introduction |
+* | kTIMGroupTipChangeFlag_Notification | 修改群公告 | kTIMGroupTipChangeFlag_Notification |
+* | kTIMGroupTipChangeFlag_FaceUrl | 修改群头像URL | kTIMGroupTipChangeFlag_FaceUrl |
+* | kTIMGroupTipChangeFlag_Owner | 修改群所有者 | kTIMGroupTipChangeFlag_Owner |
+* | kTIMGroupTipChangeFlag_Custom | 修改群自定义信息 | kTIMGroupTipChangeFlag_Custom |
+* | kTIMGroupTipChangeFlag_Attribute | 群属性变更 (新增) | kTIMGroupTipChangeFlag_Attribute |
+*/
+const TIMGroupTipGroupChangeFlag= {
+ kTIMGroupTipChangeFlag_Unknown : 0,
+ kTIMGroupTipChangeFlag_Name : 1,
+ kTIMGroupTipChangeFlag_Introduction : 2,
+ kTIMGroupTipChangeFlag_Notification : 3,
+ kTIMGroupTipChangeFlag_FaceUrl : 4,
+ kTIMGroupTipChangeFlag_Owner : 5,
+ kTIMGroupTipChangeFlag_Custom : 6,
+ kTIMGroupTipChangeFlag_Attribute : 7,
+ kTIMGroupTipChangeFlag_ShutupAll : 8,
+ kTIMGroupTipChangeFlag_MessageReceiveOpt : 10,
+ kTIMGroupTipChangeFlag_GroupAddOpt : 11,
+ kTIMGroupTipChangeFlag_GroupApproveOpt : 12
+}
+/**
+* @brief 群组系统消息类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMGroupTip_None | 无效的群提示 | kTIMGroupTip_None |
+* | kTIMGroupTip_Invite | 邀请加入提示 | kTIMGroupTip_Invite |
+* | kTIMGroupTip_Quit | 退群提示 | kTIMGroupTip_Quit |
+* | kTIMGroupTip_Kick | 踢人提示 | kTIMGroupTip_Kick |
+* | kTIMGroupTip_SetAdmin | 设置管理员提示 | kTIMGroupTip_SetAdmin |
+* | kTIMGroupTip_CancelAdmin | 取消管理员提示 | kTIMGroupTip_CancelAdmin |
+* | kTIMGroupTip_GroupInfoChange | 群信息修改提示 | kTIMGroupTip_GroupInfoChange |
+* | kTIMGroupTip_MemberInfoChange | 群成员信息修改提示 | kTIMGroupTip_MemberInfoChange |
+*/
+const TIMGroupTipType= {
+ kTIMGroupTip_None : 0,
+ kTIMGroupTip_Invite : 1,
+ kTIMGroupTip_Quit : 2,
+ kTIMGroupTip_Kick : 3,
+ kTIMGroupTip_SetAdmin : 4,
+ kTIMGroupTip_CancelAdmin : 5,
+ kTIMGroupTip_GroupInfoChange : 6,
+ kTIMGroupTip_MemberInfoChange : 7
+}
+/**
+* @brief 群组系统通知类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMGroupReport_None | 未知类型 | kTIMGroupReport_None |
+* | kTIMGroupReport_AddRequest | 申请加群(只有管理员会接收到) | kTIMGroupReport_AddRequest |
+* | kTIMGroupReport_AddAccept | 申请加群被同意(只有申请人自己接收到) | kTIMGroupReport_AddAccept |
+* | kTIMGroupReport_AddRefuse | 申请加群被拒绝(只有申请人自己接收到) | kTIMGroupReport_AddRefuse |
+* | kTIMGroupReport_BeKicked | 被管理员踢出群(只有被踢者接收到) | kTIMGroupReport_BeKicked |
+* | kTIMGroupReport_Delete | 群被解散(全员接收) | kTIMGroupReport_AddRefuse |
+* | kTIMGroupReport_Create | 创建群(创建者接收, 不展示) | kTIMGroupReport_Create |
+* | kTIMGroupReport_Invite | 邀请加群(被邀请者接收) | kTIMGroupReport_Invite |
+* | kTIMGroupReport_Quit | 主动退群(主动退出者接收, 不展示) | kTIMGroupReport_Quit |
+* | kTIMGroupReport_GrantAdmin | 设置管理员(被设置者接收) | kTIMGroupReport_GrantAdmin |
+* | kTIMGroupReport_CancelAdmin | 取消管理员(被取消者接收) | kTIMGroupReport_CancelAdmin |
+* | kTIMGroupReport_GroupRecycle | 群已被回收(全员接收, 不展示) | kTIMGroupReport_GroupRecycle |
+* | kTIMGroupReport_InviteReq | 邀请加群(只有被邀请者会接收到) | kTIMGroupReport_InviteReq |
+* | kTIMGroupReport_InviteAccept | 邀请加群被同意(只有发出邀请者会接收到) | kTIMGroupReport_InviteAccept |
+* | kTIMGroupReport_InviteRefuse | 邀请加群被拒绝(只有发出邀请者会接收到) | kTIMGroupReport_InviteRefuse |
+* | kTIMGroupReport_ReadReport | 已读上报多终端同步通知(只有上报人自己收到) | kTIMGroupReport_ReadReport |
+* | kTIMGroupReport_UserDefine | 用户自定义通知(默认全员接收) | kTIMGroupReport_UserDefine |
+*/
+const TIMGroupReportType= {
+ kTIMGroupReport_None : 0,
+ kTIMGroupReport_AddRequest : 1,
+ kTIMGroupReport_AddAccept : 2,
+ kTIMGroupReport_AddRefuse : 3,
+ kTIMGroupReport_BeKicked : 4,
+ kTIMGroupReport_Delete : 5,
+ kTIMGroupReport_Create : 6,
+ kTIMGroupReport_Invite : 7,
+ kTIMGroupReport_Quit : 8,
+ kTIMGroupReport_GrantAdmin : 9,
+ kTIMGroupReport_CancelAdmin : 10,
+ kTIMGroupReport_GroupRecycle : 11,
+ kTIMGroupReport_InviteReq : 12,
+ kTIMGroupReport_InviteAccept : 13,
+ kTIMGroupReport_InviteRefuse : 14,
+ kTIMGroupReport_ReadReport : 15,
+ kTIMGroupReport_UserDefine : 16
+}
+/**
+* @brief 资料变更类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMGroupReport_None | 未知类型 | kTIMGroupReport_None |
+* | kTIMProfileChange_Profile | 资料修改 | kTIMProfileChange_Profile |
+*/
+const TIMProfileChangeType= {
+ kTIMProfileChange_None : 0,
+ kTIMProfileChange_Profile : 1
+}
+/**
+* @brief 好友变更类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMFriendChange_None | 未知类型 | kTIMFriendChange_None |
+* | kTIMFriendChange_FriendAdd | 好友表增加 | kTIMFriendChange_FriendAdd |
+* | kTIMFriendChange_FriendDel | 好友表删除 | kTIMFriendChange_FriendDel |
+* | kTIMFriendChange_PendencyAdd | 未决增加 | kTIMFriendChange_PendencyAdd |
+* | kTIMFriendChange_PendencyDel | 未决删除 | kTIMFriendChange_PendencyDel |
+* | kTIMFriendChange_BlackListAdd | 黑名单添加 | kTIMFriendChange_BlackListAdd |
+* | kTIMFriendChange_BlackListDel | 黑名单删除 | kTIMFriendChange_BlackListDel |
+* | kTIMFriendChange_PendencyReadedReport | 未决已读上报 | kTIMFriendChange_PendencyReadedReport |
+* | kTIMFriendChange_FriendProfileUpdate | 好友数据更新 | kTIMFriendChange_FriendProfileUpdate |
+* | kTIMFriendChange_FriendGroupAdd | 分组增加 | kTIMFriendChange_FriendGroupAdd |
+* | kTIMFriendChange_FriendGroupDel | 分组删除 | kTIMFriendChange_FriendGroupDel |
+* | kTIMFriendChange_FriendGroupModify | 分组修改 | kTIMFriendChange_FriendGroupModify |
+*/
+const TIMFriendChangeType= {
+ kTIMFriendChange_None : 0,
+ kTIMFriendChange_FriendAdd : 1,
+ kTIMFriendChange_FriendDel : 2,
+ kTIMFriendChange_PendencyAdd : 3,
+ kTIMFriendChange_PendencyDel : 4,
+ kTIMFriendChange_BlackListAdd : 5,
+ kTIMFriendChange_BlackListDel : 6,
+ kTIMFriendChange_PendencyReadedReport : 7,
+ kTIMFriendChange_FriendProfileUpdate : 8,
+ kTIMFriendChange_FriendGroupAdd : 9,
+ kTIMFriendChange_FriendGroupDel : 10,
+ kTIMFriendChange_FriendGroupModify : 11
+}
+/**
+* @brief 消息接收选项
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMRecvMsgOpt_Receive | 在线正常接收消息,离线时会进行 APNs 推送 | kTIMRecvMsgOpt_Receive |
+* | kTIMRecvMsgOpt_Not_Receive | 不会接收到消息,离线不会有推送通知 | kTIMRecvMsgOpt_Not_Receive |
+* | kTIMRecvMsgOpt_Not_Notify | 在线正常接收消息,离线不会有推送通知 | kTIMRecvMsgOpt_Not_Notify |
+*/
+const TIMReceiveMessageOpt= {
+ kTIMRecvMsgOpt_Receive : 0,
+ kTIMRecvMsgOpt_Not_Receive : 1,
+ kTIMRecvMsgOpt_Not_Notify : 2
+}
+/**
+* @brief UUID类型
+*
+* | 名称 | 含义 | 值(enum/number) |
+* | ---- | ---- | ---- |
+* | kTIMDownload_VideoThumb | 视频缩略图 | 0 |
+* | kTIMDownload_File | 文件 | kTIMDownload_File |
+* | kTIMDownload_Video | 视频 | kTIMDownload_Video |
+* | kTIMDownload_Sound | 声音 | kTIMDownload_Sound |
+*/
+const TIMDownloadType= {
+ kTIMDownload_VideoThumb : 0,
+ kTIMDownload_File : 1,
+ kTIMDownload_Video : 2,
+ kTIMDownload_Sound : 3
+}
+/**
+* @brief @ 类型
+*
+* | 名称 | 含义 | 值(enum/number) |
+* | ---- | ---- | ---- |
+* | kTIMGroup_At_Me | @ 我 | 1 |
+* | kTIMGroup_At_All | @ 群里所有人 | kTIMGroup_At_All |
+* | kTIMGroup_At_All_At_ME | @ 群里所有人并且单独 @ 我 | kTIMGroup_At_All_At_ME |
+*/
+const TIMGroupAtType= {
+ kTIMGroup_At_Me : 1,
+ kTIMGroup_At_All : 2,
+ kTIMGroup_At_All_At_ME : 3
+}
+const TIMConversationMarkType= {
+ kTIMConversationMarkTypeStar : 1,
+ kTIMConversationMarkTypeUnread : 2,
+ kTIMConversationMarkTypeFold : 4,
+ kTIMConversationMarkTypeHide : 8
+}
+/**
+* @brief 群组加群选项
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMGroupAddOpt_Forbid | 禁止加群 | 0 |
+* | kTIMGroupAddOpt_Auth | 需要管理员审批 | 1 |
+* | kTIMGroupAddOpt_Any | 任何人都可以加群 | 2 |
+*/
+const TIMGroupAddOption= {
+ kTIMGroupAddOpt_Forbid : 0,
+ kTIMGroupAddOpt_Auth : 1,
+ kTIMGroupAddOpt_Any : 2
+}
+/**
+* @brief 群组类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMGroup_Public | 公开群 | kTIMGroup_Public |
+* | kTIMGroup_Private | 私有群 | kTIMGroup_Private |
+* | kTIMGroup_ChatRoom | 聊天室 | kTIMGroup_ChatRoom |
+* | kTIMGroup_BChatRoom | 在线成员广播大群 | kTIMGroup_BChatRoom |
+* | kTIMGroup_AVChatRoom | 互动直播聊天室 | kTIMGroup_AVChatRoom |
+*/
+const TIMGroupType= {
+ kTIMGroup_Public : 0,
+ kTIMGroup_Private : 1,
+ kTIMGroup_ChatRoom : 2,
+ kTIMGroup_BChatRoom : 3,
+ kTIMGroup_AVChatRoom : 4,
+ kTIMGroup_Community : 5
+}
+/**
+* @brief 群组成员角色类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMMemberRole_None | 未定义 | kTIMMemberRole_None |
+* | kTIMMemberRole_Normal | 群成员 | kTIMMemberRole_Normal |
+* | kTIMMemberRole_Admin | 管理员 | kTIMMemberRole_Admin |
+* | kTIMMemberRole_Owner | 超级管理员(群主) | kTIMMemberRole_Owner |
+*/
+const TIMGroupMemberRole= {
+ kTIMMemberRole_None : 0,
+ kTIMMemberRole_Normal : 1,
+ kTIMMemberRole_Admin : 2,
+ kTIMMemberRole_Owner : 3
+}
+/**
+* @brief 群组基础信息
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMGroupMember_HandledErr | 失败 | kTIMGroupMember_HandledErr |
+* | kTIMGroupMember_HandledSuc | 成功 | kTIMGroupMember_HandledSuc |
+* | kTIMGroupMember_Included | 已是群成员 | kTIMGroupMember_Included |
+* | kTIMGroupMember_Invited | 已发送邀请 | kTIMGroupMember_Invited |
+*/
+const HandleGroupMemberResult= {
+ kTIMGroupMember_HandledErr : 0,
+ kTIMGroupMember_HandledSuc : 1,
+ kTIMGroupMember_Included : 2,
+ kTIMGroupMember_Invited : 3
+}
+/**
+* @brief 设置(修改)群组信息的类型
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMGroupModifyInfoFlag_None | 无 | 0x00 |
+* | kTIMGroupModifyInfoFlag_Name | 修改群组名称 | 0x01 |
+* | kTIMGroupModifyInfoFlag_Notification | 修改群公告 | 0x01 << 1 |
+* | kTIMGroupModifyInfoFlag_Introduction | 修改群简介 | 0x01 << 2 |
+* | kTIMGroupModifyInfoFlag_FaceUrl | 修改群头像URL | 0x01 << 3 |
+* | kTIMGroupModifyInfoFlag_AddOption | 修改群组添加选项 | 0x01 << 4 |
+* | kTIMGroupModifyInfoFlag_MaxMmeberNum | 修改群最大成员数 | 0x01 << 5 |
+* | kTIMGroupModifyInfoFlag_Visible | 修改群是否可见 | 0x01 << 6 |
+* | kTIMGroupModifyInfoFlag_Searchable | 修改群是否被搜索 | 0x01 << 7 |
+* | kTIMGroupModifyInfoFlag_ShutupAll | 修改群是否全体禁言 | 0x01 << 8 |
+* | kTIMGroupModifyInfoFlag_Custom | 修改群自定义信息 | 0x01 << 9 |
+* | kTIMGroupModifyInfoFlag_Owner | 修改群主 | 0x01 << 31 |
+*/
+const TIMGroupModifyInfoFlag= {
+ kTIMGroupModifyInfoFlag_None : 0,
+ kTIMGroupModifyInfoFlag_Name : 1,
+ kTIMGroupModifyInfoFlag_Notification : 2,
+ kTIMGroupModifyInfoFlag_Introduction : 4,
+ kTIMGroupModifyInfoFlag_FaceUrl : 8,
+ kTIMGroupModifyInfoFlag_AddOption : 16,
+ kTIMGroupModifyInfoFlag_MaxMemberNum : 32,
+ kTIMGroupModifyInfoFlag_Visible : 64,
+ kTIMGroupModifyInfoFlag_Searchable : 128,
+ kTIMGroupModifyInfoFlag_ShutupAll : 256,
+ kTIMGroupModifyInfoFlag_Custom : 512,
+ kTIMGroupTopicModifyInfoFlag_CustomString : 2048,
+ kTIMGroupModifyInfoFlag_ApproveOption : 4096,
+ kTIMGroupModifyInfoFlag_Owner : -2147483648
+}
+/**
+* @brief 设置(修改)群成员信息的类型
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMGroupMemberModifyFlag_None | 无 | 0x00 |
+* | kTIMGroupMemberModifyFlag_MsgFlag | 修改消息接收选项 | 0x01 |
+* | kTIMGroupMemberModifyFlag_MemberRole | 修改成员角色 | 0x01 << 1 |
+* | kTIMGroupMemberModifyFlag_ShutupTime | 修改禁言时间 | 0x01 << 2 |
+* | kTIMGroupMemberModifyFlag_NameCard | 修改群名片 | 0x01 << 3 |
+* | kTIMGroupMemberModifyFlag_Custom | 修改群成员自定义信息 | 0x01 << 4 |
+*/
+const TIMGroupMemberModifyInfoFlag= {
+ kTIMGroupMemberModifyFlag_None : 0,
+ kTIMGroupMemberModifyFlag_MsgFlag : 1,
+ kTIMGroupMemberModifyFlag_MemberRole : 2,
+ kTIMGroupMemberModifyFlag_ShutupTime : 4,
+ kTIMGroupMemberModifyFlag_NameCard : 8,
+ kTIMGroupMemberModifyFlag_Custom : 16
+}
+/**
+* @brief 未决请求类型
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMGroupPendency_RequestJoin | 请求加群 | 0 |
+* | kTIMGroupPendency_InviteJoin | 邀请加群 | 1 |
+* | kTIMGroupPendency_ReqAndInvite | 邀请和请求的 | 2 |
+*/
+const TIMGroupPendencyType= {
+ kTIMGroupPendency_RequestJoin : 0,
+ kTIMGroupPendency_InviteJoin : 1,
+ kTIMGroupPendency_ReqAndInvite : 2
+}
+/**
+* @brief 群未决处理状态
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMGroupPendency_NotHandle | 未处理 | 0 |
+* | kTIMGroupPendency_OtherHandle | 他人处理 | 1 |
+* | kTIMGroupPendency_OperatorHandle | 操作方处理 | 2 |
+*/
+const TIMGroupPendencyHandle= {
+ kTIMGroupPendency_NotHandle : 0,
+ kTIMGroupPendency_OtherHandle : 1,
+ kTIMGroupPendency_OperatorHandle : 2
+}
+/**
+* @brief 群未决处理操作类型
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMGroupPendency_Refuse | 拒绝 | 0 |
+* | kTIMGroupPendency_Accept | 同意 | 1 |
+*/
+const TIMGroupPendencyHandleResult= {
+ kTIMGroupPendency_Refuse : 0,
+ kTIMGroupPendency_Accept : 1
+}
+/**
+* @brief 群搜索 Field 的枚举
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMGroupSearchFieldKey_GroupId | 群 ID | 0x01 |
+* | kTIMGroupSearchFieldKey_GroupName | 群名称 | 0x01 << 1 |
+*/
+const TIMGroupSearchFieldKey= {
+ kTIMGroupSearchFieldKey_GroupId : 1,
+ kTIMGroupSearchFieldKey_GroupName : 2
+}
+/**
+* @brief 群成员搜索 Field 的枚举
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMGroupMemberSearchFieldKey_Identifier | 用户 ID | 0x01 |
+* | kTIMGroupMemberSearchFieldKey_NikeName | 昵称 | 0x01 << 1 |
+* | kTIMGroupMemberSearchFieldKey_Remark | 备注 | 0x01 << 2 |
+* | kTIMGroupMemberSearchFieldKey_NameCard | 名片 | 0x01 << 3 |
+*/
+const TIMGroupMemberSearchFieldKey= {
+ kTIMGroupMemberSearchFieldKey_Identifier : 1,
+ kTIMGroupMemberSearchFieldKey_NickName : 2,
+ kTIMGroupMemberSearchFieldKey_Remark : 4,
+ kTIMGroupMemberSearchFieldKey_NameCard : 8
+}
+/**
+* @brief 用户性别类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMGenderType_Unkown | 未知性别 | kTIMGenderType_Unkown |
+* | kTIMGenderType_Male | 性别男 | kTIMGenderType_Male |
+* | kTIMGenderType_Female | 性别女 | kTIMGenderType_Female |
+*/
+const TIMGenderType= {
+ kTIMGenderType_Unknown : 0,
+ kTIMGenderType_Male : 1,
+ kTIMGenderType_Female : 2
+}
+/**
+* @brief 用户加好友的选项
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | kTIMProfileAddPermission_Unknown | 未知 | kTIMProfileAddPermission_Unknown |
+* | kTIMProfileAddPermission_AllowAny | 允许任何人添加好友 | kTIMProfileAddPermission_AllowAny |
+* | kTIMProfileAddPermission_NeedConfirm | 添加好友需要验证 | kTIMProfileAddPermission_NeedConfirm |
+* | kTIMProfileAddPermission_DenyAny | 拒绝任何人添加好友 | kTIMProfileAddPermission_DenyAny |
+*/
+const TIMProfileAddPermission= {
+ kTIMProfileAddPermission_Unknown : 0,
+ kTIMProfileAddPermission_AllowAny : 1,
+ kTIMProfileAddPermission_NeedConfirm : 2,
+ kTIMProfileAddPermission_DenyAny : 3
+}
+/**
+* @brief 好友类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | FriendTypeSignle | 单向好友:用户A的好友表中有用户B,但B的好友表中却没有A | FriendTypeSignle |
+* | FriendTypeBoth | 双向好友:用户A的好友表中有用户B,B的好友表中也有A | FriendTypeBoth |
+*/
+const TIMFriendType= {
+ FriendTypeSingle : 0,
+ FriendTypeBoth : 1
+}
+/**
+* @brief 好友添加请求未决类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | FriendPendencyTypeComeIn | 别人发给我的 | FriendPendencyTypeComeIn |
+* | FriendPendencyTypeSendOut | 我发给别人的 | FriendPendencyTypeSendOut |
+* | FriendPendencyTypeBoth | 双向的 | FriendPendencyTypeBoth |
+*/
+const TIMFriendPendencyType= {
+ FriendPendencyTypeComeIn : 0,
+ FriendPendencyTypeSendOut : 1,
+ FriendPendencyTypeBoth : 2
+}
+/**
+* @brief 好友添加请求响应的动作类型
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | ResponseActionAgree | 同意 | ResponseActionAgree |
+* | ResponseActionAgreeAndAdd | 同意并添加 | ResponseActionAgreeAndAdd |
+* | ResponseActionReject | 拒绝 | ResponseActionReject |
+*/
+const TIMFriendResponseAction= {
+ ResponseActionAgree : 0,
+ ResponseActionAgreeAndAdd : 1,
+ ResponseActionReject : 2
+}
+/**
+* @brief 二者之间的关系
+*
+* | 名称 | 含义 | 值(enum) |
+* | ---- | ---- | ---- |
+* | FriendCheckNoRelation | 无关系 | FriendCheckNoRelation |
+* | FriendCheckAWithB | 仅A中有B | FriendCheckAWithB |
+* | FriendCheckBWithA | 仅B中有A | FriendCheckBWithA |
+* | FriendCheckBothWay | 双向的 | FriendCheckBothWay |
+*/
+const TIMFriendCheckRelation= {
+ FriendCheckNoRelation : 0,
+ FriendCheckAWithB : 1,
+ FriendCheckBWithA : 2,
+ FriendCheckBothWay : 3
+}
+/**
+* @brief 好友搜索的枚举
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMFriendshipSearchFieldKey_Identifier | userid | 0x01 |
+* | kTIMFriendshipSearchFieldKey_NikeName | 昵称 | 0x01 << 1 |
+* | kTIMFriendshipSearchFieldKey_Remark | 备注 | 0x01 << 2 |
+*/
+const TIMFriendshipSearchFieldKey= {
+ kTIMFriendshipSearchFieldKey_Identifier : 1,
+ kTIMFriendshipSearchFieldKey_NickName : 2,
+ kTIMFriendshipSearchFieldKey_Remark : 4
+}
+/**
+* @brief 二者之间的关系
+*
+* | 名称 | 含义 | 值(number) |
+* | ---- | ---- | ---- |
+* | kTIMFriendshipRelationType_None | 未知关系 | 0 |
+* | kTIMFriendshipRelationType_InMyFriendList | 向好友:对方是我的好友,我不是对方的好友 | 0x01 << 1 |
+* | kTIMFriendshipRelationType_InOtherFriendList | 单向好友:对方不是我的好友,我是对方的好友 | 0x01 << 2 |
+* | kTIMFriendshipRelationType_BothFriend | 双向好友 | 0x01 <<2 TODO 值感觉不对 |
+*/
+const TIMFriendshipRelationType= {
+ kTIMFriendshipRelationType_None : 0,
+ kTIMFriendshipRelationType_InMyFriendList : 2,
+ kTIMFriendshipRelationType_InOtherFriendList : 4,
+ kTIMFriendshipRelationType_BothFriend : 4
+}
+/**
+* @brief 实验性接口的操作类型
+*/
+const TIMInternalOperation= {
+ kTIMInternalOperationSSOData : "internal_operation_sso_data",
+ kTIMInternalOperationUserId2TinyId : "internal_operation_userid_tinyid",
+ kTIMInternalOperationTinyId2UserId : "internal_operation_tinyid_userid",
+ kTIMInternalOperationSetEnv : "internal_operation_set_env",
+ kTIMInternalOperationSetMaxRetryCount : "internal_operation_set_max_retry_count",
+ kTIMInternalOperationSetPrivatizationInfo : "internal_operation_set_privatization_info",
+ kTIMInternalOperationInitLocalStorage : "internal_operation_init_local_storage",
+ kTIMInternalOperationSetUIPlatform : "internal_operation_set_ui_platform",
+ kTIMInternalOperationSetCosSaveRegionForConversation : "internal_operation_set_cos_save_region_for_conversation",
+ kTIMInternalOperationSetIPv6Prior : "internal_operation_set_ipv6_prior",
+ kTIMInternalOperationSetPacketRequestTimeout : "internal_operation_set_packet_request_timeout",
+ kTIMInternalOperationSetCustomServerInfo : "internal_operation_set_custom_server_info",
+ kTIMInternalOperationSetQuicChannelInfo : "internal_operation_set_quic_channel_info",
+ kTIMInternalOperationSetSM4GCMCallback : "internal_operation_set_sm4_gcm_callback",
+ kTIMInternalOperationSetDatabaseEncryptInfo : "internal_operation_set_database_encrypt_info",
+ kTIMInternalOperationIsCommercialAbilityEnabled : "internal_operation_is_commercial_ability_enabled",
+ kTIMInternalOperationSetOfflinePushState : "internal_operation_set_offline_push_state",
+ kTIMInternalOperationGetOfflinePushState : "internal_operation_get_offline_push_state",
+ kTIMInternalOperationGetMessageRevoker : "internal_operation_get_message_revoker",
+ kTIMInternalOperationWriteLog : "internal_operation_write_log",
+ kTIMInternalOperationClearLocalHistoryMessage : "internal_operation_clear_local_history_message"
+}
+/**
+* @brief ios 离线推送的类型
+*/
+const TIMIOSOfflinePushType= {
+ TIMIOSOfflinePushType_APNS : 0,
+ TIMIOSOfflinePushType_VOIP : 1
+}
+/**
+* @brief 离线推送注册的token类型
+*/
+const TIMOfflinePushTokenType= {
+ TIMOfflinePushTokenType_Default : 0,
+ TIMOfflinePushTokenType_TPNS : 1,
+ TIMOfflinePushTokenType_VOIP : 2
+}
+/**
+* @brief 用户状态类型
+*/
+const TIMUserStatusType= {
+ kTIMUserStatusType_Unkown : 0,
+ kTIMUserStatusType_Online : 1,
+ kTIMUserStatusType_Offline : 2,
+ kTIMUserStatusType_UnLogined : 3
+}
+const TIMSignalingActionType= {
+ TIMSignalingActionType_Unknown : 0,
+ TIMSignalingActionType_Invite : 1,
+ TIMSignalingActionType_CancelInvite : 2,
+ TIMSignalingActionType_AcceptInvite : 3,
+ TIMSignalingActionType_RejectInvite : 4,
+ TIMSignalingActionType_InviteTimeout : 5
+}
+const TIMPermissionGroupModifyInfoFlag= {
+ kTIMPermissionGroupModifyInfoFlag_None : 0,
+ kTIMPermissionGroupModifyInfoFlag_Name : 1,
+ kTIMPermissionGroupModifyInfoFlag_GroupPermission : 2,
+ kTIMPermissionGroupModifyInfoFlag_CustomData : 4
+}
+const V2TIMCommunityPermissionValue= {
+ V2TIM_COMMUNITY_PERMISSION_MANAGE_GROUP_INFO : 1,
+ V2TIM_COMMUNITY_PERMISSION_MANAGE_GROUP_MEMBER : 2,
+ V2TIM_COMMUNITY_PERMISSION_MANAGE_PERMISSION_GROUP_INFO : 4,
+ V2TIM_COMMUNITY_PERMISSION_MANAGE_PERMISSION_GROUP_MEMBER : 8,
+ V2TIM_COMMUNITY_PERMISSION_MANAGE_TOPIC_IN_COMMUNITY : 16,
+ V2TIM_COMMUNITY_PERMISSION_MUTE_MEMBER : 32,
+ V2TIM_COMMUNITY_PERMISSION_SEND_MESSAGE : 64,
+ V2TIM_COMMUNITY_PERMISSION_AT_ALL : 128,
+ V2TIM_COMMUNITY_PERMISSION_GET_HISTORY_MESSAGE : 256,
+ V2TIM_COMMUNITY_PERMISSION_REVOKE_OTHER_MEMBER_MESSAGE : 512,
+ V2TIM_COMMUNITY_PERMISSION_BAN_MEMBER : 1024
+}
+const V2TIMTopicPermissionValue= {
+ V2TIM_TOPIC_PERMISSION_MANAGE_TOPIC : 1,
+ V2TIM_TOPIC_PERMISSION_MANAGE_TOPIC_PERMISSION : 2,
+ V2TIM_TOPIC_PERMISSION_MUTE_MEMBER : 4,
+ V2TIM_TOPIC_PERMISSION_SEND_MESSAGE : 8,
+ V2TIM_TOPIC_PERMISSION_GET_HISTORY_MESSAGE : 16,
+ V2TIM_TOPIC_PERMISSION_REVOKE_OTHER_MEMBER_MESSAGE : 32,
+ V2TIM_TOPIC_PERMISSION_AT_ALL : 64
+}
+
+export { HandleGroupMemberResult, TIMAndroidOfflinePushNotifyMode, TIMConvEvent, TIMConvType, TIMConversationMarkType, TIMDownloadType, TIMElemType, TIMErrCode, TIMFriendChangeType, TIMFriendCheckRelation, TIMFriendPendencyType, TIMFriendResponseAction, TIMFriendType, TIMFriendshipRelationType, TIMFriendshipSearchFieldKey, TIMGenderType, TIMGroupAddOption, TIMGroupAtType, TIMGroupGetInfoFlag, TIMGroupMemberInfoFlag, TIMGroupMemberModifyInfoFlag, TIMGroupMemberRole, TIMGroupMemberRoleFlag, TIMGroupMemberSearchFieldKey, TIMGroupModifyInfoFlag, TIMGroupPendencyHandle, TIMGroupPendencyHandleResult, TIMGroupPendencyType, TIMGroupReportType, TIMGroupSearchFieldKey, TIMGroupTipGroupChangeFlag, TIMGroupTipType, TIMGroupType, TIMIOSOfflinePushType, TIMImageLevel, TIMInternalOperation, TIMLogLevel, TIMLoginStatus, TIMMsgPriority, TIMMsgStatus, TIMNetworkStatus, TIMOfflinePushFlag, TIMOfflinePushTokenType, TIMPermissionGroupModifyInfoFlag, TIMPlatform, TIMProfileAddPermission, TIMProfileChangeType, TIMReceiveMessageOpt, TIMResult, TIMSignalingActionType, TIMUserStatusType, V2TIMCommunityPermissionValue, V2TIMTopicPermissionValue };
diff --git a/src/renderer/src/plugins/imChat/imLiseners.js b/src/renderer/src/plugins/imChat/imLiseners.js
new file mode 100644
index 0000000..0342a76
--- /dev/null
+++ b/src/renderer/src/plugins/imChat/imLiseners.js
@@ -0,0 +1,420 @@
+/**
+ * @description im 事件监听
+ * @author zdg
+ * @date 2023-07-07
+ */
+// @ts-ignore
+const API = window?.api || {}
+const timRenderInstance = API?.getTimRender?.() || {}
+
+// im 事件监听
+export class IMListeners {
+ // 初始化监听
+ static initListeners(callback) {
+ /**
+ * 注销消息监听事件
+ */
+ timRenderInstance.TIMRemoveRecvNewMsgCallback()
+ /**
+ * @brief 增加接收新消息回调
+ * @param cb 新消息回调函数,请参考[TIMRecvNewMsgCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 如果用户是登录状态,ImSDK收到新消息会通过此接口设置的回调抛出,另外需要注意,抛出的消息不一定是未读的消息,
+ * 只是本地曾经没有过的消息(例如在另外一个终端已读,拉取最近联系人消息时可以获取会话最后一条消息,如果本地没有,会通过此方法抛出)。
+ * 在用户登录之后,ImSDK会拉取离线消息,为了不漏掉消息通知,需要在登录之前注册新消息通知。
+ */
+ timRenderInstance.TIMAddRecvNewMsgCallback({
+ callback:(args)=>{
+ callback({
+ type: 'TIMAddRecvNewMsgCallback',
+ data: JSON.parse(args[0])
+ })
+ },
+ user_data: "test"
+ })
+
+
+ /**
+ * @brief 1.3 设置消息已读回执回调
+ * @param cb 消息已读回执回调,请参考[TIMMsgReadedReceiptCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 发送方发送消息,接收方调用接口[TIMMsgReportReaded]()上报该消息已读,发送方ImSDK会通过此接口设置的回调抛出。
+ */
+ timRenderInstance.TIMSetMsgReadedReceiptCallback({
+ callback:(args)=>{
+ callback({
+ type: 'TIMSetMsgReadedReceiptCallback',
+ data: JSON.parse(args[0])
+ })
+ },
+ user_data: "test"
+ })
+
+
+ /**
+ * @brief 1.4 设置接收的消息被撤回回调
+ * @param cb 消息撤回通知回调,请参考[TIMMsgRevokeCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 发送方发送消息,接收方收到消息。此时发送方调用接口[TIMMsgRevoke]()撤回该消息,接收方的ImSDK会通过此接口设置的回调抛出。
+ */
+ timRenderInstance.TIMSetMsgRevokeCallback({
+ callback:(args)=>{
+ callback({
+ type: 'TIMSetMsgRevokeCallback',
+ data: JSON.parse(args[0])
+ });
+ },
+ user_data: "test"
+ })
+
+
+
+ /**
+ * @brief 1.5 设置消息内元素相关文件上传进度回调
+ * @param cb 文件上传进度回调,请参考[TIMMsgElemUploadProgressCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 设置消息元素上传进度回调。当消息内包含图片、声音、文件、视频元素时,ImSDK会上传这些文件,并触发此接口设置的回调,用户可以根据回调感知上传的进度
+ */
+ timRenderInstance.TIMSetMsgElemUploadProgressCallback({
+ callback:(args)=>{
+ try{
+ const [message, index, cur_size, total_size, user_data] = JSON.parse(args)
+ callback({
+ type: 'TIMSetMsgElemUploadProgressCallback',
+ data: {
+ message: JSON.parse(message),
+ index,
+ cur_size,
+ total_size,
+ user_data
+ }
+ })
+ }catch(err){
+ throw err;
+ }
+ },
+ user_data: "test"
+ })
+
+ /**
+ * @brief 1.6 设置群组系统消息回调
+ * @param cb 群消息回调,请参考[TIMGroupTipsEventCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 群组系统消息事件包括 加入群、退出群、踢出群、设置管理员、取消管理员、群资料变更、群成员资料变更。此消息是针对所有群组成员下发的
+ */
+ timRenderInstance.TIMSetGroupTipsEventCallback({
+ callback:(args)=>{
+ callback({
+ type: 'TIMSetGroupTipsEventCallback',
+ data: JSON.parse(args[0])
+ })
+ },
+ })
+
+
+ /**
+ * @brief 1.7 设置群组属性变更回调
+ * @param cb 群组属性变更回调,请参考[TIMGroupAttributeChangedCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 某个已加入的群的属性被修改了,会返回所在群组的所有属性(该群所有的成员都能收到)
+ */
+ timRenderInstance.TIMSetGroupAttributeChangedCallback({
+ callback: (...args) => {callback({ type: 'TIMSetGroupAttributeChangedCallback', data: args })},
+ userData: ""
+ })
+
+
+
+ /**
+ * @brief 1.8 设置会话事件回调
+ * @param cb 会话事件回调,请参考[TIMConvEventCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * > 会话事件包括:
+ * >> 会话新增
+ * >> 会话删除
+ * >> 会话更新。
+ * >> 会话开始
+ * >> 会话结束
+ * > 任何产生一个新会话的操作都会触发会话新增事件,例如调用接口[TIMConvCreate]()创建会话,接收到未知会话的第一条消息等。
+ * 任何已有会话变化的操作都会触发会话更新事件,例如收到会话新消息,消息撤回,已读上报等。
+ * 调用接口[TIMConvDelete]()删除会话成功时会触发会话删除事件。
+ */
+ timRenderInstance.TIMSetConvEventCallback({
+ callback:(args)=>{
+ callback({
+ type: 'TIMSetConvEventCallback',
+ data: {
+ type:args[0],
+ data:args[1]!=="" ? JSON.parse(args[1]) : []
+ }
+ })
+ },
+ user_data:"TIMSetConvEventCallback"
+ })
+
+ /**
+ * @brief 1.9 设置会话未读消息总数变更的回调
+ * @param cb 会话未读消息总数变更的回调,请参考[TIMConvTotalUnreadMessageCountChangedCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ */
+ timRenderInstance.TIMSetConvTotalUnreadMessageCountChangedCallback({
+ callback:(args)=>{
+ callback({
+ type: 'TIMSetConvTotalUnreadMessageCountChangedCallback',
+ data: args[0]
+ })
+ }
+ })
+
+
+ /**
+ * @brief 1.10 设置网络连接状态监听回调
+ * @param cb 连接事件回调,请参考[TIMNetworkStatusListenerCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * > 当调用接口 [TIMInit]() 时,ImSDK会去连接云后台。此接口设置的回调用于监听网络连接的状态。
+ * > 网络连接状态包含四个:正在连接、连接失败、连接成功、已连接。这里的网络事件不表示用户本地网络状态,仅指明ImSDK是否与即时通信IM云Server连接状态。
+ * > 可选设置,如果要用户感知是否已经连接服务器,需要设置此回调,用于通知调用者跟通讯后台链接的连接和断开事件,另外,如果断开网络,等网络恢复后会自动重连,自动拉取消息通知用户,用户无需关心网络状态,仅作通知之用
+ * > 只要用户处于登录状态,ImSDK内部会进行断网重连,用户无需关心。
+ */
+ timRenderInstance.TIMSetNetworkStatusListenerCallback({
+ callback: (...args) => {callback({ type: 'TIMSetNetworkStatusListenerCallback', data: args })},
+ userData: ""
+ })
+
+ /**
+ * @brief 1.11 设置被踢下线通知回调
+ * @param cb 踢下线回调,请参考[TIMKickedOfflineCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * > 用户如果在其他终端登录,会被踢下线,这时会收到用户被踢下线的通知,出现这种情况常规的做法是提示用户进行操作(退出,或者再次把对方踢下线)。
+ * > 用户如果在离线状态下被踢,下次登录将会失败,可以给用户一个非常强的提醒(登录错误码ERR_IMSDK_KICKED_BY_OTHERS:6208),开发者也可以选择忽略这次错误,再次登录即可。
+ * > 用户在线情况下的互踢情况:
+ * + 用户在设备1登录,保持在线状态下,该用户又在设备2登录,这时用户会在设备1上强制下线,收到 TIMKickedOfflineCallback 回调。
+ * 用户在设备1上收到回调后,提示用户,可继续调用login上线,强制设备2下线。这里是在线情况下互踢过程。
+ * > 用户离线状态互踢:
+ * + 用户在设备1登录,没有进行logout情况下进程退出。该用户在设备2登录,此时由于用户不在线,无法感知此事件,
+ * 为了显式提醒用户,避免无感知的互踢,用户在设备1重新登录时,会返回(ERR_IMSDK_KICKED_BY_OTHERS:6208)错误码,表明之前被踢,是否需要把对方踢下线。
+ * 如果需要,则再次调用login强制上线,设备2的登录的实例将会收到 TIMKickedOfflineCallback 回调。
+ */
+ timRenderInstance.TIMSetKickedOfflineCallback({
+ callback: (...args) => {
+ callback({
+ type: 'TIMSetKickedOfflineCallback',
+ data: args
+ });
+ },
+ userData: ""
+ })
+
+
+ /**
+ * @brief 1.12 设置票据过期回调
+ * @param cb 票据过期回调,请参考[TIMUserSigExpiredCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 用户票据,可能会存在过期的情况,如果用户票据过期,此接口设置的回调会调用。
+ * [TIMLogin]()也将会返回70001错误码。开发者可根据错误码或者票据过期回调进行票据更换
+ */
+ timRenderInstance.TIMSetUserSigExpiredCallback({
+ callback: (...args) => {callback({ type: 'TIMSetUserSigExpiredCallback', data: args })},
+ userData: ""
+ })
+
+
+ /**
+ * @brief 1.13 设置添加好友的回调
+ * @param cb 添加好友回调,请参考[TIMOnAddFriendCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 此回调为了多终端同步。例如A设备、B设备都登录了同一帐号的ImSDK,A设备添加了好友,B设备ImSDK会收到添加好友的推送,ImSDK通过此回调告知开发者。
+ */
+ timRenderInstance.TIMSetOnAddFriendCallback({
+ callback: (...args) => {
+ console.log('=====添加好友=====', args)
+ callback({ type: 'TIMSetOnAddFriendCallback', data: args })
+ },
+ userData: ""
+ })
+
+
+ /**
+ * @brief 1.14 设置删除好友的回调
+ * @param cb 删除好友回调,请参考[TIMOnDeleteFriendCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 此回调为了多终端同步。例如A设备、B设备都登录了同一帐号的ImSDK,A设备删除了好友,B设备ImSDK会收到删除好友的推送,ImSDK通过此回调告知开发者。
+ */
+ timRenderInstance.TIMSetOnDeleteFriendCallback({
+ callback: (...args) => {callback({ type: 'TIMSetOnDeleteFriendCallback', data: args })},
+ userData: ""
+ })
+
+ /**
+ * @brief 1.15 设置更新好友资料的回调
+ * @param cb 更新好友资料回调,请参考[TIMUpdateFriendProfileCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 此回调为了多终端同步。例如A设备、B设备都登录了同一帐号的ImSDK,A设备更新了好友资料,B设备ImSDK会收到更新好友资料的推送,ImSDK通过此回调告知开发者。
+ */
+ timRenderInstance.TIMSetUpdateFriendProfileCallback({
+ callback: (...args) => {callback({ type: 'TIMSetUpdateFriendProfileCallback', data: args })},
+ userData: ""
+ })
+
+
+ /**
+ * @brief 1.16 设置好友添加请求的回调
+ * @param cb 好友添加请求回调,请参考[TIMFriendAddRequestCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 当前登入用户设置添加好友需要确认时,如果有用户请求加当前登入用户为好友,会收到好友添加请求的回调,ImSDK通过此回调告知开发者。如果多终端登入同一帐号,每个终端都会收到这个回调。
+ */
+ timRenderInstance.TIMSetFriendAddRequestCallback({
+ callback: (...args) => {
+ console.log('=====添加好友请求=====', JSON.parse(args[0][0]));
+ callback({
+ type: 'TIMSetFriendAddRequestCallback',
+ data: JSON.parse(args[0][0])
+ })
+ },
+ userData: ""
+ })
+
+
+ /**
+ * @brief 1.17 设置好友申请删除的回调
+ * @param cb 好友申请删除回调,请参考[TIMFriendApplicationListDeletedCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 1. 主动删除好友申请
+ * 2. 拒绝好友申请
+ * 3. 同意好友申请
+ * 4. 申请加别人好友被拒绝
+ */
+ timRenderInstance.TIMSetFriendApplicationListDeletedCallback({
+ callback: (...args) => {callback({ type: 'TIMSetFriendApplicationListDeletedCallback', data: args })},
+ userData: ""
+ })
+
+ /**
+ * @brief 1.18 设置好友申请已读的回调
+ * @param cb 好友申请已读回调,请参考[TIMFriendApplicationListReadCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * 如果调用 setFriendApplicationRead 设置好友申请列表已读,会收到这个回调(主要用于多端同步)
+ */
+
+ timRenderInstance.TIMSetFriendApplicationListReadCallback({
+ callback: (...args) => {callback({ type: 'TIMSetFriendApplicationListReadCallback', data: args })},
+ userData: ""
+ })
+
+
+ /**
+ * @brief 1.19 设置黑名单新增的回调
+ * @param cb 黑名单新增的回调,请参考[TIMFriendBlackListAddedCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ */
+ timRenderInstance.TIMSetFriendBlackListAddedCallback({
+ callback: (...args) => {callback({ type: 'TIMSetFriendBlackListAddedCallback', data: args })},
+ userData: ""
+ })
+
+ /**
+ * @brief 1.20 设置黑名单删除的回调
+ * @param cb 黑名单删除的回调,请参考[TIMFriendBlackListDeletedCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ */
+ timRenderInstance.TIMSetFriendBlackListDeletedCallback({
+ callback: (...args) => {callback({ type: 'TIMSetFriendBlackListDeletedCallback', data: args })},
+ userData: ""
+ })
+
+
+ /**
+ * @brief 1.22 设置消息在云端被修改后回传回来的消息更新通知回调
+ * @param cb 消息更新回调,请参考[TIMMsgUpdateCallback](TIMCloudCallback.h)
+ * @param user_data 用户自定义数据,ImSDK只负责传回给回调函数cb,不做任何处理
+ *
+ * @note
+ * > 当您发送的消息在服务端被修改后,ImSDK会通过该回调通知给您
+ * > 您可以在您自己的服务器上拦截所有即时通信IM消息 [发单聊消息之前回调](https://cloud.tencent.com/document/product/269/1632)
+ * > 设置成功之后,即时通信IM服务器会将您的用户发送的每条消息都同步地通知给您的业务服务器。
+ * > 您的业务服务器可以对该条消息进行修改(例如过滤敏感词),如果您的服务器对消息进行了修改,ImSDK就会通过此回调通知您。
+ */
+ timRenderInstance.TIMSetMsgUpdateCallback({
+ callback: (...args) => {callback({ type: 'TIMSetMsgUpdateCallback', data: args })},
+ userData: ""
+ })
+ timRenderInstance.TIMOnInvited({
+ callback:(data)=>{
+ callback({
+ type: 'TIMOnInvited',
+ data: data
+ })
+ }
+ })
+ timRenderInstance.TIMOnRejected({
+ callback:(data)=>{
+ callback({
+ type: 'TIMOnRejected',
+ data: data
+ })
+ }
+ })
+ timRenderInstance.TIMOnAccepted({
+ callback:(data)=>{
+ callback({
+ type: 'TIMOnAccepted',
+ data: data
+ })
+ }
+ })
+ timRenderInstance.TIMOnCancelled({
+ callback:(data)=>{
+ callback({
+ type: 'TIMOnCancelled',
+ data: data
+ })
+ }
+ })
+
+ timRenderInstance.TIMOnTimeout({
+ callback:(data)=>{
+ callback({
+ type: 'TIMOnTimeout',
+ data: data
+ })
+ }
+ })
+ }
+}
+
+export default { timRenderInstance, initListeners: IMListeners.initListeners }
\ No newline at end of file
diff --git a/src/renderer/src/plugins/imChat/index.js b/src/renderer/src/plugins/imChat/index.js
new file mode 100644
index 0000000..dda4bfa
--- /dev/null
+++ b/src/renderer/src/plugins/imChat/index.js
@@ -0,0 +1,341 @@
+/**
+ * @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' // sdk相关枚举
+import MsgEnum from './msgEnum' // 消息相关枚举(自定义)
+import IMListeners from './imLiseners' // im消息-监听器
+// @ts-ignore
+const API = window.api
+// TIM生成签名
+// import * as GenerateUserSig from './userSig' // 引入签名生成器
+const isDev = process.env.NODE_ENV == 'development' // 环境
+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_Off,
+ // 群组类型-会议群(Meeting),成员上限 6000 人
+ group_type: TYPES.TIMGroupType.kTIMGroup_ChatRoom,
+ }
+
+
+ /**
+ * @description 构造函数
+ * @param {number} SDKAppID
+ * @param {string} userSig
+ * @param {string} userID
+ * @param {boolean} isInit
+ */
+ constructor(SDKAppID, userSig, userID, isInit) {
+ this.SDKAppID = SDKAppID
+ this.userSig = userSig
+ this.userID = userID
+ // window.test = this
+ if (isInit) this.init()
+ }
+ // 设置配置
+ async setConfig() {
+ const log_level = TYPES.TIMLogLevel.kTIMLog_Error
+ await this.timChat.TIMSetConfig({ // TIMSetConfigParam
+ json_config: { // JSONCongfig
+ set_config_log_level: log_level,
+ set_config_callback_log_level: 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 => {
+ this.setConsole('%cchat-log ', data[1])
+ },
+ user_data: ''
+ })
+ }
+ // 初始化-imChat
+ init() {
+ return new Promise(async(resolve, reject) => {
+ try {
+ if(!API) reject('preload api获取失败, 初始化-未完成')
+ // this.timChat = await API.getTimRender()
+ this.timChat = IMListeners.timRenderInstance
+ const code = await this.timChat.TIMInit()
+ if (code == 0) { // 初始化成功
+ this.setConsole('%cim-chat: init', '初始化成功')
+ this.status.isConnect = true
+ this.setConfig() // 设置日志级别
+ resolve(this)
+ } else { // 失败:具体请看code
+ console.error('[im-chat]:初始化失败', code)
+ reject(code)
+ }
+ } catch (error) {reject(error)}
+ })
+ }
+ // 生成签名
+ genTestUserSig() {
+ // const options = {
+ // SDKAppID: this.SDKAppID,
+ // secretKey: this.secretKey,
+ // userID: this.userID,
+ // }
+ // const { userSig } = GenerateUserSig.genTestUserSig(options)
+ // this.userSig = userSig
+ }
+ /**
+ * @description 监听消息
+ * @param {Function} callback
+ */
+ watch(callback) {
+ // // 先移除监听
+ // this.timChat.TIMRemoveRecvNewMsgCallback()
+ // // 消息监听
+ // this.timChat.TIMAddRecvNewMsgCallback({
+ // callback, user_data: this.toStr('msg')
+ // })
+ // // 群消息监听
+ // // 群组系统消息事件包括 加入群、退出群、踢出群、设置管理员、取消管理员、群资料变更、群成员资料变更。此消息是针对所有群组成员下发的
+ // this.timChat.TIMSetGroupTipsEventCallback({
+ // // callback, user_data: this.toStr('msg-group')
+ // callback: (data) => {
+ // // console.log('群消息', group_tips_event)
+ // this.setConsole('%c群消息', data)
+ // },
+ // })
+ if (this.timChat && this.status.isConnect) { // 连接成功允许监听
+ this.setConsole('%cim-chat: watch', '监听成功')
+ IMListeners.initListeners(callback)
+ } else {
+ this.setConsole('%cim-chat: watch', '监听失败, 未连接')
+ }
+ }
+ // 登录
+ 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.setConsole('%cim-chat: login', '登录成功')
+ this.status.isLogin = true
+ resolve({status:0, msg:'登录成功', data:res})
+ } else reject(res)
+ } else {
+ if (status == 1) { // 已登录
+ this.setConsole('%cim-chat: login', '已登录')
+ resolve({status, msg:'已登录'})
+ } else if (status == 2) { // 登录中
+ this.setConsole('%cim-chat: login', '登录中')
+ resolve({status, msg:'登录中'})
+ } else if (status == 4) { // 登出中
+ this.setConsole('%cim-chat: login', '登出中')
+ resolve({status, msg:'登出中'})
+ }
+ }
+
+ }
+ return new Promise(fn)
+ }
+ // 登出
+ logout() {
+ if (!this.timChat) return
+ return this.timChat.TIMLogout().then(res => {
+ this.setConsole('%cim-chat: logout', '登出成功')
+ this.status.isLogin = false
+ this.timChat.TIMUninit() // 反初始化
+ return res
+ }).catch(error => {
+ this.setConsole('%cim-chat: logout', '登出失败', error)
+ return error
+ })
+ }
+ /**
+ * @description 创建群组 群名和初始成员 userID
+ * @param {any} name
+ * @param {any[]} memberList
+ */
+ 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) {
+ this.setConsole('%c创建群组成功', res)
+ const timGroupId = res?.json_param?.create_group_result_groupid
+ if (!!timGroupId && timGroupId != 'undefined'){
+ // this.setConsole('%c创建群组成功', timGroupId)
+ this.timGroupId = timGroupId
+ // this.setGroupMsgReceive()
+ }
+ }
+ return res
+ })
+ }
+ // 删除群组
+ deleteGroup() {
+ if (!this.timGroupId) return
+ return this.timChat.TIMGroupDelete({
+ groupId: this.timGroupId,
+ data: '', // 用户自定义数据
+ })
+ }
+ /**
+ * @description 设置群消息接收
+ * @param {string} timGroupId
+ */
+ setGroupMsgReceive(timGroupId) {
+ if (!this.timGroupId) this.timGroupId = timGroupId || ''
+ if (!this.timGroupId) return console.log('timGroupId为空')
+ return this.timChat.TIMMsgSetGroupReceiveMessageOpt({
+ groupId: this.timGroupId,
+ opt: TYPES.TIMReceiveMessageOpt.kTIMRecvMsgOpt_Receive,
+ data: '', // 用户自定义数据
+ })
+ }
+ // 获取群组列表
+ getGroupList() {
+ return this.timChat.getGroupList().then(res => {
+ console.log('获取群组列表', res)
+ return res
+ }).catch(error => {
+ console.log('获取群组列表失败', error)
+ return error
+ })
+ }
+ /**
+ * @description 发送消息
+ * @param {any} conv_id
+ * @param {any} msg
+ */
+ sendMsg(conv_id, msg) {
+ if (!conv_id) return console.log('conv_id为空')
+ if (typeof msg == 'object') msg = JSON.stringify(msg)
+ const option = {
+ conv_id,
+ conv_type: TYPES.TIMConvType.kTIMConv_Group,
+ params: {
+ message_elem_array: [{
+ elem_type: TYPES.TIMElemType.kTIMElem_Text,
+ text_elem_content: msg
+ }],
+ // message_conv_id: conv_id,
+ // message_conv_type: TYPES.TIMConvType.kTIMConv_Group,
+ // message_sender: this.userID
+ },
+ user_data: '', // 用户自定义数据
+ // callback: (data) => {}
+ }
+ // console.log('发送消息', option)
+ this.setConsole('%cim-chat: 发送消息', option)
+ return this.timChat.TIMMsgSendMessageV2(option)
+ }
+ /**
+ * @description 发送群消息
+ * @param {any} msg
+ * @param {*} head
+ * @param {*} type
+ */
+ sendMsgGroup(msg, head, type) {
+ const msgObj = this.getMsgObj(head, msg, type)
+ // console.log('发送群消息', msgObj)
+ return this.sendMsg(this.timGroupId, msgObj)
+ }
+ // 发送关闭(下课)消息
+ sendMsgClosed(){
+ const msg = this.getMsgObj(MsgEnum.HEADS.MSG_closed, '下课')
+ return this.sendMsg(this.timGroupId, msg)
+ }
+ /**
+ * @description 获取消息对象
+ * @param {string} msgHead
+ * @param {string} msg
+ * @param {string} type
+ * @param {string|number} sender
+ */
+ getMsgObj(msgHead, msg, type, sender, option={}) {
+ if (!msgHead) throw new Error('msgHead is required')
+ if (!msg) throw new Error('msg is required')
+ if (typeof msg === 'object') msg = JSON.stringify(msg)
+ return {
+ msgKey: msgHead,
+ msgcontent: msg,
+ msgType: type || MsgEnum.TYPES.TEACHER, // 默认为老师
+ senduserid: sender ?? this.userID,
+ ...option
+ }
+ }
+ /**
+ * @description 设置控制台样式
+ * @param {string} hearStr
+ * @param {string[]} args
+ */
+ setConsole(hearStr,...args) {
+ const css = 'color: #fff;background-color:#2ccb92;padding:3px 5px;border-radius:3px;'
+ const time = new Date().toLocaleTimeString()
+ if (!hearStr) hearStr = '%c' + time
+ console.log(hearStr, css, ...args)
+ }
+ /**
+ * @description 获取数据字符串
+ * @param {*} data
+ * @returns
+ */
+ toStr = (data) => {
+ if (typeof data === 'string') data = {type: data}
+ return JSON.stringify(data)
+ }
+}
\ No newline at end of file
diff --git a/src/renderer/src/plugins/imChat/msgEnum.js b/src/renderer/src/plugins/imChat/msgEnum.js
new file mode 100644
index 0000000..da8e486
--- /dev/null
+++ b/src/renderer/src/plugins/imChat/msgEnum.js
@@ -0,0 +1,137 @@
+/**
+ * @description 消息枚举
+ * @author zdg
+ * @date 2021-07-05 14:07:01
+ */
+
+export class MsgEnum {
+ /**
+ * @description: 消息类型
+ *
+ * | 名称 | 含义 | 值(enum) |
+ * | ---- | ---- | ---- |
+ * | SYSTEM | 系统消息 | system |
+ * | TEACHER | 老师消息 | teacher |
+ * | STUDENT | 学生消息 | student |
+ * | NOTICE | 通知消息 | notice |
+ */
+ static TYPES = {
+ /** @desc: 系统消息 */
+ SYSTEM: 'system',
+ /** @desc: 老师消息 */
+ TEACHER: 'teacher',
+ /** @desc: 学生消息 */
+ STUDENT: 'student',
+ /** @desc: 通知消息 */
+ NOTICE: 'notice'
+ }
+ /**
+ * @description: 消息头-类型
+ *
+ * | 名称 | 含义 | 值(enum) |
+ * | ---- | ---- | ---- |
+ * | --- | 以下为旧定义-消息头 | --- |
+ * | MSG_closed | 结束课程(下课) | closed |
+ * | MSG_onlineStatus | 在线状态 | onlineStatus |
+ * | MSG_pushQuizOfClassWorkdata2Public | 老师端:把选中的学生习题作业,推到大屏 | pushQuizOfClassWorkdata2Public |
+ * | MSG_pushClassWorkdata2Public | 老师端:把选中的学生作业,推到大屏 | pushClassWorkdata2Public |
+ * | MSG_shareStudentPresentdata2All | 把某个学生的展示成果数据推给全班所有学生 | shareStudentPresentdata2All |
+ * | MSG_pushStudentPresentdata2Public | 老师端:课堂展示活动,把选中的学生展示数据,推到大屏 | pushStudentPresentdata2Public |
+ * | MSG_pushClassWorkPresentList2Public | 老师端:课堂展示活动,任务列表,推到大屏 | pushClassWorkPresentList2Public |
+ * | MSG_activePageType | 课标研读-分页切换 | activePageType |
+ * | MSG_slideFlapping | 幻灯片-切换 | slideFlapping |
+ * | MSG_anmationclick | 幻灯片-动画切换 | anmationclick |
+ * | MSG_classcourseopen | 群组创建成功 | classcourseopen |
+ * | MSG_classquizfeedback | 学生的测练结果反馈 | classquizfeedback |
+ * | MSG_classtaskfeedback | 老师端:接收到学生反馈消息-课堂测练中的其他任务 | classtaskfeedback |
+ * | MSG_studentfeedback | 老师端:学生反馈的消息,具体要看其中的feedbackkey,类别较繁杂 | studentfeedback |
+ * | MSG_studentfeedbackcancel | 老师端:学生反馈的消息取消,如取消学会了,取消困惑 | studentfeedbackcancel |
+ * | MSG_classshowdata | 学生提交的课堂展示数据-要在老师端显示,再由老师选择推送到公屏上 | classshowdata |
+ * | MSG_classWorkOfPresentDataUpdate | 学生在公屏上展示并完善后,保存后,老师端要更新 | classWorkOfPresentDataUpdate |
+ * | MSG_classlecturePagesrc | 课堂讲授活动,选择不同的内容 | classlecturePagesrc |
+ * | --- | 以下为新定义-消息头 | --- |
+ * | MSG_0001 | 点赞 | 0x0001 |
+ * | MSG_0002 | xx | 0x0002 |
+ * | MSG_0003 | xx | 0x0003 |
+ */
+ static HEADS = {
+ // === 旧定义-消息头(兼容以前) ===
+ /** @desc: 结束课程(下课) */
+ MSG_closed : 'closed',
+ /** @desc: 在线状态 */
+ MSG_onlineStatus : 'onlineStatus',
+ /** @desc: 老师端:把选中的学生习题作业,推到大屏 */
+ MSG_pushQuizOfClassWorkdata2Public : 'pushQuizOfClassWorkdata2Public',
+ /** @desc: 老师端:把选中的学生作业,推到大屏 */
+ MSG_pushClassWorkdata2Public : 'pushClassWorkdata2Public',
+ /** @desc: 把某个学生的展示成果数据推给全班所有学生 */
+ MSG_shareStudentPresentdata2All : 'shareStudentPresentdata2All',
+ /** @desc: 老师端:课堂展示活动,把选中的学生展示数据,推到大屏 */
+ MSG_pushStudentPresentdata2Public : 'pushStudentPresentdata2Public',
+ /** @desc: 老师端:课堂展示活动,任务列表,推到大屏 */
+ MSG_pushClassWorkPresentList2Public : 'pushClassWorkPresentList2Public',
+ /** @desc: 课标研读-分页切换 */
+ MSG_activePageType : 'activePageType',
+ /** @desc: 幻灯片-切换 */
+ MSG_slideFlapping : 'slideFlapping',
+ /** @desc: 幻灯片-动画切换 */
+ MSG_anmationclick : 'anmationclick',
+ /** @desc: 群组创建成功 */
+ MSG_classcourseopen : 'classcourseopen',
+ /** @desc: 学生的测练结果反馈 */
+ MSG_classquizfeedback : 'classquizfeedback',
+ /** @desc: 老师端:接收到学生反馈消息-课堂测练中的其他任务 */
+ MSG_classtaskfeedback : 'classtaskfeedback',
+ /** @desc: 老师端:学生反馈的消息,具体要看其中的feedbackkey,类别较繁杂 */
+ MSG_studentfeedback : 'studentfeedback',
+ /** @desc: 老师端:学生反馈的消息取消,如取消学会了,取消困惑 */
+ MSG_studentfeedbackcancel : 'studentfeedbackcancel',
+ /** @desc: 学生提交的课堂展示数据-要在老师端显示,再由老师选择推送到公屏上 */
+ MSG_classshowdata : 'classshowdata',
+ /** @desc: 学生在公屏上展示并完善后,保存后,老师端要更新 */
+ MSG_classWorkOfPresentDataUpdate : 'classWorkOfPresentDataUpdate',
+ /** @desc: 课堂讲授活动,选择不同的内容 */
+ MSG_classlecturePagesrc : 'classlecturePagesrc',
+ // === 新定义-消息头 ===
+ /** @desc: 点赞 */
+ MSG_0001: 0x0001,
+ /** @desc: 疑惑 */
+ MSG_0002: 0x0002,
+ MSG_0003: 0x0003,
+ MSG_0004: 0x0004,
+ MSG_0005: 0x0005,
+ MSG_0006: 0x0006,
+ MSG_0007: 0x0007,
+ MSG_0008: 0x0008,
+ MSG_0009: 0x0009,
+ MSG_0010: 0x000a,
+ MSG_0011: 0x000b,
+ MSG_0012: 0x000c,
+ MSG_0013: 0x000d,
+ MSG_0014: 0x000e,
+ MSG_0015: 0x000f,
+ /** @desc: 作业推送 */
+ MSG_0016: 0x0010,
+ MSG_0017: 0x0011,
+ MSG_0018: 0x0012,
+ MSG_0019: 0x0013,
+ MSG_0020: 0x0014,
+ MSG_0021: 0x0015,
+ MSG_0022: 0x0016,
+ MSG_0023: 0x0017,
+ MSG_0024: 0x0018,
+ MSG_0025: 0x0019,
+ MSG_0026: 0x001a,
+ MSG_0027: 0x001b,
+ MSG_0028: 0x001c,
+ MSG_0029: 0x001d,
+ MSG_0030: 0x001e,
+ MSG_0031: 0x001f,
+ MSG_0032: 0x0020,
+ MSG_0033: 0x0021,
+ MSG_0034: 0x0022,
+ MSG_0035: 0x0023,
+ }
+}
+
+export { MsgEnum as default }
\ No newline at end of file
diff --git a/src/renderer/src/plugins/shareStore.js b/src/renderer/src/plugins/shareStore.js
index da709f0..e291561 100644
--- a/src/renderer/src/plugins/shareStore.js
+++ b/src/renderer/src/plugins/shareStore.js
@@ -3,34 +3,114 @@
*/
const isNode = typeof require !== 'undefined' // 是否支持node函数
const { ipcRenderer } = isNode?require('electron'):{} // app使用
+import { sessionStore } from '@/utils/store'
+// import { diff } from 'jsondiffpatch'
+// const Remote = isNode?require('@electron/remote'):{} // 远程模块
+
export function shareStorePlugin({store}) {
- store.$subscribe(() => { // 自动同步
+ store.$subscribe((mutation, state) => { // 自动同步
+ // mutation 变量包含了变化前后的状态
+ // mutation.events: key newValue target oldValue oldTarget
+ // state 是变化后的状态
+ // console.log('store.$subscribe', mutation, state, store)
// 在存储变化的时候执行
- const storeName = store.$id
+ // const storeName = store.$id
+ const storeName = mutation.storeId
// 用于多窗口共享(需要共享的状态名称)
const names = ['tool']
- if (names.includes(storeName)) stateSync(store) // 需要同步
+ if (names.includes(storeName)) {
+ const { storeId: storeName, payload, events, type } = mutation // direct
+ // if (!Object.keys(payload).length) return
+ // if (type != 'direct' || !events || Array.isArray(events) || !events.key) return
+ stateSyncWatch(storeName, state) // 需要同步
+ }
})
+
// 暴露方法-手动同步
- store.stateSync = () => stateSync(store)
+ store.stateSync = (storeName, key, value) => {
+ const state = store.$state
+ if (!storeName && !!key && !!value) stateSync(storeName, key, value, state)
+ else stateSyncAll(store, state)
+ }
+ // 暴露方法-发送当前状态-新窗口
+ store.stateSyncInit = wid => stateSyncInit(wid, store)
// 监听主线程消息-同步数据
stateChange(store)
}
-// 同步数据-发送给主线程
-function stateSync(store) {
+
+// 同步数据-发送给主线程-单独
+function stateSync(storeName, key, value, state) {
+ // console.log('state-change', storeName, key, value)
+ try {
+ const { data, keystr } = filterByKey(state, key, value)
+ const jsonStr = JSON.stringify(data) // 从新组装-json数据
+ // 更新本地数据-session
+ sessionStore.set(keystr, value)
+ // 通知主线程更新
+ ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
+ // console.log('======',keystr, jsonStr )
+ } catch (error) {
+ console.log('state-change-error', error)
+ }
+}
+// 同步数据-发送给主线程-单独($subscribe-监听使用)
+function stateSyncWatch(storeName, newState) {
+ const oldState = sessionStore.store // 旧数据
+ const diffData = findDifferences(oldState, newState)
+ // console.log('state-change-diffData', diffData)
+ try {
+ for(const key in diffData) {
+ const value = diffData[key]
+ const newValue = {} // 重新组装pinia需要的数据 {a:{b:1}} 这种
+ const keyArr = key.split('.') || []
+ keyArr.reduce((o,c,i)=>{o[c] = i === keyArr.length-1 ? value : {};return o[c]}, newValue)
+ const jsonStr = JSON.stringify(newValue) // 从新组装-json数据
+ // // 更新本地数据-session
+ sessionStore.set(key, value)
+ // // 通知主线程更新
+ ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
+ // console.log('======',key, value, jsonStr )
+ }
+ } catch (error) {
+ console.log('state-change-error', error)
+ }
+}
+
+// 同步数据-发送给主线程-全量更新
+function stateSyncAll(store) {
const storeName = store.$id
- const jsonStr = JSON.stringify(store.$state)
- console.log('state-change', jsonStr, storeName)
+ const jsonStr = circularSafeStringify(store.$state)
// 通知主线程更新
ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
}
+
+// 发送当前数据状态
+function stateSyncInit(wid, store) {
+ const storeName = store.$id
+ const curJson = JSON.stringify(store.$state) // 当前数据
+ // 发送同步数据给新窗口-更新状态
+ ipcRenderer.invoke('pinia-state-init', wid, storeName, curJson)
+}
+
+// 监听session数据变化
+function sessionWatch(store) {
+ const unsubscribe = sessionStore.onDidAnyChange((newV, oldV) => {
+ if (newV !== oldV) {
+ console.log('session-change', newV, oldV)
+ // 通知主线程更新
+ // ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
+ }
+ })
+ // unsubscribe() 取消监听
+}
+
// 同步数据-接收主线程消息
function stateChange(store) {
const storeName = store.$id
ipcRenderer?.on('pinia-state-set', (e, sName, jsonStr) => {
if (sName == storeName) { // 更新对应数据
- // console.log('state-set', jsonStr, sName)
- const curJson = JSON.stringify(store.$state) // 当前数据
+ console.log('state-set', jsonStr, sName)
+ const curJson = circularSafeStringify(store.$state) // 当前数据
const isUp = curJson != jsonStr // 不同的时候才写入,不然会导致触发数据变化监听,导致死循环
if (!isUp) return
const stateJson = JSON.parse(jsonStr) // 新数据
@@ -41,3 +121,87 @@ function stateChange(store) {
}
})
}
+//解决JSON.stringify嵌套循环产生的bug
+const circularSafeStringify = (obj) => {
+ const cache = new Set();
+ return JSON.stringify(obj, (key, value) => {
+ if (typeof value === "object" && value !== null) {
+ if (cache.has(value)) {
+ // 当前对象已经存在于缓存中,说明存在循环引用,返回占位符或其他处理方式
+ return "[Circular Reference]";
+ }
+ cache.add(value);
+ }
+ return value;
+ });
+}
+
+// 过滤对象
+const filterByKey = (obj, key, value) => {
+ let res = { data:{}, keystr:'' }
+ for (let k in obj) {
+ if (obj.hasOwnProperty(k)) {
+ const isEqual = JSON.stringify(obj[k]) === JSON.stringify(value) // 值是否相同
+ if (k === key && isEqual) {
+ // 如果匹配,则添加到新对象中
+ res.data[k] = obj[k];
+ res.keystr = k;
+ } else {
+ if (obj[k] !== null && typeof obj[k] === 'object') {
+ // 如果是对象,则递归处理
+ const {data, keystr} = filterByKey(obj[k], key, value)
+ if(!!keystr) {
+ res.data[k] = data
+ res.keystr = `${k}.${keystr}`
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+// 获取对象值
+const getObjValue = (obj, key) => {
+
+}
+
+// 找出两个对象之间的差异
+const findDifferences = (obj1, obj2) => {
+ const differences = {};
+ function compareObjects(o1, o2, path = '') {
+ for (const key in o1) {
+ if (o1.hasOwnProperty(key)) {
+ const newPath = path ? `${path}.${key}` : key;
+ if (o2.hasOwnProperty(key)) {
+ const v1 = toJsonStr(o1[key])
+ const v2 = toJsonStr(o2[key])
+ if (typeof o1[key] === 'object' && typeof o2[key] === 'object' && !Array.isArray(o1[key])) {
+ compareObjects(o1[key], o2[key], newPath);
+ } else if (v1 !== v2) {
+ differences[newPath] = o2[key];
+ }
+ } else {
+ differences[newPath] = o2[key];
+ }
+ }
+ }
+
+ for (const key in o2) {
+ if (o2.hasOwnProperty(key) && !o1.hasOwnProperty(key)) {
+ const newPath = path ? `${path}.${key}` : key;
+ differences[newPath] = o2[key];
+ }
+ }
+ }
+
+ compareObjects(objClone(obj1), objClone(obj2));
+ // 特殊处理
+
+
+ return differences;
+}
+// 对象克隆
+const objClone = (obj) => JSON.parse(JSON.stringify(obj))
+// 转换为json
+const toJsonStr = (obj) => JSON.stringify(obj)
diff --git a/src/renderer/src/router/index.js b/src/renderer/src/router/index.js
index 14b0cd0..000314e 100644
--- a/src/renderer/src/router/index.js
+++ b/src/renderer/src/router/index.js
@@ -1,3 +1,8 @@
+/*
+ * @Author: 苦逼程序猿
+ * @Date: 2024-09-06 16:15:32
+ * @Warning: 千行代码,Bug露锋芒。
+ */
import { createRouter, createWebHashHistory } from 'vue-router'
import Layout from '../layout/index.vue'
@@ -57,6 +62,12 @@ export const constantRoutes = [
name: 'class',
meta: {title: '班级中心'},
},
+ {
+ path: '/classTask',
+ component: () => import('@/views/classTask/classTask.vue'),
+ name: 'class',
+ meta: {title: '作业批改'},
+ },
]
},
...toolRouters
diff --git a/src/renderer/src/store/modules/thirdTextbook.js b/src/renderer/src/store/modules/thirdTextbook.js
new file mode 100644
index 0000000..0c499da
--- /dev/null
+++ b/src/renderer/src/store/modules/thirdTextbook.js
@@ -0,0 +1,23 @@
+import { defineStore } from 'pinia'
+
+const useThirdStore = defineStore('third', {
+ state: () => ({
+ activeGrade:'',
+ gradeName:'',
+ subjectName:'',
+ textbookVersionId:''
+ }),
+ actions: {
+ // 登录
+ getSelectBookInfo(params){
+ this.activeGrade = params.activeGrade
+ this.gradeName = params.gradeName
+ this.subjectName = params.subjectName
+ this.textbookVersionId = params.textbookVersionId
+ }
+
+ },
+ persist: true
+})
+
+export default useThirdStore
diff --git a/src/renderer/src/store/modules/tool.js b/src/renderer/src/store/modules/tool.js
index ac4e440..fdddb52 100644
--- a/src/renderer/src/store/modules/tool.js
+++ b/src/renderer/src/store/modules/tool.js
@@ -2,6 +2,12 @@
* 工具类-窗口-状态管理
*/
import { defineStore } from 'pinia'
+import { sessionStore } from '@/utils/tool'
+
+// 默认数据
+const defData = sessionStore.store || {}
+// 延时
+const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
export const useToolState = defineStore('tool', {
state: () => ({
@@ -10,10 +16,18 @@ export const useToolState = defineStore('tool', {
isPdfWin: false, // pdf窗口是否打开
isToolWin: false, // 工具窗口是否打开
curSubjectNode: {
- data: {}, // 当前教材节点 (包含当前教材 单元)
+ data: {}, // 当前教材节点 (包含当前教材 单元)
querySearch: {} // 查询资源所需参数
- }
+ },
+ ...defData // 默认数据-覆盖上面的配置(不要删除, 会导致新窗口-获取状态失败)
}),
actions: {
+ async resetDef() { // 重置数据-下课
+ this.model = 'select' // 悬浮球-当前模式
+ await sleep(20) // 休眠20ms
+ this.showBoardAll = false // 全屏画板-是否显示
+ await sleep(20) // 休眠20ms
+ this.isToolWin = false // 工具窗口是否打开
+ }
}
})
diff --git a/src/renderer/src/store/modules/user.js b/src/renderer/src/store/modules/user.js
index 3878eb7..3a45327 100644
--- a/src/renderer/src/store/modules/user.js
+++ b/src/renderer/src/store/modules/user.js
@@ -37,7 +37,7 @@ const useUserStore = defineStore('user', {
return new Promise((resolve, reject) => {
getInfo()
.then((res) => {
- res.user.avatar = import.meta.env.VITE_APP_BASE_API + res.user.avatar
+ // res.user.avatar = import.meta.env.VITE_APP_BASE_API + res.user.avatar
const user = res.user
this.user = user
const avatar = user.avatar == '' || user.avatar == null ? defAva : user.avatar
diff --git a/src/renderer/src/utils/date.js b/src/renderer/src/utils/date.js
index 4a1025d..2a178ca 100644
--- a/src/renderer/src/utils/date.js
+++ b/src/renderer/src/utils/date.js
@@ -76,3 +76,61 @@ export const toTimeText = (timeStamp, simple) => {
}
return timeText
}
+
+/**
+ *
+ * @returns 当前年-月-日
+ */
+export const getCurrentTime = (format)=> {
+ const now = new Date();
+ const year = now.getFullYear();
+ const month = (now.getMonth() + 1).toString().padStart(2, '0');
+ const day = now.getDate().toString().padStart(2, '0');
+ const hours = now.getHours().toString().padStart(2, '0');
+ const minutes = now.getMinutes().toString().padStart(2, '0');
+ if(format == 'YYYY-MM-DD'){
+ return `${year}-${month}-${day}`;
+ }
+ if(format == 'HH:mm'){
+ return `${hours}:${minutes}`;
+ }
+}
+/**
+ *
+ * @param {number} m 指定时间
+ * @returns 指定时间之后的 小时:分钟
+ */
+export const getAfterMinutes = (m) => {
+ const now = new Date();
+ const afterMinutes = new Date(now.getTime() + m * 60 * 1000);
+ let hours = afterMinutes.getHours();
+ hours = hours < 10 ? ('0' + hours) : hours
+ let minutes = afterMinutes.getMinutes();
+ minutes = minutes < 10 ? ('0' + minutes) : minutes
+ return `${hours}:${minutes}`;
+}
+
+/**
+ * 表格时间格式化
+ */
+export function formatDate(cellValue) {
+ if (cellValue == null || cellValue == "") return "";
+ var date = new Date(cellValue)
+ var year = date.getFullYear()
+ var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
+ var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
+ var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
+ var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
+ var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
+ return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
+}
+export function getTimeDate() {
+ var date = new Date()
+ var year = date.getFullYear()
+ var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
+ var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
+ var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
+ var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
+ var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
+ return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
+}
\ No newline at end of file
diff --git a/src/renderer/src/utils/hasPermission.js b/src/renderer/src/utils/hasPermission.js
index 2242209..4d7cb4a 100644
--- a/src/renderer/src/utils/hasPermission.js
+++ b/src/renderer/src/utils/hasPermission.js
@@ -7,7 +7,6 @@ export const hasPermission = (value, def = true) => {
if (!value) {
return def
}
-
const allCodeList = useUserStore().roles
// 如果不是数组,直接判断pinia里的权限数组有没有相同的元素即可
if (!Array.isArray(value)) {
@@ -15,4 +14,4 @@ export const hasPermission = (value, def = true) => {
}
// intersection是lodash提供的一个方法,用于返回一个所有给定数组都存在的元素组成的数组
return array.intersection(value, allCodeList).length > 0
-}
\ No newline at end of file
+}
diff --git a/src/renderer/src/utils/linkConfig.js b/src/renderer/src/utils/linkConfig.js
index 6689322..f03f3e9 100644
--- a/src/renderer/src/utils/linkConfig.js
+++ b/src/renderer/src/utils/linkConfig.js
@@ -47,6 +47,12 @@ export default () => {
data: { ...baseConfig() },
fullPath: `${baseConfig().url}/platofai`
},
+ // 文件资源 布置作业
+ filehomework: {
+ data: { ...baseConfig() },
+
+ fullPath: `${baseConfig().url}/teaching/classtaskassign?titleName=作业布置`
+ },
getBaseData: () => {
return {
diff --git a/src/renderer/src/utils/pdfAndFabric copy.js b/src/renderer/src/utils/pdfAndFabric copy.js
deleted file mode 100644
index 5a914b6..0000000
--- a/src/renderer/src/utils/pdfAndFabric copy.js
+++ /dev/null
@@ -1,133 +0,0 @@
-// 所有事件
-export function handleevent(canvas, imgarr, type = 'defalut') {
- // // 鼠标按下
- // canvas.on('mouse:down', function (e) {})
- // // // 监听鼠标移动事件
- // // canvas.on('mouse:move', (options) => {
- // // console.log('Mouse move event:', options);
- // // });
-
- // // 监听鼠标释放事件
- // canvas.on('mouse:up', (options) => {
- // //判断是点击的哪一个
- // if (type == 'defalut') {
- // if (imgarr.value[0].index == 0) {
- // imgarr.value[0].JSONdata = canvas.toJSON()
- // }
- // if (imgarr.value[1]?.index == 0) {
- // imgarr.value[1].JSONdata = canvas.toJSON()
- // }
- // } else {
- // if (imgarr.value[0].index == 1) {
- // imgarr.value[0].JSONdata = canvas.toJSON()
- // }
- // if (imgarr.value[1]?.index == 1) {
- // imgarr.value[1].JSONdata = canvas.toJSON()
- // }
- // }
- // console.log(imgarr.value)
-
- // })
-}
-// 保存数据
-export function savecanvsStore(imgarr, canvsStore) {
- // canvsStore.pageArr = mergeAndReplace(canvsStore.pageArr, imgarr.value)
-}
-// 重显数据
-export function displayData(canvas, canvsStore, canvasobj, fabric, img) {
- // // 初始化
- // if (!canvsStore.pageArr.length) {
- // fabric.Image.fromURL(img.src, (img) => {
- // img.set({
- // left: 0,
- // top: 0,
- // scaleX: canvas.value.width / img.width,
- // scaleY: canvas.value.height / img.height
- // })
- // canvas.value.setBackgroundImage(img, canvas.value.renderAll.bind(canvas.value))
- // })
- // return
- // }
- // canvsStore.pageArr.forEach((item) => {
- // //初始化
- // if (item.page == canvasobj.page) {
- // // canvas.value.clear() // 清除 Canvas
- // // console.log(item.JSONdata, '找到一样的数据')
- // canvas.value.loadFromJSON(item.JSONdata, () => {
- // // 在所有对象加载完成后重新渲染画布
- // canvas.value.renderAll.bind(canvas.value)
- // canvas.value.renderAll()
- // // requestAnimationFrame(() => {
- // // // 渲染所有对象
-
- // // })
- // })
- // } else {
- // // 使用 requestAnimationFrame 来更新画布,确保在下一帧进行重绘
- // // // 清除 Canvas
- // canvas.value.clear()
- // requestAnimationFrame(function () {
- // fabric.Image.fromURL(img.src, (img) => {
- // img.set({
- // left: 0,
- // top: 0,
- // scaleX: canvas.value.width / img.width,
- // scaleY: canvas.value.height / img.height
- // })
- // canvas.value.setBackgroundImage(img, canvas.value.renderAll.bind(canvas.value))
- // })
- // // 渲染所有对象
- // canvas.value.renderAll.bind(canvas.value)
- // canvas.value.renderAll()
- // })
- // }
- // })
-}
-
-//page 一样替换
-const mergeAndReplace = (arr1, arr2) => {
- // // 用于存储替换后的数组
- // const resultArray = array1.map(item1 => {
- // // 在 array2 中查找 page 相同的对象
- // const replacement = array2.find(item2 => item2.page == item1.page);
- // // 如果找到替换对象,则返回替换对象,否则返回原对象
- // return replacement ? replacement : item1;
- // });
-
- // // 将 array2 中 page 不在 array1 中的对象追加到结果数组中
- // array2.forEach(item2 => {
- // const existsInArray1 = array1.some(item1 => item1.page == item2.page);
- // if (!existsInArray1) {
- // resultArray.push(item2);
- // }
- // });
-
- // return resultArray;
-
- // 创建一个映射,将 arr2 中的对象按 page 属性存储
- let map = new Map(arr2.map((item) => [item.page, item]))
-
- // 使用 map 替换 arr1 中相应 page 的对象,并添加 arr2 中的对象
- arr1 = arr1.map((item) => (map.has(item.page) ? map.get(item.page) : item))
-
- // 将 map 中存在但 arr1 中不存在的对象添加到 arr1
- for (let [page, obj] of map) {
- if (!arr1.some((item) => item.page === page)) {
- arr1.push(obj)
- }
- }
- return arr1
-}
-
-// 初始化数据
-export function initcanvasdata(canvas) {
- canvas.value.clear() // 清除 Canvas
- // 设置画布的背景色或其他属性
- canvas.value.backgroundColor = 'rgba(255, 255, 255, 1)' // 白色背景
-
- // 使用 requestAnimationFrame 来更新画布,确保在下一帧进行重绘
- requestAnimationFrame(function () {
- // 渲染所有对象
- canvas.value.renderAll.bind(canvas.value)
- })
-}
diff --git a/src/renderer/src/utils/resourceDict.js b/src/renderer/src/utils/resourceDict.js
index a1a1a61..9a26a2b 100644
--- a/src/renderer/src/utils/resourceDict.js
+++ b/src/renderer/src/utils/resourceDict.js
@@ -6,7 +6,11 @@ export const tabs = [
{
label: '校本资源',
value: '校本'
- }
+ },
+ {
+ label: '第三方资源',
+ value: '第三方'
+ },
]
@@ -67,3 +71,45 @@ export const resourceType = [
value: '教案'
}
]
+// 年级划分
+export const gradeList = [
+ {
+ label:'小学',
+ value:1
+ },
+ {
+ label:'初中',
+ value:2
+ },
+ {
+ label:'高中',
+ value:3
+ },
+]
+//课件类别
+export const coursewareTypeList = [
+ {
+ label:'全部',
+ value:''
+ },
+ {
+ label:'课件',
+ value:3
+ },
+ {
+ label:'教案',
+ value:8
+ },
+ {
+ label:'试卷',
+ value:7
+ },
+ {
+ label:'学案',
+ value:4
+ },
+ {
+ label:'素材',
+ value:6
+ },
+]
diff --git a/src/renderer/src/utils/store.js b/src/renderer/src/utils/store.js
new file mode 100644
index 0000000..aef349c
--- /dev/null
+++ b/src/renderer/src/utils/store.js
@@ -0,0 +1,25 @@
+/**
+ * @description : 存储对象-本地存储 封装工具(渲染器)
+ * @author : zdg
+ * @date : 2024-09-03
+ */
+const isNode = typeof require !== 'undefined' // 是否支持node函数
+const Store = isNode?require('electron-store'):null // 持久化存储
+
+// 暴露sessionStore存储对象
+export const sessionStore = Store ? new Store({
+ name: 'session-store', // 存储文件名
+ fileExtension: 'ini', // 文件后缀名
+ encryptionKey: 'BvPLmgCC4DSIG0KkTec5' // 数据加密-防止用户直接改配置
+}) : {}
+
+// 暴露localStore存储对象
+export const localStore = Store ? new Store({
+ name: 'local-store', // 存储文件名
+ fileExtension: 'ini', // 文件后缀名
+ encryptionKey: '6CyoHQmUaPmLzvVsh' // 数据加密-防止用户直接改配置
+}) : {}
+
+export default {
+ sessionStore, localStore
+}
\ No newline at end of file
diff --git a/src/renderer/src/utils/tool.js b/src/renderer/src/utils/tool.js
index c9c91f1..e7fcf86 100644
--- a/src/renderer/src/utils/tool.js
+++ b/src/renderer/src/utils/tool.js
@@ -6,17 +6,33 @@
// Remote.app.getAppPath() E:\njys-work\AIx_Smarttalk\dist\win-unpacked\resources\app.asar
// path.join(__dirname) 根目录 E:\njys-work\AIx_Smarttalk\dist\win-unpacked\resources\app.asar\out\renderer
-const isNode = typeof require !== 'undefined' // 是否支持node函数
+const isNode = typeof require !== 'undefined' // 是否支持node函数
const path = isNode?require('path'):{}
const Remote = isNode?require('@electron/remote'):{}
const { ipcRenderer } = isNode?require('electron'):window.electron || {}
-
+const API = isNode?window.api:{} // preload-api
+import { useToolState } from '@/store/modules/tool' // 获取store状态
+// const Store = isNode?require('electron-store'):null // 持久化存储
+import store from './store'
// 常用变量
const BaseUrl = isNode?process.env['ELECTRON_RENDERER_URL']+'/#':''
const isDev = isNode?process.env.NODE_ENV !== 'production':''
+const appPath = isNode?Remote.app.getAppPath():'' // 应用目录
+
// 暴露Remote中的属性
export const ipcMain = Remote?.ipcMain || {}
+
+// 暴露sessionStore存储对象
+export const sessionStore = store.sessionStore
+
+// 暴露localStore存储对象
+export const localStore = store.localStore
+
+// 暴露Store存储对象
+export const Store = store
+// 延时
+const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
/**
* 获取静态资源,开发和生产环境
* @param {*} url
@@ -82,38 +98,53 @@ export function ipcHandle(fn,key, cb) {
* @param {*} data 参数
* @returns
*/
+let wins_tool = null
+let winPdf=null
export const createWindow = async (type, data) => {
- if (!type) return console.error('createWindow: type is null')
switch(type) {
case 'tool-sphere': { // 创建-悬浮球
+ if (wins_tool) return console.error('createWindow: win is have')
+ if (!type) return console.error('createWindow: type is null')
const option = data.option||{}
const defOption = {
frame: false, // 要创建无边框窗口
resizable: false, // 禁止窗口大小缩放
transparent: true, // 设置透明
alwaysOnTop: true, // 窗口是否总是显示在其他窗口之前
+ type: 'toolbar', // 创建的窗口类型为工具栏窗口
// parent: mainWin, // 父窗口
// autoClose: true, // 关闭窗口后自动关闭
}
data.isConsole = true // 是否开启控制台
+ data.isWeb = false // 是否开启web安全
data.option = {...defOption, ...option}
- const win = await toolWindow(data)
- win.type = type // 唯一标识
- win.show()
- win.setFullScreen(true) // 设置窗口为全屏
- win.setIgnoreMouseEvents(true, {forward: true}) // 忽略鼠标事件但是事件继续传递给窗口
- win.setAlwaysOnTop(true,'screen-saver') // 将窗口设置为顶层窗口
- win.setVisibleOnAllWorkspaces(true) // 如果窗口在所有工作区都可见
- // win.webContents.openDevTools() // 打开调试工具
- eventHandles(type, win) // 事件监听处理
- return win
+ wins_tool = await toolWindow(data)
+ wins_tool.type = type // 唯一标识
+ wins_tool.show()
+ wins_tool.setFullScreen(true) // 设置窗口为全屏
+ wins_tool.setIgnoreMouseEvents(true, {forward: true}) // 忽略鼠标事件但是事件继续传递给窗口
+ wins_tool.setAlwaysOnTop(true,'screen-saver') // 将窗口设置为顶层窗口
+ wins_tool.setVisibleOnAllWorkspaces(true) // 如果窗口在所有工作区都可见
+ // wins_tool.webContents.openDevTools() // 打开调试工具
+ eventHandles(type, wins_tool) // 事件监听处理
+ return wins_tool
}
case 'open-PDF': { //课本展示-pdf
+ if(winPdf){ //判断是否已经打开
+ if (winPdf.isMinimized()){
+ winPdf.restore();
+ } else{
+ winPdf.focus();
+ // toolState.isPdfWin=true
+ }
+
+ return
+ }
const option = data.option||{}
const defOption = {
frame: false, // 要创建无边框窗口
- resizable: true, // 禁止窗口大小缩放
- alwaysOnTop: false, // 窗口是否总是显示在其他窗口之前
+ resizable: true, // 禁止窗口大小缩放
+ alwaysOnTop: false, // 窗口是否总是显示在其他窗口之前
}
data.isConsole = true // 是否开启控制台
data.option = {...defOption, ...option}
@@ -123,6 +154,7 @@ export const createWindow = async (type, data) => {
win.setFullScreen(true) // 设置窗口为全屏
// win.webContents.openDevTools() // 打开调试工具
eventHandles(type, win) // 事件监听处理
+ winPdf=win
break
}
default:
@@ -138,7 +170,7 @@ export const createWindow = async (type, data) => {
* @author: zdg
* @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
let width = option?.width || 800
let height = option?.height || 600
@@ -149,31 +181,38 @@ export function toolWindow({url, isConsole, option={}}) {
return new Promise((resolve) => {
const config = {
width, height,
- type: 'toolbar', // 创建的窗口类型为工具栏窗口
- icon: path.join(__dirname, '../../resources/logo2.ico'),
+ icon: path.join(appPath, '/resources/logo2.ico'),
webPreferences: {
- // preload: path.join(__dirname, '../preload/index.js'),
- preload: '@root/src/preload/index.js',
+ preload: path.join(API.preloadPath, '/index.js'),
sandbox: false,
nodeIntegration: true, // nodeApi调用
contextIsolation: false, // 沙箱取消
- // webSecurity: false // 跨域关闭
+ devTools: true
},
...option
}
// 创建-新窗口
let win = new Remote.BrowserWindow(config)
- if (!isDev) win.loadFile(urlAll,{hash: url}) // 加载文件
- else win.loadURL(urlAll) // 加载url
- win.once('ready-to-show', () => {resolve(win)})
+ if (!isDev) win.loadFile(urlAll,{hash: url}) // 加载文件
+ else win.loadURL(urlAll) // 加载url
+ win.once('ready-to-show', () => { // 窗口加载完成
+ resolve(win)
+ })
// 主窗口关闭事件
mainWin.once('closed', () => { win.destroy()})
// 内部监听器
- win.webContents.on('did-finish-load', () => {})
+ win.webContents.on('did-finish-load', () => {
+ // setTimeout(() => {
+ // toolState.stateSyncInit(win.id) // 同步状态
+ // }, 200);
+ })
// 内部监听器-是否打印
if (!!isConsole) {
win.webContents.on('console-message', (e,leve,m,lin,s) => {
- console.log(`[${win.type}]`,m)
+ if(m.startsWith('%c')){ // 特殊打印
+ const arr = m.match(/(%c[^ ]+)(?:\s+(.*;))(.*)/)
+ console.log(arr[1],arr[2],arr[3])
+ } else console.log(`[${win.type}]`,m)
})
}
})
@@ -184,16 +223,19 @@ export function toolWindow({url, isConsole, option={}}) {
* @param {*} win 窗口对象
*/
const eventHandles = (type, win) => {
+ const toolState = useToolState() // 获取store状态
const winAll = Remote.BrowserWindow.getAllWindows()
const mainWin = winAll.find(o => o.type == 'main') // 主窗口对象
// 公共方法
const publicMethods = ({onClosed}={}) => {
// 监听主窗口-关闭事件
- mainWin.once('close', () => {win.destroy()})
+ mainWin.once('close', () => {winPdf=null;win.destroy();})
win.on('closed', () => {
if(onClosed) onClosed() // 自定义关闭事件
win = null
+ wins_tool = null
})
+
// 新窗口-创建事件(如:主进程加载远程服务)
ipcRenderer.send('new-window', {id:win.id, type})
}
@@ -209,28 +251,58 @@ const eventHandles = (type, win) => {
const on = {
onClosed: () => {
Remote.ipcMain.removeHandler('tool-sphere:set:ignore', setIgnore)
- Remote.ipcMain.removeHandler('tool-sphere:reset')
+ // Remote.ipcMain.removeHandler('tool-sphere:reset')
// Remote.ipcMain.removeAllListeners() // 移除所有监听事件
+ // 设置状态(再次设置-防止未设置到)
+ if(toolState.isToolWin) toolState.isToolWin = false
+ // sessionStore.set('isToolWin', false)
}
}
publicMethods(on) // 加载公共方法
break}
case 'open-PDF': {
// 最小化窗口 minimize()
- Remote.ipcMain.once('open-PDF:minimize', () => {win&&win.destroy()})
- publicMethods() // 加载公共方法
+ Remote.ipcMain.handle('open-PDF:minimize', () => {
+ // winPdf=null
+ // win&&win.destroy()
+ win&&win.minimize(); //缩小功能
+ })
+ // 关闭窗口
+ Remote.ipcMain.once('open-PDF:close', () => {
+ winPdf=null
+ win&&win.destroy()
+ })
+
+ // 监听窗口的激活事件
+ win.on('focus', async () => {
+ console.log('激活窗口')
+ toolState.isPdfWin=true
+ await sleep(20) // 延时
+ toolState.showBoardAll=false //恢复默认值
+ // await sleep(50) // 延时
+ // 穿透开启
+ if (toolState.isToolWin) ipcRenderer.invoke('tool-sphere:set:ignore', true)
+ });
+ const on = {
+ onClosed: () => {
+ Remote.ipcMain.removeHandler('open-PDF:minimize')
+ // 设置状态(再次设置-防止未设置到)
+ if(toolState.isPdfWin) toolState.isPdfWin = false
+ }
+ }
+ publicMethods(on) // 加载公共方法
break}
default:
break
}
}
-const taskHandles = () => {
- // // 设置任务栏上下文菜单
- // const contextMenu = new Remote.Menu()
- // contextMenu.append(new Remote.MenuItem({
- // label: '关闭',
- // click: () => {Remote.app.quit()}
- // }))
-}
+// const taskHandles = () => {
+// // 设置任务栏上下文菜单
+// const contextMenu = new Remote.Menu()
+// contextMenu.append(new Remote.MenuItem({
+// label: '关闭',
+// click: () => {Remote.app.quit()}
+// }))
+// }
diff --git a/src/renderer/src/views/classBegins/index.vue b/src/renderer/src/views/classBegins/index.vue
index 5fc078a..ccc2af4 100644
--- a/src/renderer/src/views/classBegins/index.vue
+++ b/src/renderer/src/views/classBegins/index.vue
@@ -26,7 +26,10 @@
下一页
+
+
最小化
+ 关闭
@@ -40,10 +43,8 @@ import { getStaticUrl } from '@/utils/tool'
const { ipcRenderer } = require('electron')
import { getBookMarkById } from '@/api/eTextbook/index'
import {useToolState} from '@/store/modules/tool'
-// const getStaticUrl=(url)=>{
-// return url
-// }
pdfjsLib.GlobalWorkerOptions.workerSrc = getStaticUrl('/lib/build/pdf.worker.mjs')
+
const toolState = useToolState();
const route = useRoute();
const isOnLoadShow = ref(false) //加载完毕显示
@@ -73,15 +74,11 @@ const navtopage = (type) => {
if (pdfObj.numPages > numPagesTotal.value) return
pdfCanvaslist.value.initPdf('rest')
}
-// 最小化窗口
-const minimize = async () => {
- await pdfCanvaslist.value.savaDataStore()
- // toolState.isPdfWin=false
- // toolState.showBoardAll=true //恢复默认值
- // console.log(toolState.showBoardAll,"关闭")
- // // ipcRenderer.send('tool-sphere:reset') //重置tool状态
- // ipcRenderer.send('open-PDF:minimize')
+// 关闭窗口
+const minimize = async (type='minimize') => {
+ await pdfCanvaslist.value.savaDataStore(type)
}
+
const handleUpdate = (data) => {
numPagesTotal.value = data
if (numPagesTotal.value == 1) {
@@ -106,11 +103,17 @@ const switchPageMode = () => {
pdfCanvaslist.value.initPdf('rest')
}
}
-onMounted(async () => {
- toolState.isPdfWin=true
- pdfObj.pdfUrl = getStaticUrl(route.query.path, 'user', 'selfFile', true)
+onMounted(async () => {
+ const isDev = process.env.NODE_ENV == 'development'
+ // toolState.showBoardAll = false // 隐藏画板
+ toolState.isPdfWin=true //设置打开pdf窗口
+ if (isDev)
+ pdfObj.pdfUrl = getStaticUrl('aaa.pdf', 'user', 'selfFile', true) //本地
+ else
+ pdfObj.pdfUrl = getStaticUrl(route.query.path, 'user', 'selfFile', true) //线上
textbookId.value = route.query.textbookId
pdfObj.bookId=textbookId.value
+ //初始化获取接口数据
getBookMarkById(textbookId.value).then(res=>{
pdfObj.allPageData=getUniqueArrayByLastOccurrence(res.data)
isOnLoadShow.value=true
@@ -143,7 +146,8 @@ const getUniqueArrayByLastOccurrence=(array)=> {
flex-wrap: wrap;
.pdf-btn {
position: absolute;
- right: 0;
+ // right: 0;
+ left: 0;
bottom: 0;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5);
border-radius: 5px 0 0 0;
@@ -168,5 +172,28 @@ const getUniqueArrayByLastOccurrence=(array)=> {
}
}
}
+ .pdf-btn-right{
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5);
+ border-radius: 5px 0 0 0;
+ button {
+ margin-left: 0;
+ border: none;
+ font-size: 16px;
+ // padding: 4px 7px;
+ border-radius: 0;
+ width: 80px;
+ height: 70px;
+ :deep(> span) {
+ display: block !important;
+ }
+ .iconfont {
+ font-size: 26px;
+ margin-bottom: 10px;
+ }
+ }
+ }
}
-
\ No newline at end of file
+
diff --git a/src/renderer/src/views/classManage/basicGroup.vue b/src/renderer/src/views/classManage/basicGroup.vue
index 9668b2a..9e59f66 100644
--- a/src/renderer/src/views/classManage/basicGroup.vue
+++ b/src/renderer/src/views/classManage/basicGroup.vue
@@ -1,10 +1,10 @@
-
-
-
+
+
+
+
+
可用分组
@@ -108,7 +108,7 @@
diff --git a/src/renderer/src/views/classManage/index.vue b/src/renderer/src/views/classManage/index.vue
index 170056d..edb48b9 100644
--- a/src/renderer/src/views/classManage/index.vue
+++ b/src/renderer/src/views/classManage/index.vue
@@ -6,11 +6,12 @@
-
-
- 新增班级
-
-
+
+
+
+
+
+
diff --git a/src/renderer/src/views/classManage/reserv-item.vue b/src/renderer/src/views/classManage/reserv-item.vue
index 685d03a..5032c49 100644
--- a/src/renderer/src/views/classManage/reserv-item.vue
+++ b/src/renderer/src/views/classManage/reserv-item.vue
@@ -20,14 +20,14 @@
- 上课 {{item.status == '上课中'?'上课中':'上课'}}
编辑
- 删除
+ 删除
@@ -46,6 +46,7 @@ const props = defineProps({
}
})
const basePath = import.meta.env.VITE_APP_BUILD_BASE_PATH
+const toolStore = useToolState() // 获取状态管理-tool
const openEdit = () => {
emit('openEdit', props.item)
}
@@ -61,25 +62,27 @@ const deleteReserv = () => {
})
}
const startClassR = (item) => {
- startClass(item.id).then((res) => {
- if (res.data === true) {
- item.status = '上课中'
- openLesson()
- }
- })
+ // startClass(item.id).then((res) => {
+ // if (res.data === true) {
+ // item.status = '上课中'
+ // openLesson()
+ // }
+ // })
+ item.status = '上课中'
+ openLesson()
}
-const toolStore = useToolState()
+// const toolStore = useToolState()
+let wins = null;
// 上课-工具类悬浮
const openLesson = () => {
- if (toolStore.isToolWin) return ElMessage.error('您当前已开始上课,请勿重复操作')
- startClass(props.item.id)
+ // startClass(props.item.id)
listEntpcourse({
evalid: props.item.ex2,
edituserid: useUserStore().user.userId,
pageSize: 500
- }).then(res=>{
+ }).then(async res=>{
if (res.rows[0].id) {
- createWindow('tool-sphere', { url: '/tool/sphere?entpcourseid=' + res.rows[0].id + "&reservId=" + props.item.id })
+ wins = await createWindow('tool-sphere', { url: '/tool/sphere?entpcourseid=' + res.rows[0].id + "&reservId=" + props.item.id })
}
})
}
diff --git a/src/renderer/src/views/classManage/studentList.vue b/src/renderer/src/views/classManage/studentList.vue
index 9c95d4a..f0ec324 100644
--- a/src/renderer/src/views/classManage/studentList.vue
+++ b/src/renderer/src/views/classManage/studentList.vue
@@ -3,17 +3,17 @@
-
- 新增学生
- 导入学生
-
+
+
+
+
点击学生头像查看学生信息
-
+
-
-
-
-
- 系统自动创建
-
-
-
-
- 系统自动创建,默认为123123
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- 移出班级
- 删除学生
- 取 消
- 确 定
-
+
+
+
+
+
+
@@ -112,7 +112,7 @@
+
+
diff --git a/src/renderer/src/views/classTask/container/item-dialog-score.vue b/src/renderer/src/views/classTask/container/item-dialog-score.vue
new file mode 100644
index 0000000..fcd5ee4
--- /dev/null
+++ b/src/renderer/src/views/classTask/container/item-dialog-score.vue
@@ -0,0 +1,964 @@
+
+
+
+
+ {{ classWorkFormScore.name }} 答题详情
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ quItem.worktag }}
+
+
+
+
+
+
+
+
+ 【答案】
+
+ 【分析】
+
+ 【解答】
+
+ 【点评】
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 参考答案:
+
+ {{ quItem.workanswerFormat.replace(/<[^>]+>/g, '') }}
+
+
+
+
+
+ 学生答案:
+
+
+ {{ stuItem.feedcontent.replace('#', '、') }}
+
+
+
+
+ {{
+ JSON.parse(quItem.workdesc)
+ .map((item, index) => {
+ if (item == stuItem.feedcontent) {
+ return String.fromCharCode(65 + Number(index))
+ }
+ })
+ .filter(Boolean)[0]
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 分
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 温馨提示:点击图片可放大预览
+
+
+
+
+
+
+
+
+ 温馨提示:点击此处 可预览其他类型附件!
+
+
+
+
+
+
+
+
+
+
+
+
学生答题附件内容
+
+
+
+ 温馨提示:点击图片可放大预览
+
+
+
+
+
+
+
+
+ 温馨提示:点击此处 可预览其他类型附件!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+ 预览
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+ 预览
+
+
+
+
+
+
+ 预览展示区域
+ 温馨提示:若预览失败,{{ props.name }} 可点击此处下载!
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/src/views/classTask/container/item-dialog.vue b/src/renderer/src/views/classTask/container/item-dialog.vue
new file mode 100644
index 0000000..4519b62
--- /dev/null
+++ b/src/renderer/src/views/classTask/container/item-dialog.vue
@@ -0,0 +1,616 @@
+
+
+
+
+
+ {{ classWorkAnalysis.title }}答题情况
+ {{
+ classWorkAnalysis.worktype
+ }}
+
+
+
+
+ 作业批阅
+ 作业概况
+ 作业报告
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 待批阅
+
+ 优
+ 优-
+ 良
+ 良-
+ 差
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/src/views/classTask/container/task-item.vue b/src/renderer/src/views/classTask/container/task-item.vue
new file mode 100644
index 0000000..9e7142e
--- /dev/null
+++ b/src/renderer/src/views/classTask/container/task-item.vue
@@ -0,0 +1,143 @@
+
+
+
+
+ {{ item.worktype }}
+ {{ item.uniquekey }}
+
+
+
+
+ {{ item.classcaption }}
+ | 截止时间:{{ item.deaddate }} | {{ tabactive }}
+
+
+
+
+ {{ item.workdataresultcount }} /{{ item.workdatacount }}
+ 已交
+
+
+ 2
+ 待批阅
+
+
+ {{ item.averagetime?item.averagetime:0 }} 分钟
+ 平均用时
+
+
+ {{ item.scoingRate }}
+ 得分率
+
+
+
+
+
diff --git a/src/renderer/src/views/login/index.vue b/src/renderer/src/views/login/index.vue
index 2828429..b11d241 100644
--- a/src/renderer/src/views/login/index.vue
+++ b/src/renderer/src/views/login/index.vue
@@ -47,7 +47,7 @@
/>
-
+
-
-
\ No newline at end of file
diff --git a/src/renderer/src/views/prepare/index.vue b/src/renderer/src/views/prepare/index.vue
index c086d14..0cb65bc 100644
--- a/src/renderer/src/views/prepare/index.vue
+++ b/src/renderer/src/views/prepare/index.vue
@@ -3,8 +3,8 @@
@@ -180,4 +297,15 @@ watchEffect(() => {
}
}
}
+.a-fade-leave-active,.a-fade-enter-active{
+ transition: all .3s;
+}
+.a-fade-enter-from,.a-fade-leave-to{
+ width: 0;
+ opacity: 0;
+}
+.a-fade-enter-to,.a-fade-leave-from{
+ width: 350px;
+ opacity: 1;
+}