gengx
This commit is contained in:
+1
-306
@@ -1,307 +1,2 @@
|
||||
"use strict";
|
||||
const common_vendor = require("../../common/vendor.js");
|
||||
const tongji_utils_aiStreamRequest = require("../utils/aiStreamRequest.js");
|
||||
const DIET_AI_PREFILL_KEY = "tongji_diet_ai_prefill";
|
||||
const emptyDietAiRecommend = () => ({
|
||||
breakfast: "",
|
||||
drinks: "",
|
||||
lunch: "",
|
||||
dinner: "",
|
||||
tips: "",
|
||||
avoid: [],
|
||||
analysis: [],
|
||||
exercise: null,
|
||||
disclaimer: "",
|
||||
rules_summary: "",
|
||||
source: "",
|
||||
date: ""
|
||||
});
|
||||
function useDietAi(proxy, { diagnosisId, showUserToast, formatUserMessage }) {
|
||||
const dietAiRecommend = common_vendor.ref(emptyDietAiRecommend());
|
||||
const dietAiLoading = common_vendor.ref(false);
|
||||
const dietAiStreaming = common_vendor.ref(false);
|
||||
const dietAiReplacing = common_vendor.ref(false);
|
||||
const dietAiStreamField = common_vendor.ref("");
|
||||
let dietStreamBuffer = "";
|
||||
const dietAiAskText = common_vendor.ref("");
|
||||
const dietAiAsking = common_vendor.ref(false);
|
||||
const dietAiAskResult = common_vendor.ref({ advice: "", level: "", level_label: "", portion: "", food: "" });
|
||||
const hasDietAiMealContent = common_vendor.computed(() => {
|
||||
const r = dietAiRecommend.value;
|
||||
return !!(r.breakfast || r.drinks || r.lunch || r.dinner || r.tips);
|
||||
});
|
||||
function detectDietStreamField(text) {
|
||||
const tail = String(text || "").match(/(?:^|\n)(早餐|喝的|午餐|晚餐|提示|少碰)[::]\s*([^\n]*)$/);
|
||||
if (!tail)
|
||||
return "breakfast";
|
||||
const map = { "早餐": "breakfast", "喝的": "drinks", "午餐": "lunch", "晚餐": "dinner", "提示": "tips", "少碰": "tips" };
|
||||
return map[tail[1]] || "breakfast";
|
||||
}
|
||||
function mergePartialDietFromStream() {
|
||||
const partial = tongji_utils_aiStreamRequest.parsePartialDietPlainText(dietStreamBuffer);
|
||||
const next = { ...dietAiRecommend.value };
|
||||
for (const key of ["breakfast", "drinks", "lunch", "dinner", "tips"]) {
|
||||
if (partial[key] != null)
|
||||
next[key] = partial[key];
|
||||
}
|
||||
if (Array.isArray(partial.avoid))
|
||||
next.avoid = partial.avoid;
|
||||
dietAiRecommend.value = next;
|
||||
dietAiStreamField.value = detectDietStreamField(dietStreamBuffer);
|
||||
}
|
||||
function applyDietRecommendPayload(payload) {
|
||||
if (!payload)
|
||||
return;
|
||||
const prevAnalysis = dietAiRecommend.value.analysis;
|
||||
const prevExercise = dietAiRecommend.value.exercise;
|
||||
dietAiRecommend.value = {
|
||||
...emptyDietAiRecommend(),
|
||||
...payload,
|
||||
avoid: Array.isArray(payload.avoid) ? payload.avoid : [],
|
||||
analysis: Array.isArray(payload.analysis) && payload.analysis.length ? payload.analysis : Array.isArray(prevAnalysis) ? prevAnalysis : [],
|
||||
exercise: payload.exercise && typeof payload.exercise === "object" ? payload.exercise : prevExercise || null
|
||||
};
|
||||
}
|
||||
function resolveDietDonePayload(payload) {
|
||||
if (!payload)
|
||||
return payload;
|
||||
if (payload.source !== "rule" || !dietStreamBuffer)
|
||||
return payload;
|
||||
const streamed = tongji_utils_aiStreamRequest.parsePartialDietPlainText(dietStreamBuffer);
|
||||
if (!streamed.breakfast)
|
||||
return payload;
|
||||
return {
|
||||
...emptyDietAiRecommend(),
|
||||
...payload,
|
||||
breakfast: streamed.breakfast,
|
||||
drinks: streamed.drinks || payload.drinks || "",
|
||||
lunch: streamed.lunch || payload.lunch || "",
|
||||
dinner: streamed.dinner || payload.dinner || "",
|
||||
tips: streamed.tips || payload.tips || "",
|
||||
avoid: Array.isArray(streamed.avoid) && streamed.avoid.length ? streamed.avoid : payload.avoid || [],
|
||||
source: "ai",
|
||||
disclaimer: "AI 建议供参考,仍请结合医嘱与血糖监测。"
|
||||
};
|
||||
}
|
||||
function onDietAiStreamEvent(event, payload) {
|
||||
if (event === "analysis") {
|
||||
if (Array.isArray(payload == null ? void 0 : payload.analysis)) {
|
||||
dietAiRecommend.value = { ...dietAiRecommend.value, analysis: payload.analysis };
|
||||
}
|
||||
} else if (event === "exercise") {
|
||||
if ((payload == null ? void 0 : payload.exercise) && typeof payload.exercise === "object") {
|
||||
dietAiRecommend.value = { ...dietAiRecommend.value, exercise: payload.exercise };
|
||||
}
|
||||
} else if (event === "delta") {
|
||||
dietAiStreaming.value = true;
|
||||
if (payload.buffer != null) {
|
||||
dietStreamBuffer = payload.buffer;
|
||||
} else {
|
||||
dietStreamBuffer += payload.text || "";
|
||||
}
|
||||
mergePartialDietFromStream();
|
||||
} else if (event === "done") {
|
||||
applyDietRecommendPayload(resolveDietDonePayload(payload));
|
||||
dietStreamBuffer = "";
|
||||
dietAiStreamField.value = "";
|
||||
} else if (event === "error") {
|
||||
showUserToast(formatUserMessage(payload == null ? void 0 : payload.message, "饮食建议加载失败"));
|
||||
}
|
||||
}
|
||||
async function fetchDietAiRecommendFallback(refresh) {
|
||||
const res = await proxy.apiUrl({
|
||||
url: "/api/tcm/dailyDietAiRecommend",
|
||||
method: "GET",
|
||||
timeout: 35e3,
|
||||
data: {
|
||||
diagnosis_id: diagnosisId.value,
|
||||
refresh: refresh ? 1 : 0,
|
||||
...refresh ? { _t: Date.now() } : {}
|
||||
}
|
||||
}, false);
|
||||
if ((res == null ? void 0 : res.code) === 1 && res.data) {
|
||||
applyDietRecommendPayload(res.data);
|
||||
} else if (res == null ? void 0 : res.msg) {
|
||||
showUserToast(res.msg);
|
||||
}
|
||||
}
|
||||
async function fetchDietAiRecommend(refresh = false) {
|
||||
if (!diagnosisId.value || dietAiLoading.value)
|
||||
return;
|
||||
dietAiLoading.value = true;
|
||||
dietAiStreaming.value = false;
|
||||
dietAiReplacing.value = !!refresh;
|
||||
dietStreamBuffer = "";
|
||||
dietAiStreamField.value = "";
|
||||
if (refresh) {
|
||||
applyDietRecommendPayload(emptyDietAiRecommend());
|
||||
}
|
||||
try {
|
||||
await tongji_utils_aiStreamRequest.requestAiStream({
|
||||
baseUrl: proxy.$url,
|
||||
url: "/api/tcm/dailyDietAiRecommendStream",
|
||||
method: "GET",
|
||||
data: {
|
||||
diagnosis_id: diagnosisId.value,
|
||||
refresh: refresh ? 1 : 0,
|
||||
...refresh ? { _t: Date.now() } : {}
|
||||
},
|
||||
onEvent: onDietAiStreamEvent,
|
||||
fallback: () => fetchDietAiRecommendFallback(refresh)
|
||||
});
|
||||
} catch (e) {
|
||||
try {
|
||||
await fetchDietAiRecommendFallback(refresh);
|
||||
} catch (e2) {
|
||||
showUserToast("饮食建议加载失败");
|
||||
}
|
||||
} finally {
|
||||
dietAiLoading.value = false;
|
||||
dietAiStreaming.value = false;
|
||||
dietAiReplacing.value = false;
|
||||
dietAiStreamField.value = "";
|
||||
}
|
||||
}
|
||||
function refreshDietAiRecommend() {
|
||||
if (dietAiLoading.value)
|
||||
return;
|
||||
fetchDietAiRecommend(true);
|
||||
}
|
||||
async function askDietAiFoodFallback(q) {
|
||||
const res = await proxy.apiUrl({
|
||||
url: "/api/tcm/dailyDietAiAsk",
|
||||
method: "POST",
|
||||
timeout: 35e3,
|
||||
data: {
|
||||
diagnosis_id: diagnosisId.value,
|
||||
question: q
|
||||
}
|
||||
}, false);
|
||||
if ((res == null ? void 0 : res.code) === 1 && res.data) {
|
||||
dietAiAskResult.value = {
|
||||
advice: res.data.advice || "",
|
||||
level: res.data.level || "",
|
||||
level_label: res.data.level_label || "",
|
||||
portion: res.data.portion || "",
|
||||
food: res.data.food || q
|
||||
};
|
||||
} else {
|
||||
showUserToast(formatUserMessage(res == null ? void 0 : res.msg, "咨询失败"));
|
||||
}
|
||||
}
|
||||
async function askDietAiFood() {
|
||||
const q = String(dietAiAskText.value || "").trim();
|
||||
if (!q) {
|
||||
showUserToast("请输入想咨询的食物");
|
||||
return;
|
||||
}
|
||||
if (!diagnosisId.value)
|
||||
return;
|
||||
dietAiAskText.value = "";
|
||||
dietAiAsking.value = true;
|
||||
dietAiAskResult.value = { advice: "", level: "", level_label: "", portion: "", food: "" };
|
||||
try {
|
||||
await tongji_utils_aiStreamRequest.requestAiStream({
|
||||
baseUrl: proxy.$url,
|
||||
url: "/api/tcm/dailyDietAiAskStream",
|
||||
method: "POST",
|
||||
data: {
|
||||
diagnosis_id: diagnosisId.value,
|
||||
question: q
|
||||
},
|
||||
onEvent: (event, payload) => {
|
||||
if (event === "start" && !dietAiAskResult.value.advice) {
|
||||
dietAiAskResult.value = { ...dietAiAskResult.value, advice: payload.message || "正在想…" };
|
||||
} else if (event === "delta") {
|
||||
dietAiAskResult.value = {
|
||||
...dietAiAskResult.value,
|
||||
advice: payload.advice || dietAiAskResult.value.advice
|
||||
};
|
||||
} else if (event === "done") {
|
||||
dietAiAskResult.value = {
|
||||
advice: payload.advice || "",
|
||||
level: payload.level || "",
|
||||
level_label: payload.level_label || "",
|
||||
portion: payload.portion || "",
|
||||
food: payload.food || q
|
||||
};
|
||||
} else if (event === "error") {
|
||||
showUserToast(formatUserMessage(payload == null ? void 0 : payload.message, "咨询失败"));
|
||||
}
|
||||
},
|
||||
fallback: () => askDietAiFoodFallback(q)
|
||||
});
|
||||
} catch (e) {
|
||||
try {
|
||||
await askDietAiFoodFallback(q);
|
||||
} catch (e2) {
|
||||
showUserToast("网络异常,请稍后再试");
|
||||
}
|
||||
} finally {
|
||||
dietAiAsking.value = false;
|
||||
}
|
||||
}
|
||||
function onDietAiAskInput(e) {
|
||||
var _a;
|
||||
dietAiAskText.value = ((_a = e == null ? void 0 : e.detail) == null ? void 0 : _a.value) ?? "";
|
||||
}
|
||||
function saveDietPrefillToStorage() {
|
||||
const r = dietAiRecommend.value;
|
||||
if (!r.breakfast)
|
||||
return;
|
||||
try {
|
||||
common_vendor.index.setStorageSync(DIET_AI_PREFILL_KEY, {
|
||||
breakfast: r.breakfast,
|
||||
drinks: r.drinks || "",
|
||||
lunch: r.lunch,
|
||||
dinner: r.dinner,
|
||||
tips: r.tips || ""
|
||||
});
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
function goMoreDietForm(withPrefill = false) {
|
||||
if (withPrefill)
|
||||
saveDietPrefillToStorage();
|
||||
common_vendor.index.navigateTo({ url: "/tongji/pages/more?openDiet=1" });
|
||||
}
|
||||
async function applyDietAiToForm() {
|
||||
if (!dietAiRecommend.value.breakfast) {
|
||||
await fetchDietAiRecommend(false);
|
||||
}
|
||||
goMoreDietForm(true);
|
||||
}
|
||||
function openDietForm() {
|
||||
goMoreDietForm(false);
|
||||
}
|
||||
return {
|
||||
dietAiRecommend,
|
||||
dietAiLoading,
|
||||
dietAiStreaming,
|
||||
dietAiReplacing,
|
||||
dietAiStreamField,
|
||||
dietAiAskText,
|
||||
dietAiAsking,
|
||||
dietAiAskResult,
|
||||
hasDietAiMealContent,
|
||||
fetchDietAiRecommend,
|
||||
refreshDietAiRecommend,
|
||||
askDietAiFood,
|
||||
onDietAiAskInput,
|
||||
applyDietAiToForm,
|
||||
openDietForm
|
||||
};
|
||||
}
|
||||
function consumeDietAiPrefill() {
|
||||
try {
|
||||
const data = common_vendor.index.getStorageSync(DIET_AI_PREFILL_KEY);
|
||||
common_vendor.index.removeStorageSync(DIET_AI_PREFILL_KEY);
|
||||
if (data && typeof data === "object" && data.breakfast)
|
||||
return data;
|
||||
} catch (e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.consumeDietAiPrefill = consumeDietAiPrefill;
|
||||
exports.useDietAi = useDietAi;
|
||||
"use strict";const r=require("../../common/vendor.js"),d=require("../utils/aiStreamRequest.js"),_="tongji_diet_ai_prefill",A=()=>({breakfast:"",drinks:"",lunch:"",dinner:"",tips:"",avoid:[],analysis:[],exercise:null,disclaimer:"",rules_summary:"",source:"",date:""});function B(a,{diagnosisId:u,showUserToast:c,formatUserMessage:k}){const n=r.ref(A()),f=r.ref(!1),v=r.ref(!1),D=r.ref(!1),o=r.ref("");let s="";const m=r.ref(""),b=r.ref(!1),l=r.ref({advice:"",level:"",level_label:"",portion:"",food:""}),R=r.computed(()=>{const e=n.value;return!!(e.breakfast||e.drinks||e.lunch||e.dinner||e.tips)});function F(e){const i=String(e||"").match(/(?:^|\n)(早餐|喝的|午餐|晚餐|提示|少碰)[::]\s*([^\n]*)$/);return i&&{早餐:"breakfast",喝的:"drinks",午餐:"lunch",晚餐:"dinner",提示:"tips",少碰:"tips"}[i[1]]||"breakfast"}function E(){const e=d.parsePartialDietPlainText(s),i={...n.value};for(const t of["breakfast","drinks","lunch","dinner","tips"])e[t]!=null&&(i[t]=e[t]);Array.isArray(e.avoid)&&(i.avoid=e.avoid),n.value=i,o.value=F(s)}function g(e){if(!e)return;const i=n.value.analysis,t=n.value.exercise;n.value={...A(),...e,avoid:Array.isArray(e.avoid)?e.avoid:[],analysis:Array.isArray(e.analysis)&&e.analysis.length?e.analysis:Array.isArray(i)?i:[],exercise:e.exercise&&typeof e.exercise=="object"?e.exercise:t||null}}function q(e){if(!e||e.source!=="rule"||!s)return e;const i=d.parsePartialDietPlainText(s);return i.breakfast?{...A(),...e,breakfast:i.breakfast,drinks:i.drinks||e.drinks||"",lunch:i.lunch||e.lunch||"",dinner:i.dinner||e.dinner||"",tips:i.tips||e.tips||"",avoid:Array.isArray(i.avoid)&&i.avoid.length?i.avoid:e.avoid||[],source:"ai",disclaimer:"AI 建议供参考,仍请结合医嘱与血糖监测。"}:e}function j(e,i){e==="analysis"?Array.isArray(i==null?void 0:i.analysis)&&(n.value={...n.value,analysis:i.analysis}):e==="exercise"?i!=null&&i.exercise&&typeof i.exercise=="object"&&(n.value={...n.value,exercise:i.exercise}):e==="delta"?(v.value=!0,i.buffer!=null?s=i.buffer:s+=i.text||"",E()):e==="done"?(g(q(i)),s="",o.value=""):e==="error"&&c(k(i==null?void 0:i.message,"饮食建议加载失败"))}async function x(e){const i=await a.apiUrl({url:"/api/tcm/dailyDietAiRecommend",method:"GET",timeout:35e3,data:{diagnosis_id:u.value,refresh:e?1:0,...e?{_t:Date.now()}:{}}},!1);(i==null?void 0:i.code)===1&&i.data?g(i.data):i!=null&&i.msg&&c(i.msg)}async function S(e=!1){if(!(!u.value||f.value)){f.value=!0,v.value=!1,D.value=!!e,s="",o.value="",e&&g(A());try{await d.requestAiStream({baseUrl:a.$url,url:"/api/tcm/dailyDietAiRecommendStream",method:"GET",data:{diagnosis_id:u.value,refresh:e?1:0,...e?{_t:Date.now()}:{}},onEvent:j,fallback:()=>x(e)})}catch{try{await x(e)}catch{c("饮食建议加载失败")}}finally{f.value=!1,v.value=!1,D.value=!1,o.value=""}}}function T(){f.value||S(!0)}async function h(e){const i=await a.apiUrl({url:"/api/tcm/dailyDietAiAsk",method:"POST",timeout:35e3,data:{diagnosis_id:u.value,question:e}},!1);(i==null?void 0:i.code)===1&&i.data?l.value={advice:i.data.advice||"",level:i.data.level||"",level_label:i.data.level_label||"",portion:i.data.portion||"",food:i.data.food||e}:c(k(i==null?void 0:i.msg,"咨询失败"))}async function w(){const e=String(m.value||"").trim();if(!e){c("请输入想咨询的食物");return}if(u.value){m.value="",b.value=!0,l.value={advice:"",level:"",level_label:"",portion:"",food:""};try{await d.requestAiStream({baseUrl:a.$url,url:"/api/tcm/dailyDietAiAskStream",method:"POST",data:{diagnosis_id:u.value,question:e},onEvent:(i,t)=>{i==="start"&&!l.value.advice?l.value={...l.value,advice:t.message||"正在想…"}:i==="delta"?l.value={...l.value,advice:t.advice||l.value.advice}:i==="done"?l.value={advice:t.advice||"",level:t.level||"",level_label:t.level_label||"",portion:t.portion||"",food:t.food||e}:i==="error"&&c(k(t==null?void 0:t.message,"咨询失败"))},fallback:()=>h(e)})}catch{try{await h(e)}catch{c("网络异常,请稍后再试")}}finally{b.value=!1}}}function L(e){var i;m.value=((i=e==null?void 0:e.detail)==null?void 0:i.value)??""}function $(){const e=n.value;if(e.breakfast)try{r.index.setStorageSync(_,{breakfast:e.breakfast,drinks:e.drinks||"",lunch:e.lunch,dinner:e.dinner,tips:e.tips||""})}catch{}}function P(e=!1){e&&$(),r.index.navigateTo({url:"/tongji/pages/more?openDiet=1"})}async function G(){n.value.breakfast||await S(!1),P(!0)}function O(){P(!1)}return{dietAiRecommend:n,dietAiLoading:f,dietAiStreaming:v,dietAiReplacing:D,dietAiStreamField:o,dietAiAskText:m,dietAiAsking:b,dietAiAskResult:l,hasDietAiMealContent:R,fetchDietAiRecommend:S,refreshDietAiRecommend:T,askDietAiFood:w,onDietAiAskInput:L,applyDietAiToForm:G,openDietForm:O}}function C(){try{const a=r.index.getStorageSync(_);if(r.index.removeStorageSync(_),a&&typeof a=="object"&&a.breakfast)return a}catch{}return null}exports.consumeDietAiPrefill=C;exports.useDietAi=B;
|
||||
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/composables/useDietAi.js.map
|
||||
|
||||
+1
-290
@@ -1,291 +1,2 @@
|
||||
"use strict";
|
||||
const common_vendor = require("../../common/vendor.js");
|
||||
const MIN_HOLD_MS = 280;
|
||||
function isBenignSttError(msg, retcode) {
|
||||
const m = String(msg || "").toLowerCase();
|
||||
if (/internal voice data failed/i.test(m))
|
||||
return true;
|
||||
if (/voice data failed/i.test(m))
|
||||
return true;
|
||||
if (/recordfailed|record failed|record manager/i.test(m))
|
||||
return true;
|
||||
if (/please stop after start/i.test(m))
|
||||
return true;
|
||||
if (/no recognition|not start|未开始|无识别/i.test(m))
|
||||
return true;
|
||||
const code = Number(retcode);
|
||||
if (code === -30002 || code === -30003 || code === -30012)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
function friendlySttError(msg) {
|
||||
const m = String(msg || "").trim();
|
||||
if (!m || isBenignSttError(m))
|
||||
return "";
|
||||
if (/not allowed|auth|permission|权限|麦克风/i.test(m))
|
||||
return "请允许使用麦克风";
|
||||
if (/network|网络|timeout/i.test(m))
|
||||
return "网络异常,请稍后再试";
|
||||
return "语音识别失败,请重试";
|
||||
}
|
||||
let sharedManager = null;
|
||||
let sharedManagerInited = false;
|
||||
let sharedRecording = false;
|
||||
let activeCtl = null;
|
||||
function getSharedRecordManager() {
|
||||
if (sharedManagerInited)
|
||||
return sharedManager;
|
||||
sharedManagerInited = true;
|
||||
try {
|
||||
const plugin = requirePlugin("WechatSI");
|
||||
sharedManager = plugin.getRecordRecognitionManager();
|
||||
} catch (e) {
|
||||
common_vendor.index.__f__("warn", "at tongji/composables/useSpeechToText.js:44", "WechatSI record recognition not available", e);
|
||||
sharedManager = null;
|
||||
return null;
|
||||
}
|
||||
if (!sharedManager)
|
||||
return null;
|
||||
sharedManager.onStart = () => {
|
||||
sharedRecording = true;
|
||||
if (activeCtl)
|
||||
activeCtl.handleStart();
|
||||
};
|
||||
sharedManager.onRecognize = (res) => {
|
||||
if (activeCtl)
|
||||
activeCtl.handleRecognize(res);
|
||||
};
|
||||
sharedManager.onStop = (res) => {
|
||||
sharedRecording = false;
|
||||
if (activeCtl)
|
||||
activeCtl.handleStop(res);
|
||||
};
|
||||
sharedManager.onError = (res) => {
|
||||
sharedRecording = false;
|
||||
if (activeCtl)
|
||||
activeCtl.handleError(res);
|
||||
};
|
||||
return sharedManager;
|
||||
}
|
||||
function useSpeechToText({ onResult, onSettle, onPartial, onError, showToast } = {}) {
|
||||
const sttListening = common_vendor.ref(false);
|
||||
const sttHolding = common_vendor.ref(false);
|
||||
const sttSupported = common_vendor.ref(false);
|
||||
let wxRecordManager = null;
|
||||
let holdStartTs = 0;
|
||||
let holdSessionId = 0;
|
||||
let startRequested = false;
|
||||
let recordAuthorized = null;
|
||||
let pendingStartSession = 0;
|
||||
function notify(msg) {
|
||||
if (!msg)
|
||||
return;
|
||||
if (typeof showToast === "function") {
|
||||
showToast(msg);
|
||||
return;
|
||||
}
|
||||
common_vendor.index.showToast({ title: msg, icon: "none", duration: 2e3 });
|
||||
}
|
||||
function emitResult(text) {
|
||||
const t = String(text || "").trim();
|
||||
const heldMs = Date.now() - holdStartTs;
|
||||
if (typeof onSettle === "function") {
|
||||
const handled = onSettle(t, { heldMs });
|
||||
if (handled === true)
|
||||
return;
|
||||
}
|
||||
if (!t) {
|
||||
if (heldMs < MIN_HOLD_MS)
|
||||
return;
|
||||
notify("没听清,请再试一次");
|
||||
return;
|
||||
}
|
||||
if (typeof onResult === "function") {
|
||||
onResult(t);
|
||||
}
|
||||
}
|
||||
function stopListening() {
|
||||
const ownsRecording = sharedRecording && activeCtl === controller;
|
||||
if (wxRecordManager && (sttListening.value || ownsRecording)) {
|
||||
try {
|
||||
wxRecordManager.stop();
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
startRequested = false;
|
||||
sttHolding.value = false;
|
||||
sttListening.value = false;
|
||||
}
|
||||
function ensureRecordAuth(onGranted) {
|
||||
if (recordAuthorized === true) {
|
||||
onGranted();
|
||||
return;
|
||||
}
|
||||
common_vendor.index.authorize({
|
||||
scope: "scope.record",
|
||||
success() {
|
||||
recordAuthorized = true;
|
||||
onGranted();
|
||||
},
|
||||
fail() {
|
||||
recordAuthorized = false;
|
||||
sttHolding.value = false;
|
||||
common_vendor.index.showModal({
|
||||
title: "需要麦克风权限",
|
||||
content: "请在设置中允许使用麦克风,以便长按说话",
|
||||
confirmText: "去设置",
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
common_vendor.index.openSetting({});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function actuallyStartWechat(session) {
|
||||
if (!wxRecordManager || session !== holdSessionId || !sttHolding.value) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
startRequested = true;
|
||||
wxRecordManager.start({
|
||||
duration: 6e4,
|
||||
lang: "zh_CN"
|
||||
});
|
||||
} catch (e) {
|
||||
startRequested = false;
|
||||
sharedRecording = false;
|
||||
sttHolding.value = false;
|
||||
notify("无法开始录音");
|
||||
}
|
||||
}
|
||||
function startWechatRecord(session) {
|
||||
if (!wxRecordManager || session !== holdSessionId || !sttHolding.value) {
|
||||
return;
|
||||
}
|
||||
if (sharedRecording || startRequested) {
|
||||
pendingStartSession = session;
|
||||
try {
|
||||
wxRecordManager.stop();
|
||||
} catch (e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
actuallyStartWechat(session);
|
||||
}
|
||||
function beginHoldSpeech() {
|
||||
if (!sttSupported.value) {
|
||||
notify("当前环境不支持语音输入");
|
||||
return;
|
||||
}
|
||||
if (sttHolding.value)
|
||||
return;
|
||||
const session = ++holdSessionId;
|
||||
holdStartTs = Date.now();
|
||||
sttHolding.value = true;
|
||||
activeCtl = controller;
|
||||
ensureRecordAuth(() => startWechatRecord(session));
|
||||
}
|
||||
function endHoldSpeech(force = false) {
|
||||
let recording = false;
|
||||
recording = sharedRecording && activeCtl === controller;
|
||||
if (!force && !sttHolding.value && !sttListening.value && !startRequested && !recording) {
|
||||
return;
|
||||
}
|
||||
sttHolding.value = false;
|
||||
pendingStartSession = 0;
|
||||
if (sttListening.value || recording) {
|
||||
stopListening();
|
||||
return;
|
||||
}
|
||||
if (startRequested) {
|
||||
startRequested = false;
|
||||
holdSessionId += 1;
|
||||
return;
|
||||
}
|
||||
holdSessionId += 1;
|
||||
}
|
||||
const controller = {
|
||||
handleStart() {
|
||||
if (!sttHolding.value) {
|
||||
startRequested = false;
|
||||
sttListening.value = false;
|
||||
return;
|
||||
}
|
||||
startRequested = false;
|
||||
sttListening.value = true;
|
||||
},
|
||||
handleRecognize(res) {
|
||||
if (typeof onPartial !== "function")
|
||||
return;
|
||||
const t = String((res == null ? void 0 : res.result) || "").trim();
|
||||
if (t)
|
||||
onPartial(t);
|
||||
},
|
||||
handleStop(res) {
|
||||
sttListening.value = false;
|
||||
startRequested = false;
|
||||
if (pendingStartSession && pendingStartSession === holdSessionId && sttHolding.value) {
|
||||
const s = pendingStartSession;
|
||||
pendingStartSession = 0;
|
||||
actuallyStartWechat(s);
|
||||
return;
|
||||
}
|
||||
pendingStartSession = 0;
|
||||
emitResult(res == null ? void 0 : res.result);
|
||||
},
|
||||
handleError(res) {
|
||||
sttListening.value = false;
|
||||
startRequested = false;
|
||||
const msg = res && res.msg || "";
|
||||
const retcode = res && (res.retcode ?? res.errCode ?? res.code);
|
||||
if (/please stop after start/i.test(msg)) {
|
||||
try {
|
||||
wxRecordManager.stop();
|
||||
} catch (e) {
|
||||
}
|
||||
pendingStartSession = sttHolding.value ? holdSessionId : 0;
|
||||
return;
|
||||
}
|
||||
pendingStartSession = 0;
|
||||
sttHolding.value = false;
|
||||
const heldMs = Date.now() - holdStartTs;
|
||||
if (isBenignSttError(msg, retcode) || heldMs < MIN_HOLD_MS) {
|
||||
if (typeof onError === "function")
|
||||
onError(msg || "");
|
||||
return;
|
||||
}
|
||||
if (typeof onError === "function") {
|
||||
const handled = onError(msg || "");
|
||||
if (handled === true)
|
||||
return;
|
||||
}
|
||||
const tip = friendlySttError(msg);
|
||||
if (tip)
|
||||
notify(tip);
|
||||
}
|
||||
};
|
||||
wxRecordManager = getSharedRecordManager();
|
||||
sttSupported.value = !!wxRecordManager;
|
||||
common_vendor.onUnmounted(() => {
|
||||
sttHolding.value = false;
|
||||
holdSessionId += 1;
|
||||
pendingStartSession = 0;
|
||||
stopListening();
|
||||
if (activeCtl === controller) {
|
||||
activeCtl = null;
|
||||
}
|
||||
});
|
||||
return {
|
||||
sttListening,
|
||||
sttHolding,
|
||||
sttSupported,
|
||||
beginHoldSpeech,
|
||||
endHoldSpeech,
|
||||
stopSpeechToText: endHoldSpeech
|
||||
};
|
||||
}
|
||||
exports.isBenignSttError = isBenignSttError;
|
||||
exports.useSpeechToText = useSpeechToText;
|
||||
"use strict";const s=require("../../common/vendor.js"),C=280;function w(n,u){const d=String(n||"").toLowerCase();if(/internal voice data failed/i.test(d)||/voice data failed/i.test(d)||/recordfailed|record failed|record manager/i.test(d)||/please stop after start/i.test(d)||/no recognition|not start|未开始|无识别/i.test(d))return!0;const h=Number(u);return h===-30002||h===-30003||h===-30012}function N(n){const u=String(n||"").trim();return!u||w(u)?"":/not allowed|auth|permission|权限|麦克风/i.test(u)?"请允许使用麦克风":/network|网络|timeout/i.test(u)?"网络异常,请稍后再试":"语音识别失败,请重试"}let c=null,D=!1,g=!1,i=null;function j(){if(D)return c;D=!0;try{c=requirePlugin("WechatSI").getRecordRecognitionManager()}catch(n){return s.index.__f__("warn","at tongji/composables/useSpeechToText.js:44","WechatSI record recognition not available",n),c=null,null}return c?(c.onStart=()=>{g=!0,i&&i.handleStart()},c.onRecognize=n=>{i&&i.handleRecognize(n)},c.onStop=n=>{g=!1,i&&i.handleStop(n)},c.onError=n=>{g=!1,i&&i.handleError(n)},c):null}function A({onResult:n,onSettle:u,onPartial:d,onError:h,showToast:M}={}){const l=s.ref(!1),r=s.ref(!1),y=s.ref(!1);let f=null,R=0,p=0,o=!1,x=null,a=0;function v(e){if(e){if(typeof M=="function"){M(e);return}s.index.showToast({title:e,icon:"none",duration:2e3})}}function H(e){const t=String(e||"").trim(),m=Date.now()-R;if(!(typeof u=="function"&&u(t,{heldMs:m})===!0)){if(!t){if(m<C)return;v("没听清,请再试一次");return}typeof n=="function"&&n(t)}}function T(){const e=g&&i===S;if(f&&(l.value||e))try{f.stop()}catch{}o=!1,r.value=!1,l.value=!1}function L(e){if(x===!0){e();return}s.index.authorize({scope:"scope.record",success(){x=!0,e()},fail(){x=!1,r.value=!1,s.index.showModal({title:"需要麦克风权限",content:"请在设置中允许使用麦克风,以便长按说话",confirmText:"去设置",success(t){t.confirm&&s.index.openSetting({})}})}})}function _(e){if(!(!f||e!==p||!r.value))try{o=!0,f.start({duration:6e4,lang:"zh_CN"})}catch{o=!1,g=!1,r.value=!1,v("无法开始录音")}}function W(e){if(!(!f||e!==p||!r.value)){if(g||o){a=e;try{f.stop()}catch{}return}_(e)}}function q(){if(!y.value){v("当前环境不支持语音输入");return}if(r.value)return;const e=++p;R=Date.now(),r.value=!0,i=S,L(()=>W(e))}function z(e=!1){let t=!1;if(t=g&&i===S,!(!e&&!r.value&&!l.value&&!o&&!t)){if(r.value=!1,a=0,l.value||t){T();return}if(o){o=!1,p+=1;return}p+=1}}const S={handleStart(){if(!r.value){o=!1,l.value=!1;return}o=!1,l.value=!0},handleRecognize(e){if(typeof d!="function")return;const t=String((e==null?void 0:e.result)||"").trim();t&&d(t)},handleStop(e){if(l.value=!1,o=!1,a&&a===p&&r.value){const t=a;a=0,_(t);return}a=0,H(e==null?void 0:e.result)},handleError(e){l.value=!1,o=!1;const t=e&&e.msg||"",m=e&&(e.retcode??e.errCode??e.code);if(/please stop after start/i.test(t)){try{f.stop()}catch{}a=r.value?p:0;return}a=0,r.value=!1;const I=Date.now()-R;if(w(t,m)||I<C){typeof h=="function"&&h(t||"");return}if(typeof h=="function"&&h(t||"")===!0)return;const b=N(t);b&&v(b)}};return f=j(),y.value=!!f,s.onUnmounted(()=>{r.value=!1,p+=1,a=0,T(),i===S&&(i=null)}),{sttListening:l,sttHolding:r,sttSupported:y,beginHoldSpeech:q,endHoldSpeech:z,stopSpeechToText:z}}exports.isBenignSttError=w;exports.useSpeechToText=A;
|
||||
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/composables/useSpeechToText.js.map
|
||||
|
||||
Reference in New Issue
Block a user