首次上传

This commit is contained in:
田岩
2025-11-27 20:48:18 +08:00
commit 492a164bff
1746 changed files with 219083 additions and 0 deletions
@@ -0,0 +1,3 @@
{
"type": "module"
}
@@ -0,0 +1,11 @@
var __getOwnPropNames = Object.getOwnPropertyNames;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var require_app_css = __commonJS({
"app.css.js"(exports) {
const _style_0 = {};
exports.styles = [_style_0];
}
});
export default require_app_css();
@@ -0,0 +1,2 @@
Promise.resolve("./app.css.js").then(() => {
});
@@ -0,0 +1,5 @@
interface Uni {
getBatteryInfo: typeof import("@/uni_modules/uni-getbatteryinfo")["getBatteryInfo"]
getBatteryInfoSync: typeof import("@/uni_modules/uni-getbatteryinfo")["getBatteryInfoSync"]
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>View</title>
<link rel="icon" href="data:,">
<link rel="stylesheet" href="app.css" />
<script>var __uniConfig = {"globalStyle":{},"darkmode":false}</script>
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
</head>
<body>
<div id="app"></div>
<script src="uni-app-view.umd.js"></script>
</body>
</html>
@@ -0,0 +1,11 @@
;(function(){
let u=void 0,isReady=false,onReadyCallbacks=[],isServiceReady=false,onServiceReadyCallbacks=[];
const __uniConfig = {"pages":[],"globalStyle":{"backgroundColor":"#F8F8F8","navigationBar":{"backgroundColor":"#F8F8F8","titleText":"uni-app","type":"default","titleColor":"#000000"},"isNVue":false},"nvue":{"compiler":"uni-app","styleCompiler":"uni-app","flex-direction":"column"},"renderer":"auto","appname":"测试流式传输uniapp","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":true},"compilerVersion":"4.85","entryPagePath":"pages/index/index","entryPageQuery":"","realEntryPagePath":"","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000},"locales":{},"darkmode":false,"themeConfig":{}};
const __uniRoutes = [{"path":"pages/index/index","meta":{"isQuit":true,"isEntry":true,"navigationBar":{"titleText":"uni-app","type":"default"},"isNVue":false}}].map(uniRoute=>(uniRoute.meta.route=uniRoute.path,__uniConfig.pages.push(uniRoute.path),uniRoute.path='/'+uniRoute.path,uniRoute));
__uniConfig.styles=[];//styles
__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
__uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
service.register("uni-app-config",{create(a,b,c){if(!__uniConfig.viewport){var d=b.weex.config.env.scale,e=b.weex.config.env.deviceWidth,f=Math.ceil(e/d);Object.assign(__uniConfig,{viewport:f,defaultFontSize:16})}return{instance:{__uniConfig:__uniConfig,__uniRoutes:__uniRoutes,global:u,window:u,document:u,frames:u,self:u,location:u,navigator:u,localStorage:u,history:u,Caches:u,screen:u,alert:u,confirm:u,prompt:u,fetch:u,XMLHttpRequest:u,WebSocket:u,webkit:u,print:u}}}});
})();
@@ -0,0 +1 @@
(function(){})();
@@ -0,0 +1,247 @@
if (typeof Promise !== "undefined" && !Promise.prototype.finally) {
Promise.prototype.finally = function(callback) {
const promise = this.constructor;
return this.then(
(value) => promise.resolve(callback()).then(() => value),
(reason) => promise.resolve(callback()).then(() => {
throw reason;
})
);
};
}
;
if (typeof uni !== "undefined" && uni && uni.requireGlobal) {
const global = uni.requireGlobal();
ArrayBuffer = global.ArrayBuffer;
Int8Array = global.Int8Array;
Uint8Array = global.Uint8Array;
Uint8ClampedArray = global.Uint8ClampedArray;
Int16Array = global.Int16Array;
Uint16Array = global.Uint16Array;
Int32Array = global.Int32Array;
Uint32Array = global.Uint32Array;
Float32Array = global.Float32Array;
Float64Array = global.Float64Array;
BigInt64Array = global.BigInt64Array;
BigUint64Array = global.BigUint64Array;
}
;
if (uni.restoreGlobal) {
uni.restoreGlobal(Vue, weex, plus, setTimeout, clearTimeout, setInterval, clearInterval);
}
(function(vue) {
"use strict";
function formatAppLog(type, filename, ...args) {
if (uni.__log__) {
uni.__log__(type, filename, ...args);
} else {
console[type].apply(console, [...args, filename]);
}
}
const _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
target[key] = val;
}
return target;
};
const _sfc_main$1 = {
data() {
return {
inputAreaHeight: 80,
status: "未录音",
recorder: null,
// 录音实例
recordTimer: null
// 定时读取定时器
};
},
onUnload() {
this.stopRecord();
},
methods: {
// 申请录音权限
async applyRecordPermission() {
return true;
},
// 开始录音
async startRecord() {
const AudioRecord = plus.android.importClass("android.media.AudioRecord");
const AudioFormat = plus.android.importClass("android.media.AudioFormat");
const MediaRecorder = plus.android.importClass("android.media.MediaRecorder");
plus.android.importClass("android.content.Context");
plus.android.runtimeMainActivity();
const SAMPLE_RATE = 44100;
const CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
const AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
const BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT);
const audioRecord = new AudioRecord(
MediaRecorder.AudioSource.MIC,
// 麦克风源
SAMPLE_RATE,
CHANNEL_CONFIG,
AUDIO_FORMAT,
BUFFER_SIZE * 2
// 缓冲区放大2倍,避免数据溢出
);
const pcmDataList = [];
const SLICE_DURATION = 200;
const SAMPLES_PER_SLICE = SAMPLE_RATE * (SLICE_DURATION / 1e3);
function createShortArray(length) {
try {
const ArrayClass = plus.android.importClass("java.lang.reflect.Array");
const ShortClass = plus.android.importClass("java.lang.Short").TYPE;
return ArrayClass.newInstance(ShortClass, length);
} catch (e) {
const ShortArray = plus.android.createInstance("short[]", [length]);
return ShortArray;
}
}
audioRecord.startRecording();
formatAppLog("log", "at pages/index/index.vue:127", "Android原生录音启动,开始实时PCM捕获");
const readPCMData = () => {
formatAppLog("log", "at pages/index/index.vue:131", "持续读取PCM数据到内存(修复newArray报错)", audioRecord.getRecordingState(), AudioRecord.RECORDSTATE_RECORDING);
if (!audioRecord || audioRecord.getRecordingState() !== AudioRecord.RECORDSTATE_RECORDING)
return;
const shortArray = createShortArray(BUFFER_SIZE);
const readSize = audioRecord.read(shortArray, 0, BUFFER_SIZE);
formatAppLog("log", "at pages/index/index.vue:139", "读取麦克风的PCM数据", readSize);
if (readSize > 0) {
const pcmChunk = [];
for (let i = 0; i < readSize; i++) {
try {
pcmChunk.push(plus.android.getArrayElement(shortArray, i));
} catch (e) {
pcmChunk.push(plus.android.getAttribute(shortArray, i));
}
}
pcmDataList.push(...pcmChunk);
}
setTimeout(readPCMData, 0);
};
setTimeout(() => {
readPCMData();
}, 1e3);
setInterval(() => {
if (pcmDataList.length >= SAMPLES_PER_SLICE) {
const slicePCM = pcmDataList.splice(0, SAMPLES_PER_SLICE);
handlePCMSlice(slicePCM, SAMPLE_RATE);
formatAppLog("log", "at pages/index/index.vue:171", `截取200ms PCM切片,采样点数:${slicePCM.length}`);
}
}, SLICE_DURATION);
},
// 停止录音
stopRecord() {
if (this.recorder) {
this.recorder.stop();
this.recorder = null;
}
if (this.recordTimer) {
clearInterval(this.recordTimer);
this.recordTimer = null;
}
this.status = "录音已停止";
}
}
};
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return vue.openBlock(), vue.createElementBlock("view", { class: "ai-chat-container" }, [
vue.createElementVNode(
"scroll-view",
{
class: "chat-list",
"scroll-y": "",
"scroll-bottom": "{{scrollBottom}}",
style: vue.normalizeStyle({ height: `calc(100vh - ${$data.inputAreaHeight}px)` })
},
[
vue.createElementVNode("view", { class: "chat-item system" }, [
vue.createElementVNode("view", { class: "chat-avatar system-avatar" }, [
vue.createElementVNode("text", { class: "iconfont icon-robot" })
]),
vue.createElementVNode("view", { class: "chat-content" }, [
vue.createElementVNode("view", { class: "content-text" }, "你好,我是智能助手,有什么可以帮你的?")
])
]),
vue.createElementVNode("view", { class: "chat-item user" }, [
vue.createElementVNode("view", { class: "chat-content" }, [
vue.createElementVNode("view", { class: "content-text" }, "请问uniapp怎么实现语音流式传输?")
]),
vue.createElementVNode("view", { class: "chat-avatar user-avatar" }, [
vue.createElementVNode("text", { class: "iconfont icon-user" })
])
]),
vue.createElementVNode("view", { class: "chat-item ai" }, [
vue.createElementVNode("view", { class: "chat-avatar ai-avatar" }, [
vue.createElementVNode("text", { class: "iconfont icon-ai" })
]),
vue.createElementVNode("view", { class: "chat-content" }, [
vue.createElementVNode("view", { class: "content-text" }, " UniApp实现语音流式传输可通过5+ API采集音频帧,结合WebSocket分块发送,核心步骤包括: 1. 初始化录音实例(16k采样率、单声道PCM); 2. 建立WebSocket连接; 3. 分块发送音频数据; 4. 接收实时ASR结果。 ")
])
])
],
4
/* STYLE */
),
vue.createElementVNode(
"view",
{
class: "input-area",
style: vue.normalizeStyle({ height: $data.inputAreaHeight + "px" })
},
[
vue.createElementVNode("view", { class: "send-btn-area" }, [
vue.createElementVNode(
"button",
{
class: "hold-send-btn",
onTouchstart: _cache[0] || (_cache[0] = (...args) => $options.startRecord && $options.startRecord(...args))
},
[
vue.createElementVNode(
"text",
{ class: "btn-text" },
vue.toDisplayString($data.status ? "松开发送" : "按住 说话/发送"),
1
/* TEXT */
)
],
32
/* NEED_HYDRATION */
)
])
],
4
/* STYLE */
)
]);
}
const PagesIndexIndex = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__scopeId", "data-v-1cf27b2a"], ["__file", "C:/Users/Administrator/Desktop/新建文件夹 (8)/测试流式传输uniapp/pages/index/index.vue"]]);
__definePage("pages/index/index", PagesIndexIndex);
const _sfc_main = {
onLaunch: function() {
formatAppLog("log", "at App.vue:4", "App Launch");
},
onShow: function() {
formatAppLog("log", "at App.vue:7", "App Show");
},
onHide: function() {
formatAppLog("log", "at App.vue:10", "App Hide");
}
};
const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "C:/Users/Administrator/Desktop/新建文件夹 (8)/测试流式传输uniapp/App.vue"]]);
function createApp() {
const app = vue.createVueApp(App);
return {
app
};
}
const { app: __app__, Vuex: __Vuex__, Pinia: __Pinia__ } = createApp();
uni.Vuex = __Vuex__;
uni.Pinia = __Pinia__;
__app__.provide("__globalStyles", __uniConfig.styles);
__app__._component.mpType = "app";
__app__._component.render = () => {
};
__app__.mount("#app");
})(Vue);
File diff suppressed because one or more lines are too long
@@ -0,0 +1,107 @@
{
"@platforms": [
"android",
"iPhone",
"iPad"
],
"id": "",
"name": "测试流式传输uniapp",
"version": {
"name": "1.0.0",
"code": "100"
},
"description": "",
"developer": {
"name": "",
"email": "",
"url": ""
},
"permissions": {
"UniNView": {
"description": "UniNView原生渲染"
}
},
"plus": {
"useragent": {
"value": "uni-app",
"concatenate": true
},
"splashscreen": {
"target": "id:1",
"autoclose": true,
"waiting": true,
"delay": 0
},
"popGesture": "close",
"launchwebview": {
"render": "always",
"id": "1",
"kernel": "WKWebview"
},
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"distribute": {
"google": {
"permissions": [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
"apple": {},
"plugins": {
"audio": {
"mp3": {
"description": "Android平台录音支持MP3格式文件"
}
}
}
},
"statusbar": {
"immersed": "supportedDevice",
"style": "dark",
"background": "#F8F8F8"
},
"uniStatistics": {
"enable": false
},
"allowsInlineMediaPlayback": true,
"uni-app": {
"control": "uni-v3",
"vueVersion": "3",
"compilerVersion": "4.85",
"nvueCompiler": "uni-app",
"renderer": "auto",
"nvue": {
"flex-direction": "column"
},
"nvueLaunchMode": "normal",
"webView": {
"minUserAgentVersion": "49.0"
}
}
},
"app-harmony": {
"useragent": {
"value": "uni-app",
"concatenate": true
},
"uniStatistics": {
"enable": false
}
},
"launch_path": "__uniappview.html"
}
@@ -0,0 +1,198 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
/* 文字基本颜色 */
/* 背景颜色 */
/* 边框颜色 */
/* 尺寸变量 */
/* 文字尺寸 */
/* 图片尺寸 */
/* Border Radius */
/* 水平间距 */
/* 垂直间距 */
/* 透明度 */
/* 文章场景相关 */
.ai-chat-container[data-v-1cf27b2a] {
width: 100%;
height: 100vh;
background-color: #f5f7fa;
box-sizing: border-box;
padding-bottom: env(safe-area-inset-bottom);
}
.chat-list[data-v-1cf27b2a] {
width: 100%;
padding: 0.625rem 0.5rem;
box-sizing: border-box;
}
.chat-list .chat-item[data-v-1cf27b2a] {
display: flex;
margin-bottom: 0.9375rem;
max-width: 85%;
}
.chat-list .chat-item .chat-avatar[data-v-1cf27b2a] {
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-right: 0.625rem;
background-color: #fff;
box-shadow: 0 0.0625rem 0.25rem rgba(0, 0, 0, 0.1);
}
.chat-list .chat-item .chat-avatar .iconfont[data-v-1cf27b2a] {
font-size: 1.25rem;
}
.chat-list .chat-item .chat-content .content-text[data-v-1cf27b2a] {
padding: 0.75rem 0.875rem;
border-radius: 0.625rem;
line-height: 1.6;
font-size: 1rem;
color: #333;
word-wrap: break-word;
word-break: break-all;
}
.chat-list .system[data-v-1cf27b2a] {
margin: 0 auto 0.9375rem;
}
.chat-list .system .chat-avatar[data-v-1cf27b2a] {
background-color: #e8f4f8;
}
.chat-list .system .chat-avatar .iconfont[data-v-1cf27b2a] {
color: #4299e1;
}
.chat-list .system .chat-content .content-text[data-v-1cf27b2a] {
background-color: #e8f4f8;
color: #2d3748;
border-radius: 0.5rem;
}
.chat-list .user[data-v-1cf27b2a] {
margin-left: auto;
flex-direction: row-reverse;
}
.chat-list .user .chat-avatar[data-v-1cf27b2a] {
margin-right: 0;
margin-left: 0.625rem;
background-color: #4299e1;
}
.chat-list .user .chat-avatar .iconfont[data-v-1cf27b2a] {
color: #fff;
}
.chat-list .user .chat-content .content-text[data-v-1cf27b2a] {
background-color: #4299e1;
color: #fff;
border-radius: 0.625rem 0.625rem 0.25rem 0.625rem;
}
.chat-list .ai .chat-avatar[data-v-1cf27b2a] {
background-color: #9f7aea;
}
.chat-list .ai .chat-avatar .iconfont[data-v-1cf27b2a] {
color: #fff;
}
.chat-list .ai .chat-content .content-text[data-v-1cf27b2a] {
background-color: #fff;
border-radius: 0.625rem 0.625rem 0.625rem 0.25rem;
box-shadow: 0 0.0625rem 0.25rem rgba(0, 0, 0, 0.08);
}
.input-area[data-v-1cf27b2a] {
width: 100%;
background-color: #fff;
padding: 0.5rem 0.625rem;
box-sizing: border-box;
border-top: 0.03125rem solid #eee;
}
.input-area .input-box[data-v-1cf27b2a] {
position: relative;
width: 100%;
margin-bottom: 0.625rem;
}
.input-area .input-box .input-text[data-v-1cf27b2a] {
width: 100%;
min-height: 2.5rem;
max-height: 6.25rem;
padding: 0.625rem 0.75rem;
padding-right: 2.5rem;
box-sizing: border-box;
border: 0.03125rem solid #e5e7eb;
border-radius: 1.25rem;
font-size: 1rem;
line-height: 1.5;
background-color: #f9fafb;
resize: none;
outline: none;
}
.input-area .input-box .clear-btn[data-v-1cf27b2a] {
position: absolute;
right: 0.625rem;
top: 50%;
transform: translateY(-50%);
width: 1.875rem;
height: 1.875rem;
border-radius: 50%;
background-color: #e5e7eb;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
margin: 0;
}
.input-area .input-box .clear-btn .iconfont[data-v-1cf27b2a] {
font-size: 1rem;
color: #6b7280;
}
.input-area .send-btn-area[data-v-1cf27b2a] {
width: 100%;
display: flex;
justify-content: center;
}
.input-area .send-btn-area .hold-send-btn[data-v-1cf27b2a] {
width: 80%;
height: 2.8125rem;
line-height: 2.8125rem;
border-radius: 1.40625rem;
background-color: #4299e1;
color: #fff;
font-size: 1.0625rem;
font-weight: 500;
box-shadow: 0 0.125rem 0.375rem rgba(66, 153, 225, 0.3);
transition: all 0.2s ease;
padding: 0;
margin: 0;
}
.input-area .send-btn-area .hold-send-btn[data-v-1cf27b2a]:active {
background-color: #3182ce;
transform: scale(0.98);
}
.input-area .send-btn-area .hold-send-btn .btn-text[data-v-1cf27b2a] {
font-size: 1.0625rem;
}
.iconfont[data-v-1cf27b2a] {
font-family: "iconfont" !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-robot[data-v-1cf27b2a]::before {
content: "\e600";
}
.icon-user[data-v-1cf27b2a]::before {
content: "\e601";
}
.icon-ai[data-v-1cf27b2a]::before {
content: "\e602";
}
.icon-clear[data-v-1cf27b2a]::before {
content: "\e603";
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long