308 lines
10 KiB
JavaScript
308 lines
10 KiB
JavaScript
"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;
|
||
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/composables/useDietAi.js.map
|