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,
|
"semi": false,
|
||||||
"arrowParens": "always",
|
"singleQuote": true,
|
||||||
"overrides": [{ "files": ".prettierrc", "options": { "parser": "json" } }]
|
"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",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vue-tsc && vite build",
|
"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",
|
"dev": "vite",
|
||||||
"eslint": "eslint --fix --ext .js,.ts,.vue src",
|
"lint": "eslint . --ext .js,.ts,.vue --fix",
|
||||||
"format": "prettier --write --cache --parser typescript \"**/*.[tj]s?(x)\"",
|
"prepare": "husky install"
|
||||||
"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-staged": {
|
"lint-staged": {
|
||||||
"*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}": "eslint --fix",
|
"*.{js,ts,vue}": [
|
||||||
"*.{cjs,css,cts,html,js,json,jsx,less,md,mjs,mts,scss,ts,tsx,vue,yaml,yml}": "prettier --write"
|
"eslint --fix",
|
||||||
},
|
"prettier --write"
|
||||||
"prettier": "prettier-config-ali",
|
|
||||||
"stylelint": {
|
|
||||||
"extends": [
|
|
||||||
"stylelint-config-ali",
|
|
||||||
"stylelint-prettier/recommended"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -48,36 +34,24 @@
|
|||||||
"vue-i18n": "^11.1.9"
|
"vue-i18n": "^11.1.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/config-conventional": "^19.8.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
|
"@eslint/js": "^9.33.0",
|
||||||
"@types/node": "^20.17.6",
|
"@types/node": "^20.17.6",
|
||||||
"@types/python-struct": "^1.0.4",
|
"@types/python-struct": "^1.0.4",
|
||||||
|
"@typescript-eslint/parser": "^8.39.1",
|
||||||
"@vitejs/plugin-legacy": "^7.0.0",
|
"@vitejs/plugin-legacy": "^7.0.0",
|
||||||
"@vitejs/plugin-vue": "^6.0.0",
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
"@vue/eslint-config-prettier": "^10.2.0",
|
"eslint": "^9.33.0",
|
||||||
"eslint": "^9.31.0",
|
"eslint-plugin-vue": "^10.4.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",
|
|
||||||
"globals": "^16.3.0",
|
"globals": "^16.3.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
"lint-staged": "^16.1.2",
|
"lint-staged": "^16.1.5",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"prettier-config-ali": "^1.3.4",
|
|
||||||
"simple-git-hooks": "^2.13.0",
|
"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",
|
"terser": "^5.36.0",
|
||||||
"typescript": "5.8.3",
|
|
||||||
"typescript-eslint": "^8.38.0",
|
|
||||||
"vite": "^7.0.1",
|
"vite": "^7.0.1",
|
||||||
"vite-plugin-eslint2": "^5.0.4",
|
|
||||||
"vite-plugin-mkcert": "^1.17.6",
|
"vite-plugin-mkcert": "^1.17.6",
|
||||||
"vite-plugin-stylelint": "^6.0.2",
|
|
||||||
"vue-eslint-parser": "^10.2.0",
|
|
||||||
"vue-tsc": "^3.0.1"
|
"vue-tsc": "^3.0.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
"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">
|
<script setup lang="ts">
|
||||||
import WebcamPermission from '@/components/WebcamPermission.vue';
|
import WebcamPermission from '@/components/WebcamPermission.vue'
|
||||||
import { antdLocale, locale } from '@/langs';
|
import { antdLocale, locale } from '@/langs'
|
||||||
import VideoChat from '@/views/VideoChat/index.vue';
|
import VideoChat from '@/views/VideoChat/index.vue'
|
||||||
import { ConfigProvider } from 'ant-design-vue';
|
import { ConfigProvider } from 'ant-design-vue'
|
||||||
import { useVideoChatStore } from './store';
|
import { useVideoChatStore } from './store'
|
||||||
const videoChatState = useVideoChatStore();
|
const videoChatState = useVideoChatStore()
|
||||||
videoChatState.init();
|
videoChatState.init()
|
||||||
// import dayjs from 'dayjs';
|
// import dayjs from 'dayjs';
|
||||||
// import 'dayjs/locale/zh-cn';
|
// import 'dayjs/locale/zh-cn';
|
||||||
// dayjs.locale('zh-cn');
|
// dayjs.locale('zh-cn');
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="action-group">
|
<div class="action-group">
|
||||||
<div v-if="hasCamera">
|
<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" />
|
<Iconfont :icon="cameraOff ? CameraOff : CameraOn" />
|
||||||
<div
|
<div
|
||||||
v-if="streamState === 'closed'"
|
v-if="streamState === 'closed'"
|
||||||
class="corner"
|
class="corner"
|
||||||
@click.stop.prevent="() => (cameraListShow = !cameraListShow)"
|
@click.stop.prevent="() => (cameraListShow = !cameraListShow)"
|
||||||
>
|
>
|
||||||
<div class="corner-inner"></div>
|
<div class="corner-inner" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
v-show="cameraListShow && streamState === 'closed'"
|
||||||
class="selectors"
|
class="selectors"
|
||||||
:class="{ left: isLandscape }"
|
:class="{ left: isLandscape }"
|
||||||
v-show="cameraListShow && streamState === 'closed'"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="device in availableVideoDevices"
|
v-for="device in availableVideoDevices"
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
class="selector"
|
class="selector"
|
||||||
@click.stop="
|
@click.stop="
|
||||||
() => {
|
() => {
|
||||||
handleDeviceChange(device.deviceId);
|
handleDeviceChange(device.deviceId)
|
||||||
cameraListShow = false;
|
cameraListShow = false
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@@ -38,19 +38,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasMic">
|
<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" />
|
<Iconfont :icon="micMuted ? MicOff : MicOn" />
|
||||||
<div
|
<div
|
||||||
v-if="streamState === 'closed'"
|
v-if="streamState === 'closed'"
|
||||||
class="corner"
|
class="corner"
|
||||||
@click.stop.prevent="() => (micListShow = !micListShow)"
|
@click.stop.prevent="() => (micListShow = !micListShow)"
|
||||||
>
|
>
|
||||||
<div class="corner-inner"></div>
|
<div class="corner-inner" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
v-show="micListShow && streamState === 'closed'"
|
||||||
class="selectors"
|
class="selectors"
|
||||||
:class="{ left: isLandscape }"
|
:class="{ left: isLandscape }"
|
||||||
v-show="micListShow && streamState === 'closed'"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="device in availableAudioDevices"
|
v-for="device in availableAudioDevices"
|
||||||
@@ -58,8 +58,8 @@
|
|||||||
class="selector"
|
class="selector"
|
||||||
@click.stop="
|
@click.stop="
|
||||||
(e) => {
|
(e) => {
|
||||||
handleDeviceChange(device.deviceId);
|
handleDeviceChange(device.deviceId)
|
||||||
micListShow = false;
|
micListShow = false
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@@ -86,10 +86,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useVideoChatStore } from '@/store';
|
import { useVideoChatStore } from '@/store'
|
||||||
import { useVisionStore } from '@/store/vision';
|
import { useVisionStore } from '@/store/vision'
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia'
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue'
|
||||||
import Iconfont, {
|
import Iconfont, {
|
||||||
CameraOff,
|
CameraOff,
|
||||||
CameraOn,
|
CameraOn,
|
||||||
@@ -100,9 +100,9 @@ import Iconfont, {
|
|||||||
SubtitleOn,
|
SubtitleOn,
|
||||||
VolumeOff,
|
VolumeOff,
|
||||||
VolumeOn,
|
VolumeOn,
|
||||||
} from './Iconfont';
|
} from './Iconfont'
|
||||||
const videoChatStore = useVideoChatStore();
|
const videoChatStore = useVideoChatStore()
|
||||||
const visionStore = useVisionStore();
|
const visionStore = useVisionStore()
|
||||||
const {
|
const {
|
||||||
hasCamera,
|
hasCamera,
|
||||||
hasMic,
|
hasMic,
|
||||||
@@ -116,18 +116,18 @@ const {
|
|||||||
selectedVideoDevice,
|
selectedVideoDevice,
|
||||||
availableAudioDevices,
|
availableAudioDevices,
|
||||||
availableVideoDevices,
|
availableVideoDevices,
|
||||||
} = storeToRefs(videoChatStore);
|
} = storeToRefs(videoChatStore)
|
||||||
const {
|
const {
|
||||||
handleCameraOff,
|
handleCameraOff,
|
||||||
handleMicMuted,
|
handleMicMuted,
|
||||||
handleVolumeMute,
|
handleVolumeMute,
|
||||||
handleDeviceChange,
|
handleDeviceChange,
|
||||||
handleSubtitleToggle,
|
handleSubtitleToggle,
|
||||||
} = videoChatStore;
|
} = videoChatStore
|
||||||
|
|
||||||
const { wrapperRect, isLandscape } = storeToRefs(visionStore);
|
const { wrapperRect, isLandscape } = storeToRefs(visionStore)
|
||||||
const micListShow = ref(false);
|
const micListShow = ref(false)
|
||||||
const cameraListShow = ref(false);
|
const cameraListShow = ref(false)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
:class="[
|
:class="[
|
||||||
'chat-btn',
|
'chat-btn',
|
||||||
streamState === StreamState.closed && 'start-chat',
|
streamState === StreamState.closed && 'start-chat',
|
||||||
streamState === StreamState.open && 'stop-chat'
|
streamState === StreamState.open && 'stop-chat',
|
||||||
]"
|
]"
|
||||||
@click="onStartChat"
|
@click="onStartChat"
|
||||||
>
|
>
|
||||||
@@ -14,8 +14,7 @@
|
|||||||
<template v-else-if="streamState === StreamState.waiting">
|
<template v-else-if="streamState === StreamState.waiting">
|
||||||
<div class="waiting-icon-text">
|
<div class="waiting-icon-text">
|
||||||
<div class="icon" title="spinner">
|
<div class="icon" title="spinner">
|
||||||
<!-- <Spin wrapperClassName="spin-icon"></Spin> -->
|
<Spin wrapperClassName="spin-icon"></Spin>
|
||||||
<!-- TODO: spinner 替换 -->
|
|
||||||
</div>
|
</div>
|
||||||
<span>等待中</span>
|
<span>等待中</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,23 +36,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {Spin} from 'ant-design-vue'
|
import { Spin } from 'ant-design-vue';
|
||||||
import { StreamState } from '@/interface/voiceChat'
|
import { StreamState } from '@/interface/voiceChat';
|
||||||
import AudioWave from '@/components/AudioWave.vue'
|
import AudioWave from '@/components/AudioWave.vue';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
streamState: StreamState
|
streamState: StreamState;
|
||||||
onStartChat: any
|
onStartChat: any;
|
||||||
audioSourceCallback: () => MediaStream | null
|
audioSourceCallback: () => MediaStream | null;
|
||||||
waveColor: string
|
waveColor: string;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
streamState: StreamState.closed
|
streamState: StreamState.closed,
|
||||||
}
|
},
|
||||||
)
|
);
|
||||||
|
|
||||||
const emit = defineEmits([])
|
const emit = defineEmits([]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less"></style>
|
||||||
@@ -106,6 +105,12 @@ const emit = defineEmits([])
|
|||||||
stroke: #ffffff;
|
stroke: #ffffff;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
.spin-icon {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
:global(.ant-spin-dot-item) {
|
||||||
|
background-color: #fff !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stop-chat {
|
.stop-chat {
|
||||||
|
|||||||
@@ -110,9 +110,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
this.cameraOff = false
|
this.cameraOff = false
|
||||||
this.volumeMuted = false
|
this.volumeMuted = false
|
||||||
if (!navigator.mediaDevices) {
|
if (!navigator.mediaDevices) {
|
||||||
message.error(
|
message.error('无法获取媒体设备,请确保用localhost访问或https协议访问')
|
||||||
'无法获取媒体设备,请确保用localhost访问或https协议访问',
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await navigator.mediaDevices
|
await navigator.mediaDevices
|
||||||
@@ -136,16 +134,12 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
console.log('🚀 ~ access_webcam ~ devices:', devices)
|
console.log('🚀 ~ access_webcam ~ devices:', devices)
|
||||||
const videoDeviceId =
|
const videoDeviceId =
|
||||||
this.selectedVideoDevice &&
|
this.selectedVideoDevice &&
|
||||||
devices.some(
|
devices.some((device) => device.deviceId === this.selectedVideoDevice?.deviceId)
|
||||||
(device) => device.deviceId === this.selectedVideoDevice?.deviceId,
|
|
||||||
)
|
|
||||||
? this.selectedVideoDevice.deviceId
|
? this.selectedVideoDevice.deviceId
|
||||||
: ''
|
: ''
|
||||||
const audioDeviceId =
|
const audioDeviceId =
|
||||||
this.selectedAudioDevice &&
|
this.selectedAudioDevice &&
|
||||||
devices.some(
|
devices.some((device) => device.deviceId === this.selectedAudioDevice?.deviceId)
|
||||||
(device) => device.deviceId === this.selectedAudioDevice?.deviceId,
|
|
||||||
)
|
|
||||||
? this.selectedAudioDevice.deviceId
|
? this.selectedAudioDevice.deviceId
|
||||||
: ''
|
: ''
|
||||||
console.log(videoDeviceId, audioDeviceId, ' access web device')
|
console.log(videoDeviceId, audioDeviceId, ' access web device')
|
||||||
@@ -153,7 +147,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
this.webcamAccessed = true
|
this.webcamAccessed = true
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
message.error(err.message)
|
message.error(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async init() {
|
async init() {
|
||||||
@@ -163,6 +157,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
if (config.rtc_configuration) {
|
if (config.rtc_configuration) {
|
||||||
this.rtcConfig = config.rtc_configuration
|
this.rtcConfig = config.rtc_configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(config)
|
console.log(config)
|
||||||
if (config.avatar_config) {
|
if (config.avatar_config) {
|
||||||
this.avatarType = config.avatar_config.avatar_type
|
this.avatarType = config.avatar_config.avatar_type
|
||||||
@@ -174,9 +169,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
message.error(
|
message.error('服务端链接失败,请检查是否能正确访问到 OpenAvatarChat 服务端')
|
||||||
'服务端链接失败,请检查是否能正确访问到 OpenAvatarChat 服务端',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleCameraOff() {
|
handleCameraOff() {
|
||||||
@@ -204,29 +197,19 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
console.log('🚀 ~ handle_device_change ~ devices:', devices)
|
console.log('🚀 ~ handle_device_change ~ devices:', devices)
|
||||||
let videoDeviceId =
|
let videoDeviceId =
|
||||||
this.selectedVideoDevice &&
|
this.selectedVideoDevice &&
|
||||||
devices.some(
|
devices.some((device) => device.deviceId === this.selectedVideoDevice?.deviceId)
|
||||||
(device) => device.deviceId === this.selectedVideoDevice?.deviceId,
|
|
||||||
)
|
|
||||||
? this.selectedVideoDevice.deviceId
|
? this.selectedVideoDevice.deviceId
|
||||||
: ''
|
: ''
|
||||||
let audioDeviceId =
|
let audioDeviceId =
|
||||||
this.selectedAudioDevice &&
|
this.selectedAudioDevice &&
|
||||||
devices.some(
|
devices.some((device) => device.deviceId === this.selectedAudioDevice?.deviceId)
|
||||||
(device) => device.deviceId === this.selectedAudioDevice?.deviceId,
|
|
||||||
)
|
|
||||||
? this.selectedAudioDevice.deviceId
|
? this.selectedAudioDevice.deviceId
|
||||||
: ''
|
: ''
|
||||||
if (
|
if (this.availableVideoDevices.find((video_device) => video_device.deviceId === device_id)) {
|
||||||
this.availableVideoDevices.find(
|
|
||||||
(video_device) => video_device.deviceId === device_id,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
videoDeviceId = device_id
|
videoDeviceId = device_id
|
||||||
this.cameraOff = false
|
this.cameraOff = false
|
||||||
} else if (
|
} else if (
|
||||||
this.availableAudioDevices.find(
|
this.availableAudioDevices.find((audio_device) => audio_device.deviceId === device_id)
|
||||||
(audio_device) => audio_device.deviceId === device_id,
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
audioDeviceId = device_id
|
audioDeviceId = device_id
|
||||||
this.micMuted = false
|
this.micMuted = false
|
||||||
@@ -235,6 +218,14 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
},
|
},
|
||||||
handleSubtitleToggle() {
|
handleSubtitleToggle() {
|
||||||
this.showChatRecords = !this.showChatRecords
|
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() {
|
async updateAvailableDevices() {
|
||||||
const devices = await getDevices()
|
const devices = await getDevices()
|
||||||
@@ -250,9 +241,8 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
return device.kind === 'audioinput' && device.deviceId
|
return device.kind === 'audioinput' && device.deviceId
|
||||||
}) && this.hasMicPermission
|
}) && this.hasMicPermission
|
||||||
this.hasCamera =
|
this.hasCamera =
|
||||||
devices.some(
|
devices.some((device) => device.kind === 'videoinput' && device.deviceId) &&
|
||||||
(device) => device.kind === 'videoinput' && device.deviceId,
|
this.hasCameraPermission
|
||||||
) && this.hasCameraPermission
|
|
||||||
await getStream(
|
await getStream(
|
||||||
audioDeviceId && audioDeviceId !== 'default'
|
audioDeviceId && audioDeviceId !== 'default'
|
||||||
? { deviceId: { exact: audioDeviceId } }
|
? { deviceId: { exact: audioDeviceId } }
|
||||||
@@ -260,7 +250,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
videoDeviceId && videoDeviceId !== 'default'
|
videoDeviceId && videoDeviceId !== 'default'
|
||||||
? { deviceId: { exact: videoDeviceId } }
|
? { deviceId: { exact: videoDeviceId } }
|
||||||
: this.hasCamera,
|
: this.hasCamera,
|
||||||
this.trackConstraints,
|
this.trackConstraints
|
||||||
)
|
)
|
||||||
.then(async (local_stream) => {
|
.then(async (local_stream) => {
|
||||||
console.log('local_stream', local_stream)
|
console.log('local_stream', local_stream)
|
||||||
@@ -269,20 +259,17 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const used_devices = this.stream!.getTracks().map(
|
const used_devices = this.stream!.getTracks().map(
|
||||||
(track) => track.getSettings()?.deviceId,
|
(track) => track.getSettings()?.deviceId
|
||||||
)
|
)
|
||||||
used_devices.forEach((device_id) => {
|
used_devices.forEach((device_id) => {
|
||||||
const used_device = devices.find(
|
const used_device = devices.find((device) => device.deviceId === device_id)
|
||||||
(device) => device.deviceId === device_id,
|
|
||||||
)
|
|
||||||
if (used_device && used_device?.kind.includes('video')) {
|
if (used_device && used_device?.kind.includes('video')) {
|
||||||
this.selectedVideoDevice = used_device
|
this.selectedVideoDevice = used_device
|
||||||
} else if (used_device && used_device?.kind.includes('audio')) {
|
} else if (used_device && used_device?.kind.includes('audio')) {
|
||||||
this.selectedAudioDevice = used_device
|
this.selectedAudioDevice = used_device
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
!this.selectedVideoDevice &&
|
!this.selectedVideoDevice && (this.selectedVideoDevice = this.availableVideoDevices[0])
|
||||||
(this.selectedVideoDevice = this.availableVideoDevices[0])
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.error('image.no_webcam_support', e)
|
console.error('image.no_webcam_support', e)
|
||||||
@@ -315,29 +302,22 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
if (this.streamState === 'closed') {
|
if (this.streamState === 'closed') {
|
||||||
this.chatRecords = []
|
this.chatRecords = []
|
||||||
this.peerConnection = new RTCPeerConnection() // TODO RTC_configuration
|
this.peerConnection = new RTCPeerConnection() // TODO RTC_configuration
|
||||||
this.peerConnection.addEventListener(
|
this.peerConnection.addEventListener('connectionstatechange', async (event) => {
|
||||||
'connectionstatechange',
|
switch (this.peerConnection!.connectionState) {
|
||||||
async (event) => {
|
case 'connected':
|
||||||
switch (this.peerConnection!.connectionState) {
|
this.streamState = StreamState.open
|
||||||
case 'connected':
|
break
|
||||||
this.streamState = StreamState.open
|
case 'disconnected':
|
||||||
break
|
this.streamState = StreamState.closed
|
||||||
case 'disconnected':
|
stop(this.peerConnection!)
|
||||||
this.streamState = StreamState.closed
|
// await access_webcam() //TODO 重置状态
|
||||||
stop(this.peerConnection!)
|
break
|
||||||
// await access_webcam() //TODO 重置状态
|
default:
|
||||||
break
|
break
|
||||||
default:
|
}
|
||||||
break
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
this.streamState = StreamState.waiting
|
this.streamState = StreamState.waiting
|
||||||
await setupWebRTC(
|
await setupWebRTC(this.stream!, this.peerConnection!, visionState.remoteVideoRef!)
|
||||||
this.stream!,
|
|
||||||
this.peerConnection!,
|
|
||||||
visionState.remoteVideoRef!,
|
|
||||||
)
|
|
||||||
.then(([dataChannel, webRTCId]) => {
|
.then(([dataChannel, webRTCId]) => {
|
||||||
this.streamState = StreamState.open
|
this.streamState = StreamState.open
|
||||||
this.webRTCId = webRTCId as string
|
this.webRTCId = webRTCId as string
|
||||||
@@ -355,6 +335,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
console.info('catching', e)
|
console.info('catching', e)
|
||||||
this.streamState = StreamState.closed
|
this.streamState = StreamState.closed
|
||||||
message.error(e)
|
message.error(e)
|
||||||
|
message.error('请检查是否超过数字人并发上限')
|
||||||
})
|
})
|
||||||
} else if (this.streamState === 'waiting') {
|
} else if (this.streamState === 'waiting') {
|
||||||
// waiting 中不允许操作
|
// waiting 中不允许操作
|
||||||
@@ -373,7 +354,7 @@ export const useVideoChatStore = defineStore('videoChatStore', {
|
|||||||
},
|
},
|
||||||
initWebsocket(ws_route: string, webRTCId: string) {
|
initWebsocket(ws_route: string, webRTCId: string) {
|
||||||
const ws = new WS(
|
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, () => {
|
ws.on(WsEventTypes.WS_OPEN, () => {
|
||||||
console.log('socket opened')
|
console.log('socket opened')
|
||||||
|
|||||||
@@ -85,6 +85,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
width: 50%;
|
||||||
padding: 10px;
|
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 legacyPlugin from '@vitejs/plugin-legacy'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
// import mkcert from 'vite-plugin-mkcert'
|
import mkcert from 'vite-plugin-mkcert'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
|
||||||
// server of your OpenAvatarChat
|
// server of your OpenAvatarChat
|
||||||
|
// if you are not use localhost, you need to start https
|
||||||
const serverIP = '127.0.0.1'
|
const serverIP = '127.0.0.1'
|
||||||
const serverPort = '8282'
|
const serverPort = '8282'
|
||||||
|
|
||||||
@@ -13,10 +14,6 @@ export default defineConfig({
|
|||||||
base: './',
|
base: './',
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
// input: {
|
|
||||||
// index: resolve(__dirname, 'index.html'),
|
|
||||||
// cropImage: resolve(__dirname, 'cropImage.html')
|
|
||||||
// },
|
|
||||||
output: {
|
output: {
|
||||||
entryFileNames: `assets/[name].js`,
|
entryFileNames: `assets/[name].js`,
|
||||||
chunkFileNames: `assets/[name].js`,
|
chunkFileNames: `assets/[name].js`,
|
||||||
|
|||||||
Reference in New Issue
Block a user