Initial commit: Happy Up monorepo through Sprint 5.

Document-driven MVP with FastAPI backend, Vue H5, WeChat mini shell, product demo, and Docker dev stack.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-23 11:42:40 +08:00
commit 1aaef71f52
116 changed files with 10550 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
const api = require('../../utils/api')
Page({
data: {
children: [],
error: '',
},
onShow() {
this.load()
},
async load() {
try {
const data = await api.listChildren()
this.setData({ children: data.list || [], error: '' })
} catch (err) {
this.setData({ error: err.message || '加载失败' })
}
},
goScreening() {
wx.navigateTo({ url: '/pages/screening/screening' })
},
})
+3
View File
@@ -0,0 +1,3 @@
{
"navigationBarTitleText": "首页"
}
+8
View File
@@ -0,0 +1,8 @@
<view class="card">
<view>儿童档案</view>
<view class="muted">{{error}}</view>
<block wx:for="{{children}}" wx:key="id">
<view>{{item.name}} · {{item.birthday}}</view>
</block>
<button class="btn-primary" bindtap="goScreening">去筛查</button>
</view>
View File
+23
View File
@@ -0,0 +1,23 @@
const api = require('../../utils/api')
Page({
data: {
phone: '18600000000',
code: '682139',
error: '',
},
onPhone(e) {
this.setData({ phone: e.detail.value })
},
onCode(e) {
this.setData({ code: e.detail.value })
},
async submit() {
try {
await api.login(this.data.phone, this.data.code)
wx.redirectTo({ url: '/pages/home/home' })
} catch (err) {
this.setData({ error: err.message || '登录失败' })
}
},
})
+3
View File
@@ -0,0 +1,3 @@
{
"navigationBarTitleText": "登录"
}
+8
View File
@@ -0,0 +1,8 @@
<view class="card">
<view>手机号验证码登录</view>
<view class="muted">开发验证码 682139</view>
<input value="{{phone}}" bindinput="onPhone" placeholder="手机号" />
<input value="{{code}}" bindinput="onCode" placeholder="验证码" />
<view class="muted">{{error}}</view>
<button class="btn-primary" bindtap="submit">登录</button>
</view>
+6
View File
@@ -0,0 +1,6 @@
input {
margin-top: 16rpx;
padding: 16rpx;
background: #f9fafb;
border-radius: 12rpx;
}
+76
View File
@@ -0,0 +1,76 @@
const api = require('../../utils/api')
Page({
data: {
status: '准备就绪',
reportSummary: '',
},
async run() {
this.setData({ status: '读取档案…' })
try {
const children = await api.listChildren()
const child = (children.list || [])[0]
if (!child) throw new Error('暂无儿童档案')
this.setData({ status: '获取上传凭证…' })
const tokenData = await api.request('/api/videos/upload-token', {
method: 'POST',
data: {
childId: child.id,
fileName: 'mini.mp4',
contentType: 'video/mp4',
size: 1024,
},
})
if (tokenData.storage === 'local') {
this.setData({ status: '直传本地存储…' })
await new Promise((resolve, reject) => {
wx.request({
url: tokenData.uploadUrl,
method: 'PUT',
header: {
'Content-Type': 'video/mp4',
'X-Upload-Token': tokenData.uploadToken,
'X-Object-Key': tokenData.objectKey,
},
data: new Uint8Array([0, 0, 0, 24, 102, 116, 121, 112, 105, 115, 111, 109]).buffer,
success(res) {
if (res.statusCode === 204) resolve()
else reject(new Error('直传失败'))
},
fail: reject,
})
})
}
this.setData({ status: '提交分析…' })
const video = await api.request('/api/videos', {
method: 'POST',
data: {
childId: child.id,
objectKey: tokenData.objectKey,
scene: 'front_posture',
},
})
const task = await api.request('/api/analysis/tasks', {
method: 'POST',
header: { 'Idempotency-Key': `mini-${Date.now()}` },
data: {
childId: child.id,
videoId: video.id,
taskType: 'posture_screening',
},
})
const report = await api.request(`/api/reports/${task.reportId}`)
this.setData({
status: '完成',
reportSummary: report.summary,
})
} catch (err) {
this.setData({ status: err.message || '失败' })
}
},
})
+3
View File
@@ -0,0 +1,3 @@
{
"navigationBarTitleText": "筛查联调"
}
+6
View File
@@ -0,0 +1,6 @@
<view class="card">
<view>筛查流程</view>
<view class="muted">{{status}}</view>
<view wx:if="{{reportSummary}}">{{reportSummary}}</view>
<button class="btn-primary" bindtap="run">一键联调</button>
</view>