push im model
This commit is contained in:
parent
0cc80eec5c
commit
0202c68fe0
16
README.md
16
README.md
|
@ -44,19 +44,3 @@ You don’t have to ever use `eject`. The curated feature set is suitable for sm
|
||||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||||
添加的数据
|
|
||||||
|
|
||||||
1、队伍属性管理 添加队伍
|
|
||||||
2、个人身份管理 添加身份
|
|
||||||
3、组织架构通过部门来组织
|
|
||||||
|
|
||||||
|
|
||||||
首页:
|
|
||||||
1、组织架构 用户数量统计,党员数量统计
|
|
||||||
2、武装力量 根据队伍统计
|
|
||||||
3、年度训练 按照年度统计类别,次数 (任务发布时,可选择多个类别)
|
|
||||||
|
|
||||||
4、物资管理
|
|
||||||
5、档案管理
|
|
||||||
|
|
||||||
6、评优争先
|
|
|
@ -63,7 +63,6 @@ const LayOut = (props: Store) => {
|
||||||
theme="dark"
|
theme="dark"
|
||||||
defaultSelectedKeys={["1"]}
|
defaultSelectedKeys={["1"]}
|
||||||
defaultOpenKeys={["sub1"]}
|
defaultOpenKeys={["sub1"]}
|
||||||
|
|
||||||
style={{ height: "100%", borderRight: 0 }}
|
style={{ height: "100%", borderRight: 0 }}
|
||||||
items={items}
|
items={items}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
|
|
@ -4,14 +4,13 @@ import {
|
||||||
NotificationOutlined,
|
NotificationOutlined,
|
||||||
PaperClipOutlined,
|
PaperClipOutlined,
|
||||||
FileSyncOutlined,
|
FileSyncOutlined,
|
||||||
DashboardOutlined
|
DashboardOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
|
|
||||||
export const items: ItemType<MenuItemType>[] = [
|
export const items: ItemType<MenuItemType>[] = [
|
||||||
{
|
{
|
||||||
key: "/",
|
key: "/",
|
||||||
label: `首页看板`,
|
label: `首页看板`,
|
||||||
|
|
||||||
icon: <DashboardOutlined />,
|
icon: <DashboardOutlined />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
html {
|
html ,#root {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
body {
|
body,
|
||||||
|
p,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||||
|
@ -11,9 +17,7 @@ body {
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
#root {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.projectContent {
|
.projectContent {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
|
@ -73,42 +77,6 @@ code {
|
||||||
color: #00ef97 !important;
|
color: #00ef97 !important;
|
||||||
border-color: #00ef97 !important;
|
border-color: #00ef97 !important;
|
||||||
}
|
}
|
||||||
.owner_model {
|
|
||||||
padding-bottom: 0px !important;
|
|
||||||
.ant-modal-content {
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-position: center center; /* 可选,确保图片在容器中居中 */
|
|
||||||
background-repeat: no-repeat; /* 确保图片不会重复 */
|
|
||||||
padding-bottom: 0px;
|
|
||||||
.ant-modal-header {
|
|
||||||
color: rgba(0, 0, 0, 0.85);
|
|
||||||
background: none;
|
|
||||||
border-bottom: 0px solid #f0f0f0;
|
|
||||||
align-items: center;
|
|
||||||
padding-left: 40px;
|
|
||||||
position: relative;
|
|
||||||
top: -10px;
|
|
||||||
left: -30px;
|
|
||||||
.ant-modal-title {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ant-modal-close {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.ant-modal-body {
|
|
||||||
min-height: 300px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.ant-modal-footer {
|
|
||||||
padding: 30px 30px;
|
|
||||||
text-align: center;
|
|
||||||
background: transparent;
|
|
||||||
border-top: 0px solid #f0f0f0;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-overflow {
|
.text-overflow {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
.contentBox{
|
|
||||||
overflow-y: auto;
|
|
||||||
height: 100%;
|
|
||||||
.tableName{
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
import { Button, Space, Modal, FormInstance } from "antd";
|
|
||||||
import { inject, observer } from "mobx-react";
|
|
||||||
import BTable from "@/components/b_table";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { Store } from "antd/lib/form/interface";
|
|
||||||
import React from "react";
|
|
||||||
import { columns } from "./permission_config";
|
|
||||||
import "./permission.less";
|
|
||||||
|
|
||||||
const Permission = (props: Store) => {
|
|
||||||
const { usrStore } = props;
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
|
||||||
const formRef = React.useRef<FormInstance>(null);
|
|
||||||
const [userId, setId] = useState<Number | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {}, []);
|
|
||||||
|
|
||||||
const addHandler = () => {};
|
|
||||||
return (
|
|
||||||
<div className="contentBox">
|
|
||||||
<Space direction="vertical" size="middle" style={{ display: "flex" }}>
|
|
||||||
<Button type="default" onClick={() => addHandler()}>
|
|
||||||
添加权限
|
|
||||||
</Button>
|
|
||||||
<BTable
|
|
||||||
store={usrStore}
|
|
||||||
scroll={{ x: "max-content" }}
|
|
||||||
columns={columns}
|
|
||||||
dataSource={usrStore.list}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
title={!userId ? "添加权限" : "编辑权限"}
|
|
||||||
width={800}
|
|
||||||
open={isModalOpen}
|
|
||||||
afterClose={() => formRef.current?.resetFields()}
|
|
||||||
onOk={() => formRef.current?.submit()}
|
|
||||||
okText="确定"
|
|
||||||
cancelText="取消"
|
|
||||||
onCancel={() => {
|
|
||||||
setId(null);
|
|
||||||
setIsModalOpen(false);
|
|
||||||
}}
|
|
||||||
></Modal>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default inject("usrStore")(observer(Permission));
|
|
|
@ -1,97 +0,0 @@
|
||||||
import { FormType } from "@/components/form/interface";
|
|
||||||
import { UserDataType } from "@/model/userModel";
|
|
||||||
import { ColumnsType } from "antd/lib/table";
|
|
||||||
import { Image } from "antd";
|
|
||||||
import { getBirthDateAndGender } from "@/util/util";
|
|
||||||
export const defaultConfig = (team, per) => [
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "用户名",
|
|
||||||
name: "user_name",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入用户名称!" }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
type: FormType.inputNumber,
|
|
||||||
label: "年龄",
|
|
||||||
name: "age",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入年龄" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "身份证",
|
|
||||||
name: "id_card",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入身份证" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "登录账号",
|
|
||||||
name: "account",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入登录账号" }],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "联系电话",
|
|
||||||
name: "tel",
|
|
||||||
value: "",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "邮箱",
|
|
||||||
name: "email",
|
|
||||||
value: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.upload,
|
|
||||||
label: "头像",
|
|
||||||
name: "head_img",
|
|
||||||
value: [],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const columns: ColumnsType<UserDataType> = [
|
|
||||||
{
|
|
||||||
title: "用户名",
|
|
||||||
dataIndex: "user_name",
|
|
||||||
width: 200,
|
|
||||||
fixed: "left",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "性别",
|
|
||||||
width: 150,
|
|
||||||
render: (render) => (
|
|
||||||
<span>{getBirthDateAndGender(render.id_card)?.gender}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
title: "头像",
|
|
||||||
dataIndex: "head_img",
|
|
||||||
width: 150,
|
|
||||||
render: (head_img) => {
|
|
||||||
return <Image src={head_img}></Image>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
title: "登录账号",
|
|
||||||
width: 150,
|
|
||||||
dataIndex: "account",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "身份证",
|
|
||||||
width: 150,
|
|
||||||
dataIndex: "id_card",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "联系电话",
|
|
||||||
width: 150,
|
|
||||||
dataIndex: "tel",
|
|
||||||
},
|
|
||||||
];
|
|
|
@ -1,11 +1,7 @@
|
||||||
import { createHashRouter } from "react-router-dom";
|
import { createHashRouter } from "react-router-dom";
|
||||||
import LayOut from "@/components/layout/layout";
|
import LayOut from "@/components/layout/layout";
|
||||||
import Login from "@/pages/login/login";
|
|
||||||
import User from "@/pages/user/user";
|
|
||||||
import Dashbord from "@/pages/dashbord";
|
|
||||||
import { rbac } from "./routers/rbac_router";
|
import { rbac } from "./routers/rbac_router";
|
||||||
import { sku } from "./routers/sku_router";
|
import { sku } from "./routers/sku_router";
|
||||||
import Order from "@/pages/order";
|
|
||||||
|
|
||||||
const routers = createHashRouter([
|
const routers = createHashRouter([
|
||||||
{
|
{
|
||||||
|
@ -15,25 +11,33 @@ const routers = createHashRouter([
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
index: true,
|
index: true,
|
||||||
element: <Dashbord />,
|
lazy: async() => ({
|
||||||
|
Component:(await import("@/pages/dashbord")).default,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/user/list",
|
path: "/user/list",
|
||||||
index: true,
|
index: true,
|
||||||
element: <User />,
|
lazy: async() => ({
|
||||||
|
Component:(await import("@/pages/user/user")).default,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
...rbac,
|
...rbac,
|
||||||
...sku,
|
...sku,
|
||||||
{
|
{
|
||||||
path: "/order/list",
|
path: "/order/list",
|
||||||
index: true,
|
index: true,
|
||||||
element: <Order />,
|
lazy: async() => ({
|
||||||
|
Component:(await import("@/pages/order")).default,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/login",
|
path: "/login",
|
||||||
element: <Login />,
|
lazy: async() => ({
|
||||||
|
Component:(await import("@/pages/login/login")).default,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,44 @@
|
||||||
import Rbac from "@/pages/rbac";
|
|
||||||
import Dep from "@/pages/rbac/dep";
|
|
||||||
import Menu from "@/pages/rbac/menu";
|
|
||||||
import Org from "@/pages/rbac/org";
|
|
||||||
import Role from "@/pages/rbac/role";
|
|
||||||
import Staff from "@/pages/rbac/staff";
|
|
||||||
export const rbac = [
|
export const rbac = [
|
||||||
{
|
{
|
||||||
path: "/rbac",
|
path: "/rbac",
|
||||||
element: <Rbac />,
|
lazy: async () => ({
|
||||||
|
Component: (await import("@/pages/rbac")).default,
|
||||||
|
}),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/rbac/menu",
|
path: "/rbac/menu",
|
||||||
index: true,
|
index: true,
|
||||||
element: <Menu />,
|
lazy: async () => ({
|
||||||
|
Component: (await import("@/pages/rbac/menu")).default,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/rbac/role",
|
path: "/rbac/role",
|
||||||
index: true,
|
index: true,
|
||||||
element: <Role />,
|
lazy: async () => ({
|
||||||
|
Component: (await import("@/pages/rbac/role")).default,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/rbac/org",
|
path: "/rbac/org",
|
||||||
index: true,
|
index: true,
|
||||||
element: <Org />,
|
lazy: async () => ({
|
||||||
|
Component: (await import("@/pages/rbac/org")).default,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/rbac/dep",
|
path: "/rbac/dep",
|
||||||
index: true,
|
index: true,
|
||||||
element: <Dep />,
|
lazy: async () => ({
|
||||||
}, {
|
Component: (await import("@/pages/rbac/dep")).default,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
path: "/rbac/staff",
|
path: "/rbac/staff",
|
||||||
index: true,
|
index: true,
|
||||||
element: <Staff />,
|
lazy: async () => ({
|
||||||
|
Component: (await import("@/pages/rbac/staff")).default,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import Menu from "@/pages/rbac/menu";
|
import Menu from "@/pages/rbac/menu";
|
||||||
import Sku from "@/pages/sku";
|
|
||||||
import Brand from "@/pages/sku/brand";
|
|
||||||
import Cat from "@/pages/sku/cat";
|
|
||||||
import Spec from "@/pages/sku/spec";
|
|
||||||
export const sku = [
|
export const sku = [
|
||||||
{
|
{
|
||||||
path: "/sku",
|
path: "/sku",
|
||||||
element: <Sku />,
|
lazy: async () => ({
|
||||||
|
Component: (await import("@/pages/sku")).default,
|
||||||
|
}),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/sku/list",
|
path: "/sku/list",
|
||||||
|
@ -16,17 +14,23 @@ export const sku = [
|
||||||
{
|
{
|
||||||
path: "/sku/cat",
|
path: "/sku/cat",
|
||||||
index: true,
|
index: true,
|
||||||
element: <Cat />,
|
lazy: async () => ({
|
||||||
|
Component: (await import("@/pages/sku/cat")).default,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/sku/spec",
|
path: "/sku/spec",
|
||||||
index: true,
|
index: true,
|
||||||
element: <Spec />,
|
lazy: async () => ({
|
||||||
|
Component: (await import("@/pages/sku/spec")).default,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/sku/brand",
|
path: "/sku/brand",
|
||||||
index: true,
|
index: true,
|
||||||
element: <Brand />,
|
lazy: async () => ({
|
||||||
|
Component: (await import("@/pages/sku/brand")).default,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,228 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
|
||||||
SPDX-License-Identifier: MIT
|
|
||||||
-->
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h3> Local Video </h3>
|
|
||||||
<video id="localVideo" width="160" height="120" autoplay muted></video>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<h3> Remote Video </h3>
|
|
||||||
<div id="remoteVideos"></div>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<h3> Logs </h3>
|
|
||||||
<div id="logs"></div>
|
|
||||||
<button onclick="call()">呼叫</button>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
let ws, pc, configuration,
|
|
||||||
url =
|
|
||||||
'https://rw.quwanya.cn/v1/public/webRtcConfig?service=turn&username=flutter-webrtc',
|
|
||||||
formId = "admin",
|
|
||||||
toId = "01J9T8MP541EJ54KY4505C0S4Y";
|
|
||||||
fetch(url)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
let res = data.data.credential;
|
|
||||||
configuration = {
|
|
||||||
iceServers: [
|
|
||||||
{
|
|
||||||
urls: res.uris[0],
|
|
||||||
"username": res.username,
|
|
||||||
"credential": res.password
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const call = () => {
|
|
||||||
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
|
|
||||||
.then(stream => {
|
|
||||||
pc = new RTCPeerConnection(configuration)
|
|
||||||
pc.ontrack = function (event) {
|
|
||||||
console.log(event)
|
|
||||||
if (event.track.kind === 'audio') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let el = document.createElement(event.track.kind)
|
|
||||||
el.srcObject = event.streams[0]
|
|
||||||
el.autoplay = true
|
|
||||||
el.controls = true
|
|
||||||
document.getElementById('remoteVideos').appendChild(el)
|
|
||||||
event.track.onmute = function (event) {
|
|
||||||
el.play()
|
|
||||||
}
|
|
||||||
event.streams[0].onremovetrack = ({ track }) => {
|
|
||||||
if (el.parentNode) {
|
|
||||||
el.parentNode.removeChild(el)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.getElementById('localVideo').srcObject = stream
|
|
||||||
stream.getTracks().forEach(track => pc.addTrack(track, stream))
|
|
||||||
pc.onicecandidate = e => {
|
|
||||||
if (!e.candidate) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: "candidate", data: {
|
|
||||||
"to": toId,
|
|
||||||
"from": formId,
|
|
||||||
"description": e.candidate,
|
|
||||||
"media": "video",
|
|
||||||
"session_id": toId + "-" + formId,
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
this.send({
|
|
||||||
type: "call",
|
|
||||||
"data": {
|
|
||||||
"to": toId,
|
|
||||||
"from": formId,
|
|
||||||
"description": "call",
|
|
||||||
"media": "video",
|
|
||||||
"session_id": toId + "-" + formId,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
send = (data) => {
|
|
||||||
ws.send(JSON.stringify(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// wss://rw.quwanya.cn/ws
|
|
||||||
initSocket = () => {
|
|
||||||
ws = new WebSocket("wss://rw.quwanya.cn/wsadmin?id=" + formId)
|
|
||||||
ws.addEventListener('open', function (event) {
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: "peerRoot", "data": {
|
|
||||||
"name": "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 )",
|
|
||||||
"id": formId,
|
|
||||||
"user_agent": "flutter-webrtc/js"
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
ws.onclose = function (evt) {
|
|
||||||
console.log(evt);
|
|
||||||
}
|
|
||||||
ws.onmessage = function (evt) {
|
|
||||||
let msg = JSON.parse(evt.data)
|
|
||||||
if (!msg) {
|
|
||||||
return console.log('failed to parse msg')
|
|
||||||
}
|
|
||||||
switch (msg.type) {
|
|
||||||
case 'offer':
|
|
||||||
let offer = msg.content.body.description
|
|
||||||
pc.setRemoteDescription(offer)
|
|
||||||
pc.createAnswer().then(answer => {
|
|
||||||
pc.setLocalDescription(answer)
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: 'answer', data: {
|
|
||||||
'to': msg.content.body.from,
|
|
||||||
'from': formId,
|
|
||||||
'description': { 'sdp': answer.sdp, 'type': answer.type },
|
|
||||||
'session_id': msg.content.body.from + "-" + toId,
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
return
|
|
||||||
case 'candidate':
|
|
||||||
let candidate = msg.content.body.description
|
|
||||||
console.log("candidate1111", msg.content)
|
|
||||||
if (!candidate) {
|
|
||||||
return console.log('failed to parse candidate')
|
|
||||||
}
|
|
||||||
pc.addIceCandidate(candidate)
|
|
||||||
return
|
|
||||||
case "read":
|
|
||||||
pc?.createOffer().then(function (offer) {
|
|
||||||
pc?.setLocalDescription(offer);
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: "offer", "data": {
|
|
||||||
"to": toId,//"31283192",
|
|
||||||
"from": formId,
|
|
||||||
"description": offer,
|
|
||||||
"media": "video",
|
|
||||||
"session_id": formId + "-" + toId,
|
|
||||||
}
|
|
||||||
}),)
|
|
||||||
}).catch(function (error) {
|
|
||||||
// 错误处理
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
return
|
|
||||||
case "call": // 接收到call 初始化 然后发送消息 read
|
|
||||||
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
|
|
||||||
.then(stream => {
|
|
||||||
pc = new RTCPeerConnection(configuration)
|
|
||||||
pc.ontrack = function (event) {
|
|
||||||
if (event.track.kind === 'audio') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let el = document.createElement(event.track.kind)
|
|
||||||
el.srcObject = event.streams[0]
|
|
||||||
el.autoplay = true
|
|
||||||
el.controls = true
|
|
||||||
document.getElementById('remoteVideos').appendChild(el)
|
|
||||||
event.track.onmute = function (event) {
|
|
||||||
el.play()
|
|
||||||
}
|
|
||||||
event.streams[0].onremovetrack = ({ track }) => {
|
|
||||||
if (el.parentNode) {
|
|
||||||
el.parentNode.removeChild(el)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.getElementById('localVideo').srcObject = stream
|
|
||||||
stream.getTracks().forEach(track => pc.addTrack(track, stream))
|
|
||||||
pc.onicecandidate = e => {
|
|
||||||
if (!e.candidate) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: 'candidate',
|
|
||||||
data: {
|
|
||||||
"to": toId,
|
|
||||||
"from": formId,
|
|
||||||
"description": e,
|
|
||||||
"media": "video",
|
|
||||||
"session_id": toId + "-" + formId,
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: "read", "data": {
|
|
||||||
"to": toId,
|
|
||||||
"from": formId,
|
|
||||||
"description": "read",
|
|
||||||
"media": "video",
|
|
||||||
"session_id": toId + "-" + formId,
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
})
|
|
||||||
return
|
|
||||||
case "answer":
|
|
||||||
pc?.setRemoteDescription(msg.content.body.description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.onerror = function (evt) {
|
|
||||||
console.log("ERROR: " + evt.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initSocket()
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</html>
|
|
157
src/ttttt.html
157
src/ttttt.html
|
@ -1,157 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
|
||||||
SPDX-License-Identifier: MIT
|
|
||||||
-->
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h3> Local Video </h3>
|
|
||||||
<video id="localVideo" width="160" height="120" autoplay muted></video>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<h3> Remote Video </h3>
|
|
||||||
<div id="remoteVideos"></div>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<h3> Logs </h3>
|
|
||||||
<div id="logs"></div>
|
|
||||||
<button onclick="call()">呼叫</button>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
let ws;
|
|
||||||
let pc;
|
|
||||||
let configuration;
|
|
||||||
var url =
|
|
||||||
'http://127.0.0.1:12214/v1/public/webRtcConfig?service=turn&username=flutter-webrtc';
|
|
||||||
fetch(url)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
let res = data.data.credential;
|
|
||||||
configuration = {
|
|
||||||
iceServers: [
|
|
||||||
{
|
|
||||||
urls: res.uris[0],
|
|
||||||
"username": res.username,
|
|
||||||
"credential": res.password
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const call = () => {
|
|
||||||
console.log(ws);
|
|
||||||
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
|
|
||||||
.then(stream => {
|
|
||||||
pc = new RTCPeerConnection(configuration)
|
|
||||||
pc.ontrack = function (event) {
|
|
||||||
if (event.track.kind === 'audio') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let el = document.createElement(event.track.kind)
|
|
||||||
el.srcObject = event.streams[0]
|
|
||||||
el.autoplay = true
|
|
||||||
el.controls = true
|
|
||||||
document.getElementById('remoteVideos').appendChild(el)
|
|
||||||
event.track.onmute = function (event) {
|
|
||||||
el.play()
|
|
||||||
}
|
|
||||||
event.streams[0].onremovetrack = ({ track }) => {
|
|
||||||
if (el.parentNode) {
|
|
||||||
el.parentNode.removeChild(el)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.getElementById('localVideo').srcObject = stream
|
|
||||||
stream.getTracks().forEach(track => pc.addTrack(track, stream))
|
|
||||||
pc.onicecandidate = e => {
|
|
||||||
if (!e.candidate) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ws.send(JSON.stringify({ type: 'candidate', data: e }))
|
|
||||||
}
|
|
||||||
pc?.createOffer().then(function (offer) {
|
|
||||||
pc?.setLocalDescription(offer);
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: "offer", "data": {
|
|
||||||
"to": "31283191",
|
|
||||||
"from": "31283192",
|
|
||||||
"description": offer,
|
|
||||||
"media": "video",
|
|
||||||
"session_id": "31283191-31283192",
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
// socketService.send({ type: 'offer', data: offer })
|
|
||||||
}).catch(function (error) {
|
|
||||||
// 错误处理
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
}).catch((e) => {
|
|
||||||
console.log(e);
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
// wss://rw.quwanya.cn/ws
|
|
||||||
initSocket = () => {
|
|
||||||
ws = new WebSocket("ws://127.0.0.1:12214/wsadmin?id=31283191")
|
|
||||||
ws.addEventListener('open', function (event) {
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: "peerRoot", "data": {
|
|
||||||
"name": "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 )",
|
|
||||||
"id": "31283191",
|
|
||||||
"user_agent": "flutter-webrtc/js"
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
ws.onclose = function (evt) {
|
|
||||||
console.log(evt);
|
|
||||||
// window.alert("Websocket has closed")
|
|
||||||
}
|
|
||||||
ws.onmessage = function (evt) {
|
|
||||||
let msg = JSON.parse(evt.data)
|
|
||||||
if (!msg) {
|
|
||||||
return console.log('failed to parse msg')
|
|
||||||
}
|
|
||||||
console.log(msg.type);
|
|
||||||
switch (msg.type) {
|
|
||||||
case 'offer':
|
|
||||||
let offer = msg.data.description
|
|
||||||
pc.setRemoteDescription(offer)
|
|
||||||
pc.createAnswer().then(answer => {
|
|
||||||
pc.setLocalDescription(answer)
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: 'answer', data: {
|
|
||||||
'to': msg.data.from,
|
|
||||||
'from': "31283192",
|
|
||||||
'description': { 'sdp': answer.sdp, 'type': answer.type },
|
|
||||||
'session_id': msg.data.from + "-31283192",
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
return
|
|
||||||
|
|
||||||
case 'candidate':
|
|
||||||
let candidate = msg.data.candidate
|
|
||||||
if (!candidate) {
|
|
||||||
return console.log('failed to parse candidate')
|
|
||||||
}
|
|
||||||
pc.addIceCandidate(candidate)
|
|
||||||
case "answer":
|
|
||||||
pc?.setRemoteDescription(msg.data.description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.onerror = function (evt) {
|
|
||||||
console.log("ERROR: " + evt.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initSocket()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</html>
|
|
Loading…
Reference in New Issue