0.0.0
This commit is contained in:
62
miniprogram/components/manual-add-sheet/index.js
Normal file
62
miniprogram/components/manual-add-sheet/index.js
Normal file
@@ -0,0 +1,62 @@
|
||||
// components/manual-add-sheet — 手动补记弹层(docs/05 R7)
|
||||
// 用法:父页放 <manual-add-sheet id="addSheet" dogs="{{dogs}}" bind:saved="onSaved"/>
|
||||
// this.selectComponent('#addSheet').open()
|
||||
const { call } = require('../../utils/cloud.js');
|
||||
|
||||
function pad(n) { return String(n).padStart(2, '0'); }
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
dogs: { type: Array, value: [] },
|
||||
},
|
||||
data: {
|
||||
show: false,
|
||||
dogId: '',
|
||||
date: '',
|
||||
time: '18:00',
|
||||
durationMin: '',
|
||||
distance: '',
|
||||
submitting: false,
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
const dogs = this.data.dogs;
|
||||
const t = new Date();
|
||||
const date = `${t.getFullYear()}-${pad(t.getMonth() + 1)}-${pad(t.getDate())}`;
|
||||
this.setData({
|
||||
show: true,
|
||||
dogId: dogs[0] ? dogs[0]._id : '',
|
||||
date, time: '18:00', durationMin: '', distance: '',
|
||||
});
|
||||
},
|
||||
close() { this.setData({ show: false }); },
|
||||
noop() {},
|
||||
pickDog(e) { this.setData({ dogId: e.currentTarget.dataset.id }); },
|
||||
onDate(e) { this.setData({ date: e.detail.value }); },
|
||||
onTime(e) { this.setData({ time: e.detail.value }); },
|
||||
onDuration(e) { this.setData({ durationMin: e.detail.value }); },
|
||||
onDistance(e) { this.setData({ distance: e.detail.value }); },
|
||||
|
||||
async save() {
|
||||
const { dogId, date, time, durationMin, distance, submitting } = this.data;
|
||||
if (submitting) return;
|
||||
if (!dogId) return wx.showToast({ title: '选择狗狗', icon: 'none' });
|
||||
if (!durationMin || Number(durationMin) <= 0) return wx.showToast({ title: '填写时长', icon: 'none' });
|
||||
|
||||
const startAt = new Date(`${date}T${time || '00:00'}:00`).getTime();
|
||||
this.setData({ submitting: true });
|
||||
const res = await call('walkManual', {
|
||||
dogId,
|
||||
startAt,
|
||||
duration: Math.round(Number(durationMin) * 60),
|
||||
distance: distance ? Number(distance) : null,
|
||||
}, { loading: true, loadingText: '保存中' });
|
||||
this.setData({ submitting: false });
|
||||
if (!res.ok) return;
|
||||
|
||||
this.setData({ show: false });
|
||||
wx.showToast({ title: '补记成功', icon: 'success' });
|
||||
this.triggerEvent('saved');
|
||||
},
|
||||
},
|
||||
});
|
||||
4
miniprogram/components/manual-add-sheet/index.json
Normal file
4
miniprogram/components/manual-add-sheet/index.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
47
miniprogram/components/manual-add-sheet/index.wxml
Normal file
47
miniprogram/components/manual-add-sheet/index.wxml
Normal file
@@ -0,0 +1,47 @@
|
||||
<view class="overlay {{show?'show':''}}" bindtap="close">
|
||||
<view class="sheet" catchtap="noop">
|
||||
<view class="handle"></view>
|
||||
<view class="title">手动补记</view>
|
||||
<view class="subtitle">忘记开 App 也能补上这次遛狗</view>
|
||||
|
||||
<scroll-view scroll-y class="sheet-body">
|
||||
<view class="field">
|
||||
<view class="flabel">哪只狗</view>
|
||||
<view class="chips">
|
||||
<view wx:for="{{dogs}}" wx:key="_id" class="chip {{dogId===item._id?'sel':''}}" data-id="{{item._id}}" bindtap="pickDog">{{item.name}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<view class="flabel">日期</view>
|
||||
<picker mode="date" value="{{date}}" bindchange="onDate">
|
||||
<view class="fpick">{{date}} <text class="chev">›</text></view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<view class="flabel">时间</view>
|
||||
<picker mode="time" value="{{time}}" bindchange="onTime">
|
||||
<view class="fpick">{{time}} <text class="chev">›</text></view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<view class="flabel">时长(分钟)</view>
|
||||
<input class="finput" type="digit" placeholder="如 15" value="{{durationMin}}" bindinput="onDuration" />
|
||||
</view>
|
||||
|
||||
<view class="field last">
|
||||
<view class="flabel">距离(公里,可选)</view>
|
||||
<input class="finput" type="digit" placeholder="可不填" value="{{distance}}" bindinput="onDistance" />
|
||||
</view>
|
||||
|
||||
<view class="rule">补记不影响连续打卡,但不计入排行榜。</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="btns">
|
||||
<button class="btn ghost" bindtap="close">取消</button>
|
||||
<button class="btn primary" bindtap="save" loading="{{submitting}}">保存补记</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
59
miniprogram/components/manual-add-sheet/index.wxss
Normal file
59
miniprogram/components/manual-add-sheet/index.wxss
Normal file
@@ -0,0 +1,59 @@
|
||||
.overlay {
|
||||
font-family: var(--font);
|
||||
position: fixed; inset: 0; z-index: 60;
|
||||
background: rgba(46,42,36,0.42);
|
||||
display: flex; align-items: flex-end;
|
||||
opacity: 0; pointer-events: none; transition: opacity .22s;
|
||||
}
|
||||
.overlay.show { opacity: 1; pointer-events: auto; }
|
||||
.sheet {
|
||||
width: 100%;
|
||||
background: var(--surface);
|
||||
border-radius: 44rpx 44rpx 0 0;
|
||||
padding: 16rpx 32rpx calc(32rpx + env(safe-area-inset-bottom));
|
||||
transform: translateY(40rpx); transition: transform .26s;
|
||||
max-height: 88vh;
|
||||
display: flex; flex-direction: column; /* 头部/底部固定,中间表单滚动 */
|
||||
}
|
||||
.overlay.show .sheet { transform: translateY(0); }
|
||||
.handle { flex-shrink: 0; width: 72rpx; height: 8rpx; border-radius: 4rpx; background: var(--line-2); margin: 12rpx auto 24rpx; }
|
||||
.title { flex-shrink: 0; font-size: 32rpx; font-weight: 600; text-align: center; }
|
||||
.subtitle { flex-shrink: 0; font-size: 22rpx; color: var(--ink-2); text-align: center; margin: 8rpx 0 20rpx; }
|
||||
|
||||
/* 可滚动表单区:内容再多,底部按钮也不会被挤出可视区 */
|
||||
.sheet-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
|
||||
|
||||
.field { padding: 22rpx 0; border-bottom: 1rpx solid var(--line); }
|
||||
.field.last { border-bottom: none; }
|
||||
.flabel { font-size: var(--form-label-size); color: var(--ink-2); margin-bottom: 12rpx; }
|
||||
.chips { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||||
.chip { font-size: var(--chip-font-size); padding: var(--chip-padding-y) var(--chip-padding-x); border-radius: var(--radius-pill); border: 1rpx solid var(--line-2); background: var(--surface-2); line-height: 1.4; }
|
||||
.chip.sel { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-deep); font-weight: 600; }
|
||||
|
||||
.fpick { min-height: var(--input-height); font-size: var(--input-font-size); background: var(--surface-2); border-radius: var(--input-radius); padding: var(--input-padding-y) var(--input-padding-x); line-height: 1.4; display: flex; align-items: center; justify-content: space-between; }
|
||||
.fpick .chev { color: var(--ink-3); font-size: var(--icon-size-sm); line-height: 1; }
|
||||
.finput { height: var(--input-height); min-height: var(--input-height); font-size: var(--input-font-size); background: var(--surface-2); border-radius: var(--input-radius); padding: var(--input-padding-y) var(--input-padding-x); line-height: 1.4; }
|
||||
|
||||
.rule {
|
||||
margin: 16rpx 0;
|
||||
font-size: 22rpx; color: #7a5a1e;
|
||||
background: #FFF7E8; border: 1rpx dashed var(--accent);
|
||||
border-radius: 12rpx; padding: 16rpx 18rpx; line-height: 1.5;
|
||||
}
|
||||
.btns { flex-shrink: 0; display: flex; gap: 20rpx; padding-top: 16rpx; }
|
||||
|
||||
/* 组件样式隔离:app.wxss 的 .btn 不会透传进来,按本组件已有做法在此本地补全 */
|
||||
.btn {
|
||||
height: var(--btn-height);
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1rpx solid var(--line-2);
|
||||
background: var(--surface);
|
||||
color: var(--ink);
|
||||
font-size: 28rpx; font-weight: 600;
|
||||
display: flex; align-items: center; justify-content: center; gap: 12rpx;
|
||||
width: 100%; line-height: 1;
|
||||
}
|
||||
.btn::after { border: none; }
|
||||
.btn.primary { background: var(--accent); color: #fff; border-color: transparent; }
|
||||
.btn.ghost { background: transparent; }
|
||||
.btn:active { opacity: 0.96; }
|
||||
Reference in New Issue
Block a user