first commit

This commit is contained in:
wang_yp 2025-06-04 17:36:24 +08:00
parent fb488762f5
commit 22d46fe2c4
16 changed files with 188 additions and 392 deletions

1
.gitignore vendored
View File

@ -13,7 +13,6 @@
/scripts /scripts
/src/package/dante2/ /src/package/dante2/
/src/package/dante3/ /src/package/dante3/
/public
# misc # misc
.DS_Store .DS_Store
.env.local .env.local

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

22
public/index.html Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<title>黄甲</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script>
window._AMapSecurityConfig = {
securityJsCode: "9910764bc7736f77868ef6df416ec77e",
}
</script>
</body>
</html>

8
public/manifest.json Normal file
View File

@ -0,0 +1,8 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -19,7 +19,6 @@ const BTable = (props: any) => {
selectCallback, selectCallback,
scroll, scroll,
editCallback, editCallback,
deleteCallback,
actionCloumn, actionCloumn,
onSizeChange, onSizeChange,
onPageChange, onPageChange,
@ -83,7 +82,7 @@ const BTable = (props: any) => {
title="删除确认" title="删除确认"
description="您是否需要删除该数据?" description="您是否需要删除该数据?"
onConfirm={() => { onConfirm={() => {
deleteCallback(record.identity); store.deleteItem(record.identity);
}} }}
onCancel={cancel} onCancel={cancel}
okText="Yes" okText="Yes"

View File

@ -17,10 +17,11 @@ export const items = [
key: "/permi", key: "/permi",
label: `权限管理`, label: `权限管理`,
children: [ children: [
{ label: `企业管理`, key: "/permi/company" },
{ label: `部门管理`, key: "/permi/dep" },
{ label: `权限管理`, key: "/permi/permi" }, { label: `权限管理`, key: "/permi/permi" },
{ label: `角色管理`, key: "/permi/role" }, { label: `角色管理`, key: "/permi/role" },
{ label: `菜单管理`, key: "/permi/menu" }, { label: `菜单管理`, key: "/permi/menu" },
{ label: `部门管理`, key: "/permi/dep" }
], ],
}, },
// { // {

View File

@ -0,0 +1,8 @@
.contentBox{
overflow-y: auto;
height: 100%;
.tableName{
height: 100%;
overflow-y: auto;
}
}

View File

@ -0,0 +1,84 @@
import { FormType } from "@/components/form/interface";
import { UserDataType } from "@/model/userModel";
import { CompanyConfig } from "@/service/user_config";
import { ColumnsType } from "antd/lib/table";
export const defaultConfig = [
{
type: FormType.input,
label: "名称",
name: "name",
value: "",
rules: [{ required: true, message: "请输入企业名称!" }],
},
{
type: FormType.input,
label: "描述",
name: "desc",
value: "",
rules: [{ required: true, message: "请输入企业描述" }],
},
{
type: FormType.input,
label: "负责人",
name: "head",
value: "",
rules: [{ required: true, message: "请输入负责人" }],
},
{
type: FormType.input,
label: "电话",
name: "phone",
value: "",
rules: [{ required: true, message: "请输入企业电话" }],
},
{
type: FormType.input,
label: "邮箱",
name: "email",
value: "",
rules: [{ required: true, message: "请输入邮箱" }],
},
{
type: FormType.select,
label: "上级企业",
name: "p_id",
value: 0,
selectUrl: CompanyConfig.LIST,
key: "name",
rules: [],
},
];
export const columns: ColumnsType<UserDataType> = [
{
title: "企业名称",
dataIndex: "name",
width: 200,
fixed: "left",
},
{
title: "企业描述",
dataIndex: "desc",
},
{
title: "负责人",
dataIndex: "head",
},
{
title: "企业电话",
width: 150,
dataIndex: "phone",
},
{
title: "企业邮箱",
width: 150,
dataIndex: "email",
},
{
title: "上级企业名称",
width: 150,
dataIndex: "email",
},
];

View File

@ -0,0 +1,29 @@
import { inject, observer } from "mobx-react";
import BTable from "@/components/b_table";
import { useEffect } from "react";
import { Store } from "antd/lib/form/interface";
import { columns, defaultConfig } from "./company_config";
import "./company.less";
const Company = (props: Store) => {
const { companyStore } = props;
useEffect(() => {
companyStore.getlist();
}, [companyStore]);
return (
<div className="contentBox">
<BTable
store={companyStore}
scroll={{ x: "max-content" }}
columns={columns}
config={defaultConfig}
dataSource={companyStore.list}
btnText="添加企业"
/>
</div>
);
};
export default inject("companyStore")(observer(Company));

View File

@ -8,6 +8,7 @@ import Menu from "@/pages/menu";
import Role from "@/pages/role"; import Role from "@/pages/role";
import Permission from "@/pages/permission"; import Permission from "@/pages/permission";
import Dep from "@/pages/dep"; import Dep from "@/pages/dep";
import Company from "@/pages/company";
const routers = createHashRouter([ const routers = createHashRouter([
{ {
@ -48,6 +49,11 @@ const routers = createHashRouter([
index: true, index: true,
element: <Role />, element: <Role />,
}, },
{
path: "/permi/company",
index: true,
element: <Company />,
},
], ],
}, },
{ {

View File

@ -28,5 +28,12 @@ class DepConfig {
static DELETE: string = "/dep"; static DELETE: string = "/dep";
} }
class CompanyConfig {
static ADD: string = "/company";
static EDIT: string = "/company";
static LIST: string = "/company/list";
static DELETE: string = "/company";
}
export { RoleConfig, UserConfig, MenuConfig, DepConfig };
export { RoleConfig, UserConfig, MenuConfig, DepConfig, CompanyConfig };

13
src/store/company.ts Normal file
View File

@ -0,0 +1,13 @@
import { makeObservable } from "mobx";
import BaseStore from "./baseStore";
import { UserDataType } from "@/model/userModel";
import { CompanyConfig } from "@/service/user_config";
class CompanyStore extends BaseStore<UserDataType> {
constructor() {
super(CompanyConfig)
makeObservable(this, {})
}
}
const companyStore = new CompanyStore();
export default companyStore;

View File

@ -3,13 +3,15 @@ import sourceStore from '@/store/source';
import roleStore from './role'; import roleStore from './role';
import menuStore from './menu'; import menuStore from './menu';
import depStore from './dep'; import depStore from './dep';
import companyStore from './company';
const store = { const store = {
usrStore, usrStore,
sourceStore, sourceStore,
roleStore, roleStore,
menuStore, menuStore,
depStore depStore,
companyStore
}; };
export default store; export default store;

View File

@ -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>

View File

@ -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>