feat: add multi-source breed encyclopedia and two-hour refresh sessions
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
const { secure } = require("../../utils/api");
|
||||
const providers = {
|
||||
cku: "CKU",
|
||||
fci: "FCI",
|
||||
akc: "AKC",
|
||||
rkc: "英国皇家犬业俱乐部",
|
||||
editorial: "编辑整理",
|
||||
};
|
||||
Page({
|
||||
data: {
|
||||
breed: null,
|
||||
loading: true,
|
||||
error: "",
|
||||
traits: [],
|
||||
facts: [],
|
||||
references: [],
|
||||
traitSource: "",
|
||||
},
|
||||
onLoad(options) {
|
||||
this.id = options.id;
|
||||
this.load();
|
||||
},
|
||||
async load() {
|
||||
if (this.pending) return;
|
||||
this.pending = true;
|
||||
this.setData({ loading: true, error: "" });
|
||||
try {
|
||||
const breed = await secure("/breeds/" + this.id);
|
||||
let scores = {};
|
||||
try {
|
||||
scores = JSON.parse(breed.traits || "{}") || {};
|
||||
} catch (_) {}
|
||||
const fieldSources = breed.field_sources || {};
|
||||
const facts = [
|
||||
["origin", "起源地"],
|
||||
["fci_number", "FCI 编号"],
|
||||
["height", "参考肩高"],
|
||||
["weight", "参考体重"],
|
||||
["life_span", "寿命参考"],
|
||||
["breed_group", "犬种分组"],
|
||||
].map(([key, label]) => ({
|
||||
key,
|
||||
label,
|
||||
value: breed[key] || "待补充",
|
||||
source: breed[key] ? providers[fieldSources[key]] || "" : "",
|
||||
}));
|
||||
const references = (breed.references || []).map((ref) => ({
|
||||
...ref,
|
||||
name: providers[ref.provider] || ref.provider,
|
||||
}));
|
||||
this.setData({
|
||||
breed,
|
||||
facts,
|
||||
references,
|
||||
traits: Object.keys(scores)
|
||||
.filter(
|
||||
(name) =>
|
||||
Number.isInteger(scores[name]) &&
|
||||
scores[name] >= 1 &&
|
||||
scores[name] <= 5,
|
||||
)
|
||||
.map((name) => ({ name, value: scores[name] })),
|
||||
traitSource: providers[fieldSources.traits] || "来源资料",
|
||||
exerciseSource: providers[fieldSources.exercise] || "",
|
||||
groomingSource: providers[fieldSources.grooming] || "",
|
||||
});
|
||||
} catch (error) {
|
||||
this.setData({ error: error.message });
|
||||
} finally {
|
||||
this.pending = false;
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
source(e) {
|
||||
const ref = this.data.references.find(
|
||||
(item) => item.id === e.currentTarget.dataset.id,
|
||||
);
|
||||
const url = ref ? ref.url : this.data.breed?.source_url;
|
||||
if (!url) return;
|
||||
wx.setClipboardData({
|
||||
data: url,
|
||||
success: () => wx.showToast({ title: "资料链接已复制", icon: "none" }),
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
{"disableScroll":true}
|
||||
@@ -0,0 +1,14 @@
|
||||
<page-layout id="page-layout" title="犬种小百科" back="{{true}}">
|
||||
<view class="screen detail-screen">
|
||||
<view wx:if="{{loading}}" class="empty">正在认识这位小伙伴…</view>
|
||||
<view wx:elif="{{error}}" class="empty"><text>{{error}}</text><button class="secondary" bindtap="load">重新加载</button></view>
|
||||
<block wx:elif="{{breed}}">
|
||||
<view class="breed-cover"><app-icon kind="pet" size="100" color="#b95738"/><text class="breed-name">{{breed.name}}</text><text class="english">{{breed.english_name}}</text><text wx:if="{{breed.aliases}}" class="aliases">也叫:{{breed.aliases}}</text></view>
|
||||
<view class="card"><view class="section-heading">认识它</view><text class="paragraph">{{breed.summary||'这个犬种的详细资料还在整理中,可以通过下方来源链接查看官方资料。'}}</text><view class="facts"><view wx:for="{{facts}}" wx:key="key"><text>{{item.label}}</text><text class="fact-value">{{item.value}}</text><text wx:if="{{item.source}}" class="fact-source">{{item.source}}</text></view></view></view>
|
||||
<view wx:if="{{breed.temperament||traits.length}}" class="card"><view class="section-heading">它的小性格</view><text class="paragraph">{{breed.temperament}}</text><view wx:for="{{traits}}" wx:key="name" class="trait"><text>{{item.name}}</text><view class="trait-meter"><view style="width:{{item.value*20}}%"></view></view><text>{{item.value}}/5</text></view><text wx:if="{{traits.length}}" class="footnote">{{traitSource}} 指数 · 仅作为犬种倾向参考</text></view>
|
||||
<view wx:if="{{breed.exercise||breed.grooming||breed.care_notes}}" class="card"><view class="section-heading">一起过好每一天</view><view wx:if="{{breed.exercise}}" class="care-item"><app-icon kind="paw" size="30" color="#bd7955"/><view><text class="care-title">散步与互动</text><text class="paragraph">{{breed.exercise}}</text><text wx:if="{{exerciseSource}}" class="footnote">{{exerciseSource}}</text></view></view><view wx:if="{{breed.grooming}}" class="care-item"><view class="care-circle"></view><view><text class="care-title">毛发小护理</text><text class="paragraph">{{breed.grooming}}</text><text wx:if="{{groomingSource}}" class="footnote">{{groomingSource}}</text></view></view><text wx:if="{{breed.care_notes}}" class="paragraph care-note">{{breed.care_notes}}</text><text class="footnote">需求会随年龄、身体状态和个体而变化。</text></view>
|
||||
<view class="source"><view class="section-heading">这份资料从哪里来</view><view wx:for="{{references}}" wx:key="id" class="source-row" data-id="{{item.id}}" bindtap="source"><view><text class="source-name">{{item.name}}</text><text class="source-date">核对于 {{item.retrieved_at}}</text></view><text class="source-link">复制链接 ›</text></view><block wx:if="{{!references.length}}"><text>资料来源 · {{breed.source||'编辑整理'}}</text><text class="source-link" wx:if="{{breed.source_url}}" bindtap="source">复制来源链接 ›</text></block><text wx:if="{{breed.encyclopedia_updated_at}}" class="source-date">中文资料整理于 {{breed.encyclopedia_updated_at}}</text></view>
|
||||
<view class="individual-note">品种是认识它的起点,每只狗狗也有自己的个性。</view>
|
||||
</block>
|
||||
</view>
|
||||
</page-layout>
|
||||
@@ -0,0 +1,30 @@
|
||||
.detail-screen {padding-bottom:calc(50rpx + env(safe-area-inset-bottom));}
|
||||
.breed-cover {display:flex;flex-direction:column;align-items:center;text-align:center;padding:46rpx 30rpx;background:linear-gradient(130deg,#fff0df,#eee8f7);border-radius:34rpx;margin-bottom:24rpx;}
|
||||
.breed-name {font-size:37rpx;font-weight:600;margin:20rpx 0 10rpx;}
|
||||
.english,.aliases {font-size:23rpx;color:#9d8779;line-height:1.7;}
|
||||
.section-heading {font-size:29rpx;font-weight:600;margin-bottom:20rpx;}
|
||||
.paragraph {font-size:26rpx;line-height:1.9;white-space:pre-wrap;}
|
||||
.facts {display:grid;grid-template-columns:1fr 1fr;gap:26rpx;margin:30rpx 0;}
|
||||
.facts view {padding:22rpx;background:#fff9f3;border-radius:20rpx;}
|
||||
.facts text {display:block;font-size:22rpx;color:#9d8779;margin-bottom:8rpx;}
|
||||
.facts strong {font-size:25rpx;font-weight:500;}
|
||||
.group,.footnote {display:block;font-size:22rpx;color:#9d8779;}
|
||||
.trait {display:flex;align-items:center;gap:18rpx;font-size:23rpx;margin:22rpx 0;}
|
||||
.trait>text:first-child {width:140rpx;flex:none;}
|
||||
.trait-meter {flex:1;height:12rpx;background:#f4e8de;border-radius:99rpx;overflow:hidden;}
|
||||
.trait-meter view {height:100%;background:#e5a47b;border-radius:99rpx;}
|
||||
.source {display:flex;flex-direction:column;gap:10rpx;padding:24rpx;color:#9d8779;font-size:22rpx;}
|
||||
.source-link {color:#b95738;}
|
||||
.individual-note {text-align:center;color:#a48d7e;font-size:22rpx;margin-top:20rpx;}
|
||||
.facts text.fact-value{font-size:25rpx;color:#675146;font-weight:500;line-height:1.5;margin-bottom:6rpx}
|
||||
.facts text.fact-source{font-size:19rpx;color:#ba9f8d;margin:0}
|
||||
.care-item{display:flex;gap:18rpx;padding:22rpx 0;border-bottom:1rpx solid #f3e9df}
|
||||
.care-item>view:last-child{flex:1;min-width:0}
|
||||
.care-title{display:block;font-size:25rpx;color:#aa6e4e;margin-bottom:8rpx}
|
||||
.care-circle{height:28rpx;width:28rpx;border:4rpx solid #d8af92;border-radius:50%;flex:none;box-sizing:border-box;margin-top:4rpx}
|
||||
.care-note{display:block;margin:24rpx 0}
|
||||
.source{background:#fff4e8;border-radius:28rpx;margin-top:24rpx}
|
||||
.source-row{display:flex;align-items:center;justify-content:space-between;gap:20rpx;padding:18rpx 0;border-bottom:1rpx solid #efdfcf}
|
||||
.source-name{display:block;font-size:25rpx;color:#735e51}
|
||||
.source-date{display:block;font-size:20rpx;line-height:1.8;color:#b49a88}
|
||||
.source-link{flex:none;font-size:23rpx}
|
||||
Reference in New Issue
Block a user