mirror of
https://github.com/HumanAIGC-Engineering/OpenAvatarChat-WebUI.git
synced 2026-02-04 09:29:21 +08:00
@@ -1,12 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
quote_type = single
|
||||
trim_trailing_whitespace = true
|
||||
semi = false
|
||||
@@ -1,11 +0,0 @@
|
||||
build
|
||||
coverage
|
||||
dist
|
||||
es
|
||||
lib
|
||||
node_modules
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
*.min.js
|
||||
*.min.css
|
||||
11
.prettierrc
11
.prettierrc
@@ -1,10 +1,7 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"proseWrap": "never",
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"arrowParens": "always",
|
||||
"overrides": [{ "files": ".prettierrc", "options": { "parser": "json" } }]
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100,
|
||||
"htmlWhitespaceSensitivity": "ignore"
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
build
|
||||
coverage
|
||||
dist
|
||||
es
|
||||
lib
|
||||
node_modules
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
*.min.css
|
||||
91338
dist/assets/index-legacy.js
vendored
91338
dist/assets/index-legacy.js
vendored
File diff suppressed because one or more lines are too long
2
dist/assets/index.css
vendored
2
dist/assets/index.css
vendored
File diff suppressed because one or more lines are too long
70731
dist/assets/index.js
vendored
70731
dist/assets/index.js
vendored
File diff suppressed because one or more lines are too long
21
eslint.config.js
Normal file
21
eslint.config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import pluginVue from 'eslint-plugin-vue'
|
||||
import globals from 'globals'
|
||||
|
||||
export default [
|
||||
// add more generic rulesets here, such as:
|
||||
// js.configs.recommended,
|
||||
...pluginVue.configs['flat/recommended'],
|
||||
// ...pluginVue.configs['flat/vue2-recommended'], // Use this if you are using Vue.js 2.x.
|
||||
{
|
||||
rules: {
|
||||
// override/add rules settings here, such as:
|
||||
// 'vue/no-unused-vars': 'error'
|
||||
},
|
||||
languageOptions: {
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...globals.browser,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -1,37 +0,0 @@
|
||||
import { base as aliBase } from 'eslint-config-ali';
|
||||
import prettier from 'eslint-plugin-prettier/recommended';
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
import globals from 'globals';
|
||||
// import tslintPlugin from 'typescript-eslint';
|
||||
export default defineConfig([
|
||||
// ...tslintPlugin.configs.recommended,
|
||||
...aliBase,
|
||||
prettier,
|
||||
{
|
||||
ignores: ['dist/**/*', 'node_modules/**/*'],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
|
||||
'@/semi': [1, 'never'],
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
printWidth: 80,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
proseWrap: 'never',
|
||||
tabWidth: 2,
|
||||
semi: false,
|
||||
arrowParens: 'always',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
globalIgnores(['**/dist/**', '**/node_modules/**']),
|
||||
]);
|
||||
48
package.json
48
package.json
@@ -5,28 +5,14 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vue-tsc && vite build",
|
||||
"ci:eslint": "eslint -f json src -o ./.ci/eslint.json",
|
||||
"ci:test": "vitest -c ./vitest.config.ts --coverage",
|
||||
"dev": "vite",
|
||||
"eslint": "eslint --fix --ext .js,.ts,.vue src",
|
||||
"format": "prettier --write --cache --parser typescript \"**/*.[tj]s?(x)\"",
|
||||
"lint": "eslint . && stylelint --allow-empty-input \"**/*.{css,less,scss}\"",
|
||||
"lint-staged": "lint-staged",
|
||||
"lint:fix": "prettier --write . && eslint --fix . && stylelint --allow-empty-input --fix \"**/*.{css,less,scss}\"",
|
||||
"local": "sudo vite",
|
||||
"prepare": "husky",
|
||||
"preview": "vite preview",
|
||||
"test": "vitest"
|
||||
"lint": "eslint . --ext .js,.ts,.vue --fix",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}": "eslint --fix",
|
||||
"*.{cjs,css,cts,html,js,json,jsx,less,md,mjs,mts,scss,ts,tsx,vue,yaml,yml}": "prettier --write"
|
||||
},
|
||||
"prettier": "prettier-config-ali",
|
||||
"stylelint": {
|
||||
"extends": [
|
||||
"stylelint-config-ali",
|
||||
"stylelint-prettier/recommended"
|
||||
"*.{js,ts,vue}": [
|
||||
"eslint --fix",
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -48,36 +34,24 @@
|
||||
"vue-i18n": "^11.1.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/config-conventional": "^19.8.1",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.33.0",
|
||||
"@types/node": "^20.17.6",
|
||||
"@types/python-struct": "^1.0.4",
|
||||
"@typescript-eslint/parser": "^8.39.1",
|
||||
"@vitejs/plugin-legacy": "^7.0.0",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"@vue/eslint-config-prettier": "^10.2.0",
|
||||
"eslint": "^9.31.0",
|
||||
"eslint-config-ali": "^16.3.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.3",
|
||||
"eslint-plugin-vue": "^10.3.0",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-plugin-vue": "^10.4.0",
|
||||
"globals": "^16.3.0",
|
||||
"husky": "^9.1.7",
|
||||
"less": "^4.2.0",
|
||||
"lint-staged": "^16.1.2",
|
||||
"lint-staged": "^16.1.5",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-config-ali": "^1.3.4",
|
||||
"simple-git-hooks": "^2.13.0",
|
||||
"stylelint": "^16.22.0",
|
||||
"stylelint-config-ali": "^2.1.2",
|
||||
"stylelint-config-standard": "^38.0.0",
|
||||
"stylelint-prettier": "^5.0.3",
|
||||
"terser": "^5.36.0",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "^8.38.0",
|
||||
"vite": "^7.0.1",
|
||||
"vite-plugin-eslint2": "^5.0.4",
|
||||
"vite-plugin-mkcert": "^1.17.6",
|
||||
"vite-plugin-stylelint": "^6.0.2",
|
||||
"vue-eslint-parser": "^10.2.0",
|
||||
"vue-tsc": "^3.0.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
||||
|
||||
2584
pnpm-lock.yaml
generated
2584
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
14
src/App.vue
14
src/App.vue
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import WebcamPermission from '@/components/WebcamPermission.vue';
|
||||
import { antdLocale, locale } from '@/langs';
|
||||
import VideoChat from '@/views/VideoChat/index.vue';
|
||||
import { ConfigProvider } from 'ant-design-vue';
|
||||
import { useVideoChatStore } from './store';
|
||||
const videoChatState = useVideoChatStore();
|
||||
videoChatState.init();
|
||||
import WebcamPermission from '@/components/WebcamPermission.vue'
|
||||
import { antdLocale, locale } from '@/langs'
|
||||
import VideoChat from '@/views/VideoChat/index.vue'
|
||||
import { ConfigProvider } from 'ant-design-vue'
|
||||
import { useVideoChatStore } from './store'
|
||||
const videoChatState = useVideoChatStore()
|
||||
videoChatState.init()
|
||||
// import dayjs from 'dayjs';
|
||||
// import 'dayjs/locale/zh-cn';
|
||||
// dayjs.locale('zh-cn');
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<div class="action-group">
|
||||
<div v-if="hasCamera">
|
||||
<div class="action" @click="handleCameraOff" v-click-outside="() => (cameraListShow = false)">
|
||||
<div v-click-outside="() => (cameraListShow = false)" class="action" @click="handleCameraOff">
|
||||
<Iconfont :icon="cameraOff ? CameraOff : CameraOn" />
|
||||
<div
|
||||
v-if="streamState === 'closed'"
|
||||
class="corner"
|
||||
@click.stop.prevent="() => (cameraListShow = !cameraListShow)"
|
||||
>
|
||||
<div class="corner-inner"></div>
|
||||
<div class="corner-inner" />
|
||||
</div>
|
||||
<div
|
||||
v-show="cameraListShow && streamState === 'closed'"
|
||||
class="selectors"
|
||||
:class="{ left: isLandscape }"
|
||||
v-show="cameraListShow && streamState === 'closed'"
|
||||
>
|
||||
<div
|
||||
v-for="device in availableVideoDevices"
|
||||
@@ -21,8 +21,8 @@
|
||||
class="selector"
|
||||
@click.stop="
|
||||
() => {
|
||||
handleDeviceChange(device.deviceId);
|
||||
cameraListShow = false;
|
||||
handleDeviceChange(device.deviceId)
|
||||
cameraListShow = false
|
||||
}
|
||||
"
|
||||
>
|
||||
@@ -38,19 +38,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="hasMic">
|
||||
<div class="action" @click="handleMicMuted" v-click-outside="() => (micListShow = false)">
|
||||
<div v-click-outside="() => (micListShow = false)" class="action" @click="handleMicMuted">
|
||||
<Iconfont :icon="micMuted ? MicOff : MicOn" />
|
||||
<div
|
||||
v-if="streamState === 'closed'"
|
||||
class="corner"
|
||||
@click.stop.prevent="() => (micListShow = !micListShow)"
|
||||
>
|
||||
<div class="corner-inner"></div>
|
||||
<div class="corner-inner" />
|
||||
</div>
|
||||
<div
|
||||
v-show="micListShow && streamState === 'closed'"
|
||||
class="selectors"
|
||||
:class="{ left: isLandscape }"
|
||||
v-show="micListShow && streamState === 'closed'"
|
||||
>
|
||||
<div
|
||||
v-for="device in availableAudioDevices"
|
||||
@@ -58,8 +58,8 @@
|
||||
class="selector"
|
||||
@click.stop="
|
||||
(e) => {
|
||||
handleDeviceChange(device.deviceId);
|
||||
micListShow = false;
|
||||
handleDeviceChange(device.deviceId)
|
||||
micListShow = false
|
||||
}
|
||||
"
|
||||
>
|
||||
@@ -86,10 +86,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useVideoChatStore } from '@/store';
|
||||
import { useVisionStore } from '@/store/vision';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { useVideoChatStore } from '@/store'
|
||||
import { useVisionStore } from '@/store/vision'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import Iconfont, {
|
||||
CameraOff,
|
||||
CameraOn,
|
||||
@@ -100,9 +100,9 @@ import Iconfont, {
|
||||
SubtitleOn,
|
||||
VolumeOff,
|
||||
VolumeOn,
|
||||
} from './Iconfont';
|
||||
const videoChatStore = useVideoChatStore();
|
||||
const visionStore = useVisionStore();
|
||||
} from './Iconfont'
|
||||
const videoChatStore = useVideoChatStore()
|
||||
const visionStore = useVisionStore()
|
||||
const {
|
||||
hasCamera,
|
||||
hasMic,
|
||||
@@ -116,18 +116,18 @@ const {
|
||||
selectedVideoDevice,
|
||||
availableAudioDevices,
|
||||
availableVideoDevices,
|
||||
} = storeToRefs(videoChatStore);
|
||||
} = storeToRefs(videoChatStore)
|
||||
const {
|
||||
handleCameraOff,
|
||||
handleMicMuted,
|
||||
handleVolumeMute,
|
||||
handleDeviceChange,
|
||||
handleSubtitleToggle,
|
||||
} = videoChatStore;
|
||||
} = videoChatStore
|
||||
|
||||
const { wrapperRect, isLandscape } = storeToRefs(visionStore);
|
||||
const micListShow = ref(false);
|
||||
const cameraListShow = ref(false);
|
||||
const { wrapperRect, isLandscape } = storeToRefs(visionStore)
|
||||
const micListShow = ref(false)
|
||||
const cameraListShow = ref(false)
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
:class="[
|
||||
'chat-btn',
|
||||
streamState === StreamState.closed && 'start-chat',
|
||||
streamState === StreamState.open && 'stop-chat'
|
||||
streamState === StreamState.open && 'stop-chat',
|
||||
]"
|
||||
@click="onStartChat"
|
||||
>
|
||||
@@ -14,8 +14,7 @@
|
||||
<template v-else-if="streamState === StreamState.waiting">
|
||||
<div class="waiting-icon-text">
|
||||
<div class="icon" title="spinner">
|
||||
<!-- <Spin wrapperClassName="spin-icon"></Spin> -->
|
||||
<!-- TODO: spinner 替换 -->
|
||||
<Spin wrapperClassName="spin-icon"></Spin>
|
||||
</div>
|
||||
<span>等待中</span>
|
||||
</div>
|
||||
@@ -37,23 +36,23 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {Spin} from 'ant-design-vue'
|
||||
import { StreamState } from '@/interface/voiceChat'
|
||||
import AudioWave from '@/components/AudioWave.vue'
|
||||
import { Spin } from 'ant-design-vue';
|
||||
import { StreamState } from '@/interface/voiceChat';
|
||||
import AudioWave from '@/components/AudioWave.vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
streamState: StreamState
|
||||
onStartChat: any
|
||||
audioSourceCallback: () => MediaStream | null
|
||||
waveColor: string
|
||||
streamState: StreamState;
|
||||
onStartChat: any;
|
||||
audioSourceCallback: () => MediaStream | null;
|
||||
waveColor: string;
|
||||
}>(),
|
||||
{
|
||||
streamState: StreamState.closed
|
||||
}
|
||||
)
|
||||
streamState: StreamState.closed,
|
||||
},
|
||||
);
|
||||
|
||||
const emit = defineEmits([])
|
||||
const emit = defineEmits([]);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
@@ -106,6 +105,12 @@ const emit = defineEmits([])
|
||||
stroke: #ffffff;
|
||||
color: #ffffff;
|
||||
}
|
||||
.spin-icon {
|
||||
color: #fff;
|
||||
}
|
||||
:global(.ant-spin-dot-item) {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
}
|
||||
|
||||
.stop-chat {
|
||||
|
||||
@@ -110,9 +110,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
this.cameraOff = false
|
||||
this.volumeMuted = false
|
||||
if (!navigator.mediaDevices) {
|
||||
message.error(
|
||||
'无法获取媒体设备,请确保用localhost访问或https协议访问',
|
||||
)
|
||||
message.error('无法获取媒体设备,请确保用localhost访问或https协议访问')
|
||||
return
|
||||
}
|
||||
await navigator.mediaDevices
|
||||
@@ -136,16 +134,12 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
console.log('🚀 ~ access_webcam ~ devices:', devices)
|
||||
const videoDeviceId =
|
||||
this.selectedVideoDevice &&
|
||||
devices.some(
|
||||
(device) => device.deviceId === this.selectedVideoDevice?.deviceId,
|
||||
)
|
||||
devices.some((device) => device.deviceId === this.selectedVideoDevice?.deviceId)
|
||||
? this.selectedVideoDevice.deviceId
|
||||
: ''
|
||||
const audioDeviceId =
|
||||
this.selectedAudioDevice &&
|
||||
devices.some(
|
||||
(device) => device.deviceId === this.selectedAudioDevice?.deviceId,
|
||||
)
|
||||
devices.some((device) => device.deviceId === this.selectedAudioDevice?.deviceId)
|
||||
? this.selectedAudioDevice.deviceId
|
||||
: ''
|
||||
console.log(videoDeviceId, audioDeviceId, ' access web device')
|
||||
@@ -153,7 +147,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
this.webcamAccessed = true
|
||||
} catch (err: any) {
|
||||
console.log(err)
|
||||
message.error(err.message)
|
||||
message.error(err)
|
||||
}
|
||||
},
|
||||
async init() {
|
||||
@@ -163,6 +157,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
if (config.rtc_configuration) {
|
||||
this.rtcConfig = config.rtc_configuration
|
||||
}
|
||||
|
||||
console.log(config)
|
||||
if (config.avatar_config) {
|
||||
this.avatarType = config.avatar_config.avatar_type
|
||||
@@ -174,9 +169,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
message.error(
|
||||
'服务端链接失败,请检查是否能正确访问到 OpenAvatarChat 服务端',
|
||||
)
|
||||
message.error('服务端链接失败,请检查是否能正确访问到 OpenAvatarChat 服务端')
|
||||
})
|
||||
},
|
||||
handleCameraOff() {
|
||||
@@ -204,29 +197,19 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
console.log('🚀 ~ handle_device_change ~ devices:', devices)
|
||||
let videoDeviceId =
|
||||
this.selectedVideoDevice &&
|
||||
devices.some(
|
||||
(device) => device.deviceId === this.selectedVideoDevice?.deviceId,
|
||||
)
|
||||
devices.some((device) => device.deviceId === this.selectedVideoDevice?.deviceId)
|
||||
? this.selectedVideoDevice.deviceId
|
||||
: ''
|
||||
let audioDeviceId =
|
||||
this.selectedAudioDevice &&
|
||||
devices.some(
|
||||
(device) => device.deviceId === this.selectedAudioDevice?.deviceId,
|
||||
)
|
||||
devices.some((device) => device.deviceId === this.selectedAudioDevice?.deviceId)
|
||||
? this.selectedAudioDevice.deviceId
|
||||
: ''
|
||||
if (
|
||||
this.availableVideoDevices.find(
|
||||
(video_device) => video_device.deviceId === device_id,
|
||||
)
|
||||
) {
|
||||
if (this.availableVideoDevices.find((video_device) => video_device.deviceId === device_id)) {
|
||||
videoDeviceId = device_id
|
||||
this.cameraOff = false
|
||||
} else if (
|
||||
this.availableAudioDevices.find(
|
||||
(audio_device) => audio_device.deviceId === device_id,
|
||||
)
|
||||
this.availableAudioDevices.find((audio_device) => audio_device.deviceId === device_id)
|
||||
) {
|
||||
audioDeviceId = device_id
|
||||
this.micMuted = false
|
||||
@@ -235,6 +218,14 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
},
|
||||
handleSubtitleToggle() {
|
||||
this.showChatRecords = !this.showChatRecords
|
||||
const visionState = useVisionStore()
|
||||
const { wrapperRef, wrapperRect } = visionState
|
||||
console.log(wrapperRect, wrapperRef)
|
||||
if (!wrapperRef || !wrapperRect) return
|
||||
wrapperRef.getBoundingClientRect()
|
||||
wrapperRect.width = wrapperRef!.clientWidth
|
||||
wrapperRect.height = wrapperRef!.clientHeight
|
||||
visionState.isLandscape = wrapperRect.width > wrapperRect.height
|
||||
},
|
||||
async updateAvailableDevices() {
|
||||
const devices = await getDevices()
|
||||
@@ -250,9 +241,8 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
return device.kind === 'audioinput' && device.deviceId
|
||||
}) && this.hasMicPermission
|
||||
this.hasCamera =
|
||||
devices.some(
|
||||
(device) => device.kind === 'videoinput' && device.deviceId,
|
||||
) && this.hasCameraPermission
|
||||
devices.some((device) => device.kind === 'videoinput' && device.deviceId) &&
|
||||
this.hasCameraPermission
|
||||
await getStream(
|
||||
audioDeviceId && audioDeviceId !== 'default'
|
||||
? { deviceId: { exact: audioDeviceId } }
|
||||
@@ -260,7 +250,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
videoDeviceId && videoDeviceId !== 'default'
|
||||
? { deviceId: { exact: videoDeviceId } }
|
||||
: this.hasCamera,
|
||||
this.trackConstraints,
|
||||
this.trackConstraints
|
||||
)
|
||||
.then(async (local_stream) => {
|
||||
console.log('local_stream', local_stream)
|
||||
@@ -269,20 +259,17 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
})
|
||||
.then(() => {
|
||||
const used_devices = this.stream!.getTracks().map(
|
||||
(track) => track.getSettings()?.deviceId,
|
||||
(track) => track.getSettings()?.deviceId
|
||||
)
|
||||
used_devices.forEach((device_id) => {
|
||||
const used_device = devices.find(
|
||||
(device) => device.deviceId === device_id,
|
||||
)
|
||||
const used_device = devices.find((device) => device.deviceId === device_id)
|
||||
if (used_device && used_device?.kind.includes('video')) {
|
||||
this.selectedVideoDevice = used_device
|
||||
} else if (used_device && used_device?.kind.includes('audio')) {
|
||||
this.selectedAudioDevice = used_device
|
||||
}
|
||||
})
|
||||
!this.selectedVideoDevice &&
|
||||
(this.selectedVideoDevice = this.availableVideoDevices[0])
|
||||
!this.selectedVideoDevice && (this.selectedVideoDevice = this.availableVideoDevices[0])
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('image.no_webcam_support', e)
|
||||
@@ -315,29 +302,22 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
if (this.streamState === 'closed') {
|
||||
this.chatRecords = []
|
||||
this.peerConnection = new RTCPeerConnection() // TODO RTC_configuration
|
||||
this.peerConnection.addEventListener(
|
||||
'connectionstatechange',
|
||||
async (event) => {
|
||||
switch (this.peerConnection!.connectionState) {
|
||||
case 'connected':
|
||||
this.streamState = StreamState.open
|
||||
break
|
||||
case 'disconnected':
|
||||
this.streamState = StreamState.closed
|
||||
stop(this.peerConnection!)
|
||||
// await access_webcam() //TODO 重置状态
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
},
|
||||
)
|
||||
this.peerConnection.addEventListener('connectionstatechange', async (event) => {
|
||||
switch (this.peerConnection!.connectionState) {
|
||||
case 'connected':
|
||||
this.streamState = StreamState.open
|
||||
break
|
||||
case 'disconnected':
|
||||
this.streamState = StreamState.closed
|
||||
stop(this.peerConnection!)
|
||||
// await access_webcam() //TODO 重置状态
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
this.streamState = StreamState.waiting
|
||||
await setupWebRTC(
|
||||
this.stream!,
|
||||
this.peerConnection!,
|
||||
visionState.remoteVideoRef!,
|
||||
)
|
||||
await setupWebRTC(this.stream!, this.peerConnection!, visionState.remoteVideoRef!)
|
||||
.then(([dataChannel, webRTCId]) => {
|
||||
this.streamState = StreamState.open
|
||||
this.webRTCId = webRTCId as string
|
||||
@@ -355,6 +335,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
console.info('catching', e)
|
||||
this.streamState = StreamState.closed
|
||||
message.error(e)
|
||||
message.error('请检查是否超过数字人并发上限')
|
||||
})
|
||||
} else if (this.streamState === 'waiting') {
|
||||
// waiting 中不允许操作
|
||||
@@ -373,7 +354,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
||||
},
|
||||
initWebsocket(ws_route: string, webRTCId: string) {
|
||||
const ws = new WS(
|
||||
`${window.location.protocol.includes('https') ? 'wss' : 'ws'}://${window.location.host}${ws_route}/${webRTCId}`,
|
||||
`${window.location.protocol.includes('https') ? 'wss' : 'ws'}://${window.location.host}${ws_route}/${webRTCId}`
|
||||
)
|
||||
ws.on(WsEventTypes.WS_OPEN, () => {
|
||||
console.log('socket opened')
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 50%;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
export default {
|
||||
extends: ['stylelint-config-standard'],
|
||||
// stylelint不识别:global, 添加selector-pseudo-class-no-unknown忽略:global
|
||||
rules: {
|
||||
'selector-pseudo-class-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignorePseudoClasses: ['global'],
|
||||
},
|
||||
],
|
||||
// 对:global处理有问题, 所以关掉该规则
|
||||
'no-descending-specificity': null,
|
||||
// 要求css的选择器名称是kebab-case, 历史代码很多是驼峰的, 所以关掉该规则
|
||||
'selector-class-pattern': null,
|
||||
// 该规则不允许供应商前缀值; 而最多显示几行时需要display: -webkit-box; 所以忽略'box'
|
||||
'value-no-vendor-prefix': [true, { ignoreValues: ['box'] }],
|
||||
'custom-property-pattern': '^([a-zA-Z0-9]|-|_)*$',
|
||||
'rule-empty-line-before': null,
|
||||
'declaration-empty-line-before': null,
|
||||
'allow-empty-input': true,
|
||||
// 采用系统默认字体
|
||||
'font-family-no-missing-generic-family-keyword': null,
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.less'],
|
||||
customSyntax: 'postcss-less',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,10 +1,11 @@
|
||||
import legacyPlugin from '@vitejs/plugin-legacy'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { defineConfig } from 'vite'
|
||||
// import mkcert from 'vite-plugin-mkcert'
|
||||
import mkcert from 'vite-plugin-mkcert'
|
||||
import { join } from 'path'
|
||||
|
||||
// server of your OpenAvatarChat
|
||||
// if you are not use localhost, you need to start https
|
||||
const serverIP = '127.0.0.1'
|
||||
const serverPort = '8282'
|
||||
|
||||
@@ -13,10 +14,6 @@ export default defineConfig({
|
||||
base: './',
|
||||
build: {
|
||||
rollupOptions: {
|
||||
// input: {
|
||||
// index: resolve(__dirname, 'index.html'),
|
||||
// cropImage: resolve(__dirname, 'cropImage.html')
|
||||
// },
|
||||
output: {
|
||||
entryFileNames: `assets/[name].js`,
|
||||
chunkFileNames: `assets/[name].js`,
|
||||
|
||||
Reference in New Issue
Block a user