webrtc
This commit is contained in:
parent
87963fa8e1
commit
3d3f2fd89e
|
@ -6,7 +6,7 @@ import { useEffect } from "react";
|
||||||
import SocketService from "./util/socket";
|
import SocketService from "./util/socket";
|
||||||
const socketService = SocketService.getInstance();
|
const socketService = SocketService.getInstance();
|
||||||
const onMessage = (data: any) => {
|
const onMessage = (data: any) => {
|
||||||
console.log("message", data);
|
// console.log("message", data);
|
||||||
};
|
};
|
||||||
socketService.on("message", onMessage);
|
socketService.on("message", onMessage);
|
||||||
const onConnected = () => {
|
const onConnected = () => {
|
||||||
|
|
|
@ -12,6 +12,7 @@ interface UploadFileProps {
|
||||||
interface UploadFileEx extends UploadFile {
|
interface UploadFileEx extends UploadFile {
|
||||||
systemImageId?: number;
|
systemImageId?: number;
|
||||||
bannerName?: string;
|
bannerName?: string;
|
||||||
|
file_name?: string;
|
||||||
redictUrl?: string;
|
redictUrl?: string;
|
||||||
id?: number;
|
id?: number;
|
||||||
}
|
}
|
||||||
|
@ -48,6 +49,7 @@ const AliUpload = (props: UploadFileProps) => {
|
||||||
const handleChange: UploadProps["onChange"] = ({ fileList: newFileList }) => {
|
const handleChange: UploadProps["onChange"] = ({ fileList: newFileList }) => {
|
||||||
newFileList.forEach((i)=>{
|
newFileList.forEach((i)=>{
|
||||||
i.url = `${Config.uploadUrl}uploads/`+i.name
|
i.url = `${Config.uploadUrl}uploads/`+i.name
|
||||||
|
i.fileName = i.name
|
||||||
})
|
})
|
||||||
setFileList(newFileList);
|
setFileList(newFileList);
|
||||||
props.onChnage(newFileList)
|
props.onChnage(newFileList)
|
||||||
|
@ -66,7 +68,7 @@ const AliUpload = (props: UploadFileProps) => {
|
||||||
fileList={files}
|
fileList={files}
|
||||||
action={`${Config.uploadUrl}v1/public/fts/upload`}
|
action={`${Config.uploadUrl}v1/public/fts/upload`}
|
||||||
onPreview={handlePreview}
|
onPreview={handlePreview}
|
||||||
maxCount={1}
|
maxCount={4}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
>
|
>
|
||||||
{files.length >= 4 ? null : uploadButton}
|
{files.length >= 4 ? null : uploadButton}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Pagination, PaginationProps, Table } from "antd";
|
import { Pagination, PaginationProps, Table } from "antd";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
const BTable = (props: any) => {
|
const BTable = (props: any) => {
|
||||||
const { store, dataSource } = props;
|
const { store, dataSource ,selectCallback} = props;
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
|
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
|
||||||
setSelectedRowKeys(newSelectedRowKeys);
|
setSelectedRowKeys(newSelectedRowKeys);
|
||||||
|
selectCallback(newSelectedRowKeys);
|
||||||
};
|
};
|
||||||
|
|
||||||
const rowSelection = {
|
const rowSelection = {
|
||||||
|
|
|
@ -87,6 +87,13 @@ const LayOut = (props: Store) => {
|
||||||
{ label: "训练任务", key: "/admin/training" },
|
{ label: "训练任务", key: "/admin/training" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "/admin/sys",
|
||||||
|
label: `系统管理`,
|
||||||
|
children: [
|
||||||
|
{ label: "系统设置", key: "/admin/sys/setting" },
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<div className="layout">
|
<div className="layout">
|
||||||
|
|
|
@ -37,7 +37,6 @@ export default function MapFrom(props:any) {
|
||||||
// 开启坐标选择功能
|
// 开启坐标选择功能
|
||||||
mouseTool.on("draw", function (event) {
|
mouseTool.on("draw", function (event) {
|
||||||
var lnglat = event.obj.getBounds().getCenter(); // 获取图形的中心点坐标
|
var lnglat = event.obj.getBounds().getCenter(); // 获取图形的中心点坐标
|
||||||
console.log("选择的坐标是:", lnglat.lng, lnglat.lat);
|
|
||||||
markers = mouseTool.overlays.marker
|
markers = mouseTool.overlays.marker
|
||||||
props.onChange({lng:lnglat.lng,lat:lnglat.lat})
|
props.onChange({lng:lnglat.lng,lat:lnglat.lat})
|
||||||
var mk = []
|
var mk = []
|
||||||
|
|
|
@ -20,6 +20,7 @@ export interface UserDataType {
|
||||||
startTime: any;
|
startTime: any;
|
||||||
endTime: any;
|
endTime: any;
|
||||||
status: any;
|
status: any;
|
||||||
|
identity: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TagDataType {
|
export interface TagDataType {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import SimpleForm from "@/components/form/simple_form";
|
import SimpleForm from "@/components/form/simple_form";
|
||||||
import { TrainingConfig } from "@/pages/training/traning_config";
|
import { traningConfig } from "@/pages/training/traning_config";
|
||||||
import { Button, Form, FormInstance, Modal, Select } from "antd";
|
import { Button, Form, FormInstance, Modal, Select } from "antd";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
@ -15,7 +15,7 @@ const Dispath = (props: Store) => {
|
||||||
const openDispatch = () => {
|
const openDispatch = () => {
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
setProjectConfig([
|
setProjectConfig([
|
||||||
...TrainingConfig,
|
...traningConfig,
|
||||||
{
|
{
|
||||||
type: FormType.cehckboxGroup,
|
type: FormType.cehckboxGroup,
|
||||||
label: "参与人员选择",
|
label: "参与人员选择",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Modal } from "antd";
|
import { Modal } from "antd";
|
||||||
import { Store } from "antd/es/form/interface";
|
import { Store } from "antd/es/form/interface";
|
||||||
import { inject, observer } from "mobx-react";
|
import { inject, observer } from "mobx-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
@ -18,10 +18,10 @@ const Ec = (props: Store) => {
|
||||||
const openDispatch = () => {
|
const openDispatch = () => {
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
};
|
};
|
||||||
const callphone=(record:any)=>{
|
const callphone = (record: any) => {
|
||||||
webRTC.init()
|
webRTC.init();
|
||||||
webRTC.calls()
|
webRTC.calls();
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span onClick={openDispatch}> 应急连线</span>
|
<span onClick={openDispatch}> 应急连线</span>
|
||||||
|
@ -49,7 +49,13 @@ const Ec = (props: Store) => {
|
||||||
<div>姓名:{item.account} : 未在线</div>
|
<div>姓名:{item.account} : 未在线</div>
|
||||||
<p></p>
|
<p></p>
|
||||||
<div>
|
<div>
|
||||||
点击呼叫:<PhoneTwoTone style={{ fontSize: "20px" }} onClick={(item)=>{callphone(item)}} />
|
点击呼叫:
|
||||||
|
<PhoneTwoTone
|
||||||
|
style={{ fontSize: "20px" }}
|
||||||
|
onClick={(item) => {
|
||||||
|
callphone(item);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -57,10 +63,22 @@ const Ec = (props: Store) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ec_right">
|
<div className="ec_right">
|
||||||
<video id="rtcVideo"></video>
|
<div style={{display:"flex"}}>
|
||||||
<p className="ec_right_end" onClick={()=>{
|
<video id="rtcVideo" style={{ width: "300px", height: "300px" }}></video>
|
||||||
webRTC.close()
|
<video
|
||||||
}}>结束通话</p>
|
id="remoteVideo"
|
||||||
|
style={{ width: "300px", height: "300px" }}
|
||||||
|
></video>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p
|
||||||
|
className="ec_right_end"
|
||||||
|
onClick={() => {
|
||||||
|
webRTC.close();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
结束通话
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import AliUpload from "@/components/ali_upload";
|
||||||
|
import { Button, UploadFile } from "antd";
|
||||||
|
import { Store } from "antd/es/form/interface";
|
||||||
|
import { inject, observer } from "mobx-react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
interface UploadFilewx extends UploadFile {
|
||||||
|
file_name: string;
|
||||||
|
file_url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Banner = (props: Store) => {
|
||||||
|
const { sysStore } = props;
|
||||||
|
const [files, setFileList] = useState<UploadFilewx[]>([]);
|
||||||
|
const uploadChange = (v) => {
|
||||||
|
v.forEach((i) => {
|
||||||
|
i.file_name = i.fileName;
|
||||||
|
i.file_url = i.url
|
||||||
|
});
|
||||||
|
setFileList(v);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
sysStore.getlist().then(() => {
|
||||||
|
let data = sysStore.list
|
||||||
|
data.forEach((e)=>{
|
||||||
|
e.fileNmae = e.file_name
|
||||||
|
e.name = e.file_name
|
||||||
|
e.url = e.file_url
|
||||||
|
})
|
||||||
|
setFileList(data);
|
||||||
|
});
|
||||||
|
}, [sysStore]);
|
||||||
|
const save = () => {
|
||||||
|
sysStore.add({ list: files });
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button type="dashed" onClick={save}>
|
||||||
|
保存
|
||||||
|
</Button>
|
||||||
|
<AliUpload imgList={files} onChnage={uploadChange} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default inject("sysStore")(observer(Banner));
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { Tabs, TabsProps } from "antd";
|
||||||
|
import Banner from "./banner";
|
||||||
|
|
||||||
|
const SystemPage = () => {
|
||||||
|
const items: TabsProps["items"] = [
|
||||||
|
{
|
||||||
|
key: "1",
|
||||||
|
label: "banner管理",
|
||||||
|
children: <Banner />,
|
||||||
|
},
|
||||||
|
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Tabs defaultActiveKey="1" items={items} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SystemPage;
|
|
@ -7,14 +7,17 @@ import { UserDataType } from "@/model/userModel";
|
||||||
import { Store } from "antd/lib/form/interface";
|
import { Store } from "antd/lib/form/interface";
|
||||||
import SimpleForm from "@/components/form/simple_form";
|
import SimpleForm from "@/components/form/simple_form";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormType } from "@/components/form/interface";
|
|
||||||
import baseHttp from "@/service/base";
|
import baseHttp from "@/service/base";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import { traningConfig } from "./traning_config";
|
||||||
|
import { FormType } from "@/components/form/interface";
|
||||||
|
import TraningUser from "./traningUser";
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
const Trainings = (props: Store) => {
|
const Trainings = (props: Store) => {
|
||||||
const { trainingStore, trainingCatStore } = props;
|
const { trainingStore, trainingCatStore } = props;
|
||||||
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
||||||
|
const [isModalOpenUser, setIsModalOpenUser] = useState<boolean>(false);
|
||||||
const [projectConfig, setProjectConfig] = useState<any>([]);
|
const [projectConfig, setProjectConfig] = useState<any>([]);
|
||||||
const formRef = React.useRef<FormInstance>(null);
|
const formRef = React.useRef<FormInstance>(null);
|
||||||
const [record, setRecord] = useState<any>(null);
|
const [record, setRecord] = useState<any>(null);
|
||||||
|
@ -34,6 +37,16 @@ const Trainings = (props: Store) => {
|
||||||
dataIndex: "id",
|
dataIndex: "id",
|
||||||
render: (any, record) => (
|
render: (any, record) => (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
trainingStore.id = record?.identity;
|
||||||
|
setIsModalOpenUser(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
训练人员管理
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="dashed"
|
type="dashed"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -66,12 +79,25 @@ const Trainings = (props: Store) => {
|
||||||
start_time: dayjs(record.start_time),
|
start_time: dayjs(record.start_time),
|
||||||
end_time: dayjs(record.end_time),
|
end_time: dayjs(record.end_time),
|
||||||
};
|
};
|
||||||
setProjectConfig(defaultConfig);
|
setProjectConfig([
|
||||||
|
...traningConfig,
|
||||||
|
{
|
||||||
|
type: FormType.cehckboxGroup,
|
||||||
|
label: "参与人员选择",
|
||||||
|
name: "user_id",
|
||||||
|
value: [],
|
||||||
|
checkboxData: userList,
|
||||||
|
rules: [{ required: true, message: "请选择参与人员!" }],
|
||||||
|
},
|
||||||
|
]);
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
formRef.current?.setFieldsValue(data);
|
formRef.current?.setFieldsValue(data);
|
||||||
setRecord(data);
|
setRecord(data);
|
||||||
setId(record.id);
|
setId(record.id);
|
||||||
};
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
trainingStore.getlist();
|
||||||
|
}, [trainingStore]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
trainingCatStore.getlist().then(() => {
|
trainingCatStore.getlist().then(() => {
|
||||||
setStash(trainingCatStore.list);
|
setStash(trainingCatStore.list);
|
||||||
|
@ -99,86 +125,31 @@ const Trainings = (props: Store) => {
|
||||||
}
|
}
|
||||||
setIsModalOpen(false);
|
setIsModalOpen(false);
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
|
||||||
trainingStore.getlist();
|
// 任务发布handler
|
||||||
}, [trainingStore]);
|
const fabu = () => {
|
||||||
const defaultConfig = [
|
setProjectConfig([
|
||||||
{
|
...traningConfig,
|
||||||
type: FormType.input,
|
{
|
||||||
label: "任务标题",
|
type: FormType.cehckboxGroup,
|
||||||
name: "title",
|
label: "参与人员选择",
|
||||||
value: "",
|
name: "user_id",
|
||||||
rules: [{ required: true, message: "请输入任务标题!" }],
|
value: [],
|
||||||
},
|
checkboxData: userList,
|
||||||
{
|
rules: [{ required: true, message: "请选择参与人员!" }],
|
||||||
type: FormType.input,
|
},
|
||||||
label: "任务描述",
|
]);
|
||||||
name: "desc",
|
setId(null);
|
||||||
value: "",
|
setIsModalOpen(true);
|
||||||
rules: [{ required: true, message: "请输入任务描述!" }],
|
};
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "任务地点",
|
|
||||||
name: "address",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入任务地点!" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.date,
|
|
||||||
label: "任务开始时间",
|
|
||||||
name: "start_time",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入任务开始时间!" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.date,
|
|
||||||
label: "任务结束时间",
|
|
||||||
name: "end_time",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入任务结束时间!" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "任务积分设置",
|
|
||||||
name: "score",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入任务积分设置!" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "训练次数",
|
|
||||||
name: "count",
|
|
||||||
value: 0,
|
|
||||||
rules: [{ required: true, message: "请输入训练次数!" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.cehckboxGroup,
|
|
||||||
label: "参与人员选择",
|
|
||||||
name: "user_id",
|
|
||||||
value: [],
|
|
||||||
checkboxData: userList,
|
|
||||||
rules: [{ required: true, message: "请选择参与人员!" }],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const onFinishFailed = () => {};
|
const onFinishFailed = () => {};
|
||||||
// 用户选择回调
|
// 用户选择回调
|
||||||
return (
|
return (
|
||||||
<div className="contentBox">
|
<div className="contentBox">
|
||||||
<Space direction="vertical" size="middle" style={{ display: "flex" }}>
|
<Space direction="vertical" size="middle" style={{ display: "flex" }}>
|
||||||
<Space direction="horizontal" size={"middle"}>
|
<Space direction="horizontal" size={"middle"}>
|
||||||
<Button
|
<Button onClick={() => fabu()}>任务发布</Button>
|
||||||
type="default"
|
|
||||||
onClick={() => {
|
|
||||||
setProjectConfig(defaultConfig);
|
|
||||||
setId(null);
|
|
||||||
setIsModalOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
任务发布
|
|
||||||
</Button>
|
|
||||||
</Space>
|
</Space>
|
||||||
|
|
||||||
<BTable
|
<BTable
|
||||||
store={trainingStore}
|
store={trainingStore}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
@ -228,6 +199,18 @@ const Trainings = (props: Store) => {
|
||||||
</>
|
</>
|
||||||
</SimpleForm>
|
</SimpleForm>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
title="训练人员"
|
||||||
|
width={1200}
|
||||||
|
open={isModalOpenUser}
|
||||||
|
footer={null}
|
||||||
|
onCancel={() => {
|
||||||
|
setIsModalOpenUser(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TraningUser/>
|
||||||
|
</Modal>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
import BTable from "@/components/b_table";
|
||||||
|
import { UserDataType } from "@/model/userModel";
|
||||||
|
import { Button, InputNumber, message, Modal, Space } from "antd";
|
||||||
|
import { Store } from "antd/es/form/interface";
|
||||||
|
import { ColumnsType } from "antd/lib/table";
|
||||||
|
import { inject, observer } from "mobx-react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const TraningUser = (props: Store) => {
|
||||||
|
const { trainingStore } = props;
|
||||||
|
const [keys, setKeys] = useState<Array<string>>([]);
|
||||||
|
const [score, setScore] = useState<number | null>(0);
|
||||||
|
const [isModalOpenUser, setIsModalOpenUser] = useState<boolean>(false);
|
||||||
|
useEffect(() => {
|
||||||
|
trainingStore.getUserListByTraning();
|
||||||
|
}, [trainingStore]);
|
||||||
|
|
||||||
|
const okhandler = () => {
|
||||||
|
let arr: any[] = [];
|
||||||
|
for (let index = 0; index < keys.length; index++) {
|
||||||
|
arr.push({
|
||||||
|
score: score,
|
||||||
|
user_identity: keys[index],
|
||||||
|
source: 2,
|
||||||
|
source_identity: trainingStore.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let res = trainingStore.setUserScore(arr);
|
||||||
|
if (res){
|
||||||
|
message.success("积分设置成功")
|
||||||
|
setIsModalOpenUser(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const _columns: ColumnsType<UserDataType> = [
|
||||||
|
{ title: "民兵名称", dataIndex: "user_name" },
|
||||||
|
{ title: "民兵描述", dataIndex: "remark" },
|
||||||
|
{ title: "民兵账号", dataIndex: "account" },
|
||||||
|
{
|
||||||
|
title: "操作",
|
||||||
|
dataIndex: "id",
|
||||||
|
render: (any, record) => (
|
||||||
|
<Space wrap>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
setKeys([record.identity]);
|
||||||
|
setIsModalOpenUser(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
设置积分
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
|
<p>训练人员</p>
|
||||||
|
<Button
|
||||||
|
disabled={keys.length === 0}
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
setIsModalOpenUser(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
批量设置
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Modal
|
||||||
|
title="积分设置"
|
||||||
|
width={300}
|
||||||
|
open={isModalOpenUser}
|
||||||
|
onOk={() => okhandler()}
|
||||||
|
afterClose={() => setKeys([])}
|
||||||
|
onCancel={() => {
|
||||||
|
setIsModalOpenUser(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<InputNumber
|
||||||
|
style={{ width: "200px" }}
|
||||||
|
defaultValue={0}
|
||||||
|
value={score}
|
||||||
|
onChange={(e) => {
|
||||||
|
setScore(e);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
<BTable
|
||||||
|
store={trainingStore}
|
||||||
|
columns={_columns}
|
||||||
|
dataSource={trainingStore.userList}
|
||||||
|
selectCallback={(e) => {
|
||||||
|
setKeys(e);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default inject("trainingStore")(observer(TraningUser));
|
|
@ -1,54 +1,53 @@
|
||||||
import { FormType } from "@/components/form/interface";
|
import { FormType } from "@/components/form/interface";
|
||||||
|
export const traningConfig = [
|
||||||
|
{
|
||||||
|
type: FormType.input,
|
||||||
|
label: "任务标题",
|
||||||
|
name: "title",
|
||||||
|
value: "",
|
||||||
|
rules: [{ required: true, message: "请输入任务标题!" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: FormType.input,
|
||||||
|
label: "任务描述",
|
||||||
|
name: "desc",
|
||||||
|
value: "",
|
||||||
|
rules: [{ required: true, message: "请输入任务描述!" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: FormType.input,
|
||||||
|
label: "任务地点",
|
||||||
|
name: "address",
|
||||||
|
value: "",
|
||||||
|
rules: [{ required: true, message: "请输入任务地点!" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: FormType.date,
|
||||||
|
label: "任务开始时间",
|
||||||
|
name: "start_time",
|
||||||
|
value: "",
|
||||||
|
rules: [{ required: true, message: "请输入任务开始时间!" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: FormType.date,
|
||||||
|
label: "任务结束时间",
|
||||||
|
name: "end_time",
|
||||||
|
value: "",
|
||||||
|
rules: [{ required: true, message: "请输入任务结束时间!" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: FormType.input,
|
||||||
|
label: "任务积分设置",
|
||||||
|
name: "score",
|
||||||
|
value: "",
|
||||||
|
rules: [{ required: true, message: "请输入任务积分设置!" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: FormType.input,
|
||||||
|
label: "训练次数",
|
||||||
|
name: "count",
|
||||||
|
value: 0,
|
||||||
|
rules: [{ required: true, message: "请输入训练次数!" }],
|
||||||
|
},
|
||||||
|
|
||||||
export const TrainingConfig = [
|
];
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "任务标题",
|
|
||||||
name: "title",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入任务标题!" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "任务描述",
|
|
||||||
name: "desc",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入任务描述!" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "任务地点",
|
|
||||||
name: "address",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入任务地点!" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.date,
|
|
||||||
label: "任务开始时间",
|
|
||||||
name: "start_time",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入任务开始时间!" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.date,
|
|
||||||
label: "任务结束时间",
|
|
||||||
name: "end_time",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入任务结束时间!" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "任务积分设置",
|
|
||||||
name: "score",
|
|
||||||
value: "",
|
|
||||||
rules: [{ required: true, message: "请输入任务积分设置!" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: FormType.input,
|
|
||||||
label: "训练次数",
|
|
||||||
name: "count",
|
|
||||||
value: 0,
|
|
||||||
rules: [{ required: true, message: "请输入训练次数!" }],
|
|
||||||
},
|
|
||||||
|
|
||||||
]
|
|
|
@ -19,6 +19,7 @@ import Training from "@/pages/training";
|
||||||
import OrgChart from "@/pages/OrgChart";
|
import OrgChart from "@/pages/OrgChart";
|
||||||
import PoverPage from "@/pages/poverPage";
|
import PoverPage from "@/pages/poverPage";
|
||||||
import PoverDetail from "@/pages/poverDetail";
|
import PoverDetail from "@/pages/poverDetail";
|
||||||
|
import SystemPage from "@/pages/system";
|
||||||
export const homeRouter = [
|
export const homeRouter = [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
|
@ -130,7 +131,17 @@ export const homeRouter = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/admin/sys",
|
||||||
|
element: <WhseMgmtRoute />,
|
||||||
|
children:[
|
||||||
|
{
|
||||||
|
path: "/admin/sys/setting",
|
||||||
|
index: true,
|
||||||
|
element: <SystemPage />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { emergencyStore } from './emergency';
|
||||||
import { patrolStore } from './patrol';
|
import { patrolStore } from './patrol';
|
||||||
import { trainingStore } from './training';
|
import { trainingStore } from './training';
|
||||||
import { trainingCatStore } from './trainingCat';
|
import { trainingCatStore } from './trainingCat';
|
||||||
|
import { sysStore } from './sys';
|
||||||
|
|
||||||
const store = {
|
const store = {
|
||||||
usrStore,
|
usrStore,
|
||||||
|
@ -41,7 +42,8 @@ const store = {
|
||||||
emergencyStore,
|
emergencyStore,
|
||||||
patrolStore,
|
patrolStore,
|
||||||
trainingStore,
|
trainingStore,
|
||||||
trainingCatStore
|
trainingCatStore,
|
||||||
|
sysStore,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default store;
|
export default store;
|
|
@ -0,0 +1,15 @@
|
||||||
|
// 档案分类
|
||||||
|
import BaseStore from "./baseStore";
|
||||||
|
import { TagDataType } from "@/model/userModel";
|
||||||
|
|
||||||
|
class SysConfig {
|
||||||
|
static LIST: string = "sys/banner"
|
||||||
|
static ADD: string = "sys/banner"
|
||||||
|
}
|
||||||
|
class SysStore extends BaseStore<TagDataType> {
|
||||||
|
constructor() {
|
||||||
|
super(SysConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const sysStore = new SysStore()
|
||||||
|
|
|
@ -1,28 +1,60 @@
|
||||||
import { action, makeObservable } from "mobx";
|
import { action, makeObservable, observable, runInAction } from "mobx";
|
||||||
// 档案分类
|
// 档案分类
|
||||||
import BaseStore from "./baseStore";
|
import BaseStore from "./baseStore";
|
||||||
import { TagDataType } from "@/model/userModel";
|
import { TagDataType } from "@/model/userModel";
|
||||||
import baseHttp from "@/service/base";
|
import baseHttp from "@/service/base";
|
||||||
|
|
||||||
|
|
||||||
class TrainingConfig {
|
class TrainingConfig {
|
||||||
static LIST: string = "training/list"
|
static LIST: string = "training/list"
|
||||||
static ADD: string = "training"
|
static ADD: string = "training"
|
||||||
static DELETE: string = "training"
|
static DELETE: string = "training"
|
||||||
static EDIT: string = "training"
|
static EDIT: string = "training"
|
||||||
static ACCESS: string = "training/accept"
|
static ACCESS: string = "training/accept"
|
||||||
|
static tran_user: string = "training/user"
|
||||||
|
static addScores: string = "scoreMgmt/scores"
|
||||||
|
|
||||||
}
|
}
|
||||||
class TrainingStore extends BaseStore<TagDataType> {
|
class TrainingStore extends BaseStore<TagDataType> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(TrainingConfig)
|
super(TrainingConfig)
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
access: action
|
access: action,
|
||||||
|
id: observable,
|
||||||
|
userList: observable,
|
||||||
|
getUserListByTraning: action,
|
||||||
|
setUserScore: action,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
async setUserScore(ids: Array<any>) {
|
||||||
|
let res= await baseHttp.post(TrainingConfig.addScores, {"list": ids})
|
||||||
|
if (res.data==="ok"){
|
||||||
|
this.getUserListByTraning();
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
async access(id: number, param: any) {
|
async access(id: number, param: any) {
|
||||||
await baseHttp.put(TrainingConfig.ACCESS + "/" + id, param)
|
await baseHttp.put(TrainingConfig.ACCESS + "/" + id, param)
|
||||||
this.getlist()
|
this.getlist()
|
||||||
}
|
}
|
||||||
|
async getUserListByTraning() {
|
||||||
|
let res = await baseHttp.get(TrainingConfig.tran_user + "/" + this.id, null)
|
||||||
|
let data: Array<any> = []
|
||||||
|
if (!res?.data?.record) {
|
||||||
|
runInAction(() => {
|
||||||
|
this.userList = data;
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < res.data.record.length; i++) {
|
||||||
|
data.push({
|
||||||
|
key: res.data.record[i].identity,
|
||||||
|
...res.data.record[i]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.userList = data;
|
||||||
|
}
|
||||||
|
id!: string
|
||||||
|
userList!: Array<any>
|
||||||
}
|
}
|
||||||
export const trainingStore = new TrainingStore()
|
export const trainingStore = new TrainingStore()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
<!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;
|
||||||
|
const call = () => {
|
||||||
|
console.log(ws);
|
||||||
|
pc?.createOffer().then(function (offer) {
|
||||||
|
pc?.setLocalDescription(offer);
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: "offer", "data": {
|
||||||
|
"to": "576030",
|
||||||
|
"from": "31283192",
|
||||||
|
"description": offer,
|
||||||
|
"media": "video",
|
||||||
|
"session_id": "576030-31283192",
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
// socketService.send({ type: 'offer', data: offer })
|
||||||
|
}).catch(function (error) {
|
||||||
|
// 错误处理
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
|
||||||
|
.then(stream => {
|
||||||
|
const configuration = {
|
||||||
|
iceServers: [{ urls: 'stun:rw.quwanya.cn/rtc' }]
|
||||||
|
};
|
||||||
|
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))
|
||||||
|
ws = new WebSocket("ws://127.0.0.1:12214/ws?id=admin123")
|
||||||
|
pc.onicecandidate = e => {
|
||||||
|
if (!e.candidate) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ws.send(JSON.stringify({ type: 'candidate', data: e }))
|
||||||
|
}
|
||||||
|
ws.addEventListener('open', function (event) {
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: "new", "data": {
|
||||||
|
"name": "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 )",
|
||||||
|
"id": "31283192",
|
||||||
|
"user_agent": "flutter-webrtc/js"
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
ws.onclose = function (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')
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
|
||||||
|
// let answer = JSON.parse(msg.content.body)
|
||||||
|
// if (!answer) {
|
||||||
|
// return console.log('failed to parse answer')
|
||||||
|
// }
|
||||||
|
// console.log(answer)
|
||||||
|
// this.setOffer(answer.data.description)
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onerror = function (evt) {
|
||||||
|
console.log("ERROR: " + evt.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,146 @@
|
||||||
|
<!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;
|
||||||
|
|
||||||
|
const call = () => {
|
||||||
|
console.log(ws);
|
||||||
|
pc?.createOffer().then(function (offer) {
|
||||||
|
pc?.setLocalDescription(offer);
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: "offer", "data": {
|
||||||
|
"to": "467056",
|
||||||
|
"from": "31283192",
|
||||||
|
"description": offer,
|
||||||
|
"media": "video",
|
||||||
|
"session_id": "467056-31283192",
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
// socketService.send({ type: 'offer', data: offer })
|
||||||
|
}).catch(function (error) {
|
||||||
|
// 错误处理
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
// wss://rw.quwanya.cn/ws
|
||||||
|
|
||||||
|
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
|
||||||
|
.then(stream => {
|
||||||
|
const configuration = {
|
||||||
|
iceServers: [{ urls: 'stun:127.0.0.1:19302' }]
|
||||||
|
};
|
||||||
|
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))
|
||||||
|
ws = new WebSocket("ws://rw.quwanya.cn:12217/ws")
|
||||||
|
pc.onicecandidate = e => {
|
||||||
|
if (!e.candidate) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ws.send(JSON.stringify({ type: 'candidate', data: e }))
|
||||||
|
}
|
||||||
|
ws.addEventListener('open', function (event) {
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: "new", "data": {
|
||||||
|
"name": "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 )",
|
||||||
|
"id": "31283192",
|
||||||
|
"user_agent": "flutter-webrtc/js"
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
ws.onclose = function (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')
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
|
||||||
|
// let answer = JSON.parse(msg.content.body)
|
||||||
|
// if (!answer) {
|
||||||
|
// return console.log('failed to parse answer')
|
||||||
|
// }
|
||||||
|
// console.log(answer)
|
||||||
|
// this.setOffer(answer.data.description)
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onerror = function (evt) {
|
||||||
|
console.log("ERROR: " + evt.data)
|
||||||
|
}
|
||||||
|
}).catch(window.alert)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -1,5 +1,12 @@
|
||||||
class Config {
|
class Config {
|
||||||
|
// static baseUrl = "https://rw.quwanya.cn/";
|
||||||
|
// static uploadUrl = "https://rw.quwanya.cn/";
|
||||||
|
// static ws = "wss://rw.quwanya.cn/ws?id=admin";
|
||||||
|
|
||||||
|
|
||||||
static baseUrl = "http://127.0.0.1:12214/";
|
static baseUrl = "http://127.0.0.1:12214/";
|
||||||
static uploadUrl = "http://127.0.0.1:12214/";
|
static uploadUrl = "http://127.0.0.1:12214/";
|
||||||
|
static ws = "ws://rw.quwanya.cn:12217/ws";
|
||||||
|
|
||||||
}
|
}
|
||||||
export default Config;
|
export default Config;
|
|
@ -1,3 +1,5 @@
|
||||||
|
import Config from "./config"
|
||||||
|
|
||||||
export type AutoReconnectOptions = boolean | {
|
export type AutoReconnectOptions = boolean | {
|
||||||
maxRetries?: number
|
maxRetries?: number
|
||||||
retryInterval?: number
|
retryInterval?: number
|
||||||
|
@ -35,7 +37,7 @@ export type AutoReconnectOptions = boolean | {
|
||||||
}
|
}
|
||||||
|
|
||||||
public connect() {
|
public connect() {
|
||||||
this.ws = new WebSocket('ws://127.0.0.1:12214/ws?id=admin')
|
this.ws = new WebSocket(Config.ws)
|
||||||
this.ws.onopen = () => {
|
this.ws.onopen = () => {
|
||||||
this.connectionStatus = ConnectionStatus.Connected
|
this.connectionStatus = ConnectionStatus.Connected
|
||||||
this.emit('connected', null)
|
this.emit('connected', null)
|
||||||
|
|
|
@ -1,12 +1,114 @@
|
||||||
|
import SocketService from "./socket";
|
||||||
|
|
||||||
|
const socketService = SocketService.getInstance();
|
||||||
class WebRtc {
|
class WebRtc {
|
||||||
private mediaStream: MediaStream | Blob | null = null;
|
private mediaStream: MediaStream | Blob | null = null;
|
||||||
|
private pee: RTCPeerConnection | null = null;
|
||||||
private video: HTMLVideoElement | null = null;
|
private video: HTMLVideoElement | null = null;
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
let constraints = { audio: false, video: true };
|
|
||||||
this.video = document.getElementById('rtcVideo') as HTMLVideoElement;
|
this.video = document.getElementById('rtcVideo') as HTMLVideoElement;
|
||||||
this.mediaStream = await navigator.mediaDevices.getUserMedia(constraints) as MediaStream | Blob
|
socketService.on("message", (e) => {
|
||||||
|
let msg = JSON.parse(e)
|
||||||
|
switch (msg.type) {
|
||||||
|
case "answer":
|
||||||
|
let answer = JSON.parse(msg.content.body)
|
||||||
|
if (!answer) {
|
||||||
|
return console.log('failed to parse answer')
|
||||||
|
}
|
||||||
|
console.log(answer)
|
||||||
|
this.setOffer(answer.data.description)
|
||||||
|
break;
|
||||||
|
case "offer":
|
||||||
|
let offer = msg.data.description
|
||||||
|
this.pee?.setRemoteDescription(offer)
|
||||||
|
this.pee?.createAnswer().then(answer => {
|
||||||
|
this.pee?.setLocalDescription(answer)
|
||||||
|
socketService.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",
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
case 'candidate':
|
||||||
|
this.pee?.setRemoteDescription(msg.data.description)
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.createOffer();
|
||||||
|
}
|
||||||
|
async createOffer() {
|
||||||
|
try {
|
||||||
|
const configuration = {
|
||||||
|
iceServers: [{ urls: 'stun:127.0.0.1:19302' }]
|
||||||
|
};
|
||||||
|
const peerConnection = new RTCPeerConnection(configuration);
|
||||||
|
this.pee = peerConnection
|
||||||
|
// 获取远方流添加到页面播放
|
||||||
|
peerConnection.ontrack = event => {
|
||||||
|
const remoteVideo = document.querySelector('#remoteVideo') as HTMLVideoElement;
|
||||||
|
remoteVideo.autoplay = true
|
||||||
|
remoteVideo.controls = true
|
||||||
|
remoteVideo.srcObject = event.streams[0];
|
||||||
|
event.track.onmute = function (event) {
|
||||||
|
remoteVideo.play()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await this.getMedia(peerConnection)
|
||||||
|
this.sendOffer()
|
||||||
|
peerConnection.onicecandidate = e => {
|
||||||
|
if (!e.candidate) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
socketService.send({ type: 'candidate', data: e.candidate })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendOffer() {
|
||||||
|
let that = this;
|
||||||
|
that.pee?.createOffer().then(function (offer) {
|
||||||
|
that.pee?.setLocalDescription(offer);
|
||||||
|
socketService.send(JSON.stringify({
|
||||||
|
type: "offer", "data": {
|
||||||
|
"to": "848401",
|
||||||
|
"from": "31283192",
|
||||||
|
"description": offer,
|
||||||
|
"media": "video",
|
||||||
|
"session_id": "848401-31283192",
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}).catch(function (error) {
|
||||||
|
// 错误处理
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setOffer(offer) {
|
||||||
|
this.pee?.setRemoteDescription(offer)
|
||||||
|
}
|
||||||
|
addIceCandidate(candidate) {
|
||||||
|
console.log('addIceCandidate');
|
||||||
|
this.pee?.addIceCandidate(candidate)
|
||||||
|
}
|
||||||
|
async getMedia(pee: RTCPeerConnection) {
|
||||||
|
try {
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
|
video: true,
|
||||||
|
audio: true
|
||||||
|
});
|
||||||
|
this.mediaStream = stream;
|
||||||
|
stream.getTracks().forEach(track => pee.addTrack(track, stream))
|
||||||
|
this.calls();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
calls() {
|
calls() {
|
||||||
if (this.video) {
|
if (this.video) {
|
||||||
|
@ -15,6 +117,7 @@ class WebRtc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.video?.pause();
|
this.video?.pause();
|
||||||
(this.mediaStream as MediaStream)?.getTracks().forEach(track => track.stop());
|
(this.mediaStream as MediaStream)?.getTracks().forEach(track => track.stop());
|
||||||
|
|
Loading…
Reference in New Issue