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}
|
||||
@@ -0,0 +1,50 @@
|
||||
const { loadBreeds, groupBreeds } = require("../../utils/breeds");
|
||||
Page({
|
||||
data: {
|
||||
groups: [],
|
||||
query: "",
|
||||
loading: true,
|
||||
error: "",
|
||||
total: 0,
|
||||
initial: "",
|
||||
},
|
||||
onLoad() {
|
||||
this.load();
|
||||
},
|
||||
async load() {
|
||||
if (this.pending) return;
|
||||
this.pending = true;
|
||||
this.setData({ loading: true, error: "" });
|
||||
try {
|
||||
this.rows = await loadBreeds();
|
||||
this.filter();
|
||||
} catch (error) {
|
||||
this.setData({ error: error.message });
|
||||
} finally {
|
||||
this.pending = false;
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
filter() {
|
||||
let groups = groupBreeds(this.rows || [], this.data.query);
|
||||
if (this.data.initial)
|
||||
groups = groups.filter((g) => g.letter === this.data.initial);
|
||||
this.setData({
|
||||
groups,
|
||||
total: groups.reduce((n, g) => n + g.items.length, 0),
|
||||
});
|
||||
},
|
||||
search(e) {
|
||||
this.setData({ query: e.detail.value, initial: "" });
|
||||
this.filter();
|
||||
},
|
||||
letter(e) {
|
||||
this.setData({ initial: e.currentTarget.dataset.letter });
|
||||
this.filter();
|
||||
},
|
||||
open(e) {
|
||||
wx.navigateTo({
|
||||
url: "/pages/breed-detail/breed-detail?id=" + e.currentTarget.dataset.id,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
{"disableScroll":true,"usingComponents":{"tab-bar":"/components/tab-bar/index"}}
|
||||
@@ -0,0 +1,12 @@
|
||||
<page-layout id="page-layout" title="一起懂它更多">
|
||||
<view class="screen">
|
||||
<view class="knowledge-hero"><text class="eyebrow">肉垫小百科</text><view class="hero-title"><text>每个小伙伴</text><text>都有自己的模样</text></view><text class="hero-copy">从认识品种开始,慢慢读懂它。</text><view class="hero-book"><view class="book-middle"></view><view class="book-stripe"></view></view></view>
|
||||
<input class="knowledge-search" value="{{query}}" bindinput="search" placeholder="搜索犬种、常用名或拼音" maxlength="128" />
|
||||
<scroll-view class="alphabet-scroll" scroll-x enhanced show-scrollbar="{{false}}"><view class="alphabet"><text class="{{initial===''?'selected':''}}" data-letter="" bindtap="letter">全部</text><text wx:for="{{['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']}}" wx:key="*this" class="{{initial===item?'selected':''}}" data-letter="{{item}}" bindtap="letter">{{item}}</text></view></scroll-view>
|
||||
<view class="catalog-heading"><text>犬种百科</text><text class="catalog-count">{{total}} 个小伙伴</text></view>
|
||||
<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:else><view wx:for="{{groups}}" wx:for-item="group" wx:key="letter"><view class="group-letter">{{group.letter}}</view><view wx:for="{{group.items}}" wx:key="id" class="breed-entry" data-id="{{item.id}}" bindtap="open"><view class="entry-letter">{{item.initial}}</view><view class="entry-copy"><text class="entry-name">{{item.name}}</text><text class="entry-alias">{{item.origin||item.aliases||item.english_name||'点开认识这位小伙伴'}}</text><text class="entry-status">{{item.has_details?'认识它的小性格':'资料整理中'}}</text></view><text class="entry-arrow">›</text></view></view><view wx:if="{{!total}}" class="empty">暂时没找到这个品种,换个名字试试吧。</view></block>
|
||||
</view>
|
||||
</page-layout>
|
||||
<tab-bar current="/pages/knowledge/knowledge" />
|
||||
@@ -0,0 +1,25 @@
|
||||
.knowledge-hero { position:relative;overflow:hidden;padding:34rpx;background:#eee8f7;border-radius:34rpx;color:#79658c; }
|
||||
.knowledge-hero .eyebrow {color:#9782aa;letter-spacing:1rpx;}
|
||||
.hero-title {font-size:37rpx;font-weight:600;line-height:1.5;margin:16rpx 0 12rpx;}
|
||||
.hero-copy {font-size:23rpx;}
|
||||
.hero-book {position:absolute;right:45rpx;top:70rpx;width:106rpx;height:94rpx;border:5rpx solid #b4a3c5;border-radius:20rpx 20rpx 8rpx 8rpx;transform:rotate(9deg);background:#fff9f380;}
|
||||
.book-middle {position:absolute;left:48rpx;height:100%;border-left:4rpx solid #b4a3c5;}
|
||||
.book-stripe {position:absolute;left:14rpx;right:14rpx;top:26rpx;height:24rpx;border-top:3rpx solid #b4a3c5;border-bottom:3rpx solid #b4a3c5;}
|
||||
.knowledge-search {height:92rpx;padding:0 28rpx;background:#fff;border-radius:24rpx;margin:28rpx 0 16rpx;font-size:26rpx;}
|
||||
.alphabet-scroll {width:100%;white-space:nowrap;}
|
||||
.alphabet {display:inline-flex;gap:10rpx;padding:4rpx 0 12rpx;}
|
||||
.alphabet text {display:inline-block;padding:10rpx 20rpx;border-radius:16rpx;color:#9d8779;background:#fff;}
|
||||
.alphabet .selected {color:#b95738;background:#ffeadc;}
|
||||
.catalog-heading {display:flex;align-items:center;justify-content:space-between;margin:30rpx 0 8rpx;font-size:32rpx;font-weight:600;}
|
||||
.catalog-count {font-size:23rpx;color:#9d8779;font-weight:400;}
|
||||
.group-letter {color:#b95738;font-size:29rpx;font-weight:600;padding:20rpx 8rpx 12rpx;}
|
||||
.breed-entry {display:flex;align-items:center;gap:20rpx;padding:26rpx;background:#fff;border-radius:26rpx;margin-bottom:14rpx;}
|
||||
.entry-letter {width:66rpx;height:66rpx;display:flex;align-items:center;justify-content:center;flex:none;background:#fff0e2;border-radius:22rpx;color:#ba7355;font-size:30rpx;}
|
||||
.entry-copy {flex:1;min-width:0;}
|
||||
.entry-name {display:block;font-size:27rpx;font-weight:600;}
|
||||
.entry-alias {display:block;color:#9d8779;font-size:21rpx;margin-top:6rpx;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}
|
||||
.entry-arrow {color:#bfa697;font-size:36rpx;}
|
||||
|
||||
.entry-status{display:block;font-size:19rpx;color:#bc9c85;margin-top:6rpx}
|
||||
|
||||
.hero-title>text{display:block}
|
||||
@@ -1,108 +0,0 @@
|
||||
const { secure, toast } = require("../../utils/api");
|
||||
Page({
|
||||
data: { meetups: [], pets: [], selected: [], active: null, userId: "", joined: false, busy: false },
|
||||
onShow() {
|
||||
this.load();
|
||||
},
|
||||
async load() {
|
||||
try {
|
||||
const [meetups, pets, me] = await Promise.all([
|
||||
secure("/meetups"),
|
||||
secure("/pets"),
|
||||
secure("/me"),
|
||||
]);
|
||||
this.setData({
|
||||
meetups: meetups.map((m) => ({
|
||||
...m,
|
||||
date: new Date(m.starts_at).toLocaleString("zh-CN"),
|
||||
})),
|
||||
pets,
|
||||
userId: me.id,
|
||||
});
|
||||
} catch (e) {
|
||||
toast(e);
|
||||
}
|
||||
},
|
||||
create() {
|
||||
wx.navigateTo({ url: "/pages/meetup-edit/meetup-edit" });
|
||||
},
|
||||
async open(e) {
|
||||
const summary = this.data.meetups[e.currentTarget.dataset.index];
|
||||
try {
|
||||
const active = await secure("/meetups/" + summary.id);
|
||||
const participant = (active.people || []).find((person) => person.user_id === this.data.userId);
|
||||
this.setData({
|
||||
active: { ...active, date: new Date(active.starts_at).toLocaleString("zh-CN") },
|
||||
joined: !!participant,
|
||||
selected: participant ? participant.pet_ids : [],
|
||||
});
|
||||
} catch (error) {
|
||||
toast(error);
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.setData({ active: null, joined: false, selected: [] });
|
||||
},
|
||||
pick(e) {
|
||||
this.setData({ selected: e.detail.selected });
|
||||
},
|
||||
async join() {
|
||||
if (this.data.busy) return;
|
||||
if (!this.data.selected.length) {
|
||||
wx.showToast({ title: "请选择同行的狗狗", icon: "none" });
|
||||
return;
|
||||
}
|
||||
this.setData({ busy: true });
|
||||
try {
|
||||
await secure("/meetups/" + this.data.active.id + "/join", "POST", {
|
||||
pet_ids: this.data.selected,
|
||||
});
|
||||
wx.showToast({ title: "报名成功", icon: "success" });
|
||||
this.close();
|
||||
await this.load();
|
||||
} catch (e) {
|
||||
toast(e);
|
||||
} finally {
|
||||
this.setData({ busy: false });
|
||||
}
|
||||
},
|
||||
async leave() {
|
||||
if (this.data.busy) return;
|
||||
const result = await wx.showModal({ title: "退出约遛", content: "确定不参加这场约遛了吗?" });
|
||||
if (!result.confirm) return;
|
||||
this.setData({ busy: true });
|
||||
try {
|
||||
await secure("/meetups/" + this.data.active.id + "/join", "DELETE");
|
||||
this.close();
|
||||
await this.load();
|
||||
} catch (error) {
|
||||
toast(error);
|
||||
} finally {
|
||||
this.setData({ busy: false });
|
||||
}
|
||||
},
|
||||
location() {
|
||||
const active = this.data.active;
|
||||
if (active && active.latitude && active.longitude) {
|
||||
wx.openLocation({ latitude: active.latitude, longitude: active.longitude, name: active.place_name, scale: 16 });
|
||||
}
|
||||
},
|
||||
async cancel() {
|
||||
if (this.data.busy) return;
|
||||
const r = await wx.showModal({
|
||||
title: "取消约遛",
|
||||
content: "确定取消这场约遛吗?",
|
||||
});
|
||||
if (!r.confirm) return;
|
||||
this.setData({ busy: true });
|
||||
try {
|
||||
await secure("/meetups/" + this.data.active.id + "/cancel", "POST");
|
||||
this.close();
|
||||
await this.load();
|
||||
} catch (e) {
|
||||
toast(e);
|
||||
} finally {
|
||||
this.setData({ busy: false });
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"disableScroll": true,
|
||||
"usingComponents": {
|
||||
"tab-bar": "/components/tab-bar/index",
|
||||
"pet-picker": "/components/pet-picker/index"
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
<page-layout id="page-layout" title="约个伴一起遛">
|
||||
<view class="screen">
|
||||
<view class="banner">
|
||||
<view class="banner-copy">
|
||||
<text>好天气,和新朋友一起走走</text>
|
||||
<view class="banner-title first">下一次快乐</view>
|
||||
<view class="banner-title">从这里出发</view>
|
||||
</view>
|
||||
<image src="/assets/corgi-family-five.png" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="section-title">即将开始的约遛</text>
|
||||
<text class="add" bindtap="create">+ 发起约遛</text>
|
||||
</view>
|
||||
<view
|
||||
wx:for="{{meetups}}"
|
||||
wx:key="id"
|
||||
class="card meetup"
|
||||
data-index="{{index}}"
|
||||
bindtap="open"
|
||||
>
|
||||
<view class="row">
|
||||
<text class="badge">
|
||||
{{item.participants}} / {{item.max_people}} 人
|
||||
</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
<strong>{{item.title}}</strong>
|
||||
<view class="meta">地点 · {{item.place_name}}</view>
|
||||
<view class="meta">时间 · {{item.date}}</view>
|
||||
<view class="desc">{{item.description}}</view>
|
||||
</view>
|
||||
<view wx:if="{{!meetups.length}}" class="empty">
|
||||
还没有约遛,发起第一场吧
|
||||
</view>
|
||||
<button class="primary" bindtap="create">+ 发起一场约遛</button>
|
||||
</view>
|
||||
</page-layout>
|
||||
<view wx:if="{{active}}" class="overlay">
|
||||
<view class="sheet">
|
||||
<view class="row">
|
||||
<strong>{{active.title}}</strong>
|
||||
<text bindtap="close">×</text>
|
||||
</view>
|
||||
<view class="detail">
|
||||
<view bindtap="location">地点 · {{active.place_name}} {{active.latitude?'· 点开地图':''}}</view>
|
||||
<view>时间 · {{active.date}}</view>
|
||||
<view> {{active.participants}} / {{active.max_people}} 人已报名</view>
|
||||
</view>
|
||||
<view class="desc">{{active.description}}</view>
|
||||
<view wx:if="{{active.host_user_id!==userId&&!joined}}">
|
||||
<view class="select-title">带哪几只?可以多选</view>
|
||||
<pet-picker pets="{{pets}}" selected="{{selected}}" bind:change="pick" />
|
||||
<button class="primary" bindtap="join" disabled="{{busy||active.participants>=active.max_people}}">{{active.participants>=active.max_people?'人数已满':'带狗狗报名'}}</button>
|
||||
</view>
|
||||
<button wx:elif="{{joined&&active.host_user_id!==userId}}" class="secondary" bindtap="leave" disabled="{{busy}}">已报名 · 退出约遛</button>
|
||||
<button wx:else class="secondary" bindtap="cancel" disabled="{{busy}}">取消这场约遛</button>
|
||||
</view>
|
||||
</view>
|
||||
<tab-bar current="/pages/meetups/meetups" />
|
||||
@@ -1,101 +0,0 @@
|
||||
.banner {
|
||||
background: #edf2fb;
|
||||
border-radius: 34rpx;
|
||||
min-height: 245rpx;
|
||||
padding: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
.banner-copy {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.banner-copy text,
|
||||
.banner-title {
|
||||
display: block;
|
||||
}
|
||||
.banner-copy text {
|
||||
color: #576c88;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
.banner-title {
|
||||
font-size: 35rpx;
|
||||
line-height: 1.3;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.banner-title.first {
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
.banner image {
|
||||
width: 52%;
|
||||
height: 190rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.add {
|
||||
font-size: 24rpx;
|
||||
color: #b95738;
|
||||
font-weight: 600;
|
||||
}
|
||||
.meetup strong {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
margin: 15rpx 0;
|
||||
}
|
||||
.badge {
|
||||
background: #edf2fb;
|
||||
color: #b95738;
|
||||
border-radius: 99rpx;
|
||||
padding: 8rpx 15rpx;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
.arrow {
|
||||
font-size: 39rpx;
|
||||
color: #9d8779;
|
||||
}
|
||||
.meta {
|
||||
font-size: 24rpx;
|
||||
color: #886c5f;
|
||||
margin-top: 9rpx;
|
||||
}
|
||||
.desc {
|
||||
font-size: 24rpx;
|
||||
color: #886c5f;
|
||||
margin-top: 15rpx;
|
||||
}
|
||||
.overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: #604a40a0;
|
||||
z-index: 30;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.sheet {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 36rpx 36rpx 0 0;
|
||||
padding: 35rpx 32rpx calc(env(safe-area-inset-bottom) + 35rpx);
|
||||
}
|
||||
.sheet > .row strong {
|
||||
font-size: 34rpx;
|
||||
}
|
||||
.sheet > .row text {
|
||||
font-size: 45rpx;
|
||||
}
|
||||
.detail {
|
||||
font-size: 25rpx;
|
||||
color: #886c5f;
|
||||
line-height: 2;
|
||||
margin: 25rpx 0;
|
||||
}
|
||||
.select-title {
|
||||
font-size: 25rpx;
|
||||
font-weight: 600;
|
||||
margin: 30rpx 0 15rpx;
|
||||
}
|
||||
.sheet button {
|
||||
margin-top: 26rpx;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ Page({
|
||||
id: "",
|
||||
name: "",
|
||||
breed: "",
|
||||
breed_id: "",
|
||||
gender: "male",
|
||||
birth_date: "",
|
||||
weight_kg: "",
|
||||
@@ -30,6 +31,9 @@ Page({
|
||||
field(e) {
|
||||
this.setData({ [e.currentTarget.dataset.key]: e.detail.value });
|
||||
},
|
||||
breedChange(e) {
|
||||
this.setData({ breed: e.detail.name, breed_id: e.detail.id || "" });
|
||||
},
|
||||
date(e) {
|
||||
this.setData({ birth_date: e.detail.value });
|
||||
},
|
||||
@@ -55,6 +59,7 @@ Page({
|
||||
name: this.data.name,
|
||||
species: "dog",
|
||||
breed: this.data.breed,
|
||||
breed_id: this.data.breed_id || null,
|
||||
gender: this.data.gender,
|
||||
birth_date: this.data.birth_date || "",
|
||||
weight_kg: Number(this.data.weight_kg) || 0,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"disableScroll": true,
|
||||
"usingComponents": {}
|
||||
"usingComponents": {
|
||||
"breed-picker": "/components/breed-picker/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</view>
|
||||
<view class="field">
|
||||
<view class="field-label">品种</view>
|
||||
<input placeholder="填写它的品种" value="{{breed}}" data-key="breed" bindinput="field" />
|
||||
<breed-picker value="{{breed}}" breed-id="{{breed_id||''}}" bind:change="breedChange" />
|
||||
</view>
|
||||
<view class="field">
|
||||
<view class="field-label">性别</view>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const { secure, toast } = require("../../utils/api");
|
||||
Page({
|
||||
data: {
|
||||
mode: "walk",
|
||||
pets: [],
|
||||
selected: [],
|
||||
walk: null,
|
||||
@@ -14,6 +15,10 @@ Page({
|
||||
busy: false,
|
||||
locationError: "",
|
||||
},
|
||||
changeMode(e) {
|
||||
const mode = e.currentTarget.dataset.mode;
|
||||
if (mode !== this.data.mode) this.setData({ mode });
|
||||
},
|
||||
onShow() {
|
||||
this.load();
|
||||
clearInterval(this.clock);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"disableScroll": true,
|
||||
"usingComponents": {
|
||||
"tab-bar": "/components/tab-bar/index",
|
||||
"pet-picker": "/components/pet-picker/index"
|
||||
"pet-picker": "/components/pet-picker/index",
|
||||
"meetup-panel": "/components/meetup-panel/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<page-layout id="page-layout" title="一起去兜风">
|
||||
<view class="screen walk-screen">
|
||||
<view class="walk-modes"><view class="mode {{mode==='walk'?'chosen':''}}" data-mode="walk" bindtap="changeMode">记录遛狗<text wx:if="{{walk&&walk.status==='active'}}" class="recording-dot"></text></view><view class="mode {{mode==='meetups'?'chosen':''}}" data-mode="meetups" bindtap="changeMode">约个伴</view></view>
|
||||
<meetup-panel wx:if="{{mode==='meetups'}}" />
|
||||
<view wx:else class="screen walk-screen">
|
||||
<view class="map-box">
|
||||
<map
|
||||
id="walk-map"
|
||||
|
||||
@@ -111,3 +111,8 @@
|
||||
border-radius: 20rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.walk-modes { margin:12rpx 32rpx 22rpx; padding:7rpx; display:flex; border-radius:99rpx; background:#f5e9de; }
|
||||
.mode { flex:1;padding:16rpx;text-align:center;color:#9d8779;font-size:26rpx;border-radius:99rpx; }
|
||||
.mode.chosen { background:#fff;color:#b95738;box-shadow:0 5rpx 15rpx #604a4010; }
|
||||
.recording-dot { display:inline-block;width:10rpx;height:10rpx;background:#b95738;border-radius:50%;margin-left:10rpx; }
|
||||
|
||||
Reference in New Issue
Block a user