This commit is contained in:
sx1989827 2024-06-04 17:31:28 +08:00
parent f7858d1c77
commit f485a45ac0
10 changed files with 26 additions and 906 deletions

View File

@ -16,7 +16,13 @@ Teamlinker是一个集成了多功能模块的团队协作平台涵盖团队
该平台解决了团队内高效协作的难题避免了公司使用多个工具分别处理项目进程、同事交流和客户会议的问题。相较于传统工具Teamlinker不仅提供基本而全面的协同办公需求而且成本极低更符合当前经济环境。
Teamlinker基于TeamOS系统开发是一种web操作系统用户可以并行处理不同任务类似于Win和Mac等操作系统。主要包含六个功能模块项目、Wiki、日历、会议、聊天和网盘这些功能之间无缝整合使团队协作更加顺畅。
### 用心做开源我们也很需要你的鼓励右上角Star🌟等你点亮
**我们同时也提供了相关的开源组件**
[TLCalendar](https://github.com/Teamlinker/Teamlinker/tree/dev/code/client/src/business/common/component/calendar)
[TLEditor](https://github.com/Teamlinker/Teamlinker/tree/dev/code/client/src/business/common/component/richEditorCore)
[TLMeetingClient](https://github.com/Teamlinker/Teamlinker/tree/dev/code/client/src/business/common/component/meeting)
[TLMeetingServer](https://github.com/Teamlinker/Teamlinker/tree/dev/code/server/common/meeting)
## 📋 官网

View File

@ -19,6 +19,13 @@ The platform solves the problem of efficient collaboration within the team and a
Teamlinker is developed based on the TeamOS system. It is a web operating system that allows users to process different tasks in parallel, similar to operating systems such as Win and Mac. It mainly contains six functional modules: project, wiki, calendar, meeting, chat and network disk. These functions are seamlessly integrated to make team collaboration smoother.
**We also support the relevant open-source components**
[TLCalendar](https://github.com/Teamlinker/Teamlinker/tree/dev/code/client/src/business/common/component/calendar)
[TLEditor](https://github.com/Teamlinker/Teamlinker/tree/dev/code/client/src/business/common/component/richEditorCore)
[TLMeetingClient](https://github.com/Teamlinker/Teamlinker/tree/dev/code/client/src/business/common/component/meeting)
[TLMeetingServer](https://github.com/Teamlinker/Teamlinker/tree/dev/code/server/common/meeting)
## 📋 Official website
https://team-linker.com

View File

@ -1,144 +0,0 @@
<h1 align="center">
TLCalendar
</h1>
<p align="center">
A simple calendar component based on <b>vue3</b> and <b>typescript</b>
</p>
<p align="center">
<a href="https://www.npmjs.com/package/tlcalendar">
<img src="https://flat.badgen.net/npm/v/tlcalendar?icon=npm" alt="npm"/>
</a>
<a href="https://www.npmjs.com/package/tlcalendar">
<img src="https://flat.badgen.net/bundlephobia/minzip/tlcalendar?color=green" alt="Minzipped size"/>
</a>
</p>
## About
It is an open-source calendar of [Teamlinker](https://team-linker.com). It provides a variety of features to help users to build their own calendar components like these below:
![](https://team-linker.com/assets/exampleCalendar2-92024062.png)
![](https://team-linker.com/assets/exampleCalendar1-c0bf9528.png)
## Features
1. Customize the calendar event show dialog
2. Switch between day,week and month
3. Support timezone switch
4. Calendar event freely drag and move
5. Support cross-day event and all-day event
6. Support multiply calendars
## Demo
[Teamlinker](https://team-linker.com)
Teamlinker provides a full experience of this package.Have a try!
## Installation
```shell
npm i tlcalendar
```
## Usage
### Global
main.ts
```typescript
import Calendar from "tlcalendar"
import "tlcalendar/style.css"
app.use(Calendar)
```
.vue
```vue
<script setup lang="ts">
import {computed, ref} from "vue";
import moment from "moment";
import "moment-timezone"
const startDay=ref(moment().startOf("weeks").format("YYYY-MM-DD"))
const endDay=ref(moment().endOf("weeks").format("YYYY-MM-DD"))
const month=ref(moment().format("YYYY-MM"))
const timezone=moment.tz.guess(true)
const content=ref([])
</script>
<template>
<div style="width: 600px;height: 800px">
<TLCalendar mode="day" :start-date="startDay" :end-date="endDay" :month="month" :event-list="[]" :utc-offset="8" :time-zone="timezone"></TLCalendar>
</div>
</template>
```
### Sfc
.vue
```vue
<script setup lang="ts">
import {computed, ref} from "vue";
import moment from "moment";
import "moment-timezone"
import {TLCalendar} from "tlcalendar";
import "tlcalendar/style.css"
const startDay=ref(moment().startOf("weeks").format("YYYY-MM-DD"))
const endDay=ref(moment().endOf("weeks").format("YYYY-MM-DD"))
const month=ref(moment().format("YYYY-MM"))
const timezone=moment.tz.guess(true)
</script>
<template>
<div style="width: 600px;height: 800px">
<TLCalendar mode="day" :start-date="startDay" :end-date="endDay" :month="month" :event-list="[]" :utc-offset="8" :time-zone="timezone"></TLCalendar>
</div>
</template>
```
## Customization
### Props
```typescript
eventList: {
type: PropType<IClient_Calendar_Info[]>;
required: true;
};
startDate: {
type: PropType<string>;
};
endDate: {
type: PropType<string>;
};
mode: {
type: PropType<"day" | "month">;
required: true;
};
month: {
type: PropType<string>;
};
utcOffset: {
type: PropType<number>;
};
timeZone: {
type: PropType<string>;
required: true;
};
```
### emits
```typescript
changeEventDate: (event: IClient_Calendar_Info, originalDateRange: {
start: IClient_Calendar_Date;
end: IClient_Calendar_Date;
}, type: "resize" | "move") => void;
//when user drag event or adjust event ,this event will be triggered
blankClick: (date: moment_2.Moment, point: {
x: number;
y: number;
}) => void;
//when user click blank area of calendar,this event will be triggered
```
### Slots
```vue
<template #shortView="{timeZone,selectedEvent,maskInfoTop,maskInfoLeft,onClose}">
</template>
```
## About Teamlinker
Teamlinker is a cooperation platform that integrates different kind of modules.You can contact your teammates,assign your tasks,start a meeting,schedule your events,manage your files and so on with Teamlinker.

View File

@ -1,15 +1,13 @@
{
"name": "tlcalendar",
"version": "0.0.3",
"version": "0.0.7",
"description": "A simple calendar component based on vue3 and typescript",
"main": "TLCalendar.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/Teamlinker/TLCalendar.git"
},
"repository": "https://github.com/Teamlinker/Teamlinker",
"homepage": "https://github.com/Teamlinker/Teamlinker/blob/dev/code/client/src/business/common/component/calendar/npm/README.md",
"keywords": ["vue","calendar","date","date-picker","time-picker","teamlinker"],
"author": "teamlinker",
"license": "ISC",

View File

@ -1,303 +0,0 @@
<h1 align="center">
TLMeetingClient
</h1>
<p align="center">
A simple video meeting library based on <b>node.js</b> and <b>typescript</b>
</p>
<p align="center">
This is <b>client</b> package,you can retrieve server package from <a href="https://github.com/Teamlinker/TLMeetingServer">here</a>
</p>
<p align="center">
<a href="https://www.npmjs.com/package/tlmeetingclient">
<img src="https://flat.badgen.net/npm/v/tlmeetingclient?icon=npm" alt="npm"/>
</a>
<a href="https://www.npmjs.com/package/tlmeetingclient">
<img src="https://flat.badgen.net/bundlephobia/minzip/tlmeetingclient?color=green" alt="Minzipped size"/>
</a>
</p>
## About
It is an open-source video meeting package of [Teamlinker](https://team-linker.com). It provides a variety of features to help users to build their own meeting rooms like these below:
![](https://team-linker.com/assets/exampleMeeting1-0dc2e795.png)
## Features
1. Very easy to complete video meeting functionality
2. Support screen share,presenter management
3. mute & unmute
4. meeting chat
5. Free and open-source based on mediasoup
## Demo
[Teamlinker](https://team-linker.com)
Teamlinker provides a full experience of this package.Have a try!
## Installation
```shell
npm i tlmeetingclient
```
## Usage
TLMeetingClient is based on socket.io,you should build a socket.io connection from backend and pass the socket instance to the TLMeetingClient construct function.
.vue
```typescript
const props=defineProps<{
meetingId:string,
password?:string,
inviteBusinessIds?:{
id:string,
type:ECommon_Model_Organization_Member_Type
}[]
}>()
const loading=ref(true)
const unReadCount=ref(0)
const organizationUserList=ref<OrganizationUserItem[]>([])
const tabValue=ref("participant")
const speaker=ref<OrganizationUserItem>()
const myOrganizationUserId=SessionStorage.get("organizationUserId")
const me=ref<OrganizationUserItem>({
organizationUserId:myOrganizationUserId,
name:"",
permission:ECommon_Meeting_Room_Permission.NORMAL,
audioStream:null,
videoStream:null,
video:true,
audio:true
})
const {t}=useI18n()
const meetingChat=ref<InstanceType<typeof MeetingChat>>(null)
const socket=SocketIOClient.get(ECommon_Socket_Type.MEETING)
const navigator=getCurrentNavigator()
const root=getRootNavigatorRef()
const appContext=getCurrentInstance().appContext
const currentMeeting=ref<DCSType<ICommon_Model_Meeting_Room>>()
const screenShareInfo=ref<{
video:MediaStream,
audio:MediaStream,
organizationUserId:string
}>()
let meetingClient=new MeetingClient(socket.getSocket())
watch(tabValue,()=>{
if(tabValue.value==="chat") {
unReadCount.value=0
}
})
meetingClient.onProducerStateChange=async (state, kind, businessId,type, stream, producerId) => {
let objOrganizationUser=organizationUserList.value.find(value => value.organizationUserId===businessId)
if(state=="new") {
if(type==="camera" || type==="data") {
if(!objOrganizationUser) {
let obj=userTeamInfoPick.getInfos([{
id:businessId,
type:ECommon_IM_Message_EntityType.USER
}])
organizationUserList.value.push({
organizationUserId:businessId,
name:obj[businessId]?obj[businessId].name:"",
permission:ECommon_Meeting_Room_Permission.NORMAL,
audioStream:kind==="audio"?stream:null,
videoStream:kind==="video"?stream:null,
audio:kind==="audio"?true:false,
video:kind==="video"?true:false,
})
objOrganizationUser=organizationUserList.value.at(-1);
} else {
if(kind=="video") {
objOrganizationUser.videoStream=stream
objOrganizationUser.video=true
} else if(kind=="audio") {
objOrganizationUser.audioStream=stream
objOrganizationUser.audio=true
}
}
if(!speaker.value) {
speaker.value=objOrganizationUser
}
} else if(type==="screen") {
if(!screenShareInfo.value) {
screenShareInfo.value={
organizationUserId:businessId,
audio:kind==="audio"?stream:null,
video:kind==="video"?stream:null,
}
} else {
if(kind==="video") {
screenShareInfo.value.video=stream
} else if(kind==="audio") {
screenShareInfo.value.audio=stream
}
}
}
} else if(state=="close") {
if(type==="camera" || type==="data") {
if(objOrganizationUser) {
let index=organizationUserList.value.findIndex(value => value.organizationUserId===businessId)
organizationUserList.value.splice(index,1)
if(objOrganizationUser===speaker.value) {
speaker.value=null
}
}
} else if(type==="screen") {
screenShareInfo.value=null;
}
} else if(state=="pause") {
if(objOrganizationUser) {
if(kind=="video") {
objOrganizationUser.video=false
} else if(kind=="audio") {
objOrganizationUser.audio=false
}
}
} else if(state=="resume") {
if(objOrganizationUser) {
if(kind=="video") {
objOrganizationUser.video=true
} else if(kind=="audio") {
objOrganizationUser.audio=true
}
}
}
handleState()
}
meetingClient.onKick=() => {
navigator.pop()
}
meetingClient.onJoinedRoom=async roomInfo => {
getCurrentMeeting()
meetingChat.value.getMessage()
if(props.inviteBusinessIds) {
socket.getSocket().emit("meeting_invite",props.inviteBusinessIds)
}
}
meetingClient.onLeavedRoom=roomInfo => {
}
meetingClient.onSpeaker=async businessId => {
let obj=organizationUserList.value.find(item=>item.organizationUserId===businessId)
if(obj && obj.organizationUserId!==myOrganizationUserId) {
speaker.value=obj
}
}
meetingClient.onLocalProducerInit=async stream => {
let obj=userTeamInfoPick.getInfos([{
id:myOrganizationUserId,
type:ECommon_IM_Message_EntityType.USER
}])
me.value={
organizationUserId:myOrganizationUserId,
name:obj[myOrganizationUserId]?obj[myOrganizationUserId].name:"",
permission:ECommon_Meeting_Room_Permission.NORMAL,
audioStream:stream,
videoStream:stream,
video:true,
audio:true
}
organizationUserList.value.unshift(me.value)
}
meetingClient.onLocalProducerStart=kind => {
if(kind=="video" || kind=="audio") {
handleState()
loading.value=false
}
}
const initMeeting=async ()=>{
let password=props.password
if(password==null) {
password=await Dialog.input(root.value,appContext,t("placeholder.typeMeetingPassword"))
if(!password) {
Message.error(t("tip.joinMeetingFailed"))
return
}
}
let preview:any=await Dialog.open(root.value,appContext,t("controller.app.meeting.meetingProfile.meetingPreview"),markRaw(MeetingPreview))
if(preview) {
let ret=await meetingClient.join(props.meetingId,password,preview.enableVideo,preview.enableAudio,preview.cameraId)
if(!ret?.success) {
Message.error(ret.msg)
navigator.pop()
}
} else {
navigator.pop()
}
}
const handleState=async ()=>{
let [retState,retPermission]=await Promise.all([
meetingClient.states(),
socket.getSocket().emitWithAck("meeting_get_presenters")
])
for(let obj of organizationUserList.value) {
if(retPermission[obj.organizationUserId]) {
obj.permission=retPermission[obj.organizationUserId]
}
}
for(let objState of retState) {
for(let objOrganizationUser of organizationUserList.value) {
if (objState.businessId===objOrganizationUser.organizationUserId) {
objOrganizationUser.video=objState.kinds["video"]
objOrganizationUser.audio=objState.kinds["audio"]
}
}
}
}
const handleUserInfo = (id: string, info: {
id: string,
name: string,
photo: string
}) => {
for(let obj of organizationUserList.value) {
if(obj.organizationUserId==id) {
obj.name=info.name;
}
}
}
const onPresenterChange=async (organizationUserId, permission) => {
let obj=organizationUserList.value.find(item=>item.organizationUserId===organizationUserId)
if(obj) {
obj.permission=permission
}
}
const onNewMessage=()=>{
if(tabValue.value!=="chat") {
unReadCount.value=1
}
}
const getCurrentMeeting=async ()=>{
let res=await apiMeeting.getCurrentRoom()
if(res?.code==0) {
currentMeeting.value=res.data
}
}
const handleLeaveMeeting=async ()=>{
await meetingClient.leave()
navigator.pop()
}
onBeforeMount(()=>{
eventBus.on(EClient_EVENTBUS_TYPE.UPDATE_USER_INFO, handleUserInfo)
eventBus.on(EClient_EVENTBUS_TYPE.LEAVE_MEETING, handleLeaveMeeting)
socket.getSocket().on("meeting_presenter_change", onPresenterChange)
initMeeting()
})
onBeforeUnmount(()=>{
eventBus.off(EClient_EVENTBUS_TYPE.UPDATE_USER_INFO, handleUserInfo)
eventBus.off(EClient_EVENTBUS_TYPE.LEAVE_MEETING, handleLeaveMeeting)
socket.getSocket().off("meeting_presenter_change", onPresenterChange)
if(meetingClient.getRoomInfo()) {
meetingClient.leave()
}
})
```
## About Teamlinker
Teamlinker is a cooperation platform that integrates different kind of modules.You can contact your teammates,assign your tasks,start a meeting,schedule your events,manage your files and so on with Teamlinker.

View File

@ -1,15 +1,13 @@
{
"name": "tlmeetingclient",
"version": "0.0.4",
"version": "0.0.5",
"description": "A simple video meeting library based on node.js and typescript",
"main": "client.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/Teamlinker/TLMeetingClient.git"
},
"repository": "https://github.com/Teamlinker/Teamlinker",
"homepage": "https://github.com/Teamlinker/Teamlinker/blob/dev/code/client/src/business/common/component/meeting/npm/README.md",
"keywords": ["teamlinker","meeting","video","nodejs","typescript","meeting client"],
"author": "teamlinker",
"license": "ISC",

View File

@ -1,221 +0,0 @@
<h1 align="center">
TLEditor
</h1>
<p align="center">
A simple block-style editor based on <b>vue3</b> and <b>typescript</b>
</p>
<p align="center">
<a href="https://www.npmjs.com/package/tleditor">
<img src="https://flat.badgen.net/npm/v/tleditor?icon=npm" alt="npm"/>
</a>
<a href="https://www.npmjs.com/package/tleditor">
<img src="https://flat.badgen.net/bundlephobia/minzip/tleditor?color=green" alt="Minzipped size"/>
</a>
</p>
## About
It is an open-source editor of [Teamlinker](https://team-linker.com). It provides a variety of features to help users to build their own text editors like these below:
![](https://team-linker.com/assets/exampleWiki1-fade1060.png)
![](https://team-linker.com/assets/exampleIM2-1c2642fb.png)
## Features
1. Output a clean json
2. Customize the style and behavior of pop menu,quote menu
3. Insert your own block style content
4. type "/" to get pop menu and "@" to get quote menu
5. Well-designed API
6. Free and open source
**It solves the cross-line selection issue that many other components can't support**
## Demo
[Teamlinker](https://team-linker.com)
Teamlinker provides a full experience of this package.Have a try!
## Installation
```shell
npm i tleditor
```
## Usage
### Global
main.ts
```typescript
import Editor from "tleditor"
import "tleditor/style.css"
app.use(Editor)
```
.vue
```vue
<script setup lang="ts">
import {ref} from "vue";
const content=ref([])
</script>
<template>
<div style="width: 500px">
<TLEditor v-model="content"></TLEditor>
</div>
</template>
```
### Sfc
.vue
```vue
<script setup lang="ts">
import {ref} from "vue";
import {TLEditor} from "tleditor"
import "tleditor/style.css"
const content=ref([])
</script>
<template>
<div style="width: 500px">
<TLEditor v-model="content"></TLEditor>
</div>
</template>
```
## Customization
### Props
```typescript
readonly: {
type: PropType<boolean>;
};
border: {
type: PropType<boolean>;
};
popMenuList: { //the pop menu list when user type "/"
type: PropType<{
type: any;
title: string;
}[]>;
};
placeholder: {
type: PropType<string>;
};
quoteType: { //a quote type should be specified when user type "@"
type: PropType<any>;
};
```
### emits
```typescript
onQuoteList: (keyword: string, handleFunc: (list: {
value: string;
label: string;
photo: string;
}[]) => void) => any;
//users use keyword to call user api and return a users' list,the call handleFunc to complete this search
onUploadFile: (file: File, handleFunc: (fileId: string, path: string) => void) => any;
//users use File object to process upload business,get a file id and path ,then call handleFunc to complete this upload
onPopMenuClick: (type: any, handleFunc: (item: IEditor_Content_Line_Config) => void) => any;
//the pop menu item click
onCustomAnchorClick: (type: any, value: string, link: string, label: string) => any;
//the anchor click of customized content
onMetaEnter: () => any;
//use press meta+enter button
onLinkClick: (type: any, value: string, x: number, y: number) => any;
//all anchors click
onSetLineConfigType: (linkElement: HTMLElement, objConfig: IEditor_Content_Line_Config) => any;
//customize the link and image to the html element
onGetLineConfigType: (config: IEditor_Content_Line_Config, linkElement: HTMLElement) => any;
//parse the link and image from html element
```
here is an example about onSetLineConfigType and onGetLineConfigType
```typescript
const onSetLineConfigType=(ele:HTMLElement,obj:IEditor_Content_Line_Config)=> {
if (obj.type == ECommon_Content_Line_Config_Type.LINK) {
ele.setAttribute("href", obj.link)
ele.setAttribute("target", "_blank")
ele.style.cursor = "pointer"
ele.innerText = obj.value
if (obj.style) {
for (let key in obj.style) {
ele.style[key] = obj.style[key]
}
}
} else if (obj.type == ECommon_Content_Line_Config_Type.IMAGE) {
ele.setAttribute("src", obj.link)
ele.setAttribute("width", String(obj.width ?? 200))
ele.setAttribute("height", "auto")
ele.setAttribute("fileId", obj.value)
} else if (obj.type === ECommon_Content_Line_Config_Type.FILE) {
ele.setAttribute("href", obj.link)
ele.setAttribute("download", obj.label)
ele.setAttribute("fileId", obj.value)
ele.style.margin = "0 2px 0 2px"
ele.style.cursor = "pointer"
ele.contentEditable = "false"
ele.innerText = obj.label
ele.style.color = "black"
let icon = document.createElement("i")
icon.className = "svg svg-file"
icon.style.marginRight = "5px"
icon.style.color = "gray"
ele.prepend(icon)
}
}
const onGetLineConfigType=(obj:IEditor_Content_Line_Config,ele:HTMLElement)=>{
if(ele.tagName=="A") {
let fileId=ele.getAttribute("fileId")
if(fileId) {
obj.type=ECommon_Content_Line_Config_Type.FILE
obj.link=ele.getAttribute("href")
obj.value=fileId
obj.label=ele.innerText??""
} else {
obj.type=ECommon_Content_Line_Config_Type.LINK
obj.link=ele.getAttribute("href")
obj.value=ele.innerText??""
}
} else if(ele.tagName=="IMG") {
obj.type=EEditor_Content_Line_Config_Type.IMAGE
obj.link=ele.getAttribute("src")
obj.width=parseInt(ele.getAttribute("width"))
obj.value=ele.getAttribute("fileId")
}
}
```
### Methods
```typescript
insertConfig: (itemList: IEditor_Content_Line_Config[]) => void;
```
here is an example:
```typescript
let arrPromise=await Promise.allSettled((data.data as File[]).map(file=>{
return apiFile.upload({
file:file as any
}).then(res=>{
let ret:ICommon_Content_Line_Config
if(res?.code==0) {
ret={
type:ECommon_Content_Line_Config_Type.FILE,
value:res.data.id,
link:res.data.path,
label:file.name
}
}
return ret;
})
}))
if(loading) {
loading.value=false
}
let itemList=arrPromise.filter(item=>{
if(item.status==="fulfilled" && item.value) {
return true
}
}).map(item=>{
return (item as any).value
})
objEditor.value.insertConfig(itemList)
```
## About Teamlinker
Teamlinker is a cooperation platform that integrates different kind of modules.You can contact your teammates,assign your tasks,start a meeting,schedule your events,manage your files and so on with Teamlinker.

View File

@ -1,12 +1,10 @@
{
"name": "tleditor",
"version": "0.0.3",
"version": "0.0.5",
"description": "A simple block-style editor based on vue3 and typescript",
"main": "TLEditor.js",
"repository": {
"type": "git",
"url": "https://github.com/Teamlinker/TLEditor.git"
},
"repository": "https://github.com/Teamlinker/Teamlinker",
"homepage": "https://github.com/Teamlinker/Teamlinker/blob/dev/code/client/src/business/common/component/richEditorCore/npm/README.md",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},

View File

@ -1,217 +0,0 @@
<h1 align="center">
TLMeetingServer
</h1>
<p align="center">
A simple video meeting library based on <b>node.js</b> and <b>typescript</b>
</p>
<p align="center">
This is <b>server</b> package,you can retrieve client package from <a href="https://github.com/Teamlinker/TLMeetingClient">here</a>
</p>
<p align="center">
<a href="https://www.npmjs.com/package/tlmeetingserver">
<img src="https://flat.badgen.net/npm/v/tlmeetingserver?icon=npm" alt="npm"/>
</a>
<a href="https://www.npmjs.com/package/tlmeetingserver">
<img src="https://flat.badgen.net/bundlephobia/minzip/tlmeetingserver?color=green" alt="Minzipped size"/>
</a>
</p>
## About
It is an open-source video meeting package of [Teamlinker](https://team-linker.com). It provides a variety of features to help users to build their own meeting rooms like these below:
![](https://team-linker.com/assets/exampleMeeting1-0dc2e795.png)
## Features
1. Very easy to complete video meeting functionality
2. Support screen share,presenter management
3. mute & unmute
4. meeting chat
5. Free and open-source based on mediasoup
## Demo
[Teamlinker](https://team-linker.com)
Teamlinker provides a full experience of this package.Have a try!
## Installation
```shell
npm i tlmeetingserver
```
## Usage
TLMeetingServer is based on socket.io,you should build a socket.io connection from client and pass the io instance and meeting config to the TLMeetingServer construct function.
```typescript
let objMeeting=new MeetingServer(io,meetingConfig as any)
objMeeting.onJoinRoom=async (roomId,extraData, socketData, socketId) => {
try {
let objRoom=await MeetingRoomService.getItemById(roomId)
if(!objRoom) {
return {
businessId:null,
roomName:null,
error:"room not found"
}
} else if(objRoom.getItem().password!==extraData){
return {
businessId:null,
roomName:null,
error:"password wrong"
}
} else if(objRoom.getItem().organization_id!==socketData.organizationId) {
return {
businessId:null,
roomName:null,
error:"access forbidden"
}
} else {
return {
businessId:socketData.organizationUserId,
roomName:objRoom.getItem().name
}
}
} catch (err) {
console.error(err)
}
}
objMeeting.onJoinedRoom=async (roomId, businessId, socketId) => {
try {
let objRoom=await MeetingRoomService.getItemById(roomId)
if(objRoom) {
await objRoom.addMember(businessId)
emit.in(businessId).socketsJoin(roomId)
}
} catch(err) {
console.error(err)
}
}
objMeeting.onLeavedRoom=async (type, roomId, businessId) => {
try {
let objRoom=await MeetingRoomService.getItemById(roomId)
if(objRoom) {
await objRoom.removeMember(businessId)
emit.in(businessId).socketsLeave(roomId)
}
} catch(err) {
console.error(err)
}
}
objMeeting.onHandleOperation=async (type, roomId, fromBusinessId, toBusinessId, kind) => {
try {
let objRoom=await MeetingRoomService.getItemById(roomId)
if(objRoom) {
let ret=await objRoom.getPermission(fromBusinessId)
if(ret===ECommon_Meeting_Room_Permission.PRESENTER) {
return true;
}
} else {
return false
}
} catch(err) {
console.error(err)
return false
}
}
objMeeting.onDeleteRoom=async roomId => {
try {
await rpcContentApi.clearByRefId(roomId)
} catch (err) {
console.error(err)
}
}
objMeeting.onMessageSend=async (roomId, fromBusinessId, message) => {
try {
await rpcContentApi.add(roomId,ECommon_Model_Content_Type.MEETING_CHAT, fromBusinessId,message as string)
} catch (err) {
console.error(err)
}
}
await objMeeting.start()
```
### Meeting Config
```typescript
export default {
"worker": {
"logLevel": "warn",
"logTags": [
"info",
"ice",
"dtls",
"rtp",
"srtp",
"rtcp"
],
"rtcMinPort": 40000,
"rtcMaxPort": 49999
},
"codecs": [
{
"kind": "audio",
"mimeType": "audio/opus",
"clockRate": 48000,
"channels": 2
},
{
"kind": "video",
"mimeType": "video/VP8",
"clockRate": 90000,
"parameters": {
"x-google-start-bitrate": 1000
}
},
{
"kind": "video",
"mimeType": "video/VP9",
"clockRate": 90000,
"parameters": {
"profile-id": 2,
"x-google-start-bitrate": 1000
}
},
{
"kind": "video",
"mimeType": "video/h264",
"clockRate": 90000,
"parameters": {
"packetization-mode": 1,
"profile-level-id": "4d0032",
"level-asymmetry-allowed": 1,
"x-google-start-bitrate": 1000
}
},
{
"kind": "video",
"mimeType": "video/h264",
"clockRate": 90000,
"parameters": {
"packetization-mode": 1,
"profile-level-id": "42e01f",
"level-asymmetry-allowed": 1,
"x-google-start-bitrate": 1000
}
}
],
"webRtcTransport": {
"listenIps": [
{
"ip": "0.0.0.0",
"announcedIp": "192.168.110.6" //this ip should be the public ip
}
],
"enableUdp": true,
"enableTcp": true,
"preferUdp": true,
"enableSctp": false,
"initialAvailableOutgoingBitrate": 1000000,
"maxSctpMessageSize": 262144
}
}
```
## About Teamlinker
Teamlinker is a cooperation platform that integrates different kind of modules.You can contact your teammates,assign your tasks,start a meeting,schedule your events,manage your files and so on with Teamlinker.

View File

@ -1,15 +1,13 @@
{
"name": "tlmeetingserver",
"version": "0.0.2",
"version": "0.0.3",
"description": "A simple video meeting library based on node.js and typescript",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/Teamlinker/TLMeetingServer.git"
},
"repository": "https://github.com/Teamlinker/Teamlinker",
"homepage": "https://github.com/Teamlinker/Teamlinker/blob/dev/code/server/common/meeting/npm/README.md",
"keywords": ["teamlinker","meeting","video","nodejs","typescript","meeting server"],
"author": "teamlinker",
"license": "ISC",