0.0.0
This commit is contained in:
198
miniprogram/pages/summary/summary.js
Normal file
198
miniprogram/pages/summary/summary.js
Normal file
@@ -0,0 +1,198 @@
|
||||
// pages/summary/summary — 3 成果卡
|
||||
// 「狗狗走了」视角;补充字段全选填(R7);Canvas 生成分享图;订阅提醒
|
||||
const { call } = require('../../utils/cloud.js');
|
||||
const store = require('../../utils/store.js');
|
||||
const { formatDuration } = require('../../utils/format.js');
|
||||
const { chooseAndUpload } = require('../../utils/media.js');
|
||||
const { SUBSCRIBE_TEMPLATE_ID } = require('../../utils/config.js');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
loaded: false,
|
||||
walkId: '',
|
||||
primaryDogId: '',
|
||||
dogName: '',
|
||||
dogAvatar: '',
|
||||
headline: '',
|
||||
bigValue: '',
|
||||
bigUnit: '',
|
||||
timeText: '',
|
||||
distanceText: '—',
|
||||
streak: 0,
|
||||
newAchievements: [],
|
||||
weathers: [{ k: 'sunny', l: '☀️ 晴' }, { k: 'cloudy', l: '☁️ 阴' }, { k: 'rainy', l: '🌧️ 雨' }],
|
||||
moods: [{ k: 'happy', l: '遛得开心' }, { k: 'tired', l: '有点累' }, { k: 'excited', l: '很兴奋' }],
|
||||
weather: '',
|
||||
mood: '',
|
||||
note: '',
|
||||
photos: [],
|
||||
submitting: false,
|
||||
},
|
||||
|
||||
_attached: false,
|
||||
|
||||
onLoad() {
|
||||
const sum = getApp().globalData.lastSummary;
|
||||
if (!sum) {
|
||||
wx.showToast({ title: '数据缺失', icon: 'none' });
|
||||
setTimeout(() => wx.switchTab({ url: '/pages/home/home' }), 1000);
|
||||
return;
|
||||
}
|
||||
const r = (sum.results && sum.results[0]) || {};
|
||||
const hasSteps = r.dogSteps != null;
|
||||
const dog = (store.getState().dogs || []).find((d) => d._id === r.dogId);
|
||||
this.setData({
|
||||
loaded: true,
|
||||
walkId: sum.walkId,
|
||||
primaryDogId: r.dogId || '',
|
||||
dogName: r.name || '狗狗',
|
||||
dogAvatar: (dog && dog.avatar) || '',
|
||||
headline: hasSteps ? `${r.name}今天走了` : `${r.name}今天遛了`,
|
||||
bigValue: hasSteps ? String(r.dogSteps) : formatDuration(r.duration || 0),
|
||||
bigUnit: hasSteps ? '狗步' : '',
|
||||
timeText: formatDuration(r.duration || 0),
|
||||
distanceText: r.distance != null ? Number(r.distance).toFixed(1) : '—',
|
||||
streak: r.streak || 0,
|
||||
newAchievements: sum.newAchievements || [],
|
||||
});
|
||||
},
|
||||
|
||||
pickWeather(e) { const k = e.currentTarget.dataset.k; this.setData({ weather: this.data.weather === k ? '' : k }); },
|
||||
pickMood(e) { const k = e.currentTarget.dataset.k; this.setData({ mood: this.data.mood === k ? '' : k }); },
|
||||
onNote(e) { this.setData({ note: e.detail.value }); },
|
||||
|
||||
async onPickPhoto() {
|
||||
const fileID = await chooseAndUpload('walk-photos');
|
||||
if (fileID) this.setData({ photos: [fileID] });
|
||||
},
|
||||
|
||||
// 选填字段回写(只回写一次;含雨天战士成就判定)
|
||||
async ensureAttached() {
|
||||
if (this._attached) return;
|
||||
const { weather, mood, note, photos, walkId } = this.data;
|
||||
// 无任何选填内容时直接返回,但不锁定:用户可能稍后再补填,待真正回写后才置位
|
||||
if (!weather && !mood && !note && !photos.length) return;
|
||||
const res = await call('walkSave', {
|
||||
action: 'attach', walkId,
|
||||
weather: weather || null, mood: mood || null, note: note || null, photos,
|
||||
});
|
||||
this._attached = true;
|
||||
if (res && res.ok && res.data.newAchievements && res.data.newAchievements.length) {
|
||||
wx.showToast({ title: `解锁 ${res.data.newAchievements[0].name}`, icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
// ===== Canvas 生成分享图 =====
|
||||
drawShareCard() {
|
||||
return new Promise((resolve) => {
|
||||
const q = wx.createSelectorQuery();
|
||||
q.select('#shareCanvas').fields({ node: true, size: true }).exec((res) => {
|
||||
if (!res[0] || !res[0].node) return resolve(null);
|
||||
const canvas = res[0].node;
|
||||
const ctx = canvas.getContext('2d');
|
||||
const dpr = (wx.getWindowInfo && wx.getWindowInfo().pixelRatio) || 2;
|
||||
const W = 600, H = 820;
|
||||
canvas.width = W * dpr;
|
||||
canvas.height = H * dpr;
|
||||
ctx.scale(dpr, dpr);
|
||||
|
||||
// 背景
|
||||
ctx.fillStyle = '#FBF8F3';
|
||||
ctx.fillRect(0, 0, W, H);
|
||||
// 顶部色带
|
||||
ctx.fillStyle = '#E8A04B';
|
||||
ctx.fillRect(0, 0, W, 12);
|
||||
// 头像圆
|
||||
ctx.fillStyle = '#F7E5CC';
|
||||
ctx.beginPath();
|
||||
ctx.arc(W / 2, 150, 70, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillStyle = '#2E2A24';
|
||||
ctx.font = '600 34px sans-serif';
|
||||
ctx.fillText(this.data.headline, W / 2, 290);
|
||||
|
||||
ctx.fillStyle = '#C77E2E';
|
||||
ctx.font = '700 96px sans-serif';
|
||||
ctx.fillText(this.data.bigValue, W / 2, 400);
|
||||
if (this.data.bigUnit) {
|
||||
ctx.fillStyle = '#6E665B';
|
||||
ctx.font = '500 28px sans-serif';
|
||||
ctx.fillText(this.data.bigUnit, W / 2, 445);
|
||||
}
|
||||
|
||||
// 三宫格
|
||||
const cols = [
|
||||
{ v: this.data.timeText, k: '时长' },
|
||||
{ v: this.data.distanceText, k: '公里' },
|
||||
{ v: `第${this.data.streak}天`, k: '连续' },
|
||||
];
|
||||
const baseY = 560;
|
||||
cols.forEach((c, i) => {
|
||||
const x = (W / 4) * (i + 1);
|
||||
ctx.fillStyle = '#2E2A24';
|
||||
ctx.font = '600 36px sans-serif';
|
||||
ctx.fillText(c.v, x, baseY);
|
||||
ctx.fillStyle = '#A89F92';
|
||||
ctx.font = '400 24px sans-serif';
|
||||
ctx.fillText(c.k, x, baseY + 40);
|
||||
});
|
||||
|
||||
// 页脚
|
||||
ctx.fillStyle = '#A89F92';
|
||||
ctx.font = '500 26px sans-serif';
|
||||
ctx.fillText('汪圈 · 每一次遛狗都值得被记录', W / 2, 760);
|
||||
|
||||
wx.canvasToTempFilePath({
|
||||
canvas,
|
||||
success: (r) => resolve(r.tempFilePath),
|
||||
fail: () => resolve(null),
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async onShare() {
|
||||
if (this.data.submitting) return;
|
||||
this.setData({ submitting: true });
|
||||
await this.ensureAttached();
|
||||
const path = await this.drawShareCard();
|
||||
this.setData({ submitting: false });
|
||||
if (!path) return wx.showToast({ title: '生成失败', icon: 'none' });
|
||||
wx.showShareImageMenu({ path }); // 用户可分享到朋友圈/好友或保存
|
||||
},
|
||||
|
||||
// 订阅「遛狗提醒」(模板未配置时静默跳过)
|
||||
requestSubscribe() {
|
||||
if (!SUBSCRIBE_TEMPLATE_ID) return Promise.resolve();
|
||||
return new Promise((resolve) => {
|
||||
wx.requestSubscribeMessage({ tmplIds: [SUBSCRIBE_TEMPLATE_ID], complete: resolve });
|
||||
});
|
||||
},
|
||||
|
||||
async onDone() {
|
||||
if (this.data.submitting) return;
|
||||
this.setData({ submitting: true });
|
||||
await this.requestSubscribe(); // 需在用户点击手势内触发
|
||||
await this.ensureAttached();
|
||||
this.setData({ submitting: false });
|
||||
this.sediment();
|
||||
},
|
||||
|
||||
// 转发好友 / 朋友圈(小程序卡片)
|
||||
onShareAppMessage() {
|
||||
return { title: `${this.data.dogName}今天的遛狗成果`, path: '/pages/login/login' };
|
||||
},
|
||||
onShareTimeline() {
|
||||
return { title: `${this.data.dogName}今天也出门遛了一圈` };
|
||||
},
|
||||
|
||||
onClose() { wx.switchTab({ url: '/pages/home/home' }); },
|
||||
|
||||
sediment() {
|
||||
const id = this.data.primaryDogId;
|
||||
if (id) wx.redirectTo({ url: `/pages/dog-detail/dog-detail?id=${id}` });
|
||||
else wx.switchTab({ url: '/pages/home/home' });
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user