Commit 38c0718d by wxl

更改

parent 75dcdad6
{
"volar.tsPlugin": false
"volar.tsPlugin": true
}
\ No newline at end of file
......@@ -4187,6 +4187,17 @@
"integrity": "sha1-ovSEN6LKqaIkNueUvwceyeYc7fY=",
"dev": true
},
"clipboard": {
"version": "2.0.6",
"resolved": "https://registry.npm.taobao.org/clipboard/download/clipboard-2.0.6.tgz",
"integrity": "sha1-UpISlu7A/fd+rRdJQhshyWhkc3Y=",
"optional": true,
"requires": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
}
},
"cliui": {
"version": "5.0.0",
"resolved": "https://registry.npm.taobao.org/cliui/download/cliui-5.0.0.tgz?cache=0&sync_timestamp=1604880226973&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-5.0.0.tgz",
......@@ -5045,6 +5056,12 @@
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"delegate": {
"version": "3.2.0",
"resolved": "https://registry.npm.taobao.org/delegate/download/delegate-3.2.0.tgz",
"integrity": "sha1-tmtxwxWFIuirV0T3INjKDCr1kWY=",
"optional": true
},
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/delegates/download/delegates-1.0.0.tgz",
......@@ -5316,6 +5333,18 @@
}
}
},
"eminent-ui": {
"version": "0.0.6",
"resolved": "https://registry.npm.taobao.org/eminent-ui/download/eminent-ui-0.0.6.tgz",
"integrity": "sha1-g4N+8bzwq03YONjW0giTxzjJMAk=",
"requires": {
"github-markdown-css": "^4.0.0",
"marked": "^1.1.1",
"prismjs": "^1.21.0",
"vue": "^3.0.0",
"vue-router": "4.0.0-beta.3"
}
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npm.taobao.org/emoji-regex/download/emoji-regex-7.0.3.tgz?cache=0&sync_timestamp=1611911444344&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Femoji-regex%2Fdownload%2Femoji-regex-7.0.3.tgz",
......@@ -6911,6 +6940,11 @@
"assert-plus": "^1.0.0"
}
},
"github-markdown-css": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/github-markdown-css/download/github-markdown-css-4.0.0.tgz",
"integrity": "sha1-vp9Mr3o4kijUw2gzYmD/yQkGHzU="
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npm.taobao.org/glob/download/glob-7.1.6.tgz",
......@@ -7039,6 +7073,15 @@
"minimist": "^1.2.5"
}
},
"good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npm.taobao.org/good-listener/download/good-listener-1.2.2.tgz",
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
"optional": true,
"requires": {
"delegate": "^3.1.2"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz",
......@@ -8826,6 +8869,11 @@
"integrity": "sha1-n8tpvP24cXv9A5jG7C2TA2743mA=",
"dev": true
},
"marked": {
"version": "1.2.9",
"resolved": "https://registry.npm.taobao.org/marked/download/marked-1.2.9.tgz",
"integrity": "sha1-U3hviwXUwBoqWna30eyZQ9Kdctw="
},
"math-random": {
"version": "1.0.4",
"resolved": "https://registry.npm.taobao.org/math-random/download/math-random-1.0.4.tgz",
......@@ -10706,6 +10754,14 @@
"renderkid": "^2.0.4"
}
},
"prismjs": {
"version": "1.23.0",
"resolved": "https://registry.npm.taobao.org/prismjs/download/prismjs-1.23.0.tgz?cache=0&sync_timestamp=1609438547304&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fprismjs%2Fdownload%2Fprismjs-1.23.0.tgz",
"integrity": "sha1-07OWf31yRAaQSXZSqdQP8EYGfzM=",
"requires": {
"clipboard": "^2.0.0"
}
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npm.taobao.org/process/download/process-0.11.10.tgz",
......@@ -11744,6 +11800,12 @@
}
}
},
"select": {
"version": "1.1.2",
"resolved": "https://registry.npm.taobao.org/select/download/select-1.1.2.tgz",
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
"optional": true
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/select-hose/download/select-hose-2.0.0.tgz",
......@@ -13525,6 +13587,12 @@
"setimmediate": "^1.0.4"
}
},
"tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/tiny-emitter/download/tiny-emitter-2.1.0.tgz",
"integrity": "sha1-HRpW7fxRxD6GPLtTgqcjMONVVCM=",
"optional": true
},
"tiny-invariant": {
"version": "1.1.0",
"resolved": "https://registry.npm.taobao.org/tiny-invariant/download/tiny-invariant-1.1.0.tgz",
......@@ -14281,10 +14349,15 @@
}
}
},
"vue-router": {
"version": "4.0.0-beta.3",
"resolved": "https://registry.npm.taobao.org/vue-router/download/vue-router-4.0.0-beta.3.tgz?cache=0&sync_timestamp=1613740713323&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-router%2Fdownload%2Fvue-router-4.0.0-beta.3.tgz",
"integrity": "sha1-5G9FgHAjpLjOc6FDoha0yeo+skM="
},
"vue-vulcan": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/vue-vulcan/-/vue-vulcan-0.1.0.tgz",
"integrity": "sha512-N+SUD/NmVPKR1Yop2S7rBtcL0iaIkOnUuag8/WqFNLzWsNLB3k4zd/KPAz1hy4rAvYrAUPB1KHvMvrneBL05NQ==",
"resolved": "https://registry.npm.taobao.org/vue-vulcan/download/vue-vulcan-0.1.0.tgz",
"integrity": "sha1-vBpvGRsRaEvxbQafeYC2Hh1fSWo=",
"requires": {
"core-js": "^3.6.5",
"vue": "^3.0.0"
......
......@@ -40,6 +40,7 @@
"@tarojs/extend": "^3.0.26",
"@tarojs/runtime": "3.0.26",
"@tarojs/taro": "3.0.26",
"eminent-ui": "0.0.6",
"rxjs": "^6.6.3",
"vue": "^3.0.0",
"vue-vulcan": "^0.1.0"
......
......@@ -2,12 +2,32 @@ export default {
pages: [
'pages/index/index',
'pages/login/index',
'pages/meeting/index'
'pages/meeting/index',
'pages/mine/index',
'pages/calling/index'
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: 'black'
}
},
tabBar: {
"selectedColor": "#40a9ff",
"borderStyle": "white",
"list": [
{
"pagePath": "pages/index/index",
"text": "联系人",
"iconPath": "assets/contacts.png",
"selectedIconPath": "assets/contacts_a.png"
},
{
"pagePath": "pages/mine/index",
"text": "我的",
"iconPath": "assets/user_center.png",
"selectedIconPath": "assets/user_center_a.png"
}
]
},
}
import { authorize } from '@tarojs/taro';
import { createApp } from 'vue';
import './app.less';
import { useProviders } from './hooks/common/useProvider';
import { useCalling } from './hooks/store/video/useCalling';
import { useAuthCheck } from './hooks/user/useAuthCheck';
const App = createApp({
onShow () {},
// 入口组件不需要实现 render 方法,即使实现了也会被 taro 所覆盖
setup() {
useAuthCheck();
useProviders(useCalling);
authorize({
scope: 'scope.camera'
})
......
import Taro from '@tarojs/taro';
import Taro, { navigateTo } from '@tarojs/taro';
import { useState } from "./useState";
import { ResBody } from '../../types/https';
import { from } from 'rxjs';
......@@ -38,6 +38,7 @@ export function useRequest<T>(
const [ status, setStatus ] = useState<HttpStatus>(null);
const [ resData, setResData ] = useState<T>(null);
const opt = { ...defaultOption, ...options };
const { token } = useInjector(useAuthStore, 'root');
const authInfo = useInjector(useAuthStore, 'root');
......@@ -68,6 +69,10 @@ export function useRequest<T>(
})
const doRequest = (data: any = {}) => {
if(!token) {
navigateTo({url: 'pages/login/index'});
return;
}
setStatus('pending') ;
from(
Taro.request({
......
import { HttpIntercept } from 'vue-vulcan';
import { useAuthStore } from '../store/auth/useAuthStore';
export function useAuthIntercept(): HttpIntercept {
const { token } = useAuthStore()
export function useAuthIntercept() {
const requestIntercept = (req: RequestInit) => {
return new Promise((resolve, reject) => {
if(token) {
req.body.token = token;
}
return req
})
}
const responseIntercept = () => {}
return {
requestIntercept,
responseIntercept
}
}
\ No newline at end of file
import { watch } from "vue";
import { useInjector } from "../../../hooks/common/useInjector";
import { useRequest } from "../../../hooks/common/useRequest";
import { useState } from "../../../hooks/common/useState";
import { useAuthStore } from "../auth/useAuthStore";
import { useAgoraClient } from "./useAgoraClient";
// import { watch } from "vue";
// import { useInjector } from "../../../hooks/common/useInjector";
// import { useRequest } from "../../../hooks/common/useRequest";
// import { useState } from "../../../hooks/common/useState";
// import { useAuthStore } from "../auth/useAuthStore";
// import { useAgoraClient } from "./useAgoraClient";
export function useVideoCallStore() {
// export function useVideoCallStore() {
const [isMini, setMiniState] = useState(false);
const [channelInfo, getChannelInfo, status] = useRequest<any>('/getAgoraToken', {}, {auto: false});
const { user } = useInjector(useAuthStore);
// const [isMini, setMiniState] = useState(false);
// const [channelInfo, getChannelInfo, status] = useRequest<any>('/getAgoraToken', {}, {auto: false});
// const { user } = useInjector(useAuthStore);
const agora = useAgoraClient();
// const agora = useAgoraClient();
const joinChannel = (channel_id?: string) => {
getChannelInfo({channel_id});
watch( status, val => {
if(val === 'success') {
const token = channelInfo.value.agora_token;
const cid = channelInfo.value.channel_id;
agora.joinChannel(token, cid, user.value.id);
}
})
}
// const joinChannel = (channel_id?: string) => {
// getChannelInfo({channel_id});
// watch( status, val => {
// if(val === 'success') {
// const token = channelInfo.value.agora_token;
// const cid = channelInfo.value.channel_id;
// agora.joinChannel(token, cid, user.value.id);
// }
// })
// }
return {
isMini,
streams: agora.streamList,
action: {
setMiniState,
joinChannel
}
}
// return {
// isMini,
// streams: agora.streamList,
// action: {
// setMiniState,
// joinChannel
// }
// }
}
// }
......@@ -3,6 +3,9 @@ import { filter, tap } from 'rxjs/operators';
import { useState } from '../../../hooks/common/useState';
import { onMounted } from 'vue';
import * as AgoraMiniappSDK from '../../../libs/Agora_SDK_for_WeChat';
import { useInjector } from '../../common/useInjector';
import { useChannelInfo } from './useChannelInfo';
import { useAuthStore } from '../auth/useAuthStore';
interface VideoItemData {
url: string;
......@@ -30,25 +33,27 @@ export function useAgoraClient() {
const channelState$ = new BehaviorSubject<string>('leave');
const [streamList, setStreamList] = useState<VideoItemData[]>([])
const [streamList, setStreamList] = useState<VideoItemData[]>([]);
const { current, onReady } = useInjector(useChannelInfo, 'root');
const { user } = useInjector(useAuthStore, 'root');
onMounted( () => {
streamLog$.subscribe(console.log);
subscribeRemoteStream();
})
const joinChannel = (ctoken: string, cid: string, uid: string) => {
onReady( () => {
agoraInit$
.pipe(tap(_=>console.log('准备加入频道')))
.subscribe(
_ => client.join(ctoken, cid, uid, () => {
_ => client.join(current.value.agora_token, current.value.channel_id, user.value.id, () => {
channelState$.next('joined');
streamLog$.next('频道加入成功'+ctoken);
streamLog$.next('频道加入成功'+current.value.agora_token);
pushLocalStream();
}, console.log),
console.log
)
}
)
})
const leaveChannel = () => {
client.leave( () => channelState$.next('leave'))
......@@ -100,7 +105,6 @@ export function useAgoraClient() {
return {
streamList,
joinChannel,
leaveChannel,
pushLocalStream
}
......
import { onSocketMessage, sendSocketMessage, showModal } from "@tarojs/taro";
import { Subject } from "rxjs";
import { useInjector } from "../../common/useInjector";
import { onMounted } from "vue";
import { useAuthStore } from "../auth/useAuthStore";
import { useChannelInfo } from "./useChannelInfo";
import { useState } from "../../common/useState";
import { UserData } from "../../../types/auth";
import { useSocket } from "./useSocket";
interface CallingMessage {
fromID: string;
fromName: string;
toID: string;
toName:string;
companyID: string;
channelID: string;
msgMainFlag:string;
msgSubFlag: string;
}
export function useCalling() {
const [targetUser, setTarget] = useState<UserData>(null)
const { user } = useInjector(useAuthStore, 'root');
const channel = useInjector(useChannelInfo, 'root');
const ws = useSocket()
// 初始化websocket
onMounted( () => {
ws.init(`wss://www.if-ar.com:3009?fromID=${user.value.id}&fromName=${user.value.nickname}&signID=SA&companyID=${user.value.company_id}`)
onSocketMessage( res => {
console.log(res)
const data = JSON.parse(res.data)
if(data.msgMainFlag === 'CallOffer') {
switch(data.msgSubFlag) {
case '':
showModal({
title: `收到来自${data.fromName}的呼叫,是否同意?`,
success: () => answer(data, 'Connect'),
fail: () => answer(data, 'Hangup')
})
}
}
})
})
// 发出呼叫
const send = () => {
channel.createChannel();
channel.onReady( () => {
const data = JSON.stringify({
fromID: user.value.id,
fromName: user.value.nickname,
toID: targetUser.value.id,
toNmame: targetUser.value.nickname,
channelID: channel.current.value.channel_id,
msgMainFlag: 'CallOffer',
msgSubFlag: 'Request'
})
sendSocketMessage({ data })
});
}
// 回应呼叫
const answer = (sendData: CallingMessage, action: 'Hangup' | 'Connect') => {
if(action === 'Connect') channel.joinCallingChannel(sendData.channelID);
const data = JSON.stringify({
fromID: user.value.id,
fromName: user.value.nickname,
toID: sendData.fromID,
toName: sendData.fromName,
channelID: sendData.channelID,
msgMainFlag: 'CallAnswer',
msgSubFlag: action
})
sendSocketMessage({ data })
}
// 挂断
const hangup = () => {
const data = JSON.stringify({
fromID: user.value.id,
fromName: user.value.nickname,
toID: targetUser.value.id,
toNmame: targetUser.value.nickname,
channelID: channel.current.value.channel_id,
msgMainFlag: 'CallOffer',
msgSubFlag: 'Hangup'
})
}
const callingState$ = new Subject()
return {
send,
targetUser,
setTarget,
callingState$
}
}
\ No newline at end of file
import { useState } from "../../common/useState";
import { useRequest } from "../../common/useRequest";
import { watch } from "vue";
import { BehaviorSubject } from "rxjs";
interface ChannelData {
channel_id: string;
agora_token: string;
members: string[];
}
export function useChannelInfo() {
const [channelInfo, request] = useRequest<any>('/getAgoraToken');
const [current, setCurrent] = useState<ChannelData>(null);
const [channelInfo, request] = useRequest<ChannelData>('/getAgoraToken', {}, {auto: false});
const ready$ = new BehaviorSubject<boolean>(false);
const createChannel = () => {
request()
}
const joinCallingChannel = (id: string) => {
request({channel_id: id})
}
const onReady = ( callback: () => void) => {
ready$.subscribe( r => { r && callback() })
}
watch(channelInfo, val => {
console.log('channel', val)
setCurrent(val);
val && ready$.next(true);
})
return {
channelInfo,
request
current,
joinCallingChannel,
createChannel,
onReady
}
}
\ No newline at end of file
import { connectSocket, onSocketOpen, sendSocketMessage } from "@tarojs/taro"
import { timer } from "rxjs";
import { useInjector } from "../../common/useInjector";
import { useAuthStore } from "../auth/useAuthStore";
export function useSocket() {
const { user } = useInjector(useAuthStore);
const init = (url: string) => {
connectSocket({url});
startHeartConnect()
}
const startHeartConnect = () => {
const data = JSON.stringify({
msgMainFlag:"Heart",
fromID: user.value.id,
fromName: user.value.nickname,
toID: '0'
})
onSocketOpen( () => {
timer(0, 5000)
.subscribe( _ => {
sendSocketMessage({ data })
})
})
}
return {
init
}
}
\ No newline at end of file
import { onMounted } from "vue";
import { useAuthStore } from "../store/auth/useAuthStore";
import { navigateTo } from '@tarojs/taro'
export function useAuthCheck() {
const user = useAuthStore();
onMounted( () => {
console.log("check", user)
if(!user.token.value) {
navigateTo({url: '/pages/login/index'})
}
})
}
\ No newline at end of file
import { useRequest } from "../common/useRequest";
export function useContactList() {
const [ data ] = useRequest<any>('/getUserList');
return data
}
\ No newline at end of file
export default {
navigationBarTitleText: '呼叫联系人'
}
<script setup>
import { useInjector } from '../../hooks/common/useInjector';
import { computed, getCurrentInstance, onMounted } from 'vue';
import { useCalling } from '../../hooks/store/video/useCalling';
const calling = useInjector(useCalling);
const targetUser = computed( () => calling.targetUser.value );
onMounted( () => {
calling.send();
})
</script>
<template>
<view class="page">
<view class="call-box">
<image class="avatar" :src="targetUser?.avatar"></image>
<text class="tips">正在呼叫{{targetUser?.nickname}}...</text>
</view>
<image class="hangup" src="../../assets/hangup.png"></image>
</view>
</template>
<style lang="less">
page{
width: 100%;
height: 100%;
}
.page{
background:#333;
width:100%;
height:100%;
.call-box{
position: absolute;
top: 30%;
left:50%;
transform: translate(-50%, -50%);
.avatar{
width: 160px;
height: 160px;
overflow: hidden;
border-radius: 50%;
display: block;
margin: 20px auto;
}
.tips{
color: #fff;
font-size: 38px;
}
}
.hangup{
width: 120px;
height: 120px;
display: block;
left: 50%;
transform: translateX(-50%);
position: absolute;
bottom: 100px;
}
}
</style>
\ No newline at end of file
<template>
<view class="index">
<button @tap="gotoLogin()">登录</button>
<button @tap="gotoMeeting()">进入会议</button>
<view class="contact-list">
<view class="contact-item" v-for="(item, key) in contacts" :key="key">
<image class="avatar" :src="item.avatar" />
<view class="info">
<view>{{item.nickname}}</view>
<text>{{item.permission}}</text>
</view>
<image @tap="callContacter(item)" class="call" src="../../assets/call.png" />
</view>
</view>
</view>
</template>
<script setup>
<script setup lang="ts">
import { navigateTo } from '@tarojs/taro';
import { UserData } from '../../types/auth';
import { useInjector } from '../../hooks/common/useInjector';
import { useCalling } from '../../hooks/store/video/useCalling';
import { useContactList } from '../../hooks/user/useContactsList';
import './index.less';
const gotoMeeting = () => {
navigateTo({url: '/pages/meeting/index'})
}
const contacts = useContactList();
const calling = useInjector(useCalling);
const gotoLogin = () => {
navigateTo({url: '/pages/login/index'})
const callContacter = (data: UserData) => {
calling.setTarget(data);
navigateTo({url: '/pages/calling/index'});
}
</script>
<style lang="less">
.contact-list{
padding: 20px 30px;
.contact-item{
margin: 25px 0;
padding: 25px 0;
border-bottom: 1px solid #f7f7f7;
display: flex;
align-items: center;
.avatar{
width: 100px;
height: 100px;
border-radius: 50%;
overflow:hidden;
}
.info{
flex: 1;
padding: 0 30px;
}
.call{
width: 80px;
height: 80px;
}
}
}
</style>
export default {
navigationBarTitleText: '登录页'
}
<script setup>
import { useFormModel } from '../../hooks/common/useFormModel';
import { useAuthStore } from '../../hooks/store/auth/useAuthStore';
import { watch } from 'vue';
import { navigateTo, navigateBack } from '@tarojs/taro';
const userInfo = useAuthStore()
const [formData, action, states, resData ] = useFormModel('/loginIn', {login_id: '', login_password: ''});
watch(states.httpStatus, status => {
if(status === 'success') {
console.log('登陆成功')
userInfo.saveToken(resData.value.token);
userInfo.saveUserInfo(resData.value);
navigateBack()
}
})
</script>
<template>
<view></view>
</template>
\ No newline at end of file
<view>
<view class="login-box">
<input class="login-item" placeholder="请输入用户名" v-model="formData.login_id">
<input class="login-item" placeholder="请输入密码" v-model="formData.login_password">
<button @tap="action.submit()">登录</button>
</view>
</view>
</template>
<style lang="less">
.login-box{
background: #999;
border-radius: 10px;
padding: 10px;
width: 90%;
position: absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%);
.login-item{
margin: 20px 0;
}
}
</style>
\ No newline at end of file
<script setup>
</script>
<template>
<view>个人中心</view>
</template>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment