This commit is contained in:
Your Name
2026-08-11 17:41:36 +08:00
parent cfe4c82c90
commit 03fe4ddf9d
18771 changed files with 3617239 additions and 0 deletions
+165
View File
@@ -0,0 +1,165 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const common_vendor = require("./common/vendor.js");
if (!Math) {
"./tongji/pages/weekly.js";
"./pages/user/user.js";
"./pages/user/userinfo.js";
"./pages/Card/Card.js";
"./pages/Card/edit_card.js";
"./pages/Card/contact.js";
"./tongji/pages/index.js";
"./tongji/pages/more.js";
"./tongji/endless-game/index.js";
"./training/pages/grip-ring.js";
"./training/pages/pilates-ring.js";
"./training/pages/foot-pedal.js";
"./training/pages/dumbbell.js";
"./training/pages/metronome.js";
}
const _sfc_main = {
__name: "App",
setup(__props) {
const { proxy } = common_vendor.getCurrentInstance();
common_vendor.onLaunch(() => {
try {
common_vendor.index.setInnerAudioOption({
obeyMuteSwitch: false,
mixWithOther: true
});
} catch (e) {
common_vendor.index.__f__("warn", "at App.vue:16", "setInnerAudioOption failed:", e);
}
common_vendor.index.login({
provider: "weixin",
success: function(loginRes) {
if (common_vendor.index.getStorageSync("token")) {
userinfo(loginRes.code);
} else {
wxcode(loginRes.code);
}
}
});
});
const wxcode = async (code) => {
try {
const res = await proxy.apiUrl(
{
url: "/api/login/mnpLogin",
method: "POST",
data: {
code
}
},
false
);
if (res && res.data && res.data.token) {
common_vendor.index.setStorageSync("token", res.data.token);
common_vendor.index.setStorageSync("userData", res.data);
}
} catch (err) {
}
};
const userinfo = async (code) => {
try {
const res = await proxy.apiUrl(
{
url: "/api/user/info",
method: "POST"
},
false
);
if (res.code == -1) {
wxcode(code);
return;
}
if (res && res.data) {
common_vendor.index.setStorageSync("userData", res.data);
}
} catch (err) {
}
};
return () => {
};
}
};
var baseUrl = "https://admin.zhenyangtang.com.cn/";
function joinApiUrl(base, path) {
const b = String(base || "").replace(/\/+$/, "");
const p = String(path || "").replace(/^\/+/, "");
return p ? `${b}/${p}` : b;
}
function apiUrl(promise, Loading = true) {
const token = common_vendor.index.getStorageSync("token");
const header = {
token,
"content-type": "application/x-www-form-urlencoded",
...promise.header || {}
};
const data = promise.data || {};
const url = joinApiUrl(baseUrl, promise.url || "");
return new Promise((resolve, reject) => {
if (Loading) {
common_vendor.index.showLoading({ title: "加载中...", mask: true });
}
common_vendor.index.request({
url,
data,
timeout: promise.timeout != null ? promise.timeout : 15e3,
method: promise.method || "POST",
header,
sslVerify: false,
success: (res) => {
const data2 = res.data;
if (data2.code === -1) {
common_vendor.index.removeStorageSync("token");
}
resolve(data2);
},
fail: (e) => {
reject(e);
},
complete: (e) => {
if (Loading) {
common_vendor.index.hideLoading();
}
}
});
});
}
function parsePageParams(options) {
const params = {};
if (!options)
return params;
if (options.scene) {
try {
const decodedScene = decodeURIComponent(options.scene);
decodedScene.split("&").forEach((item) => {
const [key, value] = item.split("=");
if (key && value) {
params[key] = value;
}
});
} catch (error) {
common_vendor.index.__f__("error", "at main.js:126", "解析scene参数失败:", error);
}
}
Object.keys(options).forEach((key) => {
if (key !== "scene" && options[key]) {
params[key] = options[key];
}
});
return params;
}
function createApp() {
const app = common_vendor.createSSRApp(_sfc_main);
app.config.globalProperties.$url = baseUrl;
app.config.globalProperties.apiUrl = apiUrl;
app.config.globalProperties.$parsePageParams = parsePageParams;
return {
app
};
}
createApp().app.mount("#app");
exports.createApp = createApp;
//# sourceMappingURL=../.sourcemap/mp-weixin/app.js.map
+42
View File
@@ -0,0 +1,42 @@
{
"pages": [
"tongji/pages/weekly",
"pages/user/user",
"pages/user/userinfo",
"pages/Card/Card",
"pages/Card/edit_card",
"pages/Card/contact",
"tongji/pages/index",
"tongji/pages/more",
"tongji/endless-game/index"
],
"subPackages": [
{
"root": "training",
"pages": [
"pages/grip-ring",
"pages/pilates-ring",
"pages/foot-pedal",
"pages/dumbbell",
"pages/metronome"
]
}
],
"window": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "甄养堂血糖管理",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"lazyCodeLoading": "requiredComponents",
"requiredBackgroundModes": [
"audio"
],
"plugins": {
"WechatSI": {
"version": "0.3.5",
"provider": "wx069ba97219f66d99"
}
},
"usingComponents": {}
}
+7
View File
@@ -0,0 +1,7 @@
/* 全局基础 */
page {
background-color: #f4fbf4;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
sans-serif;
}
page{--status-bar-height:25px;--top-window-height:0px;--window-top:0px;--window-bottom:0px;--window-left:0px;--window-right:0px;--window-magin:0px}[data-c-h="true"]{display: none !important;}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,40 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
const color = "#8a8a8a";
const activeColor = "#204e2b";
const _sfc_main = {
__name: "tab-bar-dock",
props: {
/** 0 血糖 1 我的 */
active: {
type: Number,
default: 0
}
},
setup(__props) {
const props = __props;
function goUser() {
if (props.active === 1)
return;
common_vendor.index.redirectTo({ url: "/pages/user/user" });
}
function goWeekly() {
if (props.active === 0)
return;
common_vendor.index.redirectTo({ url: "/tongji/pages/weekly" });
}
return (_ctx, _cache) => {
return {
a: __props.active === 0 ? 1 : "",
b: __props.active === 0 ? activeColor : color,
c: common_vendor.o(goWeekly, "40"),
d: __props.active === 1 ? "/static/user/user_no.png" : "/static/user/user.png",
e: __props.active === 1 ? activeColor : color,
f: common_vendor.o(goUser, "79")
};
};
}
};
const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-c3efacf5"]]);
wx.createComponent(Component);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/components/app-tab-bar/tab-bar-dock.js.map
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1 @@
<view class="tab-dock data-v-c3efacf5"><view class="tab-dock__inner data-v-c3efacf5"><view class="tab-dock__item tab-dock__item--center data-v-c3efacf5" bindtap="{{c}}"><view class="{{['tab-dock__fab', 'data-v-c3efacf5', a && 'tab-dock__fab--active']}}"><text class="tab-dock__fab-text data-v-c3efacf5">糖</text></view><text class="tab-dock__label tab-dock__label--center data-v-c3efacf5" style="{{'color:' + b}}">血糖</text></view><view class="tab-dock__item data-v-c3efacf5" bindtap="{{f}}"><image class="tab-dock__icon data-v-c3efacf5" src="{{d}}" mode="aspectFit"/><text class="tab-dock__label data-v-c3efacf5" style="{{'color:' + e}}">我的</text></view></view><view class="tab-dock__safe data-v-c3efacf5"/></view>
@@ -0,0 +1,90 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
/* 文字基本颜色 */
/* 背景颜色 */
/* 边框颜色 */
/* 尺寸变量 */
/* 文字尺寸 */
/* 图片尺寸 */
/* Border Radius */
/* 水平间距 */
/* 垂直间距 */
/* 透明度 */
/* 文章场景相关 */
.tab-dock.data-v-c3efacf5 {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
background: #ffffff;
border-top: 1rpx solid rgba(0, 0, 0, 0.06);
box-shadow: 0 -6rpx 20rpx rgba(0, 0, 0, 0.04);
}
.tab-dock__inner.data-v-c3efacf5 {
display: flex;
align-items: flex-end;
height: 100rpx;
}
.tab-dock__item.data-v-c3efacf5 {
flex: 1;
height: 100rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.tab-dock__item--center.data-v-c3efacf5 {
justify-content: flex-end;
padding-bottom: 6rpx;
}
.tab-dock__icon.data-v-c3efacf5 {
width: 46rpx;
height: 46rpx;
}
.tab-dock__label.data-v-c3efacf5 {
margin-top: 6rpx;
font-size: 22rpx;
line-height: 1.2;
}
.tab-dock__label--center.data-v-c3efacf5 {
margin-top: 8rpx;
}
.tab-dock__fab.data-v-c3efacf5 {
width: 96rpx;
height: 96rpx;
margin-top: -48rpx;
border-radius: 50%;
background: linear-gradient(180deg, #2d7340 0%, #204e2b 100%);
border: 6rpx solid #ffffff;
box-shadow: 0 10rpx 24rpx rgba(32, 78, 43, 0.35);
display: flex;
align-items: center;
justify-content: center;
}
.tab-dock__fab--active.data-v-c3efacf5 {
transform: scale(1.04);
box-shadow: 0 12rpx 28rpx rgba(32, 78, 43, 0.42);
}
.tab-dock__fab-text.data-v-c3efacf5 {
color: #ffffff;
font-size: 38rpx;
font-weight: 700;
line-height: 1;
}
.tab-dock__safe.data-v-c3efacf5 {
height: env(safe-area-inset-bottom);
background: #ffffff;
}
@@ -0,0 +1,89 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
const _sfc_main = /* @__PURE__ */ common_vendor.defineComponent({
__name: "index",
props: {
bottom: { default: 200 },
inline: { type: Boolean, default: false },
tone: { default: "default" }
},
setup(__props) {
const trainingItems = [
{ url: "/training/pages/dumbbell", title: "哑铃", sub: "力量塑形", icon: "dumbbell", iconClass: "menu-icon--dumbbell" },
{ url: "/training/pages/foot-pedal", title: "脚蹬器", sub: "下肢有氧", icon: "pedal", iconClass: "menu-icon--pedal" },
{ url: "/training/pages/pilates-ring", title: "瑜伽环", sub: "核心塑形", icon: "pilates", iconClass: "menu-icon--pilates" },
{ url: "/training/pages/grip-ring", title: "握力环", sub: "握力训练", icon: "grip", iconClass: "menu-icon--grip" },
{ url: "/training/pages/metronome", title: "耗糖节拍器", sub: "健走配速", icon: "metro", iconClass: "menu-icon--metro", featured: true }
];
const props = __props;
const inlineItems = common_vendor.computed(() => {
if (props.tone !== "weekly")
return trainingItems;
const metro = trainingItems.find((i) => i.icon === "metro");
const rest = trainingItems.filter((i) => i.icon !== "metro");
return metro ? [metro, ...rest] : trainingItems;
});
const visible = common_vendor.ref(true);
const expanded = common_vendor.ref(false);
const toggle = () => {
expanded.value = !expanded.value;
};
const goto = (url) => {
expanded.value = false;
common_vendor.index.navigateTo({ url });
};
return (_ctx, _cache) => {
return common_vendor.e({
a: visible.value && props.inline
}, visible.value && props.inline ? common_vendor.e({
b: props.tone === "weekly"
}, props.tone === "weekly" ? {} : {}, {
c: common_vendor.f(inlineItems.value, (item, k0, i0) => {
return common_vendor.e({
a: item.featured
}, item.featured ? {} : {}, {
b: item.icon === "dumbbell"
}, item.icon === "dumbbell" ? {} : item.icon === "pedal" ? {} : item.icon === "pilates" ? {} : item.icon === "grip" ? {} : item.icon === "metro" ? {} : {}, {
c: item.icon === "pedal",
d: item.icon === "pilates",
e: item.icon === "grip",
f: item.icon === "metro",
g: common_vendor.n(item.iconClass),
h: common_vendor.t(item.title),
i: common_vendor.t(item.sub),
j: item.url,
k: common_vendor.o(($event) => goto(item.url), item.url)
});
}),
d: props.tone === "weekly"
}, props.tone === "weekly" ? {} : {}, {
e: props.tone === "weekly" ? 1 : ""
}) : visible.value ? common_vendor.e({
g: expanded.value
}, expanded.value ? {
h: common_vendor.o(($event) => goto("/training/pages/dumbbell"), "fd"),
i: common_vendor.o(($event) => goto("/training/pages/foot-pedal"), "a5"),
j: common_vendor.o(($event) => goto("/training/pages/pilates-ring"), "3f"),
k: common_vendor.o(($event) => goto("/training/pages/grip-ring"), "23"),
l: common_vendor.o(($event) => goto("/training/pages/metronome"), "ca"),
m: common_vendor.o(() => {
}, "26")
} : {}, {
n: !expanded.value
}, !expanded.value ? {} : {}, {
o: !expanded.value
}, !expanded.value ? {} : {}, {
p: expanded.value
}, expanded.value ? {} : {}, {
q: expanded.value ? 1 : "",
r: common_vendor.o(toggle, "98"),
s: props.bottom + "rpx"
}) : {}, {
f: visible.value
});
};
}
});
const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-17534d15"]]);
wx.createComponent(Component);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/components/dev-training-entry/index.js.map
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,489 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
/* 文字基本颜色 */
/* 背景颜色 */
/* 边框颜色 */
/* 尺寸变量 */
/* 文字尺寸 */
/* 图片尺寸 */
/* Border Radius */
/* 水平间距 */
/* 垂直间距 */
/* 透明度 */
/* 文章场景相关 */
.dev-entry-wrap.data-v-17534d15 {
position: fixed;
right: 24rpx;
z-index: 999;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 18rpx;
}
/* ============================================================
* 主浮动按钮
* ============================================================ */
.fab-container.data-v-17534d15 {
position: relative;
width: 108rpx;
height: 108rpx;
}
.entry-fab.data-v-17534d15 {
position: absolute;
inset: 0;
border-radius: 50%;
background: linear-gradient(140deg, #34d399 0%, #047857 100%);
box-shadow: 0 8rpx 20rpx rgba(16, 185, 129, 0.42), 0 2rpx 6rpx rgba(15, 23, 42, 0.12), inset 0 -8rpx 16rpx rgba(0, 0, 0, 0.12), inset 0 8rpx 16rpx rgba(255, 255, 255, 0.18);
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.2s;
}
.entry-fab.data-v-17534d15:active {
transform: scale(0.92);
}
.entry-fab.expanded.data-v-17534d15 {
background: linear-gradient(140deg, #94a3b8 0%, #475569 100%);
transform: rotate(135deg);
box-shadow: 0 6rpx 16rpx rgba(15, 23, 42, 0.2), inset 0 -6rpx 14rpx rgba(0, 0, 0, 0.12), inset 0 6rpx 14rpx rgba(255, 255, 255, 0.12);
}
.entry-fab .fab-icon.data-v-17534d15 {
width: 60rpx;
height: 60rpx;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'><polyline points='2,12 7,12 9.5,7 12,17 14.5,9 16.5,12 22,12'/></svg>");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
filter: drop-shadow(0 2rpx 6rpx rgba(0, 0, 0, 0.22));
animation: fab-icon-beat-17534d15 1.6s ease-in-out infinite;
}
@keyframes fab-icon-beat-17534d15 {
0%, 60%, 100% {
transform: scale(1);
}
20% {
transform: scale(1.12);
}
40% {
transform: scale(1);
}
}
/* 关闭态 X 图标 (CSS 几何) */
.icon-close.data-v-17534d15 {
position: relative;
width: 36rpx;
height: 36rpx;
}
.icon-close .cross-bar.data-v-17534d15 {
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 5rpx;
background: #fff;
border-radius: 3rpx;
transform-origin: center;
}
.icon-close .bar-1.data-v-17534d15 {
transform: translateY(-50%) rotate(45deg);
}
.icon-close .bar-2.data-v-17534d15 {
transform: translateY(-50%) rotate(-45deg);
}
/* 呼吸光环(2 圈错相位扩散) */
.fab-pulse.data-v-17534d15 {
position: absolute;
inset: 0;
border-radius: 50%;
background: rgba(16, 185, 129, 0.25);
animation: fab-ring-17534d15 2.4s ease-out infinite;
pointer-events: none;
}
.fab-pulse-2.data-v-17534d15 {
animation-delay: 1.2s;
}
@keyframes fab-ring-17534d15 {
0% {
transform: scale(0.95);
opacity: 0.7;
}
100% {
transform: scale(1.55);
opacity: 0;
}
}
/* ============================================================
* 展开菜单
* ============================================================ */
.menu-list.data-v-17534d15 {
display: flex;
flex-direction: column;
gap: 14rpx;
animation: slide-up-17534d15 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes slide-up-17534d15 {
from {
opacity: 0;
transform: translateY(20rpx) scale(0.92);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.menu-item.data-v-17534d15 {
display: flex;
align-items: center;
gap: 16rpx;
background: rgba(255, 255, 255, 0.96);
padding: 14rpx 20rpx 14rpx 14rpx;
border-radius: 999rpx;
box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.1), 0 1rpx 2rpx rgba(15, 23, 42, 0.06);
min-width: 280rpx;
transition: transform 0.15s;
-webkit-backdrop-filter: blur(12rpx);
backdrop-filter: blur(12rpx);
}
.menu-item.data-v-17534d15:active {
transform: scale(0.97);
background: #f0fdf4;
}
.menu-icon.data-v-17534d15 {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
box-shadow: 0 2rpx 8rpx rgba(16, 185, 129, 0.32);
}
.menu-icon--train.data-v-17534d15 {
background: linear-gradient(140deg, #34d399, #047857);
}
.menu-icon--dumbbell.data-v-17534d15 {
background: linear-gradient(140deg, #93c5fd, #3b82f6);
box-shadow: 0 2rpx 8rpx rgba(59, 130, 246, 0.36);
}
.menu-icon--pedal.data-v-17534d15 {
background: linear-gradient(140deg, #fdba74, #f97316);
box-shadow: 0 2rpx 8rpx rgba(249, 115, 22, 0.36);
}
.menu-icon--pilates.data-v-17534d15 {
background: linear-gradient(140deg, #c4b5fd, #8b5cf6);
box-shadow: 0 2rpx 8rpx rgba(139, 92, 246, 0.36);
}
.menu-icon--grip.data-v-17534d15 {
background: linear-gradient(140deg, #5eead4, #14b8a6);
box-shadow: 0 2rpx 8rpx rgba(20, 184, 166, 0.36);
}
.menu-icon--metro.data-v-17534d15 {
background: linear-gradient(140deg, #fbbf24, #d97706);
box-shadow: 0 2rpx 8rpx rgba(245, 158, 11, 0.36);
gap: 4rpx;
}
.menu-icon-text.data-v-17534d15 {
font-size: 32rpx;
line-height: 1;
}
/* 哑铃小 icon */
.dumbbell-shape.data-v-17534d15 {
width: 40rpx;
height: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.dumbbell-plate.data-v-17534d15 {
width: 10rpx;
height: 18rpx;
background: #fff;
border-radius: 3rpx;
box-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.18);
}
.dumbbell-bar.data-v-17534d15 {
width: 16rpx;
height: 5rpx;
background: #fff;
border-radius: 2rpx;
margin: 0 2rpx;
box-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.18);
}
/* 脚蹬器小 icon: 左右踏板 */
.foot-pedal-shape.data-v-17534d15 {
width: 40rpx;
height: 28rpx;
display: flex;
align-items: flex-end;
justify-content: center;
gap: 6rpx;
}
.pedal-bar.data-v-17534d15 {
width: 14rpx;
background: #fff;
border-radius: 4rpx 4rpx 2rpx 2rpx;
box-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.18);
}
.pedal-bar-left.data-v-17534d15 {
height: 18rpx;
transform: rotate(-12deg);
}
.pedal-bar-right.data-v-17534d15 {
height: 22rpx;
transform: rotate(12deg);
}
/* 瑜伽环小 icon: 双弧阻力环 */
.pilates-ring-shape.data-v-17534d15 {
width: 36rpx;
height: 36rpx;
border-radius: 50%;
border: 5rpx solid #fff;
position: relative;
box-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.18), inset 0 1rpx 2rpx rgba(255, 255, 255, 0.6);
}
.pilates-ring-shape.data-v-17534d15::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 18rpx;
height: 18rpx;
transform: translate(-50%, -50%);
border-radius: 50%;
border: 3rpx solid rgba(255, 255, 255, 0.85);
}
/* 握力环小 icon: 白色描边的环(甜甜圈) */
.grip-ring-shape.data-v-17534d15 {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
border: 7rpx solid #fff;
box-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.18), inset 0 1rpx 2rpx rgba(255, 255, 255, 0.6);
}
/* 节拍器小 icon: 3 根高低柱模拟均衡器/节拍 */
.metro-bar.data-v-17534d15 {
width: 4rpx;
background: #fff;
border-radius: 2rpx;
box-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.18);
}
.metro-bar-1.data-v-17534d15 {
height: 16rpx;
}
.metro-bar-2.data-v-17534d15 {
height: 28rpx;
}
.metro-bar-3.data-v-17534d15 {
height: 22rpx;
}
.menu-info.data-v-17534d15 {
display: flex;
flex-direction: column;
gap: 2rpx;
flex: 1;
}
.menu-title.data-v-17534d15 {
font-size: 28rpx;
color: #0f172a;
font-weight: 700;
letter-spacing: 1rpx;
}
.menu-sub.data-v-17534d15 {
font-size: 20rpx;
color: #94a3b8;
letter-spacing: 0.5rpx;
}
.menu-arrow.data-v-17534d15 {
font-size: 32rpx;
color: #cbd5e1;
font-weight: 300;
line-height: 1;
margin-left: 4rpx;
}
.menu-icon--metro-inner.data-v-17534d15 {
display: flex;
align-items: flex-end;
justify-content: center;
gap: 4rpx;
width: 100%;
height: 100%;
}
/* ============================================================
* 内嵌模块(weekly 等)
* ============================================================ */
.dev-entry-inline.data-v-17534d15 {
width: 100%;
box-sizing: border-box;
}
.dev-entry-inline--weekly.data-v-17534d15 {
padding: 32rpx 28rpx 24rpx;
border-radius: 48rpx;
background: var(--surface-container-lowest, #fff);
border: 1rpx solid rgba(0, 108, 73, 0.12);
box-shadow: 0 24rpx 60rpx -20rpx rgba(0, 108, 73, 0.08), 0 8rpx 20rpx -8rpx rgba(0, 0, 0, 0.03);
}
.inline-hero.data-v-17534d15 {
display: flex;
align-items: center;
gap: 20rpx;
margin-bottom: 24rpx;
}
.inline-hero-icon.data-v-17534d15 {
width: 88rpx;
height: 88rpx;
border-radius: 24rpx;
background: rgba(0, 108, 73, 0.1);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.inline-hero-wave.data-v-17534d15 {
width: 52rpx;
height: 52rpx;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23006c49' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><polyline points='2,12 7,12 9.5,7 12,17 14.5,9 16.5,12 22,12'/></svg>");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.inline-hero-text.data-v-17534d15 {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 6rpx;
}
.inline-hero-title-row.data-v-17534d15 {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 10rpx;
}
.inline-title.data-v-17534d15 {
font-size: 32rpx;
font-weight: 700;
color: #0f172a;
line-height: 1.25;
}
.inline-pill.data-v-17534d15 {
font-size: 20rpx;
font-weight: 700;
color: #006c49;
padding: 4rpx 12rpx;
border-radius: 999rpx;
background: rgba(0, 108, 73, 0.1);
line-height: 1.2;
}
.inline-sub.data-v-17534d15 {
font-size: 26rpx;
font-weight: 500;
color: #475569;
line-height: 1.4;
}
.inline-scroll.data-v-17534d15 {
width: 100%;
white-space: nowrap;
}
.inline-scroll-track.data-v-17534d15 {
display: inline-flex;
flex-direction: row;
align-items: stretch;
gap: 16rpx;
padding: 4rpx 2rpx 8rpx;
}
.inline-card.data-v-17534d15 {
position: relative;
display: inline-flex;
flex-direction: column;
align-items: flex-start;
width: 212rpx;
min-height: 220rpx;
padding: 20rpx 18rpx 18rpx;
border-radius: 28rpx;
background: #f4f7f5;
border: 1rpx solid rgba(0, 108, 73, 0.1);
box-sizing: border-box;
flex-shrink: 0;
transition: transform 0.2s ease, background 0.2s ease;
}
.inline-card.data-v-17534d15:active {
transform: scale(0.98);
background: #e8f3ee;
}
.inline-card .menu-icon.data-v-17534d15 {
width: 72rpx;
height: 72rpx;
margin-bottom: 14rpx;
flex-shrink: 0;
}
.inline-card-badge.data-v-17534d15 {
position: absolute;
top: 12rpx;
right: 12rpx;
padding: 4rpx 10rpx;
border-radius: 8rpx;
background: #006c49;
}
.inline-card-badge text.data-v-17534d15 {
font-size: 18rpx;
font-weight: 700;
color: #fff;
line-height: 1.2;
}
.inline-card-title.data-v-17534d15 {
font-size: 28rpx;
font-weight: 700;
color: #0f172a;
line-height: 1.25;
width: 100%;
}
.inline-card-sub.data-v-17534d15 {
margin-top: 6rpx;
font-size: 22rpx;
font-weight: 500;
color: #64748b;
line-height: 1.35;
width: 100%;
}
.inline-card-go.data-v-17534d15 {
margin-top: auto;
padding-top: 12rpx;
font-size: 22rpx;
font-weight: 700;
color: #006c49;
line-height: 1.2;
}
.inline-scroll-hint.data-v-17534d15 {
display: block;
margin-top: 14rpx;
font-size: 22rpx;
font-weight: 600;
color: #94a3b8;
text-align: center;
line-height: 1.35;
}
@media (prefers-reduced-motion: reduce) {
.inline-card.data-v-17534d15:active {
transform: none;
}
}
@@ -0,0 +1,150 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
if (!Array) {
const _easycom_uni_icons2 = common_vendor.resolveComponent("uni-icons");
_easycom_uni_icons2();
}
const _easycom_uni_icons = () => "../../uni_modules/uni-icons/components/uni-icons/uni-icons.js";
if (!Math) {
_easycom_uni_icons();
}
const _sfc_main = {
__name: "Card",
setup(__props) {
const { proxy } = common_vendor.getCurrentInstance();
const cardList = common_vendor.ref([]);
const loading = common_vendor.ref(false);
common_vendor.onMounted(() => {
loadCardList();
});
common_vendor.onShow(() => {
loadCardList();
});
const loadCardList = async () => {
var _a;
loading.value = true;
try {
const token = common_vendor.index.getStorageSync("token");
if (!token) {
common_vendor.index.showToast({ title: "请先登录", icon: "none" });
return;
}
const userData = common_vendor.index.getStorageSync("userData");
const patientId = (_a = userData == null ? void 0 : userData.diagnosis) == null ? void 0 : _a.patient_id;
if (!patientId) {
return;
}
const res = await proxy.apiUrl({
url: "/api/tcm/getCardList",
method: "GET",
data: {
patient_id: patientId
}
});
if (res.code === 1) {
cardList.value = res.data || [];
} else {
common_vendor.index.showToast({ title: res.msg || "获取失败", icon: "none" });
}
} catch (err) {
common_vendor.index.__f__("error", "at pages/Card/Card.vue:139", "加载就诊卡列表失败:", err);
} finally {
loading.value = false;
}
};
const viewCardDetail = (card) => {
common_vendor.index.navigateTo({
url: `/pages/Card/edit_card?id=${card.id}`
});
};
const viewDailyRecord = (card) => {
const url = `/tongji/pages/index?diagnosis_id=${card.id}&patient_id=${card.patient_id || ""}&patient_name=${encodeURIComponent(card.patient_name || "")}&age=${card.age || ""}&gender=${card.gender || ""}`;
common_vendor.index.navigateTo({ url });
};
const createCard = () => {
const token = common_vendor.index.getStorageSync("token");
if (!token) {
common_vendor.index.showToast({ title: "请先登录", icon: "none" });
return;
}
common_vendor.index.navigateTo({
url: "/pages/Card/edit_card?add=1"
});
};
const getStatusText = (status) => {
return status === 1 ? "启用" : "禁用";
};
const getStatusClass = (status) => {
return status === 1 ? "status-confirmed" : "status-pending";
};
const diagnosisTypeMap = {
"first_visit": "初诊",
"follow_up": "复诊",
"consultation": "会诊"
};
const syndromeTypeMap = {
"qi_deficiency": "气虚",
"blood_deficiency": "血虚",
"yin_deficiency": "阴虚",
"yang_deficiency": "阳虚",
"qi_stagnation": "气滞",
"blood_stasis": "血瘀",
"phlegm_dampness": "痰湿",
"damp_heat": "湿热",
"cold_dampness": "寒湿",
"wind_cold": "风寒",
"wind_heat": "风热"
};
const getDiagnosisTypeName = (type) => {
return diagnosisTypeMap[type] || type || "-";
};
const getSyndromeTypeName = (type) => {
return syndromeTypeMap[type] || type || "-";
};
const formatDate = (timestamp) => {
if (!timestamp)
return "-";
const date = new Date(timestamp * 1e3);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
return (_ctx, _cache) => {
return common_vendor.e({
a: common_vendor.f(cardList.value, (card, k0, i0) => {
return {
a: common_vendor.t(card.id),
b: common_vendor.t(card.status_desc || getStatusText(card.status)),
c: common_vendor.n(getStatusClass(card.status)),
d: common_vendor.t(card.patient_name || "-"),
e: common_vendor.t(card.gender_desc || "-"),
f: common_vendor.t(card.age),
g: common_vendor.t(getDiagnosisTypeName(card.diagnosis_type)),
h: common_vendor.t(getSyndromeTypeName(card.syndrome_type)),
i: common_vendor.t(card.diagnosis_date_text || formatDate(card.diagnosis_date)),
j: common_vendor.t(card.create_time || "-"),
k: common_vendor.o(($event) => viewDailyRecord(card), card.id),
l: common_vendor.o(($event) => viewCardDetail(card), card.id),
m: card.id,
n: common_vendor.o(($event) => viewCardDetail(card), card.id)
};
}),
b: !loading.value && cardList.value.length === 0
}, !loading.value && cardList.value.length === 0 ? {
c: common_vendor.p({
type: "wallet",
size: "80",
color: "#1890ff"
}),
d: common_vendor.o(createCard, "df")
} : {}, {
e: loading.value
}, loading.value ? {} : {});
};
}
};
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-f80331d8"]]);
_sfc_main.__runtimeHooks = 2;
wx.createPage(MiniProgramPage);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/Card/Card.js.map
@@ -0,0 +1,6 @@
{
"navigationBarTitleText": "就诊卡",
"usingComponents": {
"uni-icons": "../../uni_modules/uni-icons/components/uni-icons/uni-icons"
}
}
@@ -0,0 +1 @@
<view class="card-container data-v-f80331d8"><view class="card-list data-v-f80331d8"><view wx:for="{{a}}" wx:for-item="card" wx:key="m" class="card-item data-v-f80331d8" bindtap="{{card.n}}"><view class="card-header data-v-f80331d8"><view class="card-id data-v-f80331d8"><text class="label data-v-f80331d8">诊单编号:</text><text class="value data-v-f80331d8">{{card.a}}</text></view><view class="{{['data-v-f80331d8', 'status-badge', card.c]}}">{{card.b}}</view></view><view class="card-info data-v-f80331d8"><view class="info-row data-v-f80331d8"><text class="info-label data-v-f80331d8">患者姓名:</text><text class="info-value data-v-f80331d8">{{card.d}}</text></view><view class="info-row data-v-f80331d8"><text class="info-label data-v-f80331d8">性别年龄:</text><text class="info-value data-v-f80331d8">{{card.e}} / {{card.f}}岁</text></view><view class="info-row data-v-f80331d8"><text class="info-label data-v-f80331d8">诊断类型:</text><text class="info-value data-v-f80331d8">{{card.g}}</text></view><view class="info-row data-v-f80331d8"><text class="info-label data-v-f80331d8">证型:</text><text class="info-value data-v-f80331d8">{{card.h}}</text></view></view><view class="card-footer data-v-f80331d8"><view class="time-info data-v-f80331d8"><text class="time-label data-v-f80331d8">诊断日期:</text><text class="time-value data-v-f80331d8">{{card.i}}</text></view><view class="time-info data-v-f80331d8"><text class="time-label data-v-f80331d8">创建时间:</text><text class="time-value data-v-f80331d8">{{card.j}}</text></view></view><view class="card-actions data-v-f80331d8"><view class="card-action-btn primary data-v-f80331d8" catchtap="{{card.k}}"><text class="card-action-icon data-v-f80331d8">📈</text><text class="card-action-text data-v-f80331d8">日常记录</text></view><view class="card-action-btn data-v-f80331d8" catchtap="{{card.l}}"><text class="card-action-icon data-v-f80331d8">📝</text><text class="card-action-text data-v-f80331d8">查看 / 编辑</text></view></view></view><view wx:if="{{b}}" class="empty-state data-v-f80331d8"><uni-icons wx:if="{{c}}" class="data-v-f80331d8" u-i="f80331d8-0" bind:__l="__l" u-p="{{c}}"></uni-icons><text class="empty-text data-v-f80331d8">暂无就诊卡</text><view class="add-card-btn data-v-f80331d8" bindtap="{{d}}">新建就诊卡</view></view><view wx:if="{{e}}" class="loading-state data-v-f80331d8"><text class="loading-text data-v-f80331d8">加载中...</text></view></view></view>
@@ -0,0 +1,189 @@
/* 适老化设计:50-80岁 - 大字号、高对比、大触控区 */
.card-container.data-v-f80331d8 {
min-height: 100vh;
background: linear-gradient(180deg, #e8f0fa 0%, #ffffff 100%);
padding-bottom: 56rpx;
}
.header.data-v-f80331d8 {
background: #ffffff;
padding: 48rpx 40rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
}
.header .header-title.data-v-f80331d8 {
font-size: 48rpx;
font-weight: bold;
color: #1890ff;
}
.card-list.data-v-f80331d8 {
padding: 40rpx;
}
.card-item.data-v-f80331d8 {
background: #ffffff;
border-radius: 28rpx;
padding: 44rpx;
margin-bottom: 36rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
transition: all 0.3s;
min-height: 200rpx;
}
.card-item.data-v-f80331d8:active {
transform: scale(0.98);
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.card-header.data-v-f80331d8 {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 32rpx;
padding-bottom: 28rpx;
border-bottom: 4rpx solid #e8eaed;
}
.card-id .label.data-v-f80331d8 {
font-size: 34rpx;
color: #555;
}
.card-id .value.data-v-f80331d8 {
font-size: 40rpx;
font-weight: bold;
color: #1a1a1a;
}
.status-badge.data-v-f80331d8 {
padding: 12rpx 28rpx;
border-radius: 28rpx;
font-size: 32rpx;
font-weight: 500;
}
.status-badge.status-confirmed.data-v-f80331d8 {
background: #f6ffed;
color: #204e2b;
border: 4rpx solid #204e2b;
}
.status-badge.status-pending.data-v-f80331d8 {
background: #fff7e6;
color: #fa8c16;
border: 4rpx solid #ffd591;
}
.card-info.data-v-f80331d8 {
margin-bottom: 28rpx;
}
.info-row.data-v-f80331d8 {
display: flex;
align-items: center;
margin-bottom: 24rpx;
}
.info-row .info-label.data-v-f80331d8 {
font-size: 34rpx;
color: #555;
min-width: 200rpx;
}
.info-row .info-value.data-v-f80331d8 {
font-size: 36rpx;
color: #1a1a1a;
font-weight: 500;
}
.card-footer.data-v-f80331d8 {
padding-top: 28rpx;
border-top: 4rpx solid #e8eaed;
}
.card-actions.data-v-f80331d8 {
display: flex;
gap: 20rpx;
margin-top: 28rpx;
padding-top: 28rpx;
border-top: 4rpx solid #e8eaed;
}
.card-action-btn.data-v-f80331d8 {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
padding: 24rpx 12rpx;
background: #f6faff;
border-radius: 20rpx;
border: 2rpx solid #d6e6fb;
min-height: 80rpx;
transition: all 0.2s;
}
.card-action-btn.data-v-f80331d8:active {
transform: scale(0.98);
opacity: 0.85;
}
.card-action-btn.primary.data-v-f80331d8 {
background: linear-gradient(135deg, #1890ff, #0ea5a4);
border-color: transparent;
}
.card-action-btn.primary .card-action-icon.data-v-f80331d8,
.card-action-btn.primary .card-action-text.data-v-f80331d8 {
color: #ffffff;
}
.card-action-icon.data-v-f80331d8 {
font-size: 36rpx;
color: #1890ff;
}
.card-action-text.data-v-f80331d8 {
font-size: 30rpx;
color: #1890ff;
font-weight: 500;
}
.time-info.data-v-f80331d8 {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.time-info .time-label.data-v-f80331d8 {
font-size: 30rpx;
color: #555;
min-width: 180rpx;
}
.time-info .time-value.data-v-f80331d8 {
font-size: 32rpx;
color: #555;
}
.view-count.data-v-f80331d8 {
display: flex;
align-items: center;
margin-top: 12rpx;
}
.view-count .count-icon.data-v-f80331d8 {
font-size: 34rpx;
margin-right: 12rpx;
}
.view-count .count-text.data-v-f80331d8 {
font-size: 30rpx;
color: #555;
}
.empty-state.data-v-f80331d8 {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 160rpx 0;
}
.empty-state .empty-icon.data-v-f80331d8 {
font-size: 140rpx;
margin-bottom: 40rpx;
}
.empty-state .empty-text.data-v-f80331d8 {
font-size: 40rpx;
color: #555;
margin-bottom: 56rpx;
}
.add-card-btn.data-v-f80331d8 {
padding: 36rpx 88rpx;
background: #1890ff;
color: #ffffff;
font-size: 40rpx;
font-weight: 500;
border-radius: 56rpx;
min-height: 100rpx;
}
.loading-state.data-v-f80331d8 {
display: flex;
justify-content: center;
padding: 120rpx 0;
}
.loading-state .loading-text.data-v-f80331d8 {
font-size: 36rpx;
color: #555;
}
@@ -0,0 +1,46 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
const _sfc_main = {
__name: "contact",
setup(__props) {
const { proxy } = common_vendor.getCurrentInstance();
const data = common_vendor.ref({});
const loading = common_vendor.ref(false);
const type = common_vendor.ref("");
common_vendor.onMounted(() => {
loadCardList();
});
const loadCardList = async () => {
loading.value = true;
try {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
type.value = currentPage.options.type;
const res = await proxy.apiUrl({
url: "/api/index/policy",
method: "GET",
data: {
type: type.value
}
});
data.value = res.data;
common_vendor.index.setNavigationBarTitle({
title: res.data.title
});
} catch (err) {
common_vendor.index.__f__("error", "at pages/Card/contact.vue:54", "加载就诊卡列表失败:", err);
} finally {
loading.value = false;
}
};
return (_ctx, _cache) => {
return {
a: common_vendor.t(data.value.title),
b: data.value.content
};
};
}
};
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-9c9e5aa4"]]);
wx.createPage(MiniProgramPage);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/Card/contact.js.map
@@ -0,0 +1,4 @@
{
"navigationBarTitleText": "服务",
"usingComponents": {}
}
@@ -0,0 +1 @@
<view class="card-container data-v-9c9e5aa4"><view class="header data-v-9c9e5aa4"><text class="header-title data-v-9c9e5aa4">{{a}}</text></view><view class="card-list data-v-9c9e5aa4"><rich-text class="data-v-9c9e5aa4" nodes="{{b}}"/></view></view>
@@ -0,0 +1,136 @@
.card-container.data-v-9c9e5aa4 {
min-height: 100vh;
background: linear-gradient(180deg, #f0f7ff 0%, #ffffff 100%);
padding-bottom: 40rpx;
}
.header.data-v-9c9e5aa4 {
background: #ffffff;
padding: 40rpx 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.header .header-title.data-v-9c9e5aa4 {
font-size: 40rpx;
font-weight: bold;
color: #1890ff;
}
.card-list.data-v-9c9e5aa4 {
padding: 30rpx;
}
.card-item.data-v-9c9e5aa4 {
background: #ffffff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
transition: all 0.3s;
}
.card-item.data-v-9c9e5aa4:active {
transform: scale(0.98);
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.card-header.data-v-9c9e5aa4 {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
padding-bottom: 20rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.card-id .label.data-v-9c9e5aa4 {
font-size: 28rpx;
color: #666666;
}
.card-id .value.data-v-9c9e5aa4 {
font-size: 32rpx;
font-weight: bold;
color: #333333;
}
.status-badge.data-v-9c9e5aa4 {
padding: 8rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
font-weight: 500;
}
.status-badge.status-confirmed.data-v-9c9e5aa4 {
background: #f6ffed;
color: #52c41a;
border: 2rpx solid #b7eb8f;
}
.status-badge.status-pending.data-v-9c9e5aa4 {
background: #fff7e6;
color: #fa8c16;
border: 2rpx solid #ffd591;
}
.card-info.data-v-9c9e5aa4 {
margin-bottom: 20rpx;
}
.info-row.data-v-9c9e5aa4 {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.info-row .info-label.data-v-9c9e5aa4 {
font-size: 28rpx;
color: #666666;
min-width: 160rpx;
}
.info-row .info-value.data-v-9c9e5aa4 {
font-size: 30rpx;
color: #333333;
font-weight: 500;
}
.card-footer.data-v-9c9e5aa4 {
padding-top: 20rpx;
border-top: 2rpx solid #f0f0f0;
}
.time-info.data-v-9c9e5aa4 {
display: flex;
align-items: center;
margin-bottom: 12rpx;
}
.time-info .time-label.data-v-9c9e5aa4 {
font-size: 24rpx;
color: #999999;
min-width: 140rpx;
}
.time-info .time-value.data-v-9c9e5aa4 {
font-size: 26rpx;
color: #666666;
}
.view-count.data-v-9c9e5aa4 {
display: flex;
align-items: center;
margin-top: 8rpx;
}
.view-count .count-icon.data-v-9c9e5aa4 {
font-size: 28rpx;
margin-right: 8rpx;
}
.view-count .count-text.data-v-9c9e5aa4 {
font-size: 24rpx;
color: #999999;
}
.empty-state.data-v-9c9e5aa4 {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
}
.empty-state .empty-icon.data-v-9c9e5aa4 {
font-size: 120rpx;
margin-bottom: 30rpx;
}
.empty-state .empty-text.data-v-9c9e5aa4 {
font-size: 32rpx;
color: #999999;
}
.loading-state.data-v-9c9e5aa4 {
display: flex;
justify-content: center;
padding: 80rpx 0;
}
.loading-state .loading-text.data-v-9c9e5aa4 {
font-size: 28rpx;
color: #999999;
}
@@ -0,0 +1,857 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
if (!Array) {
const _easycom_uni_icons2 = common_vendor.resolveComponent("uni-icons");
_easycom_uni_icons2();
}
const _easycom_uni_icons = () => "../../uni_modules/uni-icons/components/uni-icons/uni-icons.js";
if (!Math) {
_easycom_uni_icons();
}
const _sfc_main = {
__name: "edit_card",
setup(__props) {
const { proxy } = common_vendor.getCurrentInstance();
const loading = common_vendor.ref(false);
const submitting = common_vendor.ref(false);
const agreedToTerms = common_vendor.ref(true);
const showDatePicker = common_vendor.ref(false);
const diagnosisId = common_vendor.ref(null);
const patientId = common_vendor.ref(null);
const isAddMode = common_vendor.ref(false);
const returnUrl = common_vendor.ref("");
const formData = common_vendor.ref({
patient_name: "",
id_card: "",
gender: 1,
age: "",
// 生命体征
height: "",
weight: "",
systolic_pressure: "",
diastolic_pressure: "",
fasting_blood_sugar: "",
// 主诉
diabetes_discovery_year: "",
local_hospital_diagnosis: [],
local_hospital_name: "",
local_hospital_visit_date: "",
// 诊断信息
diagnosis_date: "",
diagnosis_type: "",
syndrome_type: "",
diabetes_type: "",
// 现病史
appetite: "",
water_intake: "",
diet_condition: [],
weight_change: "",
body_feeling: [],
sleep_condition: [],
eye_condition: [],
head_feeling: [],
sweat_condition: [],
skin_condition: [],
urine_condition: [],
stool_condition: [],
kidney_condition: [],
fatty_liver_degree: "",
// 既往史
past_history: [],
trauma_history: 0,
surgery_history: 0,
allergy_history: 0,
family_history: 0,
pregnancy_history: 0,
// 临床信息
symptoms: "",
tongue_coating: "",
tongue_images: [],
report_files: [],
pulse: "",
treatment_principle: "",
prescription: "",
doctor_advice: "",
remark: ""
});
const diagnosisTypeOptions = common_vendor.ref([]);
const syndromeTypeOptions = common_vendor.ref([]);
const diabetesTypeOptions = common_vendor.ref([]);
const pastHistoryOptions = common_vendor.ref([]);
const appetiteOptions = common_vendor.ref([]);
const waterIntakeOptions = common_vendor.ref([]);
const dietConditionOptions = common_vendor.ref([]);
const weightChangeOptions = common_vendor.ref([]);
const bodyFeelingOptions = common_vendor.ref([]);
const sleepConditionOptions = common_vendor.ref([]);
const eyeConditionOptions = common_vendor.ref([]);
const headFeelingOptions = common_vendor.ref([]);
const sweatConditionOptions = common_vendor.ref([]);
const skinConditionOptions = common_vendor.ref([]);
const urineConditionOptions = common_vendor.ref([]);
const stoolConditionOptions = common_vendor.ref([]);
const kidneyConditionOptions = common_vendor.ref([]);
const fattyLiverDegreeOptions = common_vendor.ref([]);
const datePickerValue = common_vendor.ref([0, 0, 0]);
const yearRange = common_vendor.ref([]);
const monthRange = common_vendor.ref(Array.from({ length: 12 }, (_, i) => i + 1));
const dayRange = common_vendor.ref(Array.from({ length: 31 }, (_, i) => i + 1));
common_vendor.onMounted(() => {
loadDictOptions();
loadCardDetail();
initDatePicker();
});
const loadDictOptions = async () => {
try {
const dictTypes = [
"diagnosis_type",
"syndrome_type",
"diabetes_type",
"past_history",
"appetite",
"water_intake",
"diet_condition",
"weight_change",
"body_feeling",
"sleep_condition",
"eye_condition",
"head_feeling",
"sweat_condition",
"skin_condition",
"urine_condition",
"stool_condition",
"kidney_condition",
"fatty_liver_degree"
];
const results = await Promise.all(
dictTypes.map(
(type) => proxy.apiUrl({
url: "/api/tcm/getDict",
method: "GET",
data: { type }
})
)
);
results.forEach((res, index) => {
const type = dictTypes[index];
if (res.code === 1 && res.data) {
const data = res.data[type] || [];
const options = Array.isArray(data) ? data : Object.entries(data).map(([key, value]) => ({
value: key,
name: value
}));
switch (type) {
case "diagnosis_type":
diagnosisTypeOptions.value = options;
break;
case "syndrome_type":
syndromeTypeOptions.value = options;
break;
case "diabetes_type":
diabetesTypeOptions.value = options;
break;
case "past_history":
pastHistoryOptions.value = options;
break;
case "appetite":
appetiteOptions.value = options;
break;
case "water_intake":
waterIntakeOptions.value = options;
break;
case "diet_condition":
dietConditionOptions.value = options;
break;
case "weight_change":
weightChangeOptions.value = options;
break;
case "body_feeling":
bodyFeelingOptions.value = options;
break;
case "sleep_condition":
sleepConditionOptions.value = options;
break;
case "eye_condition":
eyeConditionOptions.value = options;
break;
case "head_feeling":
headFeelingOptions.value = options;
break;
case "sweat_condition":
sweatConditionOptions.value = options;
break;
case "skin_condition":
skinConditionOptions.value = options;
break;
case "urine_condition":
urineConditionOptions.value = options;
break;
case "stool_condition":
stoolConditionOptions.value = options;
break;
case "kidney_condition":
kidneyConditionOptions.value = options;
break;
case "fatty_liver_degree":
fattyLiverDegreeOptions.value = options;
break;
}
}
});
} catch (err) {
common_vendor.index.__f__("error", "at pages/Card/edit_card.vue:979", "加载字典数据失败:", err);
}
};
const initDatePicker = () => {
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
yearRange.value = Array.from({ length: 100 }, (_, i) => currentYear - 50 + i);
};
const initDatePickerValue = (dateStr) => {
if (!dateStr)
return;
try {
const parts = dateStr.split("-");
if (parts.length !== 3)
return;
const year = parseInt(parts[0]);
const month = parseInt(parts[1]);
const day = parseInt(parts[2]);
const yearIndex = yearRange.value.indexOf(year);
if (yearIndex === -1)
return;
const monthIndex = month - 1;
const dayIndex = day - 1;
datePickerValue.value = [yearIndex, monthIndex, dayIndex];
} catch (err) {
common_vendor.index.__f__("error", "at pages/Card/edit_card.vue:1014", "初始化日期选择器失败:", err);
}
};
const loadCardDetail = async () => {
var _a;
loading.value = true;
try {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const options = currentPage.options || {};
diagnosisId.value = options.id;
isAddMode.value = options.add === "1" || options.add === 1;
if (options.returnUrl) {
try {
returnUrl.value = decodeURIComponent(String(options.returnUrl));
} catch (e) {
returnUrl.value = String(options.returnUrl);
}
}
const userData = common_vendor.index.getStorageSync("userData");
patientId.value = (_a = userData == null ? void 0 : userData.diagnosis) == null ? void 0 : _a.patient_id;
if (isAddMode.value) {
const token = common_vendor.index.getStorageSync("token");
if (!token) {
common_vendor.index.showToast({ title: "请先登录", icon: "none" });
setTimeout(() => common_vendor.index.redirectTo({ url: "/tongji/pages/weekly" }), 1500);
return;
}
loading.value = false;
return;
}
if (!patientId.value) {
common_vendor.index.showToast({ title: "请先登录", icon: "none" });
setTimeout(() => common_vendor.index.redirectTo({ url: "/tongji/pages/weekly" }), 1500);
return;
}
if (!diagnosisId.value) {
common_vendor.index.showToast({ title: "诊单ID不存在", icon: "none" });
setTimeout(() => common_vendor.index.navigateBack(), 1500);
return;
}
const res = await proxy.apiUrl({
url: "/api/tcm/diagnosisDetail",
method: "GET",
data: {
id: diagnosisId.value,
user_id: patientId.value
}
});
if (res.code === 1) {
const diagnosis = res.data;
const arrayFields = ["diet_condition", "body_feeling", "sleep_condition", "eye_condition", "head_feeling", "sweat_condition", "skin_condition", "urine_condition", "stool_condition", "kidney_condition", "past_history", "local_hospital_diagnosis", "report_files", "tongue_images"];
arrayFields.forEach((field) => {
if (typeof diagnosis[field] === "string" && diagnosis[field]) {
diagnosis[field] = diagnosis[field].split(",");
} else if (!diagnosis[field]) {
diagnosis[field] = [];
}
});
formData.value = diagnosis;
const ddy = diagnosis.diabetes_discovery_year;
formData.value.diabetes_discovery_year = ddy == null || ddy === "" ? "" : String(ddy);
if (!diagnosis.local_hospital_visit_date && diagnosis.diagnosis_date) {
formData.value.local_hospital_visit_date = diagnosis.diagnosis_date;
}
if (formData.value.local_hospital_visit_date) {
initDatePickerValue(formData.value.local_hospital_visit_date);
}
} else {
common_vendor.index.showToast({ title: res.msg || "获取失败", icon: "none" });
setTimeout(() => common_vendor.index.navigateBack(), 1500);
}
} catch (err) {
common_vendor.index.__f__("error", "at pages/Card/edit_card.vue:1109", "加载诊单详情失败:", err);
common_vendor.index.showToast({ title: "加载失败", icon: "none" });
setTimeout(() => common_vendor.index.navigateBack(), 1500);
} finally {
loading.value = false;
}
};
const toggleMultiCheckbox = (field, value) => {
const index = formData.value[field].indexOf(value);
if (index > -1) {
formData.value[field].splice(index, 1);
} else {
formData.value[field].push(value);
}
};
const calculateAgeFromIdCard = (idCard) => {
if (!idCard || idCard.length !== 18) {
return "";
}
const year = parseInt(idCard.substring(6, 10));
const month = parseInt(idCard.substring(10, 12));
const day = parseInt(idCard.substring(12, 14));
const today = /* @__PURE__ */ new Date();
const currentYear = today.getFullYear();
const currentMonth = today.getMonth() + 1;
const currentDay = today.getDate();
let age = currentYear - year;
if (currentMonth < month || currentMonth === month && currentDay < day) {
age--;
}
return age;
};
const handleIdCardChange = () => {
if (formData.value.id_card && formData.value.id_card.length === 18) {
const age = calculateAgeFromIdCard(formData.value.id_card);
if (age) {
formData.value.age = age;
}
}
};
const onDateChange = (e) => {
datePickerValue.value = e.detail.value;
};
const confirmDate = () => {
const year = yearRange.value[datePickerValue.value[0]];
const month = String(monthRange.value[datePickerValue.value[1]]).padStart(2, "0");
const day = String(dayRange.value[datePickerValue.value[2]]).padStart(2, "0");
formData.value.local_hospital_visit_date = `${year}-${month}-${day}`;
showDatePicker.value = false;
};
const openAgreement = (type) => {
common_vendor.index.navigateTo({
url: "/pages/Card/contact?type=" + type
});
};
const submitForm = async () => {
var _a;
if (!agreedToTerms.value) {
common_vendor.index.showToast({ title: "请先阅读并同意相关协议", icon: "none" });
return;
}
if (!formData.value.patient_name) {
common_vendor.index.showToast({ title: "患者姓名不能为空", icon: "none" });
return;
}
if (formData.value.gender === null || formData.value.gender === void 0) {
common_vendor.index.showToast({ title: "请选择性别", icon: "none" });
return;
}
if (!formData.value.weight) {
common_vendor.index.showToast({ title: "体重不能为空", icon: "none" });
return;
}
if (!formData.value.height) {
common_vendor.index.showToast({ title: "身高不能为空", icon: "none" });
return;
}
if (!formData.value.fasting_blood_sugar) {
common_vendor.index.showToast({ title: "空腹血糖不能为空", icon: "none" });
return;
}
if (!String(formData.value.diabetes_discovery_year ?? "").trim()) {
common_vendor.index.showToast({ title: "发现糖尿病患病史不能为空", icon: "none" });
return;
}
if (String(formData.value.diabetes_discovery_year).trim().length > 50) {
common_vendor.index.showToast({ title: "发现糖尿病患病史最多50个字", icon: "none" });
return;
}
if (!formData.value.local_hospital_diagnosis || formData.value.local_hospital_diagnosis.length === 0) {
common_vendor.index.showToast({ title: "当地医院诊断结果不能为空", icon: "none" });
return;
}
if (!formData.value.local_hospital_name) {
common_vendor.index.showToast({ title: "当地就诊医院名称不能为空", icon: "none" });
return;
}
if (!formData.value.local_hospital_visit_date) {
common_vendor.index.showToast({ title: "当地医院就诊时间不能为空", icon: "none" });
return;
}
submitting.value = true;
try {
const submitData = { ...formData.value };
submitData.diabetes_discovery_year = String(submitData.diabetes_discovery_year ?? "").trim();
const arrayFields = ["diet_condition", "body_feeling", "sleep_condition", "eye_condition", "head_feeling", "sweat_condition", "skin_condition", "urine_condition", "stool_condition", "kidney_condition", "past_history", "local_hospital_diagnosis", "report_files", "tongue_images"];
arrayFields.forEach((field) => {
if (Array.isArray(submitData[field])) {
submitData[field] = submitData[field].join(",");
}
});
if (submitData.local_hospital_visit_date) {
submitData.diagnosis_date = submitData.local_hospital_visit_date;
}
const apiUrl = isAddMode.value ? "/api/tcm/addCard" : "/api/tcm/updateCard";
const apiData = isAddMode.value ? { ...submitData, ...patientId.value ? { patient_id: patientId.value } : {} } : { id: diagnosisId.value, patient_id: patientId.value, ...submitData };
const res = await proxy.apiUrl({
url: apiUrl,
method: "POST",
data: apiData
});
if (res.code === 1) {
if (isAddMode.value && ((_a = res.data) == null ? void 0 : _a.patient_id)) {
const userData = common_vendor.index.getStorageSync("userData") || {};
if (!userData.diagnosis)
userData.diagnosis = {};
userData.diagnosis.patient_id = res.data.patient_id;
common_vendor.index.setStorageSync("userData", userData);
}
common_vendor.index.showToast({ title: isAddMode.value ? "创建成功" : "保存成功", icon: "success" });
setTimeout(() => {
const back = returnUrl.value;
if (back && back.startsWith("/")) {
common_vendor.index.redirectTo({ url: back });
} else {
common_vendor.index.navigateBack();
}
}, 1500);
} else {
common_vendor.index.showToast({ title: res.msg || "保存失败", icon: "none" });
}
} catch (err) {
common_vendor.index.__f__("error", "at pages/Card/edit_card.vue:1324", "保存失败:", err);
common_vendor.index.showToast({ title: "保存失败", icon: "none" });
} finally {
submitting.value = false;
}
};
const uploadTonguePhoto = () => {
var _a;
const maxCount = 9 - (((_a = formData.value.tongue_images) == null ? void 0 : _a.length) || 0);
common_vendor.index.chooseImage({
count: maxCount,
sizeType: ["compressed"],
sourceType: ["album", "camera"],
success: async (res) => {
const tempFilePaths = res.tempFilePaths;
try {
common_vendor.index.showLoading({ title: "上传中..." });
const token = common_vendor.index.getStorageSync("token");
const $url = proxy.$url;
if (!formData.value.tongue_images) {
formData.value.tongue_images = [];
}
for (let i = 0; i < tempFilePaths.length; i++) {
const uploadRes = await new Promise((resolve, reject) => {
common_vendor.index.uploadFile({
url: $url + "/api/upload/image",
filePath: tempFilePaths[i],
name: "file",
header: {
token,
"content-type": "multipart/form-data"
},
success: (res2) => {
const data = JSON.parse(res2.data);
resolve(data);
},
fail: (err) => {
reject(err);
}
});
});
if (uploadRes.code === 1) {
formData.value.tongue_images.push(uploadRes.data.uri || uploadRes.data);
common_vendor.index.__f__("log", "at pages/Card/edit_card.vue:1377", "-", formData);
}
}
common_vendor.index.hideLoading();
common_vendor.index.showToast({ title: "上传成功", icon: "success" });
} catch (err) {
common_vendor.index.hideLoading();
common_vendor.index.__f__("error", "at pages/Card/edit_card.vue:1385", "上传照片失败:", err);
common_vendor.index.showToast({ title: "上传失败", icon: "none" });
}
},
fail: (err) => {
common_vendor.index.__f__("error", "at pages/Card/edit_card.vue:1390", "选择图片失败:", err);
common_vendor.index.showToast({ title: "选择图片失败", icon: "none" });
}
});
};
const deleteTonguePhoto = (index) => {
common_vendor.index.showModal({
title: "提示",
content: "确定要删除这张舌苔照片吗?",
success: (res) => {
if (res.confirm) {
formData.value.tongue_images.splice(index, 1);
common_vendor.index.showToast({ title: "已删除", icon: "success" });
}
}
});
};
const uploadExaminationReport = () => {
var _a;
const maxCount = 9 - (((_a = formData.value.report_files) == null ? void 0 : _a.length) || 0);
common_vendor.index.chooseImage({
count: maxCount,
sizeType: ["compressed"],
sourceType: ["album", "camera"],
success: async (res) => {
const tempFilePaths = res.tempFilePaths;
try {
common_vendor.index.showLoading({ title: "上传中..." });
const token = common_vendor.index.getStorageSync("token");
const $url = proxy.$url;
if (!formData.value.report_files) {
formData.value.report_files = [];
}
for (let i = 0; i < tempFilePaths.length; i++) {
const uploadRes = await new Promise((resolve, reject) => {
common_vendor.index.uploadFile({
url: $url + "/api/upload/image",
filePath: tempFilePaths[i],
name: "file",
header: {
token,
"content-type": "multipart/form-data"
},
success: (res2) => {
const data = JSON.parse(res2.data);
resolve(data);
},
fail: (err) => {
reject(err);
}
});
});
if (uploadRes.code === 1) {
formData.value.report_files.push(uploadRes.data.uri || uploadRes.data);
}
}
common_vendor.index.hideLoading();
common_vendor.index.showToast({ title: "上传成功", icon: "success" });
} catch (err) {
common_vendor.index.hideLoading();
common_vendor.index.__f__("error", "at pages/Card/edit_card.vue:1471", "上传检查报告失败:", err);
common_vendor.index.showToast({ title: "上传失败", icon: "none" });
}
},
fail: (err) => {
common_vendor.index.__f__("error", "at pages/Card/edit_card.vue:1476", "选择图片失败:", err);
common_vendor.index.showToast({ title: "选择图片失败", icon: "none" });
}
});
};
const deleteExaminationReport = (index) => {
common_vendor.index.showModal({
title: "提示",
content: "确定要删除这张图片吗?",
success: (res) => {
if (res.confirm) {
formData.value.report_files.splice(index, 1);
common_vendor.index.showToast({ title: "已删除", icon: "success" });
}
}
});
};
const previewImages = (urls, current) => {
common_vendor.index.previewImage({
urls,
current
});
};
return (_ctx, _cache) => {
return common_vendor.e({
a: loading.value
}, loading.value ? {} : common_vendor.e({
b: formData.value.patient_name,
c: common_vendor.o(($event) => formData.value.patient_name = $event.detail.value, "d0"),
d: common_vendor.t(formData.value.gender === 1 ? "✓" : ""),
e: formData.value.gender === 1 ? 1 : "",
f: common_vendor.o(($event) => formData.value.gender = 1, "38"),
g: common_vendor.t(formData.value.gender === 0 ? "✓" : ""),
h: formData.value.gender === 0 ? 1 : "",
i: common_vendor.o(($event) => formData.value.gender = 0, "0f"),
j: common_vendor.o(handleIdCardChange, "47"),
k: formData.value.id_card,
l: common_vendor.o(($event) => formData.value.id_card = $event.detail.value, "f0"),
m: formData.value.age,
n: common_vendor.o(($event) => formData.value.age = $event.detail.value, "ef"),
o: formData.value.weight,
p: common_vendor.o(common_vendor.m(($event) => formData.value.weight = $event.detail.value, {
number: true
}), "f1"),
q: formData.value.height,
r: common_vendor.o(common_vendor.m(($event) => formData.value.height = $event.detail.value, {
number: true
}), "7f"),
s: formData.value.systolic_pressure,
t: common_vendor.o(common_vendor.m(($event) => formData.value.systolic_pressure = $event.detail.value, {
number: true
}), "52"),
v: formData.value.diastolic_pressure,
w: common_vendor.o(common_vendor.m(($event) => formData.value.diastolic_pressure = $event.detail.value, {
number: true
}), "7e"),
x: formData.value.fasting_blood_sugar,
y: common_vendor.o(common_vendor.m(($event) => formData.value.fasting_blood_sugar = $event.detail.value, {
number: true
}), "75"),
z: formData.value.diabetes_discovery_year,
A: common_vendor.o(($event) => formData.value.diabetes_discovery_year = $event.detail.value, "f7"),
B: formData.value.local_hospital_diagnosis.includes("糖尿病") ? 1 : "",
C: common_vendor.o(($event) => toggleMultiCheckbox("local_hospital_diagnosis", "糖尿病"), "e0"),
D: formData.value.local_hospital_diagnosis.includes("消渴病") ? 1 : "",
E: common_vendor.o(($event) => toggleMultiCheckbox("local_hospital_diagnosis", "消渴病"), "79"),
F: formData.value.local_hospital_diagnosis.includes("糖尿病前期") ? 1 : "",
G: common_vendor.o(($event) => toggleMultiCheckbox("local_hospital_diagnosis", "糖尿病前期"), "f6"),
H: formData.value.local_hospital_name,
I: common_vendor.o(($event) => formData.value.local_hospital_name = $event.detail.value, "20"),
J: common_vendor.t(formData.value.local_hospital_visit_date || "请选择确诊时间"),
K: common_vendor.o(($event) => showDatePicker.value = true, "1b"),
L: common_vendor.f(appetiteOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.appetite === item.value ? 1 : "",
d: common_vendor.o(($event) => formData.value.appetite = item.value, item.value)
};
}),
M: common_vendor.f(dietConditionOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.diet_condition.includes(item.value) ? 1 : "",
d: common_vendor.o(($event) => toggleMultiCheckbox("diet_condition", item.value), item.value)
};
}),
N: common_vendor.f(waterIntakeOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.water_intake === item.value ? 1 : "",
d: common_vendor.o(($event) => formData.value.water_intake = item.value, item.value)
};
}),
O: common_vendor.f(weightChangeOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.weight_change === item.value ? 1 : "",
d: common_vendor.o(($event) => formData.value.weight_change = item.value, item.value)
};
}),
P: common_vendor.f(fattyLiverDegreeOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.fatty_liver_degree === item.value ? 1 : "",
d: common_vendor.o(($event) => formData.value.fatty_liver_degree = item.value, item.value)
};
}),
Q: common_vendor.f(bodyFeelingOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.body_feeling.includes(item.value) ? 1 : "",
d: common_vendor.o(($event) => toggleMultiCheckbox("body_feeling", item.value), item.value)
};
}),
R: common_vendor.f(sleepConditionOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.sleep_condition.includes(item.value) ? 1 : "",
d: common_vendor.o(($event) => toggleMultiCheckbox("sleep_condition", item.value), item.value)
};
}),
S: common_vendor.f(eyeConditionOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.eye_condition.includes(item.value) ? 1 : "",
d: common_vendor.o(($event) => toggleMultiCheckbox("eye_condition", item.value), item.value)
};
}),
T: common_vendor.f(headFeelingOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.head_feeling.includes(item.value) ? 1 : "",
d: common_vendor.o(($event) => toggleMultiCheckbox("head_feeling", item.value), item.value)
};
}),
U: common_vendor.f(sweatConditionOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.sweat_condition.includes(item.value) ? 1 : "",
d: common_vendor.o(($event) => toggleMultiCheckbox("sweat_condition", item.value), item.value)
};
}),
V: common_vendor.f(skinConditionOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.skin_condition.includes(item.value) ? 1 : "",
d: common_vendor.o(($event) => toggleMultiCheckbox("skin_condition", item.value), item.value)
};
}),
W: common_vendor.f(urineConditionOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.urine_condition.includes(item.value) ? 1 : "",
d: common_vendor.o(($event) => toggleMultiCheckbox("urine_condition", item.value), item.value)
};
}),
X: common_vendor.f(stoolConditionOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.stool_condition.includes(item.value) ? 1 : "",
d: common_vendor.o(($event) => toggleMultiCheckbox("stool_condition", item.value), item.value)
};
}),
Y: common_vendor.f(kidneyConditionOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.kidney_condition.includes(item.value) ? 1 : "",
d: common_vendor.o(($event) => toggleMultiCheckbox("kidney_condition", item.value), item.value)
};
}),
Z: common_vendor.f(pastHistoryOptions.value, (item, k0, i0) => {
return {
a: common_vendor.t(item.name),
b: item.value,
c: formData.value.past_history.includes(item.value) ? 1 : "",
d: common_vendor.o(($event) => toggleMultiCheckbox("past_history", item.value), item.value)
};
}),
aa: formData.value.trauma_history === 1 ? 1 : "",
ab: common_vendor.o(($event) => formData.value.trauma_history = 1, "5e"),
ac: formData.value.trauma_history === 0 ? 1 : "",
ad: common_vendor.o(($event) => formData.value.trauma_history = 0, "db"),
ae: formData.value.surgery_history === 1 ? 1 : "",
af: common_vendor.o(($event) => formData.value.surgery_history = 1, "f6"),
ag: formData.value.surgery_history === 0 ? 1 : "",
ah: common_vendor.o(($event) => formData.value.surgery_history = 0, "69"),
ai: formData.value.allergy_history === 1 ? 1 : "",
aj: common_vendor.o(($event) => formData.value.allergy_history = 1, "b7"),
ak: formData.value.allergy_history === 0 ? 1 : "",
al: common_vendor.o(($event) => formData.value.allergy_history = 0, "f0"),
am: formData.value.family_history === 1 ? 1 : "",
an: common_vendor.o(($event) => formData.value.family_history = 1, "e3"),
ao: formData.value.family_history === 0 ? 1 : "",
ap: common_vendor.o(($event) => formData.value.family_history = 0, "18"),
aq: formData.value.pregnancy_history === 1 ? 1 : "",
ar: common_vendor.o(($event) => formData.value.pregnancy_history = 1, "71"),
as: formData.value.pregnancy_history === 0 ? 1 : "",
at: common_vendor.o(($event) => formData.value.pregnancy_history = 0, "a3"),
av: formData.value.tongue_images && formData.value.tongue_images.length > 0
}, formData.value.tongue_images && formData.value.tongue_images.length > 0 ? {
aw: common_vendor.f(formData.value.tongue_images, (img, index, i0) => {
return {
a: img,
b: common_vendor.o(($event) => previewImages(formData.value.tongue_images, index), index),
c: common_vendor.o(($event) => deleteTonguePhoto(index), index),
d: index
};
})
} : {}, {
ax: formData.value.tongue_images.length < 9
}, formData.value.tongue_images.length < 9 ? {
ay: common_vendor.p({
type: "camera",
size: "30"
}),
az: common_vendor.o(uploadTonguePhoto, "ec")
} : {}, {
aA: formData.value.report_files && formData.value.report_files.length > 0
}, formData.value.report_files && formData.value.report_files.length > 0 ? {
aB: common_vendor.f(formData.value.report_files, (img, index, i0) => {
return {
a: img,
b: common_vendor.o(($event) => previewImages(formData.value.report_files, index), index),
c: common_vendor.o(($event) => deleteExaminationReport(index), index),
d: index
};
})
} : {}, {
aC: formData.value.report_files.length < 9
}, formData.value.report_files.length < 9 ? {
aD: common_vendor.p({
type: "camera",
size: "30"
}),
aE: common_vendor.o(uploadExaminationReport, "6a")
} : {}, {
aF: formData.value.remark,
aG: common_vendor.o(($event) => formData.value.remark = $event.detail.value, "c8"),
aH: agreedToTerms.value
}, agreedToTerms.value ? {} : {}, {
aI: agreedToTerms.value ? 1 : "",
aJ: !agreedToTerms.value
}, !agreedToTerms.value ? {} : {}, {
aK: !agreedToTerms.value ? 1 : "",
aL: common_vendor.o(($event) => agreedToTerms.value = false, "2e"),
aM: common_vendor.o(($event) => openAgreement("service"), "98"),
aN: common_vendor.o(($event) => openAgreement("privacy"), "cc"),
aO: common_vendor.o(($event) => agreedToTerms.value = true, "db"),
aP: common_vendor.t(submitting.value ? "保存中..." : "我已确认无误,提交给医生"),
aQ: common_vendor.o(submitForm, "17"),
aR: submitting.value || !agreedToTerms.value
}), {
aS: showDatePicker.value
}, showDatePicker.value ? {
aT: common_vendor.o(($event) => showDatePicker.value = false, "4e"),
aU: common_vendor.o(confirmDate, "61"),
aV: common_vendor.f(yearRange.value, (year, k0, i0) => {
return {
a: common_vendor.t(year),
b: year
};
}),
aW: common_vendor.f(monthRange.value, (month, k0, i0) => {
return {
a: common_vendor.t(String(month).padStart(2, "0")),
b: month
};
}),
aX: common_vendor.f(dayRange.value, (day, k0, i0) => {
return {
a: common_vendor.t(String(day).padStart(2, "0")),
b: day
};
}),
aY: datePickerValue.value,
aZ: common_vendor.o(onDateChange, "78")
} : {});
};
}
};
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-47b8e7b0"]]);
wx.createPage(MiniProgramPage);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/Card/edit_card.js.map
@@ -0,0 +1,6 @@
{
"navigationBarTitleText": "修改就诊卡",
"usingComponents": {
"uni-icons": "../../uni_modules/uni-icons/components/uni-icons/uni-icons"
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,858 @@
.edit-container.data-v-47b8e7b0 {
min-height: 100vh;
background: #f5f5f5;
}
.loading-state.data-v-47b8e7b0 {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-size: 36rpx;
color: #999999;
}
.form-wrapper.data-v-47b8e7b0 {
padding: 20rpx;
padding-bottom: 280rpx; /* 为固定按钮留出空间 */
}
.form-section.data-v-47b8e7b0 {
background: #ffffff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
}
.section-title.data-v-47b8e7b0 {
font-size: 34rpx;
font-weight: 600;
color: #333333;
margin-bottom: 20rpx;
display: block;
padding-bottom: 12rpx;
border-bottom: 1rpx solid #e8e8e8;
}
/* 行布局 */
.form-row.data-v-47b8e7b0 {
display: flex;
align-items: center;
margin-bottom: 20rpx;
gap: 20rpx;
}
.form-row-single.data-v-47b8e7b0 {
display: flex;
align-items: center;
margin-bottom: 20rpx;
flex-wrap: wrap;
}
.form-col-left.data-v-47b8e7b0 {
flex: 1;
display: flex;
align-items: center;
}
.form-col-right.data-v-47b8e7b0 {
flex: 1;
display: flex;
align-items: center;
}
.form-col-large.data-v-47b8e7b0 {
flex: 2;
display: flex;
align-items: center;
}
.form-col-small.data-v-47b8e7b0 {
flex: 1;
display: flex;
align-items: center;
}
/* 标签样式 */
.form-label.data-v-47b8e7b0 {
font-size: 28rpx;
color: #666666;
white-space: nowrap;
margin-right: 8rpx;
}
.form-labels.data-v-47b8e7b0 {
font-size: 32rpx;
color: #222222;
white-space: nowrap;
margin-right: 8rpx;
font-weight: 700;
}
.form-label-number.data-v-47b8e7b0 {
font-size: 32rpx;
color: #333333;
font-weight: 600;
margin-right: 4rpx;
}
.required.data-v-47b8e7b0 {
color: #ff4d4f;
margin-right: 4rpx;
font-size: 32rpx;
}
.form-value.data-v-47b8e7b0 {
font-size: 28rpx;
color: #333333;
}
/* 行内输入框 */
.form-input-inline.data-v-47b8e7b0 {
flex: 1;
padding: 12rpx 16rpx;
border: none;
border-bottom: 1rpx solid #e8e8e8;
font-size: 28rpx;
color: #333333;
background: transparent;
}
.form-input-inline.data-v-47b8e7b0:disabled {
color: #999999;
background: #f5f5f5;
}
.form-input-inline.data-v-47b8e7b0::-webkit-input-placeholder {
color: #cccccc;
font-size: 30rpx;
}
.form-input-inline.data-v-47b8e7b0::placeholder {
color: #cccccc;
font-size: 30rpx;
}
.form-unit.data-v-47b8e7b0 {
font-size: 30rpx;
color: #999999;
margin-left: 8rpx;
white-space: nowrap;
}
/* 性别选择 - 行内样式 */
.gender-inline.data-v-47b8e7b0 {
display: flex;
gap: 30rpx;
margin-left: 10rpx;
}
.gender-radio.data-v-47b8e7b0 {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 32rpx;
color: #666666;
}
.gender-radio.active.data-v-47b8e7b0 {
color: #386641;
}
.radio-icon.data-v-47b8e7b0 {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #d9d9d9;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #ffffff;
background: #ffffff;
}
.gender-radio.active .radio-icon.data-v-47b8e7b0 {
background: #386641;
border-color: #386641;
}
.form-tip.data-v-47b8e7b0 {
font-size: 28rpx;
color: #ff4d4f;
margin-top: 8rpx;
}
/* 块级表单项 */
.form-item-block.data-v-47b8e7b0 {
margin-bottom: 30rpx;
}
.form-label-block.data-v-47b8e7b0 {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
/* 按钮网格布局 */
.button-grid.data-v-47b8e7b0 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
margin-bottom: 16rpx;
}
.button-grid-single.data-v-47b8e7b0 {
display: grid;
grid-template-columns: 1fr;
}
.button-grid-three.data-v-47b8e7b0 {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0;
}
.grid-button.data-v-47b8e7b0 {
padding: 4rpx 20rpx;
border: 2rpx solid #e8e8e8;
border-radius: 0;
text-align: center;
font-size: 32rpx;
color: #555555;
background: #ffffff;
transition: all 0.2s;
min-height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
margin-left: -2rpx;
margin-bottom: 10rpx;
position: relative;
}
.grid-button.data-v-47b8e7b0:first-child {
margin-left: 0;
border-top-left-radius: 8rpx;
border-bottom-left-radius: 8rpx;
}
.grid-button.data-v-47b8e7b0:last-child {
border-top-right-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.button-grid-three .grid-button.data-v-47b8e7b0:nth-child(3n+1) {
border-top-left-radius: 8rpx;
border-bottom-left-radius: 8rpx;
margin-left: 0;
}
.button-grid-three .grid-button.data-v-47b8e7b0:nth-child(3n) {
border-top-right-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.button-grid-three .grid-button.data-v-47b8e7b0:nth-child(3n+2) {
border-radius: 0;
}
.grid-button.active.data-v-47b8e7b0 {
border-color: #386641;
background: #386641;
color: #ffffff;
font-weight: 500;
z-index: 1;
}
/* 选中按钮右侧的白色分隔线 */
.grid-button.active.data-v-47b8e7b0::after {
content: '';
position: absolute;
right: -2rpx;
top: 0;
bottom: 0;
width: 2rpx;
background: #ffffff;
z-index: 2;
}
/* 选中按钮左侧的白色分隔线 */
.grid-button.active.data-v-47b8e7b0::before {
content: '';
position: absolute;
left: -2rpx;
top: 0;
bottom: 0;
width: 2rpx;
background: #ffffff;
z-index: 2;
}
/* 如果是第一个按钮被选中,不显示左侧白线 */
.grid-button.active.data-v-47b8e7b0:first-child::before,
.button-grid-three .grid-button.active.data-v-47b8e7b0:nth-child(3n+1)::before {
display: none;
}
/* 如果是最后一个按钮被选中,不显示右侧白线 */
.grid-button.active.data-v-47b8e7b0:last-child::after,
.button-grid-three .grid-button.active.data-v-47b8e7b0:nth-child(3n)::after {
display: none;
}
/* 全宽输入框 */
.form-input-full.data-v-47b8e7b0 {
width: 100%;
padding: 20rpx;
border: none;
border-bottom: 1rpx solid #e8e8e8;
font-size: 32rpx;
color: #333333;
background: transparent;
box-sizing: border-box;
min-height: 80rpx;
display: block;
}
.form-input-full.data-v-47b8e7b0::-webkit-input-placeholder {
color: #cccccc;
font-size: 30rpx;
}
.form-input-full.data-v-47b8e7b0::placeholder {
color: #cccccc;
font-size: 30rpx;
}
/* 全宽日期选择器 */
.date-input-full.data-v-47b8e7b0 {
display: flex;
align-items: center;
padding: 20rpx;
border: none;
border-bottom: 1rpx solid #e8e8e8;
background: transparent;
min-height: 80rpx;
box-sizing: border-box;
}
.date-icon-left.data-v-47b8e7b0 {
font-size: 36rpx;
color: #999999;
margin-right: 12rpx;
flex-shrink: 0;
}
.date-text.data-v-47b8e7b0 {
flex: 1;
font-size: 32rpx;
color: #333333;
word-break: break-word;
}
.date-arrow.data-v-47b8e7b0 {
font-size: 40rpx;
color: #cccccc;
margin-left: 12rpx;
flex-shrink: 0;
}
/* 原有表单组样式 - 保留用于其他部分 */
.form-group.data-v-47b8e7b0 {
margin-bottom: 24rpx;
}
.form-group.data-v-47b8e7b0:last-child {
margin-bottom: 0;
}
.form-input.data-v-47b8e7b0 {
width: 100%;
padding: 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
font-size: 34rpx;
line-height: 1.5;
color: #333333;
background: #fffaf5;
box-sizing: border-box;
display: block;
height: auto;
min-height: 80rpx;
}
.form-input.data-v-47b8e7b0::-webkit-input-placeholder {
color: #cccccc;
font-size: 32rpx;
}
.form-input.data-v-47b8e7b0::placeholder {
color: #cccccc;
font-size: 32rpx;
}
.form-textarea.data-v-47b8e7b0 {
width: 100%;
padding: 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
font-size: 34rpx;
line-height: 1.6;
color: #333333;
background: #fffaf5;
box-sizing: border-box;
min-height: 180rpx;
display: block;
font-family: inherit;
}
.form-textarea.data-v-47b8e7b0::-webkit-input-placeholder {
color: #cccccc;
font-size: 32rpx;
}
.form-textarea.data-v-47b8e7b0::placeholder {
color: #cccccc;
font-size: 32rpx;
}
.gender-group.data-v-47b8e7b0 {
display: flex;
gap: 16rpx;
}
.gender-btn.data-v-47b8e7b0 {
flex: 1;
padding: 22rpx 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
text-align: center;
font-size: 34rpx;
line-height: 1.5;
color: #666666;
background: #fffaf5;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
min-height: 80rpx;
}
.gender-btn.active.data-v-47b8e7b0 {
border-color: #386641;
background: #fff4e6;
color: #386641;
font-weight: 600;
}
.radio-group.data-v-47b8e7b0 {
display: flex;
gap: 16rpx;
}
.radio-item.data-v-47b8e7b0 {
flex: 1;
padding: 20rpx 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
text-align: center;
font-size: 32rpx;
color: #666666;
background: #fffaf5;
transition: all 0.2s;
min-height: 70rpx;
display: flex;
align-items: center;
justify-content: center;
}
.radio-item.active.data-v-47b8e7b0 {
border-color: #386641;
background: #fff4e6;
color: #386641;
font-weight: 600;
}
.checkbox-group.data-v-47b8e7b0 {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.checkbox-item.data-v-47b8e7b0 {
padding: 18rpx 28rpx;
border: 2rpx solid #386641;
border-radius: 24rpx;
font-size: 32rpx;
color: #666666;
background: #fffaf5;
transition: all 0.2s;
}
.checkbox-item.active.data-v-47b8e7b0 {
border-color: #386641;
background: #fff4e6;
color: #386641;
font-weight: 600;
}
.multi-checkbox-group.data-v-47b8e7b0 {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.multi-checkbox-item.data-v-47b8e7b0 {
padding: 18rpx 28rpx;
border: 2rpx solid #386641;
border-radius: 24rpx;
font-size: 32rpx;
color: #666666;
background: #fffaf5;
transition: all 0.2s;
}
.multi-checkbox-item.active.data-v-47b8e7b0 {
border-color: #386641;
background: #fff4e6;
color: #386641;
font-weight: 600;
}
.date-input.data-v-47b8e7b0 {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
background: #fffaf5;
font-size: 34rpx;
line-height: 1.5;
color: #333333;
min-height: 80rpx;
}
.date-icon.data-v-47b8e7b0 {
font-size: 40rpx;
margin-left: 12rpx;
color: #386641;
}
.picker-item.data-v-47b8e7b0 {
display: flex;
justify-content: center;
align-items: center;
height: 50px;
font-size: 34rpx;
color: #333333;
}
/* 上传组件样式 */
.upload-container.data-v-47b8e7b0 {
margin-top: 16rpx;
}
.upload-layout.data-v-47b8e7b0 {
display: flex;
gap: 20rpx;
margin-top: 20rpx;
align-items: flex-start;
}
.upload-left.data-v-47b8e7b0 {
flex-shrink: 0;
width: 600rpx;
}
.upload-right.data-v-47b8e7b0 {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
gap: 12rpx;
}
.upload-label.data-v-47b8e7b0 {
font-size: 32rpx;
color: #333333;
font-weight: 600;
}
.upload-desc.data-v-47b8e7b0 {
font-size: 28rpx;
color: #999999;
line-height: 1.5;
}
.more-images.data-v-47b8e7b0 {
display: flex;
gap: 12rpx;
margin-top: 12rpx;
flex-wrap: wrap;
}
.more-item.data-v-47b8e7b0 {
width: 80rpx;
height: 80rpx;
border-radius: 8rpx;
overflow: hidden;
border: 2rpx solid #386641;
}
.more-item image.data-v-47b8e7b0 {
width: 100%;
height: 100%;
}
.upload-btn-single.data-v-47b8e7b0 {
width: 200rpx;
height: 200rpx;
display: flex;
align-items: center;
justify-content: center;
border: 2rpx dashed #386641;
border-radius: 12rpx;
background: #fffaf5;
transition: all 0.2s;
}
.upload-btn-single.data-v-47b8e7b0:active {
background: #fff4e6;
border-color: #386641;
}
.upload-btn.data-v-47b8e7b0 {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 40rpx;
border: 2rpx dashed #386641;
border-radius: 12rpx;
background: #fffaf5;
transition: all 0.2s;
}
.upload-btn.data-v-47b8e7b0:active {
background: #fff4e6;
border-color: #386641;
}
.upload-icon.data-v-47b8e7b0 {
font-size: 80rpx;
margin-bottom: 16rpx;
}
.upload-text.data-v-47b8e7b0 {
font-size: 28rpx;
color: #999999;
}
.upload-tip.data-v-47b8e7b0 {
font-size: 24rpx;
color: #cccccc;
margin-top: 8rpx;
}
.form-label-tip.data-v-47b8e7b0 {
font-size: 24rpx;
color: #999999;
margin-bottom: 16rpx;
display: block;
line-height: 1.5;
}
.image-preview.data-v-47b8e7b0 {
position: relative;
border-radius: 12rpx;
overflow: hidden;
border: 2rpx solid #386641;
width: 100%;
height: 100%;
}
.preview-image.data-v-47b8e7b0 {
width: 200rpx;
height: 200rpx;
display: block;
}
.image-actions.data-v-47b8e7b0 {
position: absolute;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: space-around;
padding: 12rpx;
background: rgba(0, 0, 0, 0.6);
gap: 8rpx;
}
.action-btn.data-v-47b8e7b0 {
padding: 8rpx 16rpx;
border-radius: 6rpx;
font-size: 24rpx;
color: #ffffff;
background: rgba(255, 140, 0, 0.8);
border: none;
transition: all 0.2s;
flex: 1;
text-align: center;
}
.action-btn.data-v-47b8e7b0:active {
background: rgba(255, 140, 0, 1);
}
.action-btn.delete.data-v-47b8e7b0 {
color: #ffffff;
background: rgba(255, 77, 79, 0.8);
}
.action-btn.delete.data-v-47b8e7b0:active {
background: rgba(255, 77, 79, 1);
}
/* 多图片列表样式 */
.image-list.data-v-47b8e7b0 {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16rpx;
margin-bottom: 20rpx;
}
.image-item.data-v-47b8e7b0 {
position: relative;
width: 100%;
padding-bottom: 100%;
border-radius: 12rpx;
overflow: hidden;
border: 2rpx solid #386641;
}
.item-image.data-v-47b8e7b0 {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.delete-icon.data-v-47b8e7b0 {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 48rpx;
height: 48rpx;
background: rgba(255, 77, 79, 0.9);
color: #ffffff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
line-height: 1;
font-weight: bold;
z-index: 10;
}
.upload-btn-small.data-v-47b8e7b0 {
width: 100%;
padding-bottom: 100%;
position: relative;
border: 2rpx dashed #386641;
border-radius: 12rpx;
background: #fffaf5;
display: flex;
align-items: center;
justify-content: center;
}
.upload-icon-small.data-v-47b8e7b0 {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 60rpx;
color: #386641;
}
.picker-input.data-v-47b8e7b0 {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
background: #fffaf5;
font-size: 34rpx;
line-height: 1.5;
color: #333333;
min-height: 80rpx;
}
.picker-icon.data-v-47b8e7b0 {
font-size: 28rpx;
color: #386641;
margin-left: 12rpx;
}
.button-group.data-v-47b8e7b0 {
display: flex;
gap: 20rpx;
margin-top: 40rpx;
margin-bottom: 40rpx;
padding: 0 20rpx;
}
.button-fixed.data-v-47b8e7b0 {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 12rpx 20rpx 16rpx 20rpx;
background: #ffffff;
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.1);
z-index: 100;
box-sizing: border-box;
}
.agreement-section.data-v-47b8e7b0 {
margin-bottom: 16rpx;
}
.agreement-radio-group.data-v-47b8e7b0 {
display: flex;
align-items: center;
gap: 12rpx;
flex-wrap: wrap;
}
.radio-option.data-v-47b8e7b0 {
display: flex;
align-items: center;
gap: 8rpx;
cursor: pointer;
}
.radio-icon.data-v-47b8e7b0 {
width: 36rpx;
height: 36rpx;
border: 2rpx solid #d9d9d9;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: all 0.2s;
background: #ffffff;
}
.radio-icon.checked.data-v-47b8e7b0 {
border-color: #52c41a;
}
.radio-dot.data-v-47b8e7b0 {
width: 20rpx;
height: 20rpx;
border-radius: 50%;
background: #52c41a;
}
.radio-label.data-v-47b8e7b0 {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.agreement-text.data-v-47b8e7b0 {
font-size: 26rpx;
color: #333333;
line-height: 1.6;
}
.agreement-link.data-v-47b8e7b0 {
color: #1890ff;
text-decoration: underline;
}
.agreement-link.data-v-47b8e7b0:active {
opacity: 0.7;
}
.btn.data-v-47b8e7b0 {
width: 100%;
padding: 10rpx;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 600;
border: none;
cursor: pointer;
transition: all 0.2s;
min-height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 30rpx;
}
.btn.data-v-47b8e7b0:active {
transform: scale(0.98);
}
.btn.data-v-47b8e7b0:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.btn-cancel.data-v-47b8e7b0 {
background: #f5f5f5;
color: #666666;
border: 2rpx solid #e8e8e8;
}
.btn-submit.data-v-47b8e7b0 {
background: #389e0d;
color: #ffffff;
}
.btn-submit.data-v-47b8e7b0:active:not(:disabled) {
background: linear-gradient(135deg, #389e0d 0%, #237804 100%);
}
.date-picker-overlay.data-v-47b8e7b0 {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
flex-direction: column;
justify-content: flex-end;
z-index: 999;
}
.date-picker-container.data-v-47b8e7b0 {
background: #ffffff;
border-radius: 20rpx 20rpx 0 0;
overflow: hidden;
}
.date-picker-header.data-v-47b8e7b0 {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
border-bottom: 2rpx solid #ffe4cc;
}
.date-btn-cancel.data-v-47b8e7b0,
.date-btn-confirm.data-v-47b8e7b0 {
font-size: 34rpx;
color: #386641;
font-weight: 600;
}
.date-picker-view.data-v-47b8e7b0 {
height: 320rpx;
}
.picker-item.data-v-47b8e7b0 {
display: flex;
justify-content: center;
align-items: center;
height: 50px;
font-size: 34rpx;
color: #333333;
}
@@ -0,0 +1,122 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
const DevTrainingEntry = () => "../../components/dev-training-entry/index.js";
const TabBarDock = () => "../../components/app-tab-bar/tab-bar-dock.js";
const _sfc_main = {
name: "profileB",
components: { DevTrainingEntry, TabBarDock },
data() {
return {
userInfo: {
avatar: "",
nickname: "",
userId: "",
balance: "0",
points: "0",
coupons: "5"
},
orderTypes: [
{
name: "待付款",
image: "/static/images/my/payment.png",
path: "/pages/order/list?type=1",
count: 2
},
{
name: "待发货",
image: "/static/images/my/delivery.png",
path: "/pages/order/list?type=2",
count: 1
},
{
name: "待收货",
image: "/static/images/my/shipping.png",
path: "/pages/order/list?type=3",
count: 0
},
{
name: "待评价",
image: "/static/images/my/review.png",
path: "/pages/order/list?type=4",
count: 3
},
{
name: "退换/售后",
image: "/static/images/my/after-sale.png",
path: "/pages/order/list?type=5",
count: 0
}
],
allServices: [
{ name: "就诊卡", icon: "folder-add", path: "/pages/Card/Card", class: "bg-cyan" },
{ name: "设置", icon: "gear", path: "/pages/user/userinfo", class: "bg-gray" }
// ,
// { name: '客服中心', icon: 'headphones', path: '/pages/service/index', class: 'bg-orange', desc: '在线客服' }
]
};
},
onShow() {
this.userInfo = common_vendor.index.getStorageSync("userData");
},
methods: {
navigateTo(path) {
common_vendor.index.navigateTo({ url: path });
},
showQRCode() {
}
}
};
if (!Array) {
const _easycom_uni_icons2 = common_vendor.resolveComponent("uni-icons");
const _component_dev_training_entry = common_vendor.resolveComponent("dev-training-entry");
const _component_TabBarDock = common_vendor.resolveComponent("TabBarDock");
(_easycom_uni_icons2 + _component_dev_training_entry + _component_TabBarDock)();
}
const _easycom_uni_icons = () => "../../uni_modules/uni-icons/components/uni-icons/uni-icons.js";
if (!Math) {
_easycom_uni_icons();
}
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return {
a: $data.userInfo.avatar,
b: common_vendor.t($data.userInfo.nickname || "未登录"),
c: common_vendor.t($data.userInfo.balance || 0),
d: common_vendor.t($data.userInfo.points || 0),
e: common_vendor.t($data.userInfo.coupons || 0),
f: common_vendor.o(($event) => $options.navigateTo("/pages/user/userinfo"), "39"),
g: common_vendor.f($data.allServices, (item, index, i0) => {
return common_vendor.e({
a: "0f7520f0-0-" + i0,
b: common_vendor.p({
type: item.icon,
size: "28",
color: "#fff"
}),
c: common_vendor.n(item.class),
d: common_vendor.t(item.name),
e: item.desc
}, item.desc ? {
f: common_vendor.t(item.desc)
} : {}, {
g: "0f7520f0-1-" + i0,
h: index,
i: common_vendor.o(($event) => $options.navigateTo(item.path), index)
});
}),
h: common_vendor.p({
type: "right",
size: "20",
color: "#666"
}),
i: common_vendor.p({
bottom: 360
}),
j: common_vendor.p({
active: 1
})
};
}
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-0f7520f0"]]);
_sfc_main.__runtimeHooks = 2;
wx.createPage(MiniProgramPage);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/user/user.js.map
@@ -0,0 +1,10 @@
{
"navigationBarTitleText": "我的",
"backgroundColor": "#e8eaed",
"backgroundColorTop": "#e8eaed",
"usingComponents": {
"dev-training-entry": "../../components/dev-training-entry/index",
"tab-bar-dock": "../../components/app-tab-bar/tab-bar-dock",
"uni-icons": "../../uni_modules/uni-icons/components/uni-icons/uni-icons"
}
}
@@ -0,0 +1 @@
<view class="mine-page data-v-0f7520f0"><view class="user-card data-v-0f7520f0" bindtap="{{f}}"><view class="user-header data-v-0f7520f0"><image class="avatar data-v-0f7520f0" src="{{a}}" mode="aspectFill"/><view class="user-info data-v-0f7520f0"><text class="nickname data-v-0f7520f0">{{b}}</text></view></view><view class="stat-row data-v-0f7520f0"><view class="stat-item data-v-0f7520f0"><text class="value data-v-0f7520f0">{{c}}</text><text class="label data-v-0f7520f0">钱包(元)</text></view><view class="stat-item data-v-0f7520f0"><text class="value data-v-0f7520f0">{{d}}</text><text class="label data-v-0f7520f0">积分</text></view><view class="stat-item data-v-0f7520f0"><text class="value data-v-0f7520f0">{{e}}</text><text class="label data-v-0f7520f0">优惠券</text></view></view></view><view class="service-list data-v-0f7520f0"><view wx:for="{{g}}" wx:for-item="item" wx:key="h" class="service-item data-v-0f7520f0" bindtap="{{item.i}}"><view class="left data-v-0f7520f0"><view class="{{['service-icon', 'data-v-0f7520f0', item.c]}}"><uni-icons wx:if="{{item.b}}" class="data-v-0f7520f0" u-i="{{item.a}}" bind:__l="__l" u-p="{{item.b}}"></uni-icons></view><text class="service-name data-v-0f7520f0">{{item.d}}</text></view><view class="right data-v-0f7520f0"><text wx:if="{{item.e}}" class="desc data-v-0f7520f0">{{item.f}}</text><uni-icons wx:if="{{h}}" class="data-v-0f7520f0" u-i="{{item.g}}" bind:__l="__l" u-p="{{h}}"></uni-icons></view></view></view><dev-training-entry wx:if="{{i}}" class="data-v-0f7520f0" u-i="0f7520f0-2" bind:__l="__l" u-p="{{i}}"/><tab-bar-dock wx:if="{{j}}" class="data-v-0f7520f0" u-i="0f7520f0-3" bind:__l="__l" u-p="{{j}}"/></view>
@@ -0,0 +1,261 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
/* 文字基本颜色 */
/* 背景颜色 */
/* 边框颜色 */
/* 尺寸变量 */
/* 文字尺寸 */
/* 图片尺寸 */
/* Border Radius */
/* 水平间距 */
/* 垂直间距 */
/* 透明度 */
/* 文章场景相关 */
/* 适老化设计:50-80岁老人 - 大字号、高对比、大触控区 */
.mine-page.data-v-0f7520f0 {
min-height: 100vh;
background: #e8eaed;
padding: 40rpx;
padding-bottom: calc(240rpx + env(safe-area-inset-bottom));
}
.user-card.data-v-0f7520f0 {
background: #fff;
border-radius: 28rpx;
padding: 48rpx;
margin-bottom: 40rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
}
.user-card .user-header.data-v-0f7520f0 {
display: flex;
align-items: center;
margin-bottom: 48rpx;
}
.user-card .user-header .avatar.data-v-0f7520f0 {
width: 160rpx;
height: 160rpx;
border-radius: 80rpx;
margin-right: 36rpx;
border: 4rpx solid #e0e0e0;
}
.user-card .user-header .user-info.data-v-0f7520f0 {
flex: 1;
}
.user-card .user-header .user-info .nickname.data-v-0f7520f0 {
font-size: 48rpx;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 16rpx;
display: block;
letter-spacing: 1rpx;
}
.user-card .user-header .user-info .vip-tag.data-v-0f7520f0 {
background: linear-gradient(90deg, #FFD700 0%, #FFA500 100%);
color: #fff;
font-size: 28rpx;
padding: 6rpx 20rpx;
border-radius: 24rpx;
display: inline-block;
}
.user-card .user-header .qr-code.data-v-0f7520f0 {
width: 88rpx;
height: 88rpx;
border-radius: 44rpx;
background: #f5f6fa;
display: flex;
align-items: center;
justify-content: center;
}
.user-card .stat-row.data-v-0f7520f0 {
display: flex;
padding-top: 40rpx;
border-top: 4rpx solid #e8eaed;
}
.user-card .stat-row .stat-item.data-v-0f7520f0 {
flex: 1;
text-align: center;
min-height: 120rpx;
}
.user-card .stat-row .stat-item .value.data-v-0f7520f0 {
font-size: 52rpx;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 12rpx;
display: block;
}
.user-card .stat-row .stat-item .label.data-v-0f7520f0 {
font-size: 32rpx;
color: #555;
font-weight: 500;
}
.order-card.data-v-0f7520f0 {
background: #fff;
border-radius: 24rpx;
padding: 40rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
}
.order-card .card-header.data-v-0f7520f0 {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 40rpx;
}
.order-card .card-header .title.data-v-0f7520f0 {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.order-card .card-header .more.data-v-0f7520f0 {
display: flex;
align-items: center;
}
.order-card .card-header .more text.data-v-0f7520f0 {
font-size: 26rpx;
color: #999;
margin-right: 8rpx;
}
.order-card .order-list.data-v-0f7520f0 {
display: flex;
justify-content: space-between;
}
.order-card .order-list .order-item.data-v-0f7520f0 {
flex: 1;
text-align: center;
}
.order-card .order-list .order-item .icon-wrapper.data-v-0f7520f0 {
width: 100rpx;
height: 100rpx;
margin: 0 auto 0;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.order-card .order-list .order-item .icon-wrapper .count-badge.data-v-0f7520f0 {
position: absolute;
top: -10rpx;
right: -10rpx;
background: #ff4d4f;
color: #fff;
font-size: 20rpx;
min-width: 32rpx;
height: 32rpx;
line-height: 32rpx;
text-align: center;
border-radius: 16rpx;
padding: 0 8rpx;
}
.order-card .order-list .order-item .icon-wrapper .order-icon.data-v-0f7520f0 {
width: 56rpx;
height: 56rpx;
}
.order-card .order-list .order-item .order-name.data-v-0f7520f0 {
font-size: 26rpx;
color: #666;
}
.quick-actions.data-v-0f7520f0 {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 30rpx;
margin-bottom: 30rpx;
}
.quick-actions .action-item.data-v-0f7520f0 {
background: #fff;
border-radius: 24rpx;
padding: 30rpx 20rpx;
text-align: center;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
}
.quick-actions .action-item .action-icon.data-v-0f7520f0 {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
margin: 0 auto 16rpx;
display: flex;
align-items: center;
justify-content: center;
}
.quick-actions .action-item .action-name.data-v-0f7520f0 {
font-size: 26rpx;
color: #666;
}
.service-list.data-v-0f7520f0 {
background: #fff;
border-radius: 28rpx;
padding: 20rpx 40rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
}
.service-list .service-item.data-v-0f7520f0 {
display: flex;
align-items: center;
justify-content: space-between;
padding: 44rpx 0;
border-bottom: 4rpx solid #e8eaed;
min-height: 120rpx;
}
.service-list .service-item.data-v-0f7520f0:last-child {
border-bottom: none;
}
.service-list .service-item .left.data-v-0f7520f0 {
display: flex;
align-items: center;
}
.service-list .service-item .left .service-icon.data-v-0f7520f0 {
width: 88rpx;
height: 88rpx;
border-radius: 44rpx;
margin-right: 32rpx;
display: flex;
align-items: center;
justify-content: center;
}
.service-list .service-item .left .service-name.data-v-0f7520f0 {
font-size: 38rpx;
color: #1a1a1a;
font-weight: 500;
}
.service-list .service-item .right.data-v-0f7520f0 {
display: flex;
align-items: center;
}
.service-list .service-item .right .desc.data-v-0f7520f0 {
font-size: 30rpx;
color: #555;
margin-right: 20rpx;
}
.bg-red.data-v-0f7520f0 {
background: #ff4d4f;
}
.bg-blue.data-v-0f7520f0 {
background: #1890ff;
}
.bg-green.data-v-0f7520f0 {
background: #52c41a;
}
.bg-purple.data-v-0f7520f0 {
background: #722ed1;
}
.bg-orange.data-v-0f7520f0 {
background: #fa8c16;
}
.bg-cyan.data-v-0f7520f0 {
background: #13c2c2;
}
.bg-pink.data-v-0f7520f0 {
background: #eb2f96;
}
.bg-gray.data-v-0f7520f0 {
background: #666666;
}
@@ -0,0 +1,193 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
if (!Array) {
const _easycom_uni_icons2 = common_vendor.resolveComponent("uni-icons");
_easycom_uni_icons2();
}
const _easycom_uni_icons = () => "../../uni_modules/uni-icons/components/uni-icons/uni-icons.js";
if (!Math) {
_easycom_uni_icons();
}
const _sfc_main = {
__name: "userinfo",
setup(__props) {
const { proxy } = common_vendor.getCurrentInstance();
const instance = common_vendor.getCurrentInstance();
const $url = instance.appContext.config.globalProperties.$url;
const loading = common_vendor.ref(false);
const submitting = common_vendor.ref(false);
const formData = common_vendor.ref({
avatar: "",
patient_name: "",
phone: "",
nickname: "",
sex: 1,
age: ""
});
common_vendor.onMounted(() => {
loadUserInfo();
});
const loadUserInfo = async () => {
loading.value = true;
try {
const res = await proxy.apiUrl({
url: "/api/user/info",
method: "GET",
data: {}
});
if (res.code === 1) {
const userInfo = res.data;
formData.value = {
avatar: userInfo.avatar || "",
patient_name: userInfo.patient_name || userInfo.nickname || "",
phone: userInfo.phone || "",
nickname: userInfo.nickname || "",
sex: userInfo.sex == "男" ? 1 : 2,
age: userInfo.age || ""
};
} else {
common_vendor.index.showToast({ title: res.msg || "加载失败", icon: "none" });
}
} catch (err) {
common_vendor.index.__f__("error", "at pages/user/userinfo.vue:142", "加载用户信息失败:", err);
common_vendor.index.showToast({ title: "加载失败", icon: "none" });
} finally {
loading.value = false;
}
};
const uploadAvatar = () => {
common_vendor.index.chooseImage({
count: 1,
sizeType: ["compressed"],
sourceType: ["album", "camera"],
success: async (res) => {
const tempFilePath = res.tempFilePaths[0];
try {
common_vendor.index.showLoading({ title: "上传中..." });
const token = common_vendor.index.getStorageSync("token");
const uploadRes = await new Promise((resolve, reject) => {
common_vendor.index.uploadFile({
url: $url + "/api/upload/image",
filePath: tempFilePath,
name: "file",
header: {
token,
"content-type": "multipart/form-data"
},
success: (res2) => {
const data = JSON.parse(res2.data);
resolve(data);
},
fail: (err) => {
reject(err);
}
});
});
common_vendor.index.hideLoading();
if (uploadRes.code === 1) {
formData.value.avatar = uploadRes.data.uri || uploadRes.data;
common_vendor.index.showToast({ title: "头像上传成功", icon: "success" });
} else {
common_vendor.index.showToast({ title: uploadRes.msg || "上传失败", icon: "none" });
}
} catch (err) {
common_vendor.index.hideLoading();
common_vendor.index.__f__("error", "at pages/user/userinfo.vue:195", "上传头像失败:", err);
common_vendor.index.showToast({ title: "上传失败", icon: "none" });
}
},
fail: (err) => {
common_vendor.index.__f__("error", "at pages/user/userinfo.vue:200", "选择图片失败:", err);
common_vendor.index.showToast({ title: "选择图片失败", icon: "none" });
}
});
};
const validatePhone = (phone) => {
if (!phone)
return true;
return /^1[3-9]\d{9}$/.test(phone);
};
const submitForm = async () => {
if (!formData.value.patient_name) {
common_vendor.index.showToast({ title: "姓名不能为空", icon: "none" });
return;
}
if (formData.value.phone && !validatePhone(formData.value.phone)) {
common_vendor.index.showToast({ title: "手机号格式不正确", icon: "none" });
return;
}
submitting.value = true;
try {
const res = await proxy.apiUrl({
url: "/api/user/setInfo",
method: "POST",
data: {
avatar: formData.value.avatar,
phone: formData.value.phone,
nickname: formData.value.nickname,
sex: formData.value.sex,
age: formData.value.age,
patient_name: formData.value.patient_name
}
});
if (res.code === 1) {
const userData = common_vendor.index.getStorageSync("userData");
const updatedData = {
...userData,
avatar: formData.value.avatar,
phone: formData.value.phone,
nickname: formData.value.nickname,
sex: formData.value.sex,
age: formData.value.age,
patient_name: formData.value.patient_name,
diagnosis: {
...userData.diagnosis,
patient_name: formData.value.patient_name
}
};
common_vendor.index.setStorageSync("userData", updatedData);
common_vendor.index.showToast({ title: "保存成功", icon: "success" });
setTimeout(() => {
common_vendor.index.navigateBack();
}, 1500);
} else {
common_vendor.index.showToast({ title: res.msg || "保存失败", icon: "none" });
}
} catch (err) {
common_vendor.index.__f__("error", "at pages/user/userinfo.vue:269", "保存失败:", err);
common_vendor.index.showToast({ title: "保存失败", icon: "none" });
} finally {
submitting.value = false;
}
};
return (_ctx, _cache) => {
return common_vendor.e({
a: loading.value
}, loading.value ? {} : {
b: formData.value.avatar,
c: common_vendor.p({
type: "upload-filled",
size: "24",
color: "#FFA500"
}),
d: common_vendor.o(uploadAvatar, "bf"),
e: formData.value.nickname,
f: common_vendor.o(($event) => formData.value.nickname = $event.detail.value, "6d"),
g: formData.value.sex === 1 ? 1 : "",
h: common_vendor.o(($event) => formData.value.sex = 1, "e9"),
i: formData.value.sex === 2 ? 1 : "",
j: common_vendor.o(($event) => formData.value.sex = 2, "53"),
k: formData.value.age,
l: common_vendor.o(common_vendor.m(($event) => formData.value.age = $event.detail.value, {
number: true
}), "cf"),
m: common_vendor.t(submitting.value ? "保存中..." : "保存修改"),
n: common_vendor.o(submitForm, "84"),
o: submitting.value
});
};
}
};
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-103261b1"]]);
wx.createPage(MiniProgramPage);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/user/userinfo.js.map
@@ -0,0 +1,6 @@
{
"navigationBarTitleText": "个人资料",
"usingComponents": {
"uni-icons": "../../uni_modules/uni-icons/components/uni-icons/uni-icons"
}
}
@@ -0,0 +1 @@
<view class="userinfo-container data-v-103261b1"><view wx:if="{{a}}" class="loading-state data-v-103261b1"><text class="data-v-103261b1">加载中...</text></view><view wx:else class="form-wrapper data-v-103261b1"><view class="form-section data-v-103261b1"><view class="form-group data-v-103261b1"><text class="form-label data-v-103261b1">头像</text><view class="avatar-container data-v-103261b1"><image src="{{b}}" class="avatar-image data-v-103261b1" mode="aspectFill"/><view class="avatar-upload data-v-103261b1" bindtap="{{d}}"><uni-icons wx:if="{{c}}" class="upload-icon data-v-103261b1" u-i="103261b1-0" bind:__l="__l" u-p="{{c}}"></uni-icons><text class="upload-text data-v-103261b1">更换头像</text></view></view></view></view><view class="form-section data-v-103261b1"><text class="section-title data-v-103261b1">基本信息</text><view class="form-group data-v-103261b1"><text class="form-label data-v-103261b1">昵称</text><input class="form-input data-v-103261b1" placeholder="请输入昵称" maxlength="50" value="{{e}}" bindinput="{{f}}"/></view><view class="form-group data-v-103261b1"><text class="form-label data-v-103261b1">性别</text><view class="sex-group data-v-103261b1"><view class="{{['sex-btn', 'data-v-103261b1', g && 'active']}}" bindtap="{{h}}"> 男 </view><view class="{{['sex-btn', 'data-v-103261b1', i && 'active']}}" bindtap="{{j}}"> 女 </view></view></view><view class="form-group data-v-103261b1"><text class="form-label data-v-103261b1">年龄</text><input class="form-input data-v-103261b1" type="number" placeholder="请输入年龄" min="0" max="150" value="{{k}}" bindinput="{{l}}"/></view></view><view class="button-group data-v-103261b1"><button class="btn btn-submit data-v-103261b1" bindtap="{{n}}" disabled="{{o}}">{{m}}</button></view></view></view>
@@ -0,0 +1,188 @@
/* 适老化设计:50-80岁 - 大字号、高对比、大触控区 */
.userinfo-container.data-v-103261b1 {
min-height: 100vh;
background: #e8eaed;
}
.header.data-v-103261b1 {
display: flex;
justify-content: space-between;
align-items: center;
background: #ffffff;
padding: 24rpx 40rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
}
.header-back.data-v-103261b1 {
font-size: 48rpx;
color: #FFA500;
width: 88rpx;
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
}
.header-title.data-v-103261b1 {
font-size: 44rpx;
font-weight: bold;
color: #1a1a1a;
flex: 1;
text-align: center;
}
.header-placeholder.data-v-103261b1 {
width: 88rpx;
}
.loading-state.data-v-103261b1 {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-size: 36rpx;
color: #555;
}
.form-wrapper.data-v-103261b1 {
padding: 40rpx;
}
.form-section.data-v-103261b1 {
background: #ffffff;
border-radius: 24rpx;
padding: 40rpx;
margin-bottom: 36rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
}
.section-title.data-v-103261b1 {
font-size: 40rpx;
font-weight: bold;
color: #FFA500;
margin-bottom: 32rpx;
display: block;
padding-bottom: 24rpx;
border-bottom: 4rpx solid #f0f0f0;
}
.form-group.data-v-103261b1 {
margin-bottom: 36rpx;
}
.form-group.data-v-103261b1:last-child {
margin-bottom: 0;
}
.form-label.data-v-103261b1 {
display: block;
font-size: 36rpx;
color: #1a1a1a;
font-weight: 500;
margin-bottom: 20rpx;
line-height: 1.6;
}
.form-input.data-v-103261b1 {
width: 100%;
padding: 28rpx;
border: 4rpx solid #e0e0e0;
border-radius: 16rpx;
font-size: 36rpx;
line-height: 1.6;
color: #1a1a1a;
background: #ffffff;
box-sizing: border-box;
display: block;
height: auto;
min-height: 96rpx;
}
.form-input.data-v-103261b1::-webkit-input-placeholder {
color: #999;
font-size: 34rpx;
}
.form-input.data-v-103261b1::placeholder {
color: #999;
font-size: 34rpx;
}
.avatar-container.data-v-103261b1 {
display: flex;
align-items: center;
gap: 32rpx;
}
.avatar-image.data-v-103261b1 {
width: 160rpx;
height: 160rpx;
border-radius: 80rpx;
background: #f0f0f0;
border: 4rpx solid #e0e0e0;
}
.avatar-upload.data-v-103261b1 {
flex: 1;
padding: 36rpx;
border: 4rpx dashed #FFA500;
border-radius: 20rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16rpx;
min-height: 120rpx;
}
.upload-icon.data-v-103261b1 {
font-size: 56rpx;
}
.upload-text.data-v-103261b1 {
font-size: 34rpx;
color: #FFA500;
font-weight: 500;
}
.sex-group.data-v-103261b1 {
display: flex;
gap: 28rpx;
}
.sex-btn.data-v-103261b1 {
flex: 1;
padding: 32rpx;
border: 4rpx solid #e0e0e0;
border-radius: 16rpx;
text-align: center;
font-size: 38rpx;
line-height: 1.5;
color: #555;
background: #ffffff;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
min-height: 96rpx;
}
.sex-btn.active.data-v-103261b1 {
border-color: #FFA500;
background: #fff7e6;
color: #FFA500;
font-weight: bold;
}
.button-group.data-v-103261b1 {
display: flex;
gap: 28rpx;
margin-top: 56rpx;
margin-bottom: 56rpx;
}
.btn.data-v-103261b1 {
flex: 1;
padding: 36rpx;
border-radius: 20rpx;
font-size: 40rpx;
font-weight: bold;
border: none;
cursor: pointer;
transition: all 0.3s;
min-height: 100rpx;
}
.btn.data-v-103261b1:active {
transform: scale(0.98);
}
.btn.data-v-103261b1:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.btn-cancel.data-v-103261b1 {
background: #f0f0f0;
color: #555;
}
.btn-submit.data-v-103261b1 {
background: #FFA500;
color: #ffffff;
}
.btn-submit.data-v-103261b1:active:not(:disabled) {
background: #e69500;
}
@@ -0,0 +1,37 @@
{
"description": "项目配置文件。",
"packOptions": {
"ignore": []
},
"setting": {
"urlCheck": false,
"es6": true,
"postcss": false,
"minified": true,
"newFeature": true,
"bigPackageSizeSupport": true,
"minifyWXML": true
},
"compileType": "miniprogram",
"libVersion": "",
"appid": "wx79b9a0bfbfe7cbcd",
"projectname": "tongji-uniapp",
"condition": {
"search": {
"current": -1,
"list": []
},
"conversation": {
"current": -1,
"list": []
},
"game": {
"current": -1,
"list": []
},
"miniprogram": {
"current": -1,
"list": []
}
}
}
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="375px" height="268px" viewBox="0 0 375 268" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
<title>背景</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="50%" y1="81.9277858%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#006EFF" offset="0%"></stop>
<stop stop-color="#00C8DC" offset="100%"></stop>
</linearGradient>
</defs>
<g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="主页" fill="url(#linearGradient-1)">
<g id="编组-4" transform="translate(-315.000000, -732.000000)">
<circle id="蒙版" cx="500" cy="500" r="500"></circle>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 934 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

@@ -0,0 +1,86 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
if (!Math) {
TongjiIcon();
}
const TongjiIcon = () => "./TongjiIcon.js";
const _sfc_main = {
__name: "CelebrateBurst",
props: {
show: { type: Boolean, default: false },
title: { type: String, default: "" },
subtitle: { type: String, default: "" }
},
setup(__props) {
const props = __props;
const particles = common_vendor.ref([]);
const cardPop = common_vendor.ref(false);
const COLORS = ["#204E2B", "#386641", "#AFE2B3", "#FBBF24", "#727970", "#DC2626"];
function buildParticles() {
const list = [];
for (let i = 0; i < 18; i++) {
const left = 8 + Math.random() * 84;
const delay = Math.random() * 0.35;
const hue = COLORS[i % COLORS.length];
const size = 10 + Math.floor(Math.random() * 14);
list.push({
id: `${Date.now()}_${i}`,
style: {
left: `${left}%`,
width: `${size}rpx`,
height: `${size}rpx`,
background: hue,
animationDelay: `${delay}s`
}
});
}
return list;
}
common_vendor.watch(
() => props.show,
(v) => {
if (v) {
particles.value = buildParticles();
cardPop.value = false;
setTimeout(() => {
cardPop.value = true;
}, 30);
} else {
particles.value = [];
cardPop.value = false;
}
}
);
return (_ctx, _cache) => {
return common_vendor.e({
a: __props.show
}, __props.show ? common_vendor.e({
b: common_vendor.f(particles.value, (p, k0, i0) => {
return {
a: p.id,
b: common_vendor.s(p.style)
};
}),
c: __props.title
}, __props.title ? common_vendor.e({
d: common_vendor.p({
name: "sparkles",
size: "lg",
color: "#204E2B"
}),
e: common_vendor.t(__props.title),
f: __props.subtitle
}, __props.subtitle ? {
g: common_vendor.t(__props.subtitle)
} : {}, {
h: cardPop.value ? 1 : ""
}) : {}, {
i: common_vendor.o(() => {
}, "49")
}) : {});
};
}
};
const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-313f45d6"]]);
wx.createComponent(Component);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/components/CelebrateBurst.js.map
@@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"tongji-icon": "./TongjiIcon"
}
}
@@ -0,0 +1 @@
<view wx:if="{{a}}" class="celebrate-root data-v-313f45d6" catchtouchmove="{{i}}"><view wx:for="{{b}}" wx:for-item="p" wx:key="a" class="celebrate-particle data-v-313f45d6" style="{{p.b}}"/><view wx:if="{{c}}" class="{{['celebrate-card', 'data-v-313f45d6', h && 'pop']}}"><view class="celebrate-card-glow data-v-313f45d6"/><tongji-icon wx:if="{{d}}" class="data-v-313f45d6" u-i="313f45d6-0" bind:__l="__l" u-p="{{d}}"/><text class="celebrate-card-title data-v-313f45d6">{{e}}</text><text wx:if="{{f}}" class="celebrate-card-sub data-v-313f45d6">{{g}}</text></view></view>
@@ -0,0 +1,95 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
/* 文字基本颜色 */
/* 背景颜色 */
/* 边框颜色 */
/* 尺寸变量 */
/* 文字尺寸 */
/* 图片尺寸 */
/* Border Radius */
/* 水平间距 */
/* 垂直间距 */
/* 透明度 */
/* 文章场景相关 */
.celebrate-root.data-v-313f45d6 {
position: fixed;
inset: 0;
z-index: 9999;
pointer-events: none;
overflow: hidden;
}
.celebrate-particle.data-v-313f45d6 {
position: absolute;
top: -20rpx;
border-radius: 4rpx;
opacity: 0.9;
animation: celebrate-fall-313f45d6 1.6s ease-in forwards;
}
@keyframes celebrate-fall-313f45d6 {
0% {
transform: translateY(0) rotate(0deg) scale(1);
opacity: 1;
}
100% {
transform: translateY(110vh) rotate(540deg) scale(0.4);
opacity: 0;
}
}
.celebrate-card.data-v-313f45d6 {
position: absolute;
left: 50%;
top: 38%;
transform: translate(-50%, -50%) scale(0.82);
width: 78%;
max-width: 560rpx;
padding: 36rpx 32rpx 32rpx;
background: rgba(255, 255, 255, 0.96);
border-radius: 28rpx;
box-shadow: 0 20rpx 60rpx rgba(8, 145, 178, 0.22);
border: 2rpx solid rgba(8, 145, 178, 0.12);
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
opacity: 0;
transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.25s ease;
}
.celebrate-card.pop.data-v-313f45d6 {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
.celebrate-card-glow.data-v-313f45d6 {
position: absolute;
inset: -20rpx;
border-radius: 36rpx;
background: radial-gradient(circle, rgba(34, 211, 238, 0.2), transparent 70%);
pointer-events: none;
}
.celebrate-card-title.data-v-313f45d6 {
position: relative;
z-index: 1;
margin-top: 16rpx;
font-size: 36rpx;
font-weight: 800;
color: #1b1c1a;
}
.celebrate-card-sub.data-v-313f45d6 {
position: relative;
z-index: 1;
margin-top: 10rpx;
font-size: 26rpx;
color: #475569;
line-height: 1.45;
}
@@ -0,0 +1,100 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
const tongji_utils_svgDataUrl = require("../utils/svgDataUrl.js");
const tongji_utils_treeLevels = require("../utils/treeLevels.js");
const _sfc_main = {
__name: "SugarTreeGraphic",
props: {
level: { type: Number, default: 0 },
size: { type: String, default: "md" },
watering: { type: Boolean, default: false }
},
setup(__props) {
const MAX_LEVEL = tongji_utils_treeLevels.TREE_MAX_LEVEL;
const treeUseFallback = common_vendor.ref(true);
const props = __props;
const clampedLevel = common_vendor.computed(() => Math.min(MAX_LEVEL, Math.max(0, Number(props.level) || 0)));
const visualTier = common_vendor.computed(() => {
const lv = clampedLevel.value;
if (lv <= 0)
return 0;
if (lv <= 2)
return 1;
if (lv <= 4)
return 2;
if (lv <= 6)
return 3;
if (lv <= 8)
return 4;
return 5;
});
const treeEmoji = common_vendor.computed(() => (tongji_utils_treeLevels.TREE_LEVELS[clampedLevel.value] || tongji_utils_treeLevels.TREE_LEVELS[0]).emoji);
function buildTreeSvg(level) {
const pot = `
<ellipse cx="24" cy="50" rx="15" ry="3" fill="#204e2b" opacity="0.12"/>
<path d="M11 46h26c1.2 0 2 1 2 2.2v3.8c0 1-.8 1.8-1.8 1.8H10.8c-1 0-1.8-.8-1.8-1.8v-3.8c0-1.2.8-2.2 2-2.2z" fill="#E7E5E4"/>
<path d="M12.5 46h23c.8 0 1.5.7 1.5 1.5v2.2c0 .6-.5 1.1-1.1 1.1H12.1c-.6 0-1.1-.5-1.1-1.1v-2.2c0-.8.7-1.5 1.5-1.5z" fill="#D6D3D1"/>
<ellipse cx="24" cy="46.5" rx="9" ry="1.6" fill="#A8A29E" opacity="0.35"/>
`;
const soil = `<ellipse cx="24" cy="44.5" rx="8" ry="2.2" fill="#386641" opacity="0.18"/>`;
if (level <= 0) {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 56" fill="none">
${pot}${soil}
<circle cx="24" cy="41.5" r="2.2" fill="#a8a29e" opacity="0.7"/>
<path d="M24 41.5v3.5" stroke="#78716c" stroke-width="1" stroke-linecap="round"/>
</svg>`;
}
const trunkH = level >= 6 ? 16 : level >= 3 ? 14 : 10;
const trunkY = 46 - trunkH;
const trunk = `<rect x="22.2" y="${trunkY}" width="3.6" height="${trunkH}" rx="1.8" fill="#78716C"/>
<rect x="22.6" y="${trunkY + 1}" width="2.8" height="${trunkH - 1}" rx="1.4" fill="#A8A29E" opacity="0.35"/>`;
const leaf = (cx, cy, r, fill, opacity = 1) => `<circle cx="${cx}" cy="${cy}" r="${r}" fill="${fill}" opacity="${opacity}"/>
<circle cx="${cx - r * 0.25}" cy="${cy - r * 0.2}" r="${r * 0.35}" fill="#eef6ef" opacity="0.55"/>`;
const bloom = level >= 7 ? leaf(17, 18, 3, "#fda4af", 0.95) + leaf(31, 17, 2.8, "#f9a8d4", 0.9) + leaf(24, 13, 3.2, "#fb7185", 0.95) + `<circle cx="24" cy="12" r="1.3" fill="#fef3c7"/>` : "";
const crown = level >= 9 ? leaf(12, 22, 6, "#34d399", 0.9) + leaf(36, 22, 6, "#34d399", 0.9) + leaf(24, 10, 7, "#386641", 0.95) + bloom : bloom;
let canopy = "";
if (level === 1) {
canopy = leaf(24, 38, 4, "#afe2b3") + `<path d="M24 38v-5" stroke="#386641" stroke-width="1.2" stroke-linecap="round"/>`;
} else if (level === 2) {
canopy = leaf(24, 32, 5.5, "#34d399") + leaf(20, 34, 3.5, "#6ee7b7", 0.9);
} else if (level <= 4) {
canopy = leaf(24, 28, 7, "#34d399") + leaf(17, 30, 5, "#6ee7b7", 0.9) + leaf(31, 30, 5, "#6ee7b7", 0.9);
} else if (level <= 6) {
canopy = leaf(24, 24, 9, "#22c55e") + leaf(14, 26, 7, "#4ade80", 0.92) + leaf(34, 26, 7, "#4ade80", 0.92) + leaf(24, 16, 6, "#386641", 0.88);
} else {
canopy = leaf(24, 20, 10, "#204e2b") + leaf(13, 24, 8, "#34d399", 0.95) + leaf(35, 24, 8, "#34d399", 0.95) + crown;
}
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 56" fill="none">${pot}${soil}${trunk}${canopy}</svg>`;
}
const treeImageSrc = common_vendor.computed(() => tongji_utils_svgDataUrl.svgToDataUrl(buildTreeSvg(clampedLevel.value)));
return (_ctx, _cache) => {
return common_vendor.e({
a: clampedLevel.value >= 7
}, clampedLevel.value >= 7 ? {} : {}, {
b: !treeUseFallback.value
}, !treeUseFallback.value ? {
c: treeImageSrc.value,
d: common_vendor.o(($event) => treeUseFallback.value = true, "20")
} : {
e: common_vendor.t(treeEmoji.value)
}, {
f: clampedLevel.value >= 7
}, clampedLevel.value >= 7 ? {} : {}, {
g: clampedLevel.value >= 8
}, clampedLevel.value >= 8 ? {} : {}, {
h: clampedLevel.value >= common_vendor.unref(MAX_LEVEL)
}, clampedLevel.value >= common_vendor.unref(MAX_LEVEL) ? {} : {}, {
i: common_vendor.n(`lv-${clampedLevel.value}`),
j: common_vendor.n(`tier-${visualTier.value}`),
k: common_vendor.n(`size-${__props.size}`),
l: common_vendor.n({
watering: __props.watering,
"is-max": clampedLevel.value >= common_vendor.unref(MAX_LEVEL)
})
});
};
}
};
const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-d91cb5e7"]]);
wx.createComponent(Component);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/components/SugarTreeGraphic.js.map
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1 @@
<view class="{{['sugar-tree-graphic', 'data-v-d91cb5e7', i, j, k, l]}}"><view class="stg-glow data-v-d91cb5e7"/><view wx:if="{{a}}" class="stg-aura data-v-d91cb5e7"/><image wx:if="{{b}}" class="stg-image data-v-d91cb5e7" src="{{c}}" mode="aspectFit" binderror="{{d}}"/><text wx:else class="stg-emoji data-v-d91cb5e7">{{e}}</text><view wx:if="{{f}}" class="stg-sparkle stg-sparkle-a data-v-d91cb5e7"/><view wx:if="{{g}}" class="stg-sparkle stg-sparkle-b data-v-d91cb5e7"/><view wx:if="{{h}}" class="stg-sparkle stg-sparkle-c data-v-d91cb5e7"/></view>
@@ -0,0 +1,153 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
/* 文字基本颜色 */
/* 背景颜色 */
/* 边框颜色 */
/* 尺寸变量 */
/* 文字尺寸 */
/* 图片尺寸 */
/* Border Radius */
/* 水平间距 */
/* 垂直间距 */
/* 透明度 */
/* 文章场景相关 */
.sugar-tree-graphic.data-v-d91cb5e7 {
position: relative;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
}
.size-sm.data-v-d91cb5e7 {
width: 64rpx;
height: 72rpx;
}
.size-md.data-v-d91cb5e7 {
width: 80rpx;
height: 88rpx;
}
.size-lg.data-v-d91cb5e7 {
width: 112rpx;
height: 124rpx;
}
.stg-glow.data-v-d91cb5e7 {
position: absolute;
inset: 6%;
border-radius: 50%;
background: radial-gradient(circle, rgba(32, 78, 43, 0.22) 0%, transparent 68%);
pointer-events: none;
}
.lv-0 .stg-glow.data-v-d91cb5e7 {
background: radial-gradient(circle, rgba(148, 163, 184, 0.25) 0%, transparent 70%);
}
.tier-4 .stg-glow.data-v-d91cb5e7,
.tier-5 .stg-glow.data-v-d91cb5e7,
.is-max .stg-glow.data-v-d91cb5e7 {
background: radial-gradient(circle, rgba(251, 191, 36, 0.3) 0%, rgba(32, 78, 43, 0.15) 55%, transparent 72%);
}
.stg-aura.data-v-d91cb5e7 {
position: absolute;
inset: -8%;
border-radius: 50%;
border: 2rpx solid rgba(253, 224, 71, 0.35);
animation: stg-aura-pulse-d91cb5e7 2.4s ease-in-out infinite;
pointer-events: none;
}
@keyframes stg-aura-pulse-d91cb5e7 {
0%, 100% {
transform: scale(0.92);
opacity: 0.5;
}
50% {
transform: scale(1.05);
opacity: 1;
}
}
.stg-image.data-v-d91cb5e7 {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
transition: transform 0.35s ease;
}
.sugar-tree-graphic.watering .stg-image.data-v-d91cb5e7,
.sugar-tree-graphic.watering .stg-emoji.data-v-d91cb5e7 {
animation: stg-water-bounce-d91cb5e7 0.65s ease;
}
.stg-emoji.data-v-d91cb5e7 {
position: relative;
z-index: 1;
line-height: 1;
font-size: 72rpx;
}
.size-sm .stg-emoji.data-v-d91cb5e7 {
font-size: 48rpx;
}
.size-md .stg-emoji.data-v-d91cb5e7 {
font-size: 60rpx;
}
.size-lg .stg-emoji.data-v-d91cb5e7 {
font-size: 88rpx;
}
@keyframes stg-water-bounce-d91cb5e7 {
0%, 100% {
transform: scale(1);
}
35% {
transform: scale(1.1) translateY(-6rpx);
}
60% {
transform: scale(0.96) translateY(2rpx);
}
}
.stg-sparkle.data-v-d91cb5e7 {
position: absolute;
z-index: 2;
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #fde68a;
box-shadow: 0 0 6rpx rgba(253, 224, 71, 0.8);
pointer-events: none;
animation: stg-sparkle-twinkle-d91cb5e7 1.8s ease-in-out infinite;
}
.stg-sparkle-a.data-v-d91cb5e7 {
top: 4%;
right: 16%;
}
.stg-sparkle-b.data-v-d91cb5e7 {
top: 12%;
left: 10%;
width: 6rpx;
height: 6rpx;
animation-delay: 0.4s;
}
.stg-sparkle-c.data-v-d91cb5e7 {
top: 22%;
right: 28%;
width: 10rpx;
height: 10rpx;
animation-delay: 0.8s;
}
@keyframes stg-sparkle-twinkle-d91cb5e7 {
0%, 100% {
opacity: 0.4;
transform: scale(0.8);
}
50% {
opacity: 1;
transform: scale(1.2);
}
}
@@ -0,0 +1,129 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
const tongji_utils_svgDataUrl = require("../utils/svgDataUrl.js");
const _sfc_main = {
__name: "TongjiIcon",
props: {
name: { type: String, required: true },
size: { type: String, default: "md" },
color: { type: String, default: "#204E2B" }
},
setup(__props) {
const props = __props;
const useFallback = common_vendor.ref(false);
const ICON_PATHS = {
view: '<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>',
ticket: '<path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M13 5v2"/><path d="M13 17v2"/><path d="M13 11v2"/>',
calendar: '<rect width="18" height="18" x="3" y="4" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/>',
flame: '<path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/>',
"check-circle": '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><path d="m9 11 3 3L22 4"/>',
glucose: '<path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z"/>',
heart: '<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/>',
activity: '<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>',
users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
user: '<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>',
person: '<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>',
send: '<path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>',
"alert-triangle": '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>',
minus: '<path d="M5 12h14"/>',
droplet: '<path d="M12 22a7 7 0 0 0 7-7c0-2-1-3.5-2.5-5.5C15 7 12 2 12 2S9 7 7.5 9.5 5 13 5 15a7 7 0 0 0 7 7z"/>',
sparkles: '<path d="m12 3-1.9 5.8L4 12l5.8 1.9L12 21l1.9-5.8L20 12l-5.8-1.9L12 3Z"/><path d="M5 3v4M19 17v4M3 5h4M17 19h4"/>',
trophy: '<path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6M18 9h1.5a2.5 2.5 0 0 0 0-5H18M4 22h16M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20 7 22M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20 17 22M18 2H6v7a6 6 0 0 0 12 0V2Z"/>',
share: '<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8M16 6l-4-4-4 4M12 2v13"/>',
plus: '<path d="M5 12h14M12 5v14"/>',
refresh: '<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8M3 3v5h5M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16M21 21v-5h-5"/>',
volume: '<path d="M11 5 6 9H2v6h4l5 4V5zM15.54 8.46a5 5 0 0 1 0 7.07M19.07 4.93a10 10 0 0 1 0 14.14"/>',
pause: '<rect width="4" height="16" x="14" y="4" rx="1"/><rect width="4" height="16" x="6" y="4" rx="1"/>',
play: '<polygon points="6 3 20 12 6 21 6 3"/>',
info: '<circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/>',
sun: '<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41-1.41M17.66 6.34l1.41-1.41M6.34 4.93l1.41 1.41"/>',
moon: '<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>',
sunset: '<path d="M12 10V2M18.364 5.636l-2.12 2.12M5.636 18.364l2.12-2.12M22 18h-3M5 18H2M18.364 18.364l-2.12-2.12M5.636 5.636l2.12 2.12M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z"/>',
mic: '<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/>',
camera: '<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/>',
bulb: '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1.3.5 2.6 1.5 3.5.8.8 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/>',
leaf: '<path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10Z"/><path d="M2 21c0-3 1.85-5.36 5.08-6"/>',
utensils: '<path d="M3 2v7c0 1.1.9 2 2 2a2 2 0 0 0 2-2V2"/><path d="M7 2v20"/><path d="M21 15V2a5 5 0 0 0-3 4.5v6a2 2 0 0 0 2 2h1Z"/><path d="M18 15v7"/>',
egg: '<path d="M12 22c4.97 0 8-3.27 8-7.31C20 9.65 16.42 2 12 2S4 9.65 4 14.69C4 18.73 7.03 22 12 22Z"/>',
home: '<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/>',
"plus-circle": '<circle cx="12" cy="12" r="10"/><path d="M8 12h8M12 8v8"/>',
"chevron-right": '<path d="m9 18 6-6-6-6"/>',
"chevron-left": '<path d="m15 18-6-6 6-6"/>',
check: '<path d="M20 6 9 17l-5-5"/>',
settings: '<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/>',
syringe: '<path d="m18 2 4 4"/><path d="m17 7 3-3"/><path d="M19 9 8.7 19.3c-1 1-2.5 1-3.4 0l-.6-.6c-1-1-1-2.5 0-3.4L15 5"/><path d="m9 11 4 4"/><path d="m5 19-3 3"/><path d="m14 4 6 6"/>',
zap: '<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>'
};
const ICON_FALLBACK = {
view: "👁",
ticket: "🎫",
calendar: "📅",
flame: "🔥",
"check-circle": "✓",
glucose: "💧",
heart: "❤",
activity: "🏃",
users: "👥",
user: "👤",
person: "👤",
send: "➤",
"alert-triangle": "⚠",
minus: "—",
droplet: "💧",
sparkles: "✨",
trophy: "🏆",
share: "↗",
plus: "",
refresh: "↻",
volume: "🔊",
pause: "⏸",
play: "▶",
info: "!",
sun: "☀",
moon: "🌙",
sunset: "☀",
mic: "🎤",
camera: "📷",
bulb: "💡",
leaf: "🥬",
utensils: "🍴",
egg: "🍳",
home: "🏠",
"plus-circle": "⊕",
"chevron-right": "",
"chevron-left": "",
check: "✓",
settings: "⚙",
syringe: "💉",
zap: "⚡"
};
const strokeColor = common_vendor.computed(() => {
const c = String(props.color || "#204E2B").trim();
return /^#[0-9A-Fa-f]{3,8}$/.test(c) ? c : "#204E2B";
});
const iconSrc = common_vendor.computed(() => {
const path = ICON_PATHS[props.name] || ICON_PATHS.view;
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${strokeColor.value}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${path}</svg>`;
return tongji_utils_svgDataUrl.svgToDataUrl(svg);
});
common_vendor.computed(() => ICON_FALLBACK[props.name] || ICON_FALLBACK.view);
function onImageError() {
useFallback.value = true;
}
return (_ctx, _cache) => {
return common_vendor.e({
a: !useFallback.value
}, !useFallback.value ? {
b: common_vendor.n(`tj-icon--${__props.name}`),
c: common_vendor.n(`tj-icon--${__props.size}`),
d: iconSrc.value,
e: common_vendor.o(onImageError, "4d")
} : {}, {
f: common_vendor.n(`tj-icon-wrap--${__props.size}`)
});
};
}
};
const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-f41c6626"]]);
wx.createComponent(Component);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/components/TongjiIcon.js.map
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1 @@
<view class="{{['tj-icon-wrap', 'data-v-f41c6626', f]}}"><image wx:if="{{a}}" class="{{['tj-icon', 'data-v-f41c6626', b, c]}}" src="{{d}}" mode="aspectFit" binderror="{{e}}"/></view>
@@ -0,0 +1,75 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
/* 文字基本颜色 */
/* 背景颜色 */
/* 边框颜色 */
/* 尺寸变量 */
/* 文字尺寸 */
/* 图片尺寸 */
/* Border Radius */
/* 水平间距 */
/* 垂直间距 */
/* 透明度 */
/* 文章场景相关 */
.tj-icon-wrap.data-v-f41c6626 {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.tj-icon-wrap--sm.data-v-f41c6626 {
width: 32rpx;
height: 32rpx;
}
.tj-icon-wrap--md.data-v-f41c6626 {
width: 40rpx;
height: 40rpx;
}
.tj-icon-wrap--lg.data-v-f41c6626 {
width: 48rpx;
height: 48rpx;
}
.tj-icon-wrap--xl.data-v-f41c6626 {
width: 56rpx;
height: 56rpx;
}
.tj-icon.data-v-f41c6626 {
display: block;
width: 100%;
height: 100%;
}
/* Lucide send 路径重心偏右上,微调使圆形按钮内视觉居中 */
.tj-icon--send.data-v-f41c6626 {
transform: translate(-10%, 10%);
}
.tj-icon-fallback.data-v-f41c6626 {
display: block;
line-height: 1;
font-weight: 700;
text-align: center;
}
.tj-icon-fallback--sm.data-v-f41c6626 {
font-size: 24rpx;
}
.tj-icon-fallback--md.data-v-f41c6626 {
font-size: 30rpx;
}
.tj-icon-fallback--lg.data-v-f41c6626 {
font-size: 36rpx;
}
.tj-icon-fallback--xl.data-v-f41c6626 {
font-size: 42rpx;
}
@@ -0,0 +1,28 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
const _sfc_main = {
__name: "VoiceSpeakMascot",
props: {
/** 正在按住说话 / 录音中 */
active: { type: Boolean, default: false },
/** 深色条背景上使用浅色卡通 */
onDark: { type: Boolean, default: false },
size: { type: String, default: "md" }
},
setup(__props) {
return (_ctx, _cache) => {
return common_vendor.e({
a: __props.active
}, __props.active ? {} : {}, {
b: common_vendor.n(`vsm--${__props.size}`),
c: common_vendor.n({
"vsm--active": __props.active,
"vsm--on-dark": __props.onDark
})
});
};
}
};
const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-e1de3b0a"]]);
wx.createComponent(Component);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/components/VoiceSpeakMascot.js.map
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1 @@
<view class="{{['vsm', 'data-v-e1de3b0a', b, c]}}"><view class="vsm-body data-v-e1de3b0a"><view class="vsm-leaf data-v-e1de3b0a" aria-hidden="true"/><view class="vsm-face data-v-e1de3b0a"><view class="vsm-eye vsm-eye--l data-v-e1de3b0a"/><view class="vsm-eye vsm-eye--r data-v-e1de3b0a"/><view class="vsm-blush vsm-blush--l data-v-e1de3b0a"/><view class="vsm-blush vsm-blush--r data-v-e1de3b0a"/><view class="vsm-mouth data-v-e1de3b0a"/></view><view wx:if="{{a}}" class="vsm-waves data-v-e1de3b0a" aria-hidden="true"><view class="vsm-wave vsm-wave--1 data-v-e1de3b0a"/><view class="vsm-wave vsm-wave--2 data-v-e1de3b0a"/><view class="vsm-wave vsm-wave--3 data-v-e1de3b0a"/></view></view></view>
@@ -0,0 +1,230 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
/* 文字基本颜色 */
/* 背景颜色 */
/* 边框颜色 */
/* 尺寸变量 */
/* 文字尺寸 */
/* 图片尺寸 */
/* Border Radius */
/* 水平间距 */
/* 垂直间距 */
/* 透明度 */
/* 文章场景相关 */
.vsm.data-v-e1de3b0a {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.vsm--md.data-v-e1de3b0a {
width: 72rpx;
height: 72rpx;
}
.vsm--sm.data-v-e1de3b0a {
width: 64rpx;
height: 64rpx;
}
.vsm-body.data-v-e1de3b0a {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.vsm-leaf.data-v-e1de3b0a {
position: absolute;
top: 2rpx;
left: 50%;
width: 16rpx;
height: 16rpx;
margin-left: -8rpx;
border-radius: 0 100% 0 100%;
background: #34d399;
transform: rotate(-18deg);
z-index: 2;
}
.vsm--on-dark .vsm-leaf.data-v-e1de3b0a {
background: #a7f3d0;
}
.vsm-face.data-v-e1de3b0a {
position: relative;
width: 52rpx;
height: 52rpx;
border-radius: 50%;
background: #fef9c3;
border: 3rpx solid #047857;
box-shadow: 0 4rpx 10rpx rgba(4, 120, 87, 0.18);
z-index: 1;
}
.vsm--sm .vsm-face.data-v-e1de3b0a {
width: 46rpx;
height: 46rpx;
}
.vsm--on-dark .vsm-face.data-v-e1de3b0a {
background: #fffbeb;
border-color: rgba(255, 255, 255, 0.85);
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.12);
}
.vsm-eye.data-v-e1de3b0a {
position: absolute;
top: 16rpx;
width: 6rpx;
height: 8rpx;
border-radius: 50%;
background: #064e3b;
}
.vsm--sm .vsm-eye.data-v-e1de3b0a {
top: 14rpx;
width: 5rpx;
height: 7rpx;
}
.vsm--on-dark .vsm-eye.data-v-e1de3b0a {
background: #134e4a;
}
.vsm-eye--l.data-v-e1de3b0a {
left: 12rpx;
}
.vsm-eye--r.data-v-e1de3b0a {
right: 12rpx;
}
.vsm--sm .vsm-eye--l.data-v-e1de3b0a {
left: 10rpx;
}
.vsm--sm .vsm-eye--r.data-v-e1de3b0a {
right: 10rpx;
}
.vsm-blush.data-v-e1de3b0a {
position: absolute;
top: 24rpx;
width: 10rpx;
height: 6rpx;
border-radius: 50%;
background: rgba(251, 113, 133, 0.45);
opacity: 0.85;
}
.vsm--sm .vsm-blush.data-v-e1de3b0a {
top: 21rpx;
width: 8rpx;
height: 5rpx;
}
.vsm-blush--l.data-v-e1de3b0a {
left: 6rpx;
}
.vsm-blush--r.data-v-e1de3b0a {
right: 6rpx;
}
.vsm--on-dark .vsm-blush.data-v-e1de3b0a {
background: rgba(255, 255, 255, 0.35);
}
.vsm-mouth.data-v-e1de3b0a {
position: absolute;
left: 50%;
bottom: 10rpx;
width: 18rpx;
height: 8rpx;
margin-left: -9rpx;
border-radius: 0 0 18rpx 18rpx;
background: #047857;
transform-origin: center top;
}
.vsm--sm .vsm-mouth.data-v-e1de3b0a {
bottom: 9rpx;
width: 16rpx;
height: 7rpx;
margin-left: -8rpx;
}
.vsm--on-dark .vsm-mouth.data-v-e1de3b0a {
background: #ecfdf5;
}
.vsm-waves.data-v-e1de3b0a {
position: absolute;
right: -2rpx;
top: 50%;
display: flex;
align-items: flex-end;
gap: 4rpx;
height: 28rpx;
margin-top: -14rpx;
z-index: 0;
}
.vsm-wave.data-v-e1de3b0a {
width: 5rpx;
border-radius: 4rpx;
background: #047857;
animation: vsm-wave-jump-e1de3b0a 0.72s ease-in-out infinite;
}
.vsm--on-dark .vsm-wave.data-v-e1de3b0a {
background: rgba(255, 255, 255, 0.9);
}
.vsm-wave--1.data-v-e1de3b0a {
height: 10rpx;
animation-delay: 0s;
}
.vsm-wave--2.data-v-e1de3b0a {
height: 18rpx;
animation-delay: 0.12s;
}
.vsm-wave--3.data-v-e1de3b0a {
height: 12rpx;
animation-delay: 0.24s;
}
.vsm--active .vsm-body.data-v-e1de3b0a {
animation: vsm-bob-e1de3b0a 0.9s ease-in-out infinite;
}
.vsm--active .vsm-mouth.data-v-e1de3b0a {
animation: vsm-talk-e1de3b0a 0.32s ease-in-out infinite alternate;
}
.vsm--active .vsm-eye.data-v-e1de3b0a {
animation: vsm-blink-e1de3b0a 2.4s ease-in-out infinite;
}
@keyframes vsm-bob-e1de3b0a {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-4rpx);
}
}
@keyframes vsm-talk-e1de3b0a {
0% {
transform: scaleY(0.45);
border-radius: 0 0 18rpx 18rpx;
}
100% {
transform: scaleY(1.15);
height: 12rpx;
border-radius: 50%;
}
}
@keyframes vsm-blink-e1de3b0a {
0%, 42%, 46%, 100% {
transform: scaleY(1);
}
44% {
transform: scaleY(0.15);
}
}
@keyframes vsm-wave-jump-e1de3b0a {
0%, 100% {
transform: scaleY(0.45);
opacity: 0.55;
}
50% {
transform: scaleY(1);
opacity: 1;
}
}
@@ -0,0 +1,307 @@
"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
@@ -0,0 +1,291 @@
"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;
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/composables/useSpeechToText.js.map
@@ -0,0 +1,64 @@
"use strict";
const common_vendor = require("../../../common/vendor.js");
function useGameAuth(proxy) {
let loginPromise = null;
function hasToken() {
return !!String(common_vendor.index.getStorageSync("token") || "").trim();
}
function clearToken() {
common_vendor.index.removeStorageSync("token");
}
function wxLoginCode() {
return new Promise((resolve, reject) => {
common_vendor.index.login({
provider: "weixin",
success: (res) => (res == null ? void 0 : res.code) ? resolve(res.code) : reject(new Error("微信登录未返回 code")),
fail: reject
});
});
}
async function loginWithCode(code) {
var _a;
const res = await proxy.apiUrl({
url: "/api/login/mnpLogin",
method: "POST",
data: { code }
}, false);
if ((res == null ? void 0 : res.code) !== 1 || !((_a = res.data) == null ? void 0 : _a.token)) {
clearToken();
throw new Error((res == null ? void 0 : res.msg) || "登录失败");
}
common_vendor.index.setStorageSync("token", res.data.token);
common_vendor.index.setStorageSync("userData", res.data);
return true;
}
async function verifyOrLogin() {
if (hasToken()) {
try {
const res = await proxy.apiUrl({ url: "/api/user/info", method: "POST" }, false);
if ((res == null ? void 0 : res.code) === 1 && res.data) {
common_vendor.index.setStorageSync("userData", res.data);
return true;
}
if ((res == null ? void 0 : res.code) !== -1)
return true;
} catch (_) {
return true;
}
clearToken();
}
const code = await wxLoginCode();
return loginWithCode(code);
}
async function ensureLoggedIn() {
if (loginPromise)
return loginPromise;
loginPromise = verifyOrLogin().then((ok) => !!ok).catch(() => false).finally(() => {
loginPromise = null;
});
return loginPromise;
}
return { ensureLoggedIn };
}
exports.useGameAuth = useGameAuth;
//# sourceMappingURL=../../../../.sourcemap/mp-weixin/tongji/endless-game/composables/useGameAuth.js.map
@@ -0,0 +1,240 @@
"use strict";
const common_vendor = require("../../../common/vendor.js");
const EMPTY_BOARD = {
week_start: "",
week_end: "",
sex_label: "同行",
group_size: 7,
member_count: 0,
players: [],
me: { count: 0, rank: 1, best_score: 0, distance: 0, is_first: true },
invite_code: ""
};
const PENDING_SYNC_KEY = "tongji_endless_pending_sync_v1";
function createSessionKey() {
const random = Math.random().toString(36).slice(2, 12);
return `game_${Date.now().toString(36)}_${random}`;
}
function readPendingPayloads() {
try {
const stored = common_vendor.index.getStorageSync(PENDING_SYNC_KEY);
if (!Array.isArray(stored))
return [];
return stored.filter((item) => item && /^[A-Za-z0-9_-]{16,64}$/.test(String(item.session_key || "")) && Number(item.learned_count) >= 0).slice(-8);
} catch (_) {
return [];
}
}
function useGamePlatform(proxy, ensureLoggedIn) {
const connected = common_vendor.ref(false);
const loading = common_vendor.ref(false);
const syncStatus = common_vendor.ref("idle");
const leaderboard = common_vendor.ref({ ...EMPTY_BOARD });
const confirmedSessionLearned = common_vendor.ref(0);
let sessionKey = createSessionKey();
let syncTimer = null;
let syncing = false;
let queuedPayloads = readPendingPayloads();
let connectPromise = null;
function persistPendingPayloads() {
try {
common_vendor.index.setStorageSync(PENDING_SYNC_KEY, queuedPayloads.slice(-8));
} catch (_) {
}
}
async function api(request) {
if (!(proxy == null ? void 0 : proxy.apiUrl))
throw new Error("平台接口未初始化");
return proxy.apiUrl(request, false);
}
function applyLeaderboard(data, confirmedForSession = "") {
if (!data || !Array.isArray(data.players))
return;
const inactiveSessionResponse = confirmedForSession && confirmedForSession !== sessionKey;
if (!inactiveSessionResponse) {
leaderboard.value = {
...EMPTY_BOARD,
...data,
me: { ...EMPTY_BOARD.me, ...data.me || {} },
players: data.players
};
}
if (confirmedForSession === sessionKey && data.confirmed_session_learned != null) {
confirmedSessionLearned.value = Number(data.confirmed_session_learned) || 0;
}
connected.value = true;
syncStatus.value = "synced";
}
async function refreshLeaderboard() {
const res = await api({
url: "/api/tcm/gameWeeklyLeaderboard",
method: "GET"
});
if ((res == null ? void 0 : res.code) !== 1 || !res.data) {
throw new Error((res == null ? void 0 : res.msg) || "同行榜加载失败");
}
applyLeaderboard(res.data);
return res.data;
}
async function acceptShare(inviteCode) {
const code = String(inviteCode || "").trim().toUpperCase();
if (!code)
return;
try {
await api({
url: "/api/tcm/gameAcceptShare",
method: "POST",
data: { invite_code: code }
});
} catch (_) {
}
}
async function connect(inviteCode = "") {
if (connectPromise)
return connectPromise;
connectPromise = (async () => {
loading.value = true;
try {
const loggedIn = await ensureLoggedIn();
if (!loggedIn)
throw new Error("登录失败");
await acceptShare(inviteCode);
await refreshLeaderboard();
if (queuedPayloads.length)
flushProgress();
return true;
} catch (_) {
connected.value = false;
syncStatus.value = "offline";
return false;
} finally {
loading.value = false;
connectPromise = null;
}
})();
return connectPromise;
}
function beginSession() {
if (syncTimer)
clearTimeout(syncTimer);
sessionKey = createSessionKey();
confirmedSessionLearned.value = 0;
syncStatus.value = queuedPayloads.length ? "pending" : connected.value ? "synced" : "offline";
return sessionKey;
}
function queueProgress(learnedCount, score, ended = false) {
const nextPayload = {
session_key: sessionKey,
learned_count: Math.max(0, Number(learnedCount) || 0),
score: Math.max(0, Number(score) || 0),
ended: ended ? 1 : 0
};
const existingIndex = queuedPayloads.findIndex((item) => item.session_key === sessionKey);
if (existingIndex >= 0) {
const existing = queuedPayloads[existingIndex];
queuedPayloads[existingIndex] = {
...nextPayload,
learned_count: Math.max(existing.learned_count, nextPayload.learned_count),
score: Math.max(existing.score, nextPayload.score),
ended: Math.max(existing.ended, nextPayload.ended)
};
} else {
queuedPayloads.push(nextPayload);
}
persistPendingPayloads();
syncStatus.value = "pending";
if (syncTimer)
clearTimeout(syncTimer);
if (ended) {
flushProgress();
} else {
syncTimer = setTimeout(flushProgress, 1e3);
}
}
async function flushProgress() {
if (syncTimer)
clearTimeout(syncTimer);
syncTimer = null;
if (syncing || !queuedPayloads.length)
return;
const payload = queuedPayloads[0];
syncing = true;
syncStatus.value = "syncing";
try {
if (!connected.value) {
const loggedIn = await ensureLoggedIn();
if (!loggedIn)
throw new Error("未登录");
}
const res = await api({
url: "/api/tcm/gameSubmitProgress",
method: "POST",
data: payload
});
if ((res == null ? void 0 : res.code) !== 1 || !res.data) {
throw new Error((res == null ? void 0 : res.msg) || "成绩保存失败");
}
applyLeaderboard(res.data, payload.session_key);
queuedPayloads = queuedPayloads.filter((item) => item.session_key !== payload.session_key || Number(item.learned_count) > Number(payload.learned_count) || Number(item.score) > Number(payload.score) || Number(item.ended) > Number(payload.ended));
persistPendingPayloads();
} catch (_) {
connected.value = false;
syncStatus.value = "offline";
persistPendingPayloads();
} finally {
syncing = false;
if (queuedPayloads.length && connected.value) {
setTimeout(flushProgress, 80);
}
}
}
async function syncAndRefresh(learnedCount, score) {
queueProgress(learnedCount, score, false);
await flushProgress();
if (!connected.value) {
await connect();
if (queuedPayloads.length)
await flushProgress();
} else {
try {
await refreshLeaderboard();
} catch (_) {
}
}
}
async function recordShare() {
var _a;
try {
if (!connected.value && !await connect())
return;
const res = await api({ url: "/api/tcm/gameRecordShare", method: "POST" });
if ((res == null ? void 0 : res.code) === 1 && ((_a = res.data) == null ? void 0 : _a.invite_code)) {
leaderboard.value = { ...leaderboard.value, invite_code: res.data.invite_code };
}
} catch (_) {
}
}
function dispose() {
if (syncTimer)
clearTimeout(syncTimer);
syncTimer = null;
persistPendingPayloads();
}
return {
connected,
loading,
syncStatus,
leaderboard,
confirmedSessionLearned,
connect,
beginSession,
queueProgress,
flushProgress,
syncAndRefresh,
refreshLeaderboard,
recordShare,
dispose
};
}
exports.useGamePlatform = useGamePlatform;
//# sourceMappingURL=../../../../.sourcemap/mp-weixin/tongji/endless-game/composables/useGamePlatform.js.map
@@ -0,0 +1,366 @@
"use strict";
const common_vendor = require("../../../common/vendor.js");
const STORAGE_KEY = "tongji_game_sfx_enabled";
const COS = "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file";
const SRC = {
tap: `${COS}/20260526/20260526105128557ef8669.mp3`,
swoosh: `${COS}/20260527/202605271539371ebe13672.mp3`,
pop: `${COS}/20260526/202605261051282a0a94508.mp3`,
buzz: `${COS}/20260527/202605271539376ff628472.mp3`,
chime: `${COS}/20260528/202605280937077bad76716.mp3`,
fanfare: `${COS}/20260528/20260528093707b021f5794.mp3`,
blast: `${COS}/20260528/20260528093707ebd4d5733.mp3`,
sparkle: `${COS}/20260528/20260528093707016f07427.mp3`,
stinger: `${COS}/20260528/20260528093709c669f4659.mp3`
};
const SFX = {
// —— 基础素材别名(供分层音效直接调用)——
tap: { src: SRC.tap, volume: 0.32, rate: 1, pool: 3 },
pop: { src: SRC.pop, volume: 0.3, rate: 1, pool: 4 },
fanfare: { src: SRC.fanfare, volume: 0.56, rate: 1, pool: 2 },
blast: { src: SRC.blast, volume: 0.62, rate: 1, pool: 2 },
sparkle: { src: SRC.sparkle, volume: 0.5, rate: 1, pool: 2 },
// —— 交互 ——
select: { src: SRC.tap, volume: 0.32, rate: 1.05, pool: 3 },
deselect: { src: SRC.tap, volume: 0.22, rate: 0.82, pool: 2 },
hint: { src: SRC.tap, volume: 0.26, rate: 1.28, pool: 2 },
swap: { src: SRC.swoosh, volume: 0.38, rate: 1, pool: 3 },
swapFail: { src: SRC.buzz, volume: 0.36, rate: 1.18, pool: 2 },
drop: { src: SRC.pop, volume: 0.3, rate: 0.88, pool: 4 },
dropLight: { src: SRC.pop, volume: 0.22, rate: 1.22, pool: 2 },
// —— 消除(按连数)——
match3: { src: SRC.chime, volume: 0.48, rate: 1, pool: 4 },
match4: { src: SRC.fanfare, volume: 0.54, rate: 1.06, pool: 3 },
match5: { src: SRC.blast, volume: 0.62, rate: 1, pool: 3 },
// —— 消除(按 GI 主色)——
matchLowGi: { src: SRC.chime, volume: 0.42, rate: 0.86, pool: 2 },
matchMidGi: { src: SRC.chime, volume: 0.46, rate: 1.02, pool: 2 },
matchHighGi: { src: SRC.buzz, volume: 0.44, rate: 0.92, pool: 2 },
// —— 连消 ——
combo2: { src: SRC.fanfare, volume: 0.56, rate: 1.12, pool: 2 },
combo3: { src: SRC.blast, volume: 0.64, rate: 1.08, pool: 2 },
comboMega: { src: SRC.blast, volume: 0.72, rate: 1.22, pool: 2 },
// —— 道具 ——
insulin: { src: SRC.sparkle, volume: 0.5, rate: 1.15, pool: 2 },
fiber: { src: SRC.swoosh, volume: 0.48, rate: 1.32, pool: 2 },
meal: { src: SRC.stinger, volume: 0.42, rate: 1.35, pool: 1 },
reshuffle: { src: SRC.swoosh, volume: 0.46, rate: 0.72, pool: 2 },
// —— 反馈 ——
score: { src: SRC.tap, volume: 0.28, rate: 1.38, pool: 2 },
goalTick: { src: SRC.chime, volume: 0.36, rate: 1.45, pool: 2 },
meterWarn: { src: SRC.buzz, volume: 0.5, rate: 1, pool: 2 },
meterDanger: { src: SRC.buzz, volume: 0.58, rate: 0.78, pool: 2 },
meterStable: { src: SRC.pop, volume: 0.24, rate: 1.05, pool: 1 },
win: { src: SRC.fanfare, volume: 0.66, rate: 1, pool: 1 },
winStinger: { src: SRC.stinger, volume: 0.38, rate: 1.5, pool: 1 },
lose: { src: SRC.buzz, volume: 0.52, rate: 0.68, pool: 1 },
// 兼容旧名
match: { src: SRC.chime, volume: 0.48, rate: 1, pool: 4 },
combo: { src: SRC.fanfare, volume: 0.56, rate: 1.1, pool: 3 },
heal: { src: SRC.pop, volume: 0.44, rate: 1.18, pool: 2 },
boost: { src: SRC.sparkle, volume: 0.52, rate: 1, pool: 2 },
invalid: { src: SRC.buzz, volume: 0.4, rate: 1.15, pool: 2 },
danger: { src: SRC.blast, volume: 0.58, rate: 0.95, pool: 2 }
};
const WARMUP_SFX = [
"select",
"swap",
"swapFail",
"drop",
"dropLight",
"match3",
"match4",
"match5",
"combo2",
"combo3",
"comboMega",
"insulin",
"sparkle",
"reshuffle",
"lose"
];
function createAudioContext() {
if (typeof Audio !== "undefined") {
const audio = new Audio();
audio.preload = "auto";
return {
get src() {
return audio.src;
},
set src(value) {
audio.src = value;
},
get volume() {
return audio.volume;
},
set volume(value) {
audio.volume = value;
},
get playbackRate() {
return audio.playbackRate;
},
set playbackRate(value) {
audio.playbackRate = value;
},
play() {
const promise = audio.play();
if (promise == null ? void 0 : promise.catch)
promise.catch(() => {
});
},
stop() {
audio.pause();
try {
audio.currentTime = 0;
} catch (_) {
}
},
seek(time) {
try {
audio.currentTime = time;
} catch (_) {
}
},
destroy() {
audio.pause();
audio.removeAttribute("src");
}
};
}
return common_vendor.index.createInnerAudioContext();
}
class SfxPool {
constructor(src, size, volume, rate = 1) {
this.src = src;
this.size = size;
this.volume = volume;
this.rate = rate;
this.list = [];
this.cursor = 0;
this.warmedUp = false;
}
createOne() {
const ctx = createAudioContext();
ctx.src = this.src;
ctx.obeyMuteSwitch = false;
ctx.autoplay = false;
ctx.volume = this.volume;
try {
ctx.playbackRate = this.rate;
} catch (_) {
}
this.list.push(ctx);
return ctx;
}
create() {
while (this.list.length < this.size)
this.createOne();
}
warmUp() {
if (this.warmedUp)
return;
const ctx = this.list[0] || this.createOne();
try {
ctx.volume = 0;
ctx.play();
setTimeout(() => {
try {
ctx.stop();
ctx.volume = this.volume;
} catch (_) {
}
}, 60);
} catch (_) {
}
if (this.size > 1)
this.cursor = 1;
this.warmedUp = true;
}
play(scale = 1, rateMul = 1) {
if (this.list.length < this.size)
this.create();
const ctx = this.list[this.cursor % this.list.length];
this.cursor += 1;
const vol = Math.min(1, this.volume * scale);
const rate = Math.min(2, Math.max(0.5, this.rate * rateMul));
try {
ctx.volume = vol;
try {
ctx.playbackRate = rate;
} catch (_) {
}
ctx.stop();
ctx.seek(0);
ctx.play();
} catch (_) {
try {
ctx.play();
} catch (e) {
}
}
}
destroy() {
this.list.forEach((ctx) => {
try {
ctx.stop();
ctx.destroy();
} catch (_) {
}
});
this.list = [];
this.warmedUp = false;
}
}
function dominantGi(tiles = []) {
const cnt = { low: 0, mid: 0, high: 0 };
tiles.forEach((t) => {
var _a;
const g = (_a = t == null ? void 0 : t.type) == null ? void 0 : _a.gi;
if (g && cnt[g] !== void 0)
cnt[g] += 1;
});
let best = "mid";
let max = 0;
Object.entries(cnt).forEach(([k, v]) => {
if (v > max) {
max = v;
best = k;
}
});
return best;
}
function useGameSfx() {
const enabled = common_vendor.ref(true);
const pools = {};
try {
const stored = common_vendor.index.getStorageSync(STORAGE_KEY);
if (stored === false || stored === "0" || stored === 0) {
enabled.value = false;
}
} catch (_) {
}
function ensureAudioOption() {
try {
common_vendor.index.setInnerAudioOption({
obeyMuteSwitch: false,
mixWithOther: true
});
} catch (_) {
}
}
function getPool(name) {
const cfg = SFX[name];
if (!cfg)
return null;
if (!pools[name]) {
pools[name] = new SfxPool(cfg.src, cfg.pool, cfg.volume, cfg.rate ?? 1);
}
return pools[name];
}
function warmUp() {
if (!enabled.value)
return;
ensureAudioOption();
const names = typeof Audio !== "undefined" ? ["select"] : WARMUP_SFX;
names.forEach((name) => {
var _a;
(_a = getPool(name)) == null ? void 0 : _a.warmUp();
});
}
function play(name, scale = 1, rateMul = 1) {
var _a;
if (!enabled.value)
return;
(_a = getPool(name)) == null ? void 0 : _a.play(scale, rateMul);
}
function playLayer(primary, secondary, delayMs = 70, secScale = 0.6) {
play(primary);
if (secondary) {
setTimeout(() => play(secondary, secScale), delayMs);
}
}
function playMatch({ matchCount = 3, chain = 0, tiles = [] } = {}) {
if (!enabled.value)
return;
const giTrack = (() => {
const gi = dominantGi(tiles);
if (gi === "low")
return "matchLowGi";
if (gi === "high")
return "matchHighGi";
return "matchMidGi";
})();
if (chain >= 3) {
playLayer("comboMega", giTrack, 90, 0.55);
return;
}
if (chain === 2) {
playLayer("combo3", giTrack, 80, 0.5);
return;
}
if (chain === 1) {
playLayer("combo2", giTrack, 70, 0.45);
return;
}
if (matchCount >= 5) {
playLayer("match5", "matchHighGi", 85, 0.5);
} else if (matchCount >= 4) {
playLayer("match4", giTrack, 65, 0.48);
} else {
play(giTrack, 1, 1);
setTimeout(() => play("match3", 0.85), 40);
}
}
function playDrop(count = 1) {
if (!enabled.value || count <= 0)
return;
const name = count >= 4 ? "drop" : "dropLight";
play(name, Math.min(1.2, 0.85 + count * 0.04), count >= 6 ? 0.9 : 1);
}
function playMeter(delta, level) {
if (!enabled.value)
return;
if (level > 70)
play("meterDanger", 1 + (level - 70) * 0.02);
else if (level < 30)
play("meterWarn", 0.9, 0.95);
else if (delta > 8)
play("meterWarn", 0.75);
else if (delta < -5)
play("meterStable", 1.1);
}
function toggleEnabled() {
enabled.value = !enabled.value;
try {
common_vendor.index.setStorageSync(STORAGE_KEY, enabled.value ? "1" : "0");
} catch (_) {
}
if (enabled.value) {
warmUp();
play("select");
}
}
function destroyAll() {
Object.keys(pools).forEach((key) => {
var _a;
(_a = pools[key]) == null ? void 0 : _a.destroy();
delete pools[key];
});
}
common_vendor.onUnmounted(() => {
destroyAll();
});
return {
enabled,
play,
playLayer,
playMatch,
playDrop,
playMeter,
warmUp,
toggleEnabled,
destroyAll
};
}
exports.useGameSfx = useGameSfx;
//# sourceMappingURL=../../../../.sourcemap/mp-weixin/tongji/endless-game/composables/useGameSfx.js.map
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,11 @@
{
"navigationStyle": "custom",
"navigationBarTitleText": "识糖小课堂",
"backgroundColor": "#eefbf4",
"disableScroll": false,
"enableShareAppMessage": true,
"enableShareTimeline": true,
"usingComponents": {
"tongji-icon": "../components/TongjiIcon"
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,17 @@
{
"navigationBarTitleText": "血糖记录",
"navigationBarBackgroundColor": "#204e2b",
"navigationBarTextStyle": "white",
"backgroundColor": "#faf9f5",
"enablePullDownRefresh": true,
"usingPlugins": {
"WechatSI": {
"version": "0.3.5",
"provider": "wx069ba97219f66d99"
}
},
"usingComponents": {
"sugar-tree-graphic": "../components/SugarTreeGraphic",
"tongji-icon": "../components/TongjiIcon"
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
{
"navigationStyle": "custom",
"navigationBarTitleText": "日常护理",
"backgroundColor": "#f4fbf4",
"enablePullDownRefresh": true,
"usingPlugins": {
"WechatSI": {
"version": "0.3.5",
"provider": "wx069ba97219f66d99"
}
},
"usingComponents": {
"sugar-tree-graphic": "../components/SugarTreeGraphic",
"tongji-icon": "../components/TongjiIcon",
"celebrate-burst": "../components/CelebrateBurst",
"voice-speak-mascot": "../components/VoiceSpeakMascot"
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,17 @@
{
"navigationStyle": "custom",
"navigationBarTitleText": "血糖管理",
"backgroundColor": "#f4fbf4",
"enablePullDownRefresh": true,
"usingPlugins": {
"WechatSI": {
"version": "0.3.5",
"provider": "wx069ba97219f66d99"
}
},
"usingComponents": {
"tongji-icon": "../components/TongjiIcon",
"tab-bar-dock": "../../components/app-tab-bar/tab-bar-dock",
"dev-training-entry": "../../components/dev-training-entry/index"
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,141 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
function joinApiUrl(base, path) {
const b = String(base || "").replace(/\/+$/, "");
const p = String(path || "").replace(/^\/+/, "");
return p ? `${b}/${p}` : b;
}
function appendQuery(url, data) {
if (!data || typeof data !== "object")
return url;
const keys = Object.keys(data);
if (!keys.length)
return url;
const qs = keys.map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(data[k] ?? "")}`).join("&");
return url + (url.includes("?") ? "&" : "?") + qs;
}
function decodeChunkData(data) {
if (typeof data === "string")
return data;
if (!data)
return "";
if (typeof TextDecoder !== "undefined" && data instanceof ArrayBuffer) {
return new TextDecoder("utf-8").decode(new Uint8Array(data));
}
if (data instanceof ArrayBuffer) {
const bytes = new Uint8Array(data);
let str = "";
for (let i = 0; i < bytes.length; i++)
str += String.fromCharCode(bytes[i]);
try {
return decodeURIComponent(escape(str));
} catch (e) {
return str;
}
}
return "";
}
function consumeSseBuffer(buffer, onEvent) {
let rest = buffer;
let idx = rest.indexOf("\n\n");
while (idx !== -1) {
const block = rest.slice(0, idx);
rest = rest.slice(idx + 2);
let event = "message";
let dataLine = "";
block.split("\n").forEach((line) => {
if (line.startsWith("event:"))
event = line.slice(6).trim();
else if (line.startsWith("data:"))
dataLine += line.slice(5).trim();
});
if (dataLine) {
try {
onEvent(event, JSON.parse(dataLine));
} catch (e) {
onEvent(event, dataLine);
}
}
idx = rest.indexOf("\n\n");
}
return rest;
}
function parsePartialDietPlainText(text) {
const out = {};
if (!text)
return out;
const lineRe = /^(早餐|喝的|午餐|晚餐|提示|少碰)[:]\s*(.*)$/gm;
let match;
while ((match = lineRe.exec(text)) !== null) {
const key = { "早餐": "breakfast", "喝的": "drinks", "午餐": "lunch", "晚餐": "dinner", "提示": "tips", "少碰": "avoid" }[match[1]];
const val = (match[2] || "").trim();
if (key === "avoid") {
out.avoid = val.split(/[、,;\s]+/).map((s) => s.trim()).filter(Boolean);
} else {
out[key] = val;
}
}
const tail = text.match(/(?:^|\n)(早餐|喝的|午餐|晚餐|提示|少碰)[:]\s*([^\n]*)$/);
if (tail) {
const key = { "早餐": "breakfast", "喝的": "drinks", "午餐": "lunch", "晚餐": "dinner", "提示": "tips", "少碰": "avoid" }[tail[1]];
const val = (tail[2] || "").trim();
if (key === "avoid") {
if (val)
out.avoid = val.split(/[、,;\s]+/).map((s) => s.trim()).filter(Boolean);
} else {
out[key] = val;
}
}
return out;
}
function requestAiStream(opts) {
const {
baseUrl,
url,
method = "GET",
data = {},
onEvent,
fallback
} = opts;
return new Promise((resolve, reject) => {
const token = common_vendor.index.getStorageSync("token") || "";
let sseBuffer = "";
let requestUrl = joinApiUrl(baseUrl, url);
if (method === "GET") {
requestUrl = appendQuery(requestUrl, data);
}
const task = common_vendor.wx$1.request({
url: requestUrl,
method,
data: method === "POST" ? data : {},
enableChunked: true,
timeout: 6e4,
header: {
token,
"content-type": "application/x-www-form-urlencoded",
"Cache-Control": "no-cache"
},
success: (res) => {
if (res && res.data) {
sseBuffer += decodeChunkData(res.data);
sseBuffer = consumeSseBuffer(sseBuffer, onEvent);
}
resolve(res);
},
fail: (err) => reject(err)
});
if (task && typeof task.onChunkReceived === "function") {
task.onChunkReceived((res) => {
sseBuffer += decodeChunkData(res.data);
sseBuffer = consumeSseBuffer(sseBuffer, onEvent);
});
} else if (typeof fallback === "function") {
fallback().then(resolve).catch(reject);
} else {
reject(new Error("当前环境不支持流式请求"));
}
});
}
exports.parsePartialDietPlainText = parsePartialDietPlainText;
exports.requestAiStream = requestAiStream;
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/utils/aiStreamRequest.js.map
@@ -0,0 +1,48 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
function utf8ToBytes(str) {
const encoded = encodeURIComponent(str);
const bytes = [];
for (let i = 0; i < encoded.length; i++) {
if (encoded.charCodeAt(i) === 37) {
bytes.push(parseInt(encoded.substring(i + 1, i + 3), 16));
i += 2;
} else {
bytes.push(encoded.charCodeAt(i));
}
}
return new Uint8Array(bytes);
}
function bytesToBase64(bytes) {
if (typeof common_vendor.index !== "undefined" && typeof common_vendor.index.arrayBufferToBase64 === "function") {
return common_vendor.index.arrayBufferToBase64(bytes.buffer);
}
if (typeof btoa !== "undefined") {
let binary = "";
for (let i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let result = "";
for (let i = 0; i < bytes.length; i += 3) {
const a = bytes[i];
const b = i + 1 < bytes.length ? bytes[i + 1] : 0;
const c = i + 2 < bytes.length ? bytes[i + 2] : 0;
result += chars[a >> 2];
result += chars[(a & 3) << 4 | b >> 4];
result += i + 1 < bytes.length ? chars[(b & 15) << 2 | c >> 6] : "=";
result += i + 2 < bytes.length ? chars[c & 63] : "=";
}
return result;
}
function svgToDataUrl(svg) {
const normalized = String(svg || "").trim();
if (!normalized)
return "";
const base64 = bytesToBase64(utf8ToBytes(normalized));
return `data:image/svg+xml;base64,${base64}`;
}
exports.svgToDataUrl = svgToDataUrl;
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/utils/svgDataUrl.js.map
@@ -0,0 +1,60 @@
"use strict";
const TREE_MAX_LEVEL = 9;
const TREE_XP_PER_LEVEL = 50;
const TREE_LEVELS = [
{ level: 0, name: "种子眠", emoji: "🫘", mood: "困困", desc: "稳糖种子在土里打盹" },
{ level: 1, name: "破土芽", emoji: "🌱", mood: "探头", desc: "探出第一抹新绿" },
{ level: 2, name: "展两叶", emoji: "🌿", mood: "好奇", desc: "两片嫩叶迎风展" },
{ level: 3, name: "小树苗", emoji: "🪴", mood: "精神", desc: "身子骨硬朗起来" },
{ level: 4, name: "青枝繁", emoji: "🌳", mood: "茁壮", desc: "枝叶渐密,元气足" },
{ level: 5, name: "拔节高", emoji: "🌲", mood: "挺拔", desc: "一节一节往上蹿" },
{ level: 6, name: "稳糖冠", emoji: "💚", mood: "沉稳", desc: "树冠成形,习惯成自然" },
{ level: 7, name: "初绽香", emoji: "🌸", mood: "开心", desc: "枝头冒出第一朵花" },
{ level: 8, name: "漫开花", emoji: "🌺", mood: "灿烂", desc: "花开满枝,越记越稳" },
{ level: 9, name: "圆满树", emoji: "🏆", mood: "荣耀", desc: "满级大树,习惯大师" }
];
const WHISPERS_BY_LEVEL = {
0: ["种子在睡觉,记一笔就醒啦。", "今天浇第一滴水,芽就要冒出来。"],
1: ["破土啦!再坚持几天就长高。", "小芽最喜欢规律的记录了。"],
2: ["两片叶子为你鼓掌。", "空腹餐后都记全,我会长得更快。"],
3: ["我已经是一棵小树啦。", "家人点赞的时候,我也会发光。"],
4: ["枝叶越来越密,您真棒。", "连续记录,我会开出更多叶子。"],
5: ["拔节中!习惯比完美更重要。", "再浇一点水,我就更高啦。"],
6: ["习惯成自然,树冠成形啦。", "您今天的坚持,小树都记得。"],
7: ["开花啦!闻到春天的味道了吗?", "稳糖花只开给坚持的人。"],
8: ["满树花香,您已是控糖达人。", "明天继续来,花儿会更艳。"],
9: ["满级大树陪您一路稳糖。", "圆满不是终点,习惯才是。"]
};
function calcTreeFromPoints(points) {
const pts = Math.max(0, Number(points) || 0);
const level = Math.min(TREE_MAX_LEVEL, Math.floor(pts / TREE_XP_PER_LEVEL));
const xpInLevel = level >= TREE_MAX_LEVEL ? TREE_XP_PER_LEVEL : pts % TREE_XP_PER_LEVEL;
const progress = level >= TREE_MAX_LEVEL ? 100 : Math.round(xpInLevel / TREE_XP_PER_LEVEL * 100);
const meta = TREE_LEVELS[level] || TREE_LEVELS[0];
const nextMeta = level < TREE_MAX_LEVEL ? TREE_LEVELS[level + 1] : null;
const pointsToNext = level >= TREE_MAX_LEVEL ? 0 : TREE_XP_PER_LEVEL - xpInLevel;
return {
level,
progress,
name: meta.name,
emoji: meta.emoji,
mood: meta.mood,
desc: meta.desc,
xpInLevel,
xpNeed: TREE_XP_PER_LEVEL,
pointsToNext,
nextName: (nextMeta == null ? void 0 : nextMeta.name) || "",
isMax: level >= TREE_MAX_LEVEL
};
}
function pickTreeWhisper(level) {
const lv = Math.min(TREE_MAX_LEVEL, Math.max(0, Number(level) || 0));
const list = WHISPERS_BY_LEVEL[lv] || WHISPERS_BY_LEVEL[0];
return list[Math.floor(Math.random() * list.length)];
}
exports.TREE_LEVELS = TREE_LEVELS;
exports.TREE_MAX_LEVEL = TREE_MAX_LEVEL;
exports.TREE_XP_PER_LEVEL = TREE_XP_PER_LEVEL;
exports.calcTreeFromPoints = calcTreeFromPoints;
exports.pickTreeWhisper = pickTreeWhisper;
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/utils/treeLevels.js.map
@@ -0,0 +1,68 @@
"use strict";
const tongji_utils_treeLevels = require("./treeLevels.js");
const TREE_SPECIES_LIST = [
{
id: "classic",
name: "稳糖灵树",
tagline: "经典路线,从种子到圆满",
stages: tongji_utils_treeLevels.TREE_LEVELS.map((l) => l.emoji)
},
{
id: "bonsai",
name: "雅韵盆景",
tagline: "小巧精致,书桌旁的温柔陪伴",
stages: ["🫘", "🌱", "🪴", "🎋", "🪴", "🌳", "🎍", "🌸", "🏵️", "🏆"]
},
{
id: "pine",
name: "苍劲青松",
tagline: "挺拔向上,越记越稳",
stages: ["🫘", "🌱", "🌿", "🌲", "🌲", "🌲", "⛰️", "🌲", "🏔️", "🏆"]
},
{
id: "sakura",
name: "樱花小树",
tagline: "坚持打卡,枝头渐开",
stages: ["🫘", "🌱", "🌿", "🌳", "🌸", "🌸", "🌸", "🌸", "🌺", "🏆"]
},
{
id: "ginkgo",
name: "金秋银杏",
tagline: "叶色渐变,见证每一天",
stages: ["🫘", "🌱", "🍃", "🌳", "🍂", "🍂", "🌳", "🍁", "🌟", "🏆"]
}
];
const DAILY_MAX_TASK_POINTS = 40;
function getTreeSpecies(id) {
return TREE_SPECIES_LIST.find((s) => s.id === id) || TREE_SPECIES_LIST[0];
}
function speciesEmojiAtLevel(speciesId, level) {
const sp = getTreeSpecies(speciesId);
const lv = Math.min(tongji_utils_treeLevels.TREE_MAX_LEVEL, Math.max(0, Number(level) || 0));
return sp.stages[lv] || sp.stages[0] || "🌱";
}
function speciesGrowthRoadmap(speciesId) {
const sp = getTreeSpecies(speciesId);
return tongji_utils_treeLevels.TREE_LEVELS.map((meta, i) => {
const xpTotal = i * tongji_utils_treeLevels.TREE_XP_PER_LEVEL;
const daysHint = i === 0 ? 0 : Math.max(1, Math.ceil(xpTotal / DAILY_MAX_TASK_POINTS));
return {
level: i,
name: meta.name,
emoji: sp.stages[i] || meta.emoji,
desc: meta.desc,
xpTotal,
daysHint
};
});
}
function estimateDaysToMaxLevel() {
const totalXp = tongji_utils_treeLevels.TREE_MAX_LEVEL * tongji_utils_treeLevels.TREE_XP_PER_LEVEL;
return Math.max(1, Math.ceil(totalXp / DAILY_MAX_TASK_POINTS));
}
exports.TREE_SPECIES_LIST = TREE_SPECIES_LIST;
exports.estimateDaysToMaxLevel = estimateDaysToMaxLevel;
exports.getTreeSpecies = getTreeSpecies;
exports.speciesEmojiAtLevel = speciesEmojiAtLevel;
exports.speciesGrowthRoadmap = speciesGrowthRoadmap;
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/utils/treeSpecies.js.map
@@ -0,0 +1,310 @@
"use strict";
const CN_DIGIT = {
: 0,
"": 0,
: 1,
: 1,
: 1,
: 2,
: 2,
: 2,
: 3,
: 3,
: 4,
: 4,
: 5,
: 5,
: 6,
: 6,
: 7,
: 7,
: 8,
: 8,
: 9,
: 9
};
const CN_UNIT = { : 10, : 10, : 100, : 100, : 1e3, : 1e3, : 1e4, 亿: 1e8 };
function cnIntToNumber(s) {
let total = 0;
let section = 0;
let number = 0;
let hadUnit = false;
let lastUnit = 0;
let sawZeroAfterUnit = false;
for (const ch of s) {
if (CN_DIGIT[ch] !== void 0) {
number = CN_DIGIT[ch];
if (number === 0)
sawZeroAfterUnit = true;
} else if (CN_UNIT[ch] !== void 0) {
hadUnit = true;
const unit = CN_UNIT[ch];
lastUnit = unit;
sawZeroAfterUnit = false;
if (unit >= 1e4) {
section = (section + number) * unit;
total += section;
section = 0;
} else {
if (number === 0)
number = 1;
section += number * unit;
}
number = 0;
}
}
if (!hadUnit && s.length > 1) {
let joined = "";
for (const ch of s) {
if (CN_DIGIT[ch] !== void 0)
joined += CN_DIGIT[ch];
}
if (joined)
return Number(joined);
}
if (number > 0 && lastUnit >= 100 && !sawZeroAfterUnit) {
number = number * (lastUnit / 10);
}
return total + section + number;
}
function cnSeqToNumber(seq) {
if (seq.includes("点")) {
const parts = seq.split("点");
const intPart = parts[0];
const decPart = parts.slice(1).join("");
const intVal = intPart ? cnIntToNumber(intPart) : 0;
let decStr = "";
for (const ch of decPart) {
if (CN_DIGIT[ch] !== void 0)
decStr += CN_DIGIT[ch];
}
if (!decStr)
return intVal;
return Number(`${intVal}.${decStr}`);
}
return cnIntToNumber(seq);
}
function hasCnDigit(seq) {
for (const ch of seq) {
if (CN_DIGIT[ch] !== void 0 || CN_UNIT[ch] !== void 0)
return true;
}
return false;
}
function normalizeNumbers(text) {
const raw = String(text || "");
return raw.replace(/[零〇一壹幺二贰两三叁四肆五伍六陆七柒八捌九玖十拾百佰千仟万亿点]+/g, (m) => {
if (!hasCnDigit(m))
return m;
const n = cnSeqToNumber(m);
return Number.isFinite(n) ? String(n) : m;
});
}
function numAfter(text, keys) {
for (const k of keys) {
const i = text.indexOf(k);
if (i >= 0) {
const rest = text.slice(i + k.length);
const m = rest.match(/-?\d+(?:\.\d+)?/);
if (m)
return m[0];
}
}
return "";
}
function textAfter(text, keys, stopKeys = []) {
for (const k of keys) {
const i = text.indexOf(k);
if (i >= 0) {
let rest = text.slice(i + k.length);
rest = rest.replace(/^[是为吃了喝了吃的喝的有打了用了::,,、。\s]+/, "");
let end = rest.length;
const mStop = rest.match(/[,。.;!?\n]/);
if (mStop && mStop.index < end)
end = mStop.index;
for (const sk of stopKeys) {
const si = rest.indexOf(sk);
if (si >= 0 && si < end)
end = si;
}
const seg = rest.slice(0, end).trim();
if (seg)
return seg;
}
}
return "";
}
function parseGlucose(raw) {
const t = normalizeNumbers(raw);
const result = {};
const fasting = numAfter(t, ["空腹"]);
const post = numAfter(t, ["餐后", "饭后"]);
const other = numAfter(t, ["其他", "随机", "睡前", "凌晨", "夜间", "晚上"]);
if (fasting)
result.fasting_blood_sugar = fasting;
if (post)
result.postprandial_blood_sugar = post;
if (other)
result.other_blood_sugar = other;
if (!fasting && !post && !other) {
const nums = t.match(/\d+(?:\.\d+)?/g);
if (nums && nums.length === 1)
result.other_blood_sugar = nums[0];
}
return result;
}
function parseBloodPressure(raw) {
const t = normalizeNumbers(raw);
const result = {};
let sys = numAfter(t, ["高压", "收缩压", "收缩"]);
let dia = numAfter(t, ["低压", "舒张压", "舒张"]);
if (!sys || !dia) {
const nums = (t.match(/\d{2,3}/g) || []).map(Number).filter((n) => n >= 30 && n <= 300);
if (!sys && !dia && nums.length >= 2) {
sys = String(nums[0]);
dia = String(nums[1]);
} else if (!sys && nums.length === 1 && nums[0] >= 90) {
sys = String(nums[0]);
}
}
if (sys && dia && Number(sys) < Number(dia)) {
const tmp = sys;
sys = dia;
dia = tmp;
}
if (sys)
result.systolic_pressure = sys;
if (dia)
result.diastolic_pressure = dia;
const insulin = textAfter(raw, ["胰岛素"], ["高压", "低压", "血压", "西药", "备注"]);
if (insulin)
result.insulin = insulin;
const western = textAfter(raw, ["西药", "降糖药", "口服药"], ["胰岛素", "高压", "低压", "血压", "备注"]);
if (western)
result.western_medicine = western;
return result;
}
const DIET_MARKERS = [
{ keys: ["早餐", "早饭", "早上", "早点", "早晨"], field: "breakfast_foods" },
{ keys: ["午餐", "午饭", "中午"], field: "lunch_foods" },
{ keys: ["晚餐", "晚饭", "晚上", "夜宵", "夜里"], field: "dinner_foods" }
];
function cleanFoodSeg(seg) {
return String(seg || "").replace(/^[是为吃了吃的喝了喝的有::,,、。\s]+/, "").replace(/[。.\s]+$/, "").trim();
}
function parseDiet(raw) {
const t = String(raw || "").trim();
const result = {};
const hits = [];
DIET_MARKERS.forEach((m) => {
for (const k of m.keys) {
const idx = t.indexOf(k);
if (idx >= 0) {
hits.push({ idx, field: m.field, klen: k.length });
break;
}
}
});
if (!hits.length) {
if (t)
result.note = t;
return result;
}
hits.sort((a, b) => a.idx - b.idx);
hits.forEach((h, i) => {
const start = h.idx + h.klen;
const end = i + 1 < hits.length ? hits[i + 1].idx : t.length;
const seg = cleanFoodSeg(t.slice(start, end));
if (seg && !result[h.field])
result[h.field] = seg;
});
const head = cleanFoodSeg(t.slice(0, hits[0].idx));
if (head)
result.note = head;
return result;
}
const EXERCISE_FILLERS = [
"今天",
"我",
"做了",
"做",
"进行了",
"进行",
"运动了",
"运动",
"锻炼了",
"锻炼",
"了",
"大概",
"左右",
"持续",
"差不多",
"一共",
"总共",
"的",
"走了",
"打了",
"跑了"
];
function parseExercise(raw) {
const t = normalizeNumbers(raw);
const result = {};
const durMatch = t.match(/(\d+(?:\.\d+)?)\s*(个小时|小时|钟头|时|分钟|分)/);
if (durMatch) {
const val = parseFloat(durMatch[1]);
const isHour = /个小时|小时|钟头|时/.test(durMatch[2]);
result.duration = String(Math.round(isHour ? val * 60 : val));
}
if (/高强度|剧烈|很累|大汗|气喘/.test(t))
result.intensity = 3;
else if (/中强度|中等强度|中等|有点累|微微出汗|微汗/.test(t))
result.intensity = 2;
else if (/低强度|轻松|轻微|溜达|缓慢/.test(t))
result.intensity = 1;
let type = t;
if (durMatch)
type = type.replace(durMatch[0], "");
type = type.replace(/高强度|中强度|中等强度|低强度|剧烈|很累|大汗|气喘|有点累|微微出汗|微汗|轻松|轻微|缓慢/g, "").replace(/\d+(?:\.\d+)?/g, "").replace(/[,。.;、\s]+/g, "");
EXERCISE_FILLERS.forEach((f) => {
type = type.split(f).join("");
});
type = type.trim();
if (type && type.length <= 20)
result.exercise_type = type;
return result;
}
const FIELD_LABELS = {
fasting_blood_sugar: "空腹",
postprandial_blood_sugar: "餐后",
other_blood_sugar: "其他血糖",
systolic_pressure: "高压",
diastolic_pressure: "低压",
western_medicine: "西药",
insulin: "胰岛素",
breakfast_foods: "早餐",
lunch_foods: "午餐",
dinner_foods: "晚餐",
note: "备注",
exercise_type: "运动",
duration: "时长",
intensity: "强度"
};
const INTENSITY_LABELS = { 1: "低强度", 2: "中强度", 3: "高强度" };
function summarizeParsed(parsed) {
const parts = [];
Object.keys(parsed).forEach((k) => {
const label = FIELD_LABELS[k] || k;
let val = parsed[k];
if (k === "intensity")
val = INTENSITY_LABELS[val] || val;
parts.push(`${label}${val}`);
});
return parts.join(" · ");
}
exports.normalizeNumbers = normalizeNumbers;
exports.parseBloodPressure = parseBloodPressure;
exports.parseDiet = parseDiet;
exports.parseExercise = parseExercise;
exports.parseGlucose = parseGlucose;
exports.summarizeParsed = summarizeParsed;
//# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/utils/voiceParse.js.map
@@ -0,0 +1,238 @@
"use strict";
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
const common_vendor = require("../../common/vendor.js");
class AudioPool {
constructor(src, size, volume = 1) {
__publicField(this, "list", []);
__publicField(this, "cursor", 0);
__publicField(this, "warmedUp", false);
__publicField(this, "targetVolume", 1);
this.src = src;
this.size = size;
this.targetVolume = volume;
}
create() {
for (let i = 0; i < this.size; i++) {
const ctx = common_vendor.index.createInnerAudioContext();
ctx.src = this.src;
ctx.obeyMuteSwitch = false;
ctx.autoplay = false;
ctx.volume = this.targetVolume;
this.list.push(ctx);
}
}
setVolume(volume) {
this.targetVolume = Math.max(0, Math.min(1, volume));
this.list.forEach((ctx) => {
try {
ctx.volume = this.targetVolume;
} catch (_) {
}
});
}
/**
* 预热:短暂播放,让音频文件下载/解码到内存
* 真正首次 play 不会再卡冷启动
*/
warmUp() {
if (this.warmedUp)
return;
if (this.list.length === 0)
this.create();
this.list.forEach((ctx) => {
try {
ctx.volume = 0;
ctx.play();
setTimeout(() => {
try {
ctx.stop();
ctx.volume = this.targetVolume;
} catch (_) {
}
}, 60);
} catch (_) {
}
});
this.warmedUp = true;
}
play() {
if (this.list.length === 0) {
this.create();
this.warmUp();
}
const ctx = this.list[this.cursor];
this.cursor = (this.cursor + 1) % this.list.length;
try {
this.list.forEach((c) => {
try {
c.stop();
} catch (_) {
}
});
ctx.play();
} catch (_) {
try {
ctx.play();
} catch (__) {
}
}
}
destroy() {
this.list.forEach((ctx) => {
var _a;
try {
(_a = ctx.destroy) == null ? void 0 : _a.call(ctx);
} catch (_) {
}
});
this.list = [];
this.warmedUp = false;
}
}
const DEFAULT_CLICK_SRC = "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260526/20260526105128557ef8669.mp3";
function useMetronome(options = {}) {
const {
initialBpm = 80,
bpmMin = 40,
bpmMax = 240,
clickSrc = DEFAULT_CLICK_SRC,
accentSrc,
poolSize = 4,
volume = 1,
silent = false,
onBeat
} = options;
const bpm = common_vendor.ref(initialBpm);
const isPlaying = common_vendor.ref(false);
const beatIndex = common_vendor.ref(0);
const isAccent = common_vendor.ref(false);
const accentEveryRef = common_vendor.ref(options.accentEvery ?? 4);
const intervalMs = common_vendor.computed(() => 6e4 / bpm.value);
let clickPool = null;
let accentPool = null;
let timer = null;
let nextTickAt = 0;
const ensureAudio = () => {
if (silent)
return;
if (!clickPool) {
clickPool = new AudioPool(clickSrc, poolSize, volume);
clickPool.warmUp();
}
if (accentSrc && !accentPool) {
accentPool = new AudioPool(accentSrc, Math.max(2, Math.ceil(poolSize / 2)), volume);
accentPool.warmUp();
}
};
const playSound = (accent) => {
if (accent && accentPool) {
accentPool.play();
} else if (clickPool) {
clickPool.play();
}
};
const tick = () => {
if (!isPlaying.value)
return;
const every = Math.max(1, accentEveryRef.value);
const accent = beatIndex.value % every === 0;
isAccent.value = accent;
playSound(accent);
onBeat == null ? void 0 : onBeat(beatIndex.value, accent);
beatIndex.value++;
nextTickAt += intervalMs.value;
const nextDelay = Math.max(0, nextTickAt - Date.now());
timer = setTimeout(tick, nextDelay);
};
const start = () => {
if (isPlaying.value)
return;
ensureAudio();
isPlaying.value = true;
beatIndex.value = 0;
nextTickAt = Date.now();
tick();
};
const stop = () => {
isPlaying.value = false;
if (timer) {
clearTimeout(timer);
timer = null;
}
};
const setBpm = (val) => {
bpm.value = Math.max(bpmMin, Math.min(bpmMax, Math.round(val)));
};
const setAccentEvery = (n) => {
accentEveryRef.value = Math.max(1, Math.min(16, Math.round(n)));
beatIndex.value = 0;
};
const updateAudioSrc = (newClickSrc, newAccentSrc) => {
const wasPlaying = isPlaying.value;
if (wasPlaying)
stop();
clickPool == null ? void 0 : clickPool.destroy();
accentPool == null ? void 0 : accentPool.destroy();
clickPool = null;
accentPool = null;
clickPool = new AudioPool(newClickSrc, poolSize);
if (newAccentSrc) {
accentPool = new AudioPool(newAccentSrc, Math.max(2, Math.ceil(poolSize / 2)));
}
const previewCtx = common_vendor.index.createInnerAudioContext();
previewCtx.src = newClickSrc;
previewCtx.obeyMuteSwitch = false;
previewCtx.volume = 0.4;
try {
previewCtx.play();
setTimeout(() => {
var _a;
try {
(_a = previewCtx.destroy) == null ? void 0 : _a.call(previewCtx);
} catch (_) {
}
}, 500);
} catch (_) {
}
if (wasPlaying)
start();
};
const preload = () => {
try {
common_vendor.index.setInnerAudioOption({
obeyMuteSwitch: false,
mixWithOther: true
});
} catch (_) {
}
ensureAudio();
};
common_vendor.onUnmounted(() => {
stop();
clickPool == null ? void 0 : clickPool.destroy();
accentPool == null ? void 0 : accentPool.destroy();
clickPool = null;
accentPool = null;
});
return {
bpm,
isPlaying,
beatIndex,
isAccent,
intervalMs,
accentEvery: accentEveryRef,
start,
stop,
setBpm,
setAccentEvery,
updateAudioSrc,
preload
};
}
exports.useMetronome = useMetronome;
//# sourceMappingURL=../../../.sourcemap/mp-weixin/training/hooks/useMetronome.js.map
@@ -0,0 +1,466 @@
"use strict";
const common_vendor = require("../../../common/vendor.js");
const _sfc_main = /* @__PURE__ */ common_vendor.defineComponent({
__name: "crush-canvas",
props: {
crushSignal: {}
},
setup(__props) {
const CRUSH_ITEMS = {
egg: {
colors: ["#FFEB3B", "#FFF59D", "#FFFFFF", "#FFD54F", "#FFC107"],
sparkColors: ["#FFFFFF", "#FFF9C4", "#FFEE58"],
shapes: ["circle", "circle", "circle"],
debrisCount: 20,
sparkCount: 16,
sparkleCount: 5,
speedMin: 3,
speedMax: 6,
sizeMin: 4,
sizeMax: 10,
gravity: 0.34,
flashColor: "#FFFDE7",
shockwaveColor: "rgba(255, 224, 130, 0.85)"
},
walnut: {
colors: ["#5D4037", "#795548", "#8D6E63", "#A1887F", "#3E2723"],
sparkColors: ["#FFCC80", "#FFB74D", "#FFE0B2"],
shapes: ["square", "triangle", "rect"],
debrisCount: 22,
sparkCount: 18,
sparkleCount: 5,
speedMin: 2.8,
speedMax: 5,
sizeMin: 4,
sizeMax: 9,
gravity: 0.42,
flashColor: "#FFE0B2",
shockwaveColor: "rgba(255, 167, 38, 0.8)"
},
can: {
colors: ["#90A4AE", "#CFD8DC", "#B0BEC5", "#78909C", "#ECEFF1", "#607D8B"],
sparkColors: ["#FFFFFF", "#E1F5FE", "#B3E5FC"],
shapes: ["rect", "rect", "triangle", "square"],
debrisCount: 24,
sparkCount: 20,
sparkleCount: 6,
speedMin: 4,
speedMax: 7,
sizeMin: 3,
sizeMax: 11,
gravity: 0.36,
flashColor: "#FFFFFF",
shockwaveColor: "rgba(207, 216, 220, 0.9)"
},
balloon: {
colors: ["#FF5252", "#FF4081", "#E040FB", "#7C4DFF", "#536DFE", "#448AFF", "#FFEB3B", "#69F0AE", "#FF6E40"],
sparkColors: ["#FFFFFF", "#FF80AB", "#82B1FF", "#FFFF8D"],
shapes: ["rect", "rect", "square"],
debrisCount: 28,
sparkCount: 18,
sparkleCount: 8,
speedMin: 4,
speedMax: 7.5,
sizeMin: 4,
sizeMax: 8,
gravity: 0.14,
flashColor: "#FCE4EC",
shockwaveColor: "rgba(255, 64, 129, 0.85)",
confetti: true
}
};
const PHYSICS = {
AIR_RESISTANCE: 0.985,
SPARK_RESISTANCE: 0.92,
DEBRIS_DECAY: 0.018,
SPARK_DECAY: 0.035,
SPARKLE_DECAY: 0.045,
FLASH_DECAY: 0.11,
SHOCKWAVE_DECAY: 0.045
};
const canvasNode = common_vendor.ref(null);
const ctx = common_vendor.ref(null);
const canvasWidth = common_vendor.ref(240);
const canvasHeight = common_vendor.ref(240);
const dpr = common_vendor.ref(1);
const rafId = common_vendor.ref(null);
const renderRunning = common_vendor.ref(false);
const debrisList = common_vendor.ref([]);
const sparkList = common_vendor.ref([]);
const sparkleList = common_vendor.ref([]);
const flashList = common_vendor.ref([]);
const shockwaves = common_vendor.ref([]);
const instance = common_vendor.getCurrentInstance();
const props = __props;
common_vendor.watch(
() => props.crushSignal,
(sig) => {
if (sig)
triggerCrush(sig.type);
}
);
common_vendor.onMounted(() => {
initCanvas();
});
common_vendor.onBeforeUnmount(() => {
cleanup();
});
function initCanvas() {
setTimeout(() => {
if (!instance)
return;
const query = common_vendor.index.createSelectorQuery().in(instance);
query.select("#crush").fields({ node: true, size: true }).exec((res) => {
if (!res || !res[0] || !res[0].node) {
common_vendor.index.__f__("warn", "at training/pages/components/crush-canvas.vue:239", "[crush-canvas] canvas 2d not supported on this platform");
return;
}
const canvas = res[0].node;
const width = res[0].width;
const height = res[0].height;
const systemInfo = common_vendor.index.getSystemInfoSync();
const pixelRatio = systemInfo.pixelRatio || 1;
canvas.width = width * pixelRatio;
canvas.height = height * pixelRatio;
const context = canvas.getContext("2d");
context.scale(pixelRatio, pixelRatio);
canvasNode.value = canvas;
ctx.value = context;
canvasWidth.value = width;
canvasHeight.value = height;
dpr.value = pixelRatio;
});
}, 200);
}
function triggerCrush(itemType) {
if (!ctx.value || !canvasNode.value)
return;
const item = CRUSH_ITEMS[itemType];
if (!item)
return;
const centerX = canvasWidth.value / 2;
const centerY = canvasHeight.value / 2;
const maxR = Math.min(canvasWidth.value, canvasHeight.value);
flashList.value.push({
x: centerX,
y: centerY,
radius: maxR * 0.12,
maxRadius: maxR * 0.62,
color: item.flashColor,
life: 1,
decay: PHYSICS.FLASH_DECAY
});
shockwaves.value.push({
x: centerX,
y: centerY,
radius: 12,
maxRadius: maxR * 0.62,
color: item.shockwaveColor,
width: 5,
life: 1,
decay: PHYSICS.SHOCKWAVE_DECAY
});
shockwaves.value.push({
x: centerX,
y: centerY,
radius: 4,
maxRadius: maxR * 0.42,
color: item.shockwaveColor,
width: 3,
life: 1,
decay: PHYSICS.SHOCKWAVE_DECAY * 1.3
});
for (let i = 0; i < item.debrisCount; i++) {
const angle = Math.PI * 2 * i / item.debrisCount + (Math.random() - 0.5) * 0.7;
const speed = item.speedMin + Math.random() * (item.speedMax - item.speedMin);
const size = item.sizeMin + Math.random() * (item.sizeMax - item.sizeMin);
const color = item.colors[Math.floor(Math.random() * item.colors.length)];
const shape = item.shapes[Math.floor(Math.random() * item.shapes.length)];
const confetti = !!item.confetti;
const vy0 = confetti ? Math.sin(angle) * speed - 2.5 : Math.sin(angle) * speed;
debrisList.value.push({
x: centerX,
y: centerY,
vx: Math.cos(angle) * speed,
vy: vy0,
size,
color,
shape,
rotation: Math.random() * Math.PI * 2,
rotationSpeed: (Math.random() - 0.5) * (confetti ? 0.5 : 0.3),
life: 1,
decay: PHYSICS.DEBRIS_DECAY * (confetti ? 0.7 : 1),
gravity: item.gravity,
glow: !confetti,
// 彩纸不发光,实色碎片发光
swing: confetti ? 0.6 + Math.random() * 1.2 : 0,
age: Math.random() * Math.PI * 2
});
}
for (let i = 0; i < item.sparkCount; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = item.speedMax * (0.9 + Math.random() * 0.8);
const color = item.sparkColors[Math.floor(Math.random() * item.sparkColors.length)];
sparkList.value.push({
x: centerX,
y: centerY,
px: centerX,
py: centerY,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
size: 1.5 + Math.random() * 2,
color,
life: 1,
decay: PHYSICS.SPARK_DECAY * (0.8 + Math.random() * 0.6),
gravity: item.gravity * 0.3
});
}
for (let i = 0; i < item.sparkleCount; i++) {
const r = maxR * (0.08 + Math.random() * 0.32);
const a = Math.random() * Math.PI * 2;
const color = item.sparkColors[Math.floor(Math.random() * item.sparkColors.length)];
sparkleList.value.push({
x: centerX + Math.cos(a) * r,
y: centerY + Math.sin(a) * r,
size: 10 + Math.random() * 16,
rotation: Math.random() * Math.PI,
spin: (Math.random() - 0.5) * 0.16,
color,
life: 1 + Math.random() * 0.4,
// 错峰出现
decay: PHYSICS.SPARKLE_DECAY * (0.8 + Math.random() * 0.5)
});
}
ensureRenderLoop();
}
function ensureRenderLoop() {
if (renderRunning.value)
return;
renderRunning.value = true;
renderLoop();
}
function renderLoop() {
if (!canvasNode.value || !ctx.value) {
renderRunning.value = false;
return;
}
const w = canvasWidth.value;
const h = canvasHeight.value;
for (let i = debrisList.value.length - 1; i >= 0; i--) {
const p = debrisList.value[i];
p.vy += p.gravity;
p.vx *= PHYSICS.AIR_RESISTANCE;
p.vy *= PHYSICS.AIR_RESISTANCE;
p.x += p.vx;
p.y += p.vy;
p.age += 0.2;
if (p.swing > 0)
p.x += Math.sin(p.age) * p.swing;
p.rotation += p.rotationSpeed;
p.life -= p.decay;
if (p.life <= 0 || p.x < -60 || p.x > w + 60 || p.y > h + 60) {
debrisList.value.splice(i, 1);
}
}
for (let i = sparkList.value.length - 1; i >= 0; i--) {
const s = sparkList.value[i];
s.px = s.x;
s.py = s.y;
s.vy += s.gravity;
s.vx *= PHYSICS.SPARK_RESISTANCE;
s.vy *= PHYSICS.SPARK_RESISTANCE;
s.x += s.vx;
s.y += s.vy;
s.life -= s.decay;
if (s.life <= 0)
sparkList.value.splice(i, 1);
}
for (let i = sparkleList.value.length - 1; i >= 0; i--) {
const sp = sparkleList.value[i];
sp.rotation += sp.spin;
sp.life -= sp.decay;
if (sp.life <= 0)
sparkleList.value.splice(i, 1);
}
for (let i = flashList.value.length - 1; i >= 0; i--) {
const f = flashList.value[i];
f.radius += (f.maxRadius - f.radius) * 0.35;
f.life -= f.decay;
if (f.life <= 0)
flashList.value.splice(i, 1);
}
for (let i = shockwaves.value.length - 1; i >= 0; i--) {
const sw = shockwaves.value[i];
sw.radius += (sw.maxRadius - sw.radius) * 0.16;
sw.life -= sw.decay;
if (sw.life <= 0)
shockwaves.value.splice(i, 1);
}
draw();
const alive = debrisList.value.length > 0 || sparkList.value.length > 0 || sparkleList.value.length > 0 || flashList.value.length > 0 || shockwaves.value.length > 0;
if (alive) {
rafId.value = canvasNode.value.requestAnimationFrame(renderLoop);
} else {
rafId.value = null;
renderRunning.value = false;
}
}
function draw() {
const c = ctx.value;
const w = canvasWidth.value;
const h = canvasHeight.value;
c.clearRect(0, 0, w, h);
c.globalCompositeOperation = "source-over";
shockwaves.value.forEach((sw) => {
c.beginPath();
c.arc(sw.x, sw.y, sw.radius, 0, Math.PI * 2);
c.strokeStyle = applyAlphaToRgba(sw.color, Math.min(1, sw.life));
c.lineWidth = sw.width * sw.life;
c.stroke();
});
c.globalCompositeOperation = "lighter";
flashList.value.forEach((f) => {
const grd = c.createRadialGradient(f.x, f.y, 0, f.x, f.y, f.radius);
const a = Math.min(1, f.life);
grd.addColorStop(0, hexToRgba(f.color, 0.95 * a));
grd.addColorStop(0.4, hexToRgba(f.color, 0.5 * a));
grd.addColorStop(1, hexToRgba(f.color, 0));
c.fillStyle = grd;
c.beginPath();
c.arc(f.x, f.y, f.radius, 0, Math.PI * 2);
c.fill();
});
c.globalCompositeOperation = "source-over";
debrisList.value.forEach((p) => {
const alpha = Math.min(1, p.life);
const currentSize = p.size * (0.65 + p.life * 0.35);
if (p.glow) {
c.fillStyle = hexToRgba(p.color, alpha * 0.22);
c.beginPath();
c.arc(p.x, p.y, currentSize * 1.9, 0, Math.PI * 2);
c.fill();
}
c.fillStyle = hexToRgba(p.color, alpha);
drawDebrisShape(c, p, currentSize);
});
c.globalCompositeOperation = "lighter";
sparkList.value.forEach((s) => {
const a = Math.min(1, s.life);
c.strokeStyle = hexToRgba(s.color, a * 0.8);
c.lineWidth = s.size * a;
c.lineCap = "round";
c.beginPath();
c.moveTo(s.px, s.py);
c.lineTo(s.x, s.y);
c.stroke();
c.fillStyle = hexToRgba(s.color, a);
c.beginPath();
c.arc(s.x, s.y, s.size * a, 0, Math.PI * 2);
c.fill();
});
sparkleList.value.forEach((sp) => {
const t = Math.max(0, Math.min(1, sp.life));
const scale = Math.sin(Math.min(1, sp.life) * Math.PI);
if (scale <= 0.02)
return;
const a = t;
drawStar(c, sp.x, sp.y, sp.size * scale, sp.size * scale * 0.32, sp.rotation, hexToRgba(sp.color, a));
});
c.globalCompositeOperation = "source-over";
}
function drawDebrisShape(c, p, size) {
switch (p.shape) {
case "circle":
c.beginPath();
c.arc(p.x, p.y, size, 0, Math.PI * 2);
c.fill();
break;
case "square":
c.save();
c.translate(p.x, p.y);
c.rotate(p.rotation);
c.fillRect(-size, -size, size * 2, size * 2);
c.restore();
break;
case "rect":
c.save();
c.translate(p.x, p.y);
c.rotate(p.rotation);
c.fillRect(-size * 1.6, -size * 0.5, size * 3.2, size);
c.restore();
break;
case "triangle":
c.save();
c.translate(p.x, p.y);
c.rotate(p.rotation);
c.beginPath();
c.moveTo(0, -size);
c.lineTo(size, size);
c.lineTo(-size, size);
c.closePath();
c.fill();
c.restore();
break;
}
}
function drawStar(c, cx, cy, outerR, innerR, rotation, fill) {
const points = 4;
c.save();
c.translate(cx, cy);
c.rotate(rotation);
c.beginPath();
for (let i = 0; i < points * 2; i++) {
const r = i % 2 === 0 ? outerR : innerR;
const a = Math.PI * i / points;
const x = Math.cos(a) * r;
const y = Math.sin(a) * r;
if (i === 0)
c.moveTo(x, y);
else
c.lineTo(x, y);
}
c.closePath();
c.fillStyle = fill;
c.fill();
c.restore();
}
function hexToRgba(hex, alpha) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
function applyAlphaToRgba(rgba, alphaMultiplier) {
const match = rgba.match(/rgba?\(([^)]+)\)/);
if (!match)
return rgba;
const parts = match[1].split(",").map((s) => s.trim());
const r = parts[0];
const g = parts[1];
const b = parts[2];
const a = parts[3] ? parseFloat(parts[3]) : 1;
return `rgba(${r}, ${g}, ${b}, ${a * alphaMultiplier})`;
}
function cleanup() {
if (rafId.value !== null && canvasNode.value) {
try {
canvasNode.value.cancelAnimationFrame(rafId.value);
} catch (_) {
}
rafId.value = null;
}
renderRunning.value = false;
debrisList.value = [];
sparkList.value = [];
sparkleList.value = [];
flashList.value = [];
shockwaves.value = [];
}
return (_ctx, _cache) => {
return {};
};
}
});
const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-e11c993f"]]);
wx.createComponent(Component);
//# sourceMappingURL=../../../../.sourcemap/mp-weixin/training/pages/components/crush-canvas.js.map
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1 @@
<view class="crush-canvas-container data-v-e11c993f"><canvas type="2d" id="crush" class="crush-canvas data-v-e11c993f"></canvas></view>
@@ -0,0 +1,15 @@
.crush-canvas-container.data-v-e11c993f {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
}
.crush-canvas.data-v-e11c993f {
display: block;
width: 100%;
height: 100%;
background: transparent;
}
@@ -0,0 +1,278 @@
"use strict";
const common_vendor = require("../../../common/vendor.js");
const _sfc_main = {
props: {
isPlaying: {
type: Boolean,
default: false
},
bpm: {
type: Number,
default: 110
}
},
data() {
return {
canvasWidth: 320,
canvasHeight: 400,
ctx: null,
timer: null,
renderRunning: false,
// Interaction State (Spring Physics)
coreScale: 1,
coreTargetScale: 1,
coreVelocity: 0,
pressed: false,
// Animation Physics (Time)
time: 0,
lastFrameTime: 0,
// Metronome State
lastBeatTime: 0,
// Energy Waves
waveAmplitudeMultiplier: 1,
// Shockwaves
shockwaves: [],
// Rotation for outer ring
ringAngle: 0
};
},
mounted() {
this.initCanvas();
},
beforeUnmount() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
this.renderRunning = false;
},
watch: {
isPlaying(newVal) {
if (newVal) {
this.waveAmplitudeMultiplier = 1;
this.ensureRenderLoop();
}
}
},
methods: {
initCanvas() {
setTimeout(() => {
const query = common_vendor.index.createSelectorQuery().in(this);
query.select(".walker-canvas-container").boundingClientRect((data) => {
if (data && data.width > 0) {
this.canvasWidth = data.width;
this.canvasHeight = data.height > 100 ? data.height : data.width * 1.2;
} else {
const sys = common_vendor.index.getSystemInfoSync();
this.canvasWidth = sys.windowWidth - 30;
this.canvasHeight = this.canvasWidth * 1.2;
}
if (!this.ctx) {
this.ctx = common_vendor.index.createCanvasContext("walker", this);
this.lastFrameTime = Date.now();
this.ensureRenderLoop();
}
}).exec();
}, 100);
},
// Expose a method to be called from parent when a beat hits
triggerBeat() {
const coreRadius = Math.min(this.canvasWidth, this.canvasHeight) * 0.32;
this.shockwaves.push({
radius: coreRadius,
maxRadius: coreRadius * 2.2,
opacity: 1
});
this.waveAmplitudeMultiplier = 2.5;
this.coreVelocity -= 0.05;
this.ensureRenderLoop();
},
onTouchStart(e) {
const touch = e.touches[0];
if (!touch)
return;
const cx = this.canvasWidth / 2;
const cy = this.canvasHeight / 2 - 30;
const coreRadius = Math.min(this.canvasWidth, this.canvasHeight) * 0.32;
const dx = touch.x - cx;
const dy = touch.y - cy;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist <= coreRadius + 40) {
this.pressed = true;
this.coreTargetScale = 0.9;
this.ensureRenderLoop();
}
},
onTouchMove(e) {
if (!this.pressed)
return;
const touch = e.touches[0];
if (!touch)
return;
const cx = this.canvasWidth / 2;
const cy = this.canvasHeight / 2 - 30;
const coreRadius = Math.min(this.canvasWidth, this.canvasHeight) * 0.32;
const dx = touch.x - cx;
const dy = touch.y - cy;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist > coreRadius + 40) {
this.pressed = false;
this.coreTargetScale = 1;
}
},
onTouchEnd() {
this.coreTargetScale = 1;
if (this.pressed) {
this.$emit("toggle-play");
}
this.pressed = false;
},
onTouchCancel() {
this.coreTargetScale = 1;
this.pressed = false;
},
ensureRenderLoop() {
if (this.renderRunning)
return;
this.renderRunning = true;
this.renderLoop();
},
renderLoop() {
const fixedDt = 0.016;
if (this.isPlaying) {
this.time += fixedDt;
} else {
this.time += fixedDt * 0.2;
}
const tension = 120;
const friction = 12;
const force = (this.coreTargetScale - this.coreScale) * tension;
this.coreVelocity += force * fixedDt;
this.coreVelocity *= Math.exp(-friction * fixedDt);
this.coreScale += this.coreVelocity * fixedDt;
if (this.waveAmplitudeMultiplier > 1) {
this.waveAmplitudeMultiplier -= fixedDt * 3;
if (this.waveAmplitudeMultiplier < 1)
this.waveAmplitudeMultiplier = 1;
} else if (!this.isPlaying && this.waveAmplitudeMultiplier > 0.1) {
this.waveAmplitudeMultiplier -= fixedDt * 2;
if (this.waveAmplitudeMultiplier < 0.1)
this.waveAmplitudeMultiplier = 0.1;
}
this.draw();
this.ctx.draw(false);
const shouldContinue = this.isPlaying || this.shockwaves.length > 0 || Math.abs(this.coreScale - this.coreTargetScale) >= 1e-3 || Math.abs(this.coreVelocity) >= 1e-3 || this.waveAmplitudeMultiplier > 0.11;
if (shouldContinue) {
this.timer = setTimeout(() => {
this.renderLoop();
}, 1e3 / 60);
} else {
this.timer = null;
this.renderRunning = false;
}
},
draw() {
const ctx = this.ctx;
const W = this.canvasWidth;
const H = this.canvasHeight;
ctx.clearRect(0, 0, W, H);
const coreRadius = Math.min(W, H) * 0.32;
const cx = W / 2;
const cy = H / 2 - 30;
for (let i = this.shockwaves.length - 1; i >= 0; i--) {
const sw = this.shockwaves[i];
sw.radius += (sw.maxRadius - sw.radius) * 0.08;
sw.opacity -= 0.03;
if (sw.opacity <= 0) {
this.shockwaves.splice(i, 1);
continue;
}
ctx.beginPath();
ctx.arc(cx, cy, sw.radius, 0, Math.PI * 2);
ctx.strokeStyle = `rgba(16, 185, 129, ${sw.opacity * 0.5})`;
ctx.lineWidth = 3;
ctx.stroke();
}
this.drawWaves(ctx, W, H);
ctx.save();
ctx.translate(cx, cy);
ctx.scale(this.coreScale, this.coreScale);
ctx.setShadow(0, 12, 24, "rgba(16, 185, 129, 0.3)");
const coreGrad = ctx.createLinearGradient(-coreRadius, -coreRadius, coreRadius, coreRadius);
coreGrad.addColorStop(0, "#34d399");
coreGrad.addColorStop(1, "#047857");
ctx.beginPath();
ctx.arc(0, 0, coreRadius, 0, Math.PI * 2);
ctx.fillStyle = coreGrad;
ctx.fill();
ctx.setShadow(0, 0, 0, "transparent");
ctx.beginPath();
ctx.arc(0, 0, coreRadius - 2, 0, Math.PI * 2);
ctx.strokeStyle = "rgba(255, 255, 255, 0.35)";
ctx.lineWidth = 2;
ctx.stroke();
const fontSize = Math.floor(coreRadius * 0.7);
ctx.fillStyle = "#ffffff";
ctx.font = `bold ${fontSize}px "DIN Condensed", "Inter", sans-serif`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(this.bpm.toString(), 0, -12);
ctx.fillStyle = "rgba(255, 255, 255, 0.85)";
ctx.font = '500 16px "Inter", sans-serif';
ctx.fillText("BPM", 0, coreRadius * 0.3);
ctx.fillStyle = "#ffffff";
const iconY = coreRadius * 0.55;
if (this.isPlaying) {
ctx.fillRect(-8, iconY - 6, 5, 14);
ctx.fillRect(3, iconY - 6, 5, 14);
} else {
ctx.beginPath();
ctx.moveTo(-5, iconY - 8);
ctx.lineTo(9, iconY);
ctx.lineTo(-5, iconY + 8);
ctx.closePath();
ctx.fill();
}
ctx.restore();
},
drawWaves(ctx, W, H) {
const baseY = H * 0.82;
const dynamicAmp = H * 0.08;
const waves = [
{ color: "rgba(52, 211, 153, 0.3)", speed: 1.5, freq: 0.012, amp: dynamicAmp * 0.7, offset: 0 },
{ color: "rgba(16, 185, 129, 0.6)", speed: 2.5, freq: 0.015, amp: dynamicAmp, offset: Math.PI }
];
const activeAmp = this.waveAmplitudeMultiplier;
const t = this.time;
waves.forEach((wave) => {
ctx.beginPath();
ctx.moveTo(0, H);
ctx.lineTo(0, baseY);
const step = 25;
for (let x = 0; x <= W; x += step) {
const y = baseY + Math.sin(x * wave.freq + t * wave.speed + wave.offset) * wave.amp * activeAmp;
ctx.lineTo(x, y);
}
const finalY = baseY + Math.sin(W * wave.freq + t * wave.speed + wave.offset) * wave.amp * activeAmp;
ctx.lineTo(W, finalY);
ctx.lineTo(W, H);
ctx.closePath();
ctx.fillStyle = wave.color;
ctx.fill();
});
}
}
};
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return {
a: $data.canvasWidth + "px",
b: $data.canvasHeight + "px",
c: common_vendor.o((...args) => $options.onTouchStart && $options.onTouchStart(...args), "a1"),
d: common_vendor.o((...args) => $options.onTouchMove && $options.onTouchMove(...args), "c5"),
e: common_vendor.o((...args) => $options.onTouchEnd && $options.onTouchEnd(...args), "fe"),
f: common_vendor.o((...args) => $options.onTouchCancel && $options.onTouchCancel(...args), "2c")
};
}
const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-782dd882"]]);
wx.createComponent(Component);
//# sourceMappingURL=../../../../.sourcemap/mp-weixin/training/pages/components/walker-canvas.js.map
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1 @@
<view class="walker-canvas-container data-v-782dd882"><block wx:if="{{r0}}"><canvas canvas-id="walker" id="walker" class="walker-canvas data-v-782dd882" style="{{'width:' + a + ';' + ('height:' + b)}}" bindtouchstart="{{c}}" bindtouchmove="{{d}}" bindtouchend="{{e}}" bindtouchcancel="{{f}}"></canvas></block></view>
@@ -0,0 +1,15 @@
.walker-canvas-container.data-v-782dd882 {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.walker-canvas.data-v-782dd882 {
display: block;
width: 100%;
height: 100%;
background: transparent; /* Pure transparent to blend into page */
}
@@ -0,0 +1,446 @@
"use strict";
const common_vendor = require("../../common/vendor.js");
const TUTORIAL_VIDEO_URL = "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/video/20260528/202605281739117bf3c2914.mp4";
const TUTORIAL_POSTER_URL = "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/images/20260529/202605291142269212d9402.jpg";
const BGM_URL = "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260528/20260528093709c669f4659.mp3";
const _sfc_defineComponent = common_vendor.defineComponent({
__name: "dumbbell",
setup(__props) {
const DUMBBELL_PRESETS = [
{ id: "light", bpm: 50, met: 3.5, label: "轻度", desc: "热身 · 激活" },
{ id: "medium", bpm: 65, met: 5, label: "中度", desc: "日常 · 塑形" },
{ id: "heavy", bpm: 80, met: 6.5, label: "重度", desc: "强化 · 挑战" }
];
const TARGET_OPTIONS = [
{ minutes: 0, label: "自由" },
{ minutes: 5, label: "5 分钟" },
{ minutes: 10, label: "10 分钟" },
{ minutes: 15, label: "15 分钟" }
];
const MILESTONES = [
{ emoji: "💪", title: "热身完成", desc: "肌肉已激活!", minReps: 8, maxReps: 12 },
{ emoji: "🔥", title: "力量提升", desc: "动作很标准!", minReps: 15, maxReps: 20 },
{ emoji: "⚡", title: "塑形进阶", desc: "线条感很棒!", minReps: 25, maxReps: 30 },
{ emoji: "🏆", title: "完美收官", desc: "太强了!", minReps: 40, maxReps: 50 }
];
const FOOD_SUGAR_TABLE = [
{ emoji: "🍬", name: "糖果", sugar: 95, unit: "颗", weight: 5 },
// 1颗糖果约5g
{ emoji: "🍪", name: "饼干", sugar: 65, unit: "块", weight: 10 },
// 1块饼干约10g
{ emoji: "🍫", name: "巧克力", sugar: 51.5, unit: "块", weight: 50 },
// 1块巧克力约50g
{ emoji: "🍎", name: "苹果", sugar: 10.3, unit: "个", weight: 200 },
// 1个苹果约200g
{ emoji: "🍌", name: "香蕉", sugar: 12.2, unit: "根", weight: 120 },
// 1根香蕉约120g
{ emoji: "🍚", name: "米饭", sugar: 25.9, unit: "碗", weight: 150 },
// 1碗米饭约150g
{ emoji: "🥤", name: "可乐", sugar: 10.6, unit: "罐", weight: 330 }
// 1罐可乐330ml
];
const currentPreset = common_vendor.ref("medium");
const targetMinutes = common_vendor.ref(0);
const isPlaying = common_vendor.ref(false);
const beatTimer = common_vendor.ref(null);
const showRewardPopup = common_vendor.ref(false);
const showSummary = common_vendor.ref(false);
const currentReward = common_vendor.ref(MILESTONES[0]);
const bgmAudioCtx = common_vendor.ref(null);
const bgmEnabled = common_vendor.ref(true);
const totalReps = common_vendor.ref(0);
const elapsedSeconds = common_vendor.ref(0);
const totalSugar = common_vendor.ref(0);
const beatCount = common_vendor.ref(0);
const rewardIndex = common_vendor.ref(0);
const nextRewardAt = common_vendor.ref(0);
const startTime = common_vendor.ref(0);
const timerInterval = common_vendor.ref(null);
const preset = common_vendor.computed(() => DUMBBELL_PRESETS.find((p) => p.id === currentPreset.value) || DUMBBELL_PRESETS[1]);
const currentBpm = common_vendor.computed(() => preset.value.bpm);
const currentMet = common_vendor.computed(() => preset.value.met);
const targetExpanded = common_vendor.ref(false);
const currentTargetLabel = common_vendor.computed(() => {
const opt = TARGET_OPTIONS.find((t) => t.minutes === targetMinutes.value);
return opt ? opt.label : "自由";
});
const benefitExpanded = common_vendor.ref(false);
const TRAINING_BENEFIT = {
muscles: ["肱二头肌", "三角肌", "胸大肌", "背阔肌"],
effect: "多角度抗阻训练上肢与肩背肌群,提升肌肉力量与耐力,雕塑手臂与肩部线条,增强骨密度,改善上肢日常功能。"
};
common_vendor.watch(isPlaying, (playing) => {
const videoCtx = common_vendor.index.createVideoContext("dumbbellDemoVideo");
if (!videoCtx)
return;
if (playing) {
videoCtx.play();
} else {
videoCtx.pause();
}
});
const intervalMs = common_vendor.computed(() => 6e4 / currentBpm.value);
const hasStats = common_vendor.computed(() => isPlaying.value || totalReps.value > 0);
const isPaused = common_vendor.computed(() => !isPlaying.value && totalReps.value > 0 && !showSummary.value);
const remainingSeconds = common_vendor.computed(() => {
if (targetMinutes.value === 0)
return null;
const total = targetMinutes.value * 60;
return Math.max(0, total - elapsedSeconds.value);
});
const centerHintText = common_vendor.computed(() => isPaused.value ? "点击继续" : "点击开始");
const statusText = common_vendor.computed(() => {
if (isPlaying.value)
return "训练中";
if (isPaused.value)
return "已暂停";
return "待开始";
});
const shareTitle = common_vendor.computed(() => {
if (totalReps.value === 0)
return "哑铃训练 · 一起练起来";
return `刚刚完成 ${totalReps.value} 次哑铃训练,约消耗 ${totalSugar.value.toFixed(1)}g 糖分 💪`;
});
const rootStyle = common_vendor.computed(() => ({
"--beat-duration": `${intervalMs.value}ms`
}));
const sugarComparisons = common_vendor.computed(() => {
if (totalSugar.value === 0)
return [];
const results = [];
for (const food of FOOD_SUGAR_TABLE) {
const foodSugar = food.sugar * food.weight / 100;
const count = totalSugar.value / foodSugar;
if (count >= 0.1 && count <= 50) {
results.push({
emoji: food.emoji,
name: food.name,
count: count >= 10 ? Math.round(count).toString() : count.toFixed(1),
unit: food.unit
});
}
}
return results.slice(0, 3);
});
function onBeat() {
beatCount.value++;
if (beatCount.value % 2 === 0) {
totalReps.value++;
if (totalReps.value >= nextRewardAt.value && rewardIndex.value < MILESTONES.length) {
triggerReward();
}
}
}
function calculateSugar(seconds) {
const hours = seconds / 3600;
const weight = 60;
const calories = currentMet.value * weight * hours;
return calories / 4;
}
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins}:${secs.toString().padStart(2, "0")}`;
}
function triggerReward() {
const reward = MILESTONES[rewardIndex.value];
currentReward.value = reward;
triggerHaptic();
rewardIndex.value++;
if (rewardIndex.value < MILESTONES.length) {
const nextReward = MILESTONES[rewardIndex.value];
const range = nextReward.maxReps - nextReward.minReps;
nextRewardAt.value = totalReps.value + nextReward.minReps + Math.floor(Math.random() * (range + 1));
}
setTimeout(() => {
showRewardPopup.value = true;
}, 400);
}
function closeRewardPopup() {
showRewardPopup.value = false;
}
function triggerHaptic() {
common_vendor.index.vibrateShort({
type: "medium",
fail: () => {
try {
common_vendor.index.vibrateShort({});
} catch (_) {
}
}
});
}
function startRhythm() {
stopRhythm();
isPlaying.value = true;
const beatMs = 6e4 / currentBpm.value;
beatTimer.value = setInterval(onBeat, beatMs);
}
function stopRhythm() {
isPlaying.value = false;
if (beatTimer.value) {
clearInterval(beatTimer.value);
beatTimer.value = null;
}
}
function startBgm() {
if (!bgmEnabled.value)
return;
if (bgmAudioCtx.value) {
try {
bgmAudioCtx.value.play();
} catch (_) {
}
return;
}
bgmAudioCtx.value = common_vendor.index.createInnerAudioContext();
bgmAudioCtx.value.src = BGM_URL;
bgmAudioCtx.value.loop = true;
bgmAudioCtx.value.obeyMuteSwitch = false;
bgmAudioCtx.value.volume = 0.4;
try {
bgmAudioCtx.value.play();
} catch (_) {
}
}
function stopBgm() {
if (bgmAudioCtx.value) {
try {
bgmAudioCtx.value.pause();
} catch (_) {
}
}
}
function toggleBgm() {
bgmEnabled.value = !bgmEnabled.value;
if (bgmEnabled.value && isPlaying.value) {
startBgm();
} else {
stopBgm();
}
}
function onPresetTap(id) {
if (isPlaying.value)
return;
currentPreset.value = id;
if (beatTimer.value) {
stopRhythm();
startRhythm();
}
}
function onTargetTap(minutes) {
if (isPlaying.value)
return;
targetMinutes.value = minutes;
}
function onTogglePlay() {
if (isPlaying.value) {
pauseTraining();
} else {
if (totalReps.value === 0) {
resetStats();
}
startTrainingInternal();
}
}
function startTrainingInternal() {
startRhythm();
startBgm();
startTimer();
common_vendor.index.setKeepScreenOn({ keepScreenOn: true });
}
function pauseTraining() {
stopRhythm();
stopBgm();
stopTimer();
common_vendor.index.setKeepScreenOn({ keepScreenOn: false });
}
function onEndTraining() {
if (totalReps.value === 0)
return;
pauseTraining();
showSummary.value = true;
}
function dismissSummary() {
showSummary.value = false;
resetStats();
}
function onFinish() {
showSummary.value = false;
resetStats();
}
function onRestart() {
showSummary.value = false;
resetStats();
startTrainingInternal();
}
function resetStats() {
totalReps.value = 0;
elapsedSeconds.value = 0;
totalSugar.value = 0;
beatCount.value = 0;
rewardIndex.value = 0;
startTime.value = Date.now();
const firstReward = MILESTONES[0];
const range = firstReward.maxReps - firstReward.minReps;
nextRewardAt.value = firstReward.minReps + Math.floor(Math.random() * (range + 1));
}
function startTimer() {
if (timerInterval.value)
return;
startTime.value = Date.now() - elapsedSeconds.value * 1e3;
timerInterval.value = setInterval(() => {
elapsedSeconds.value = Math.floor((Date.now() - startTime.value) / 1e3);
totalSugar.value = calculateSugar(elapsedSeconds.value);
if (targetMinutes.value > 0 && elapsedSeconds.value >= targetMinutes.value * 60) {
onEndTraining();
}
}, 1e3);
}
function stopTimer() {
if (timerInterval.value) {
clearInterval(timerInterval.value);
timerInterval.value = null;
}
}
common_vendor.onShareAppMessage(() => ({
title: shareTitle.value,
path: "/training/pages/dumbbell",
imageUrl: TUTORIAL_POSTER_URL
}));
common_vendor.onShareTimeline(() => ({
title: shareTitle.value,
imageUrl: TUTORIAL_POSTER_URL
}));
function cleanupAllMedia() {
var _a;
stopRhythm();
stopTimer();
common_vendor.index.setKeepScreenOn({ keepScreenOn: false });
try {
(_a = common_vendor.index.createVideoContext("dumbbellDemoVideo")) == null ? void 0 : _a.pause();
} catch (_) {
}
if (bgmAudioCtx.value) {
try {
bgmAudioCtx.value.stop();
} catch (_) {
}
try {
bgmAudioCtx.value.destroy();
} catch (_) {
}
bgmAudioCtx.value = null;
}
}
common_vendor.onHide(cleanupAllMedia);
common_vendor.onUnload(cleanupAllMedia);
return (_ctx, _cache) => {
return common_vendor.e({
a: common_vendor.t(statusText.value),
b: isPlaying.value ? 1 : "",
c: isPaused.value ? 1 : "",
d: TUTORIAL_VIDEO_URL,
e: TUTORIAL_POSTER_URL,
f: !isPlaying.value
}, !isPlaying.value ? {
g: common_vendor.t(centerHintText.value)
} : {
h: common_vendor.t(currentBpm.value)
}, {
i: common_vendor.o(onTogglePlay, "f3"),
j: isPlaying.value ? 1 : "",
k: common_vendor.t(bgmEnabled.value ? "🎵" : "🔇"),
l: common_vendor.o(toggleBgm, "e3"),
m: remainingSeconds.value !== null && hasStats.value
}, remainingSeconds.value !== null && hasStats.value ? {
n: common_vendor.t(formatTime(remainingSeconds.value))
} : {}, {
o: isPlaying.value
}, isPlaying.value ? {} : {}, {
p: common_vendor.f(DUMBBELL_PRESETS, (p, k0, i0) => {
return {
a: common_vendor.t(p.label),
b: common_vendor.t(p.bpm),
c: p.id,
d: currentPreset.value === p.id ? 1 : "",
e: common_vendor.o(($event) => onPresetTap(p.id), p.id)
};
}),
q: isPlaying.value ? 1 : "",
r: common_vendor.t(currentTargetLabel.value),
s: common_vendor.t(targetExpanded.value ? "▲" : "▼"),
t: common_vendor.o(($event) => targetExpanded.value = !targetExpanded.value, "52"),
v: targetExpanded.value
}, targetExpanded.value ? {
w: common_vendor.f(TARGET_OPTIONS, (t, k0, i0) => {
return {
a: common_vendor.t(t.label),
b: t.minutes,
c: targetMinutes.value === t.minutes ? 1 : "",
d: common_vendor.o(($event) => onTargetTap(t.minutes), t.minutes)
};
}),
x: isPlaying.value ? 1 : ""
} : {}, {
y: common_vendor.t(benefitExpanded.value ? "收起 ▲" : "展开 ▼"),
z: common_vendor.o(($event) => benefitExpanded.value = !benefitExpanded.value, "f5"),
A: benefitExpanded.value
}, benefitExpanded.value ? {
B: common_vendor.f(TRAINING_BENEFIT.muscles, (m, k0, i0) => {
return {
a: common_vendor.t(m),
b: m
};
}),
C: common_vendor.t(TRAINING_BENEFIT.effect)
} : {}, {
D: isPaused.value
}, isPaused.value ? {
E: common_vendor.o(onTogglePlay, "6e"),
F: common_vendor.o(onEndTraining, "4a")
} : {}, {
G: isPlaying.value ? 1 : "",
H: isPaused.value ? 1 : "",
I: common_vendor.s(rootStyle.value),
J: showRewardPopup.value
}, showRewardPopup.value ? {
K: common_vendor.t(currentReward.value.emoji),
L: common_vendor.t(currentReward.value.title),
M: common_vendor.t(currentReward.value.desc),
N: common_vendor.t(totalReps.value),
O: common_vendor.o(closeRewardPopup, "7c"),
P: common_vendor.o(() => {
}, "ef"),
Q: common_vendor.o(closeRewardPopup, "fd")
} : {}, {
R: showSummary.value
}, showSummary.value ? common_vendor.e({
S: common_vendor.t(totalReps.value),
T: common_vendor.t(formatTime(elapsedSeconds.value)),
U: common_vendor.t(totalSugar.value.toFixed(1)),
V: sugarComparisons.value.length > 0
}, sugarComparisons.value.length > 0 ? {
W: common_vendor.f(sugarComparisons.value, (food, idx, i0) => {
return {
a: common_vendor.t(food.emoji),
b: common_vendor.t(food.count),
c: common_vendor.t(food.unit),
d: common_vendor.t(food.name),
e: idx
};
})
} : {}, {
X: common_vendor.o(onRestart, "f6"),
Y: common_vendor.o(onFinish, "44"),
Z: common_vendor.o(() => {
}, "16"),
aa: common_vendor.o(dismissSummary, "84")
}) : {});
};
}
});
_sfc_defineComponent.__runtimeHooks = 6;
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_defineComponent, [["__scopeId", "data-v-99a2f98c"]]);
wx.createPage(MiniProgramPage);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/training/pages/dumbbell.js.map
@@ -0,0 +1,7 @@
{
"navigationBarTitleText": "哑铃训练",
"navigationBarBackgroundColor": "#f8fafc",
"navigationBarTextStyle": "black",
"backgroundColor": "#f8fafc",
"usingComponents": {}
}

Some files were not shown because too many files have changed in this diff Show More