Compare commits
18 Commits
9f7df3cfed
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8adc28ae9c | |||
| e31a3432a0 | |||
| 2c6478c729 | |||
| f4232e8752 | |||
| 41e2959a72 | |||
| bf5deff045 | |||
| f94cf05f62 | |||
| 9538a21e42 | |||
| 3b091c4f18 | |||
| b64e2767c2 | |||
| df7cfa95a0 | |||
| 84fc382c91 | |||
| 107177d095 | |||
| 2966289930 | |||
| 6f74559c01 | |||
| 186a74bc77 | |||
| 51e679dd83 | |||
| 1ad1da1c4e |
@ -2,7 +2,7 @@
|
||||
import { Journal } from "../../types/Journal";
|
||||
import config from "../../config/index";
|
||||
import Toolkit from "../../utils/Toolkit";
|
||||
import { ImageMetadata, MediaAttachExt, MediaAttachType } from "../../types/Attachment";
|
||||
import { MediaAttachType, PreviewImageMetadata } from "../../types/Attachment";
|
||||
import { MediaItem, MediaItemType } from "../../types/UI";
|
||||
import Time from "../../utils/Time";
|
||||
import { JournalApi } from "../../api/JournalApi";
|
||||
@ -47,12 +47,12 @@ Component({
|
||||
return;
|
||||
}
|
||||
const mediaItems: MediaItem[] = thumbItems.map((thumbItem, index) => {
|
||||
const metadata = thumbItem.metadata as ImageMetadata;
|
||||
const ext = thumbItem.ext = JSON.parse(thumbItem.ext!.toString()) as MediaAttachExt;
|
||||
const metadata = (typeof thumbItem.metadata === "string" ? JSON.parse(thumbItem.metadata) : thumbItem.metadata) as PreviewImageMetadata;
|
||||
const thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${metadata.sourceMongoId}`;
|
||||
const isVideo = metadata.sourceMimeType?.startsWith("video/");
|
||||
return {
|
||||
type: ext.isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
type: isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
thumbURL,
|
||||
sourceURL,
|
||||
size: thumbItem.size || 0,
|
||||
|
||||
@ -57,7 +57,7 @@
|
||||
.loading,
|
||||
.finished {
|
||||
color: var(--td-text-color-placeholder);
|
||||
padding: 32rpx 0;
|
||||
padding: 32rpx 0 64rpx 0;
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// components/travel-location-popup/index.ts
|
||||
import { TravelLocation, TravelLocationTypeLabel, TravelLocationTypeIcon } from "../../types/Travel";
|
||||
import { TravelLocationApi } from "../../api/TravelLocationApi";
|
||||
import { ImageMetadata, MediaAttachType } from "../../types/Attachment";
|
||||
import { MediaAttachType, PreviewImageMetadata } from "../../types/Attachment";
|
||||
import { MediaItem, MediaItemType } from "../../types/UI";
|
||||
import config from "../../config/index";
|
||||
import Toolkit from "../../utils/Toolkit";
|
||||
@ -43,12 +43,12 @@ Component({
|
||||
|
||||
if (0 < thumbItems.length) {
|
||||
const mediaItems: MediaItem[] = thumbItems.map((thumbItem, index) => {
|
||||
const metadata = thumbItem.metadata as ImageMetadata;
|
||||
const ext = typeof thumbItem.ext === "string" ? JSON.parse(thumbItem.ext) : thumbItem.ext;
|
||||
const metadata = (typeof thumbItem.metadata === "string" ? JSON.parse(thumbItem.metadata) : thumbItem.metadata) as PreviewImageMetadata;
|
||||
const thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${metadata.sourceMongoId}`;
|
||||
const isVideo = metadata.sourceMimeType?.startsWith("video/");
|
||||
return {
|
||||
type: ext.isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
type: isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
thumbURL,
|
||||
sourceURL,
|
||||
size: thumbItem.size || 0,
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
const envArgs = {
|
||||
develop: {
|
||||
url: "http://localhost:8091"
|
||||
// url: "https://api.imyeyu.dev"
|
||||
// url: "https://api.imyeyu.com"
|
||||
// url: "http://192.168.3.123:8091"
|
||||
// url: "http://192.168.3.137:8091"
|
||||
// url: "http://192.168.3.173:8091"
|
||||
url: "http://192.168.3.174:8091"
|
||||
// url: "http://192.168.3.174:8091"
|
||||
},
|
||||
trial: {
|
||||
url: "https://api.imyeyu.com"
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
// index.ts
|
||||
|
||||
import config from "../../config/index"
|
||||
import { JournalPage, JournalPageType } from "../../types/Journal";
|
||||
import { JournalPageType } from "../../types/Journal";
|
||||
import { JournalApi } from "../../api/JournalApi";
|
||||
|
||||
interface IndexData {
|
||||
@ -22,12 +20,12 @@ Page({
|
||||
},
|
||||
async navigateToMain() {
|
||||
try {
|
||||
wx.setStorageSync("key", this.data.key);
|
||||
await JournalApi.getList({
|
||||
index: 0,
|
||||
size: 1,
|
||||
type: JournalPageType.PREVIEW
|
||||
});
|
||||
wx.setStorageSync("key", this.data.key);
|
||||
wx.switchTab({
|
||||
url: "/pages/main/journal/index",
|
||||
})
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
</view>
|
||||
<view class="item">
|
||||
<text class="label">版本:</text>
|
||||
<text>1.6.2</text>
|
||||
<text>1.6.6</text>
|
||||
</view>
|
||||
<view class="item copyright">
|
||||
<text>{{copyright}}</text>
|
||||
|
||||
@ -5,7 +5,7 @@ import Toolkit from "../../../utils/Toolkit";
|
||||
import config from "../../../config/index";
|
||||
import { Location, MediaItem, MediaItemType, WechatMediaItem } from "../../../types/UI";
|
||||
import { JournalType } from "../../../types/Journal";
|
||||
import { MediaAttachExt, MediaAttachType } from "../../../types/Attachment";
|
||||
import { MediaAttachType, PreviewImageMetadata } from "../../../types/Attachment";
|
||||
import IOSize, { Unit } from "../../../utils/IOSize";
|
||||
import { JournalApi } from "../../../api/JournalApi";
|
||||
|
||||
@ -156,11 +156,12 @@ Page({
|
||||
const thumbItems = items.filter((item) => item.attachType === MediaAttachType.THUMB);
|
||||
|
||||
const mediaList: MediaItem[] = thumbItems.map((thumbItem) => {
|
||||
const ext = thumbItem.ext = JSON.parse(thumbItem.ext!.toString()) as MediaAttachExt;
|
||||
const metadata = (typeof thumbItem.metadata === "string" ? JSON.parse(thumbItem.metadata) : thumbItem.metadata) as PreviewImageMetadata;
|
||||
const thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${metadata.sourceMongoId}`;
|
||||
const isVideo = metadata.sourceMimeType?.startsWith("video/");
|
||||
return {
|
||||
type: ext.isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
type: isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
thumbURL,
|
||||
sourceURL,
|
||||
size: thumbItem.size || 0,
|
||||
|
||||
@ -8,75 +8,74 @@
|
||||
.map {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.custom-callout {
|
||||
width: fit-content;
|
||||
min-width: 350rpx;
|
||||
max-width: 450rpx;
|
||||
background: var(--theme-bg-card-secondary);
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, .15);
|
||||
border-radius: 6rpx;
|
||||
|
||||
.callout-content {
|
||||
display: flex;
|
||||
padding: 8rpx 16rpx 8rpx 8rpx;
|
||||
align-items: flex-start;
|
||||
|
||||
.thumb-container {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
margin-right: 12rpx;
|
||||
overflow: hidden;
|
||||
border-radius: 6rpx;
|
||||
|
||||
.thumb {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.count-badge {
|
||||
top: 4rpx;
|
||||
right: 4rpx;
|
||||
color: #fff;
|
||||
padding: 2rpx 6rpx;
|
||||
position: absolute;
|
||||
font-size: 20rpx;
|
||||
background: rgba(0, 0, 0, .7);
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.text-container {
|
||||
flex: 1;
|
||||
.location {
|
||||
width: fit-content;
|
||||
background: var(--theme-bg-card);
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, .15);
|
||||
border-radius: 6rpx;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
|
||||
.location {
|
||||
color: var(--theme-text-primary);
|
||||
padding: 8rpx 16rpx 8rpx 8rpx;
|
||||
align-items: flex-start;
|
||||
|
||||
.thumb {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
margin-right: 12rpx;
|
||||
overflow: hidden;
|
||||
font-size: 30rpx;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.date-count {
|
||||
display: flex;
|
||||
|
||||
.date {
|
||||
color: var(--theme-text-secondary);
|
||||
font-size: 24rpx;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 6rpx;
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.count {
|
||||
color: var(--theme-wx);
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
|
||||
.badge {
|
||||
top: 4rpx;
|
||||
right: 4rpx;
|
||||
color: #fff;
|
||||
padding: 2rpx 6rpx;
|
||||
position: absolute;
|
||||
font-size: 20rpx;
|
||||
background: rgba(0, 0, 0, .7);
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
|
||||
.value {
|
||||
color: var(--theme-text-primary);
|
||||
width: calc(var(--title-length) * 30rpx);
|
||||
overflow: hidden;
|
||||
font-size: 30rpx;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.date-count {
|
||||
display: flex;
|
||||
|
||||
.date {
|
||||
color: var(--theme-text-secondary);
|
||||
font-size: 24rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.count {
|
||||
color: var(--theme-wx);
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,23 +13,28 @@
|
||||
bindcallouttap="onCalloutTap"
|
||||
>
|
||||
<cover-view slot="callout">
|
||||
<block wx:for="{{locations}}" wx:key="locationKey" wx:for-index="markerIndex">
|
||||
<cover-view class="custom-callout" marker-id="{{markerIndex}}">
|
||||
<cover-view class="callout-content">
|
||||
<cover-view wx:if="{{item.previewThumb}}" class="thumb-container">
|
||||
<cover-image class="thumb" src="{{item.previewThumb}}" />
|
||||
<cover-view wx:if="{{item.count > 1}}" class="count-badge">{{item.count}}</cover-view>
|
||||
</cover-view>
|
||||
<cover-view class="text-container">
|
||||
<cover-view wx:if="{{item.location}}" class="location">{{item.location}}</cover-view>
|
||||
<cover-view class="date-count">
|
||||
<cover-view class="date">{{item.date}}</cover-view>
|
||||
<cover-view wx:if="{{item.count > 1}}" class="count">{{item.count}} 条日记</cover-view>
|
||||
</cover-view>
|
||||
<cover-view
|
||||
class="location"
|
||||
wx:for="{{locations}}"
|
||||
wx:key="locationKey"
|
||||
wx:for-index="locationIndex"
|
||||
marker-id="{{locationIndex}}"
|
||||
style="{{'--title-length: ' + item.location.length}}"
|
||||
>
|
||||
<cover-view class="content">
|
||||
<cover-view wx:if="{{item.previewThumb}}" class="thumb">
|
||||
<cover-image class="img" src="{{item.previewThumb}}" />
|
||||
<cover-view wx:if="{{1 < item.count}}" class="badge">{{item.count}}</cover-view>
|
||||
</cover-view>
|
||||
<cover-view class="text">
|
||||
<cover-view wx:if="{{item.location}}" class="value">{{item.location}}</cover-view>
|
||||
<cover-view class="date-count">
|
||||
<cover-view class="date">{{item.date}}</cover-view>
|
||||
<cover-view wx:if="{{1 < item.count}}" class="count">{{item.count}} 条日记</cover-view>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
</block>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
</map>
|
||||
<view wx:if="{{isLoading}}" class="loading">
|
||||
|
||||
@ -1,8 +1,21 @@
|
||||
page {
|
||||
height: 100vh;
|
||||
background: var(--td-bg-color-page);
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100vw;
|
||||
height: calc(100vh - var(--navbar-height, 88rpx));
|
||||
.page-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
|
||||
.navbar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
<view class="custom-navbar">
|
||||
<t-navbar title="列表查找" left-arrow />
|
||||
</view>
|
||||
|
||||
<view class="content">
|
||||
<journal-list
|
||||
id="listRef"
|
||||
searchable="{{true}}"
|
||||
mode="navigate"
|
||||
bind:navigate="onNavigateItem"
|
||||
/>
|
||||
<view class="page-container">
|
||||
<view class="navbar">
|
||||
<t-navbar title="列表查找" left-arrow />
|
||||
</view>
|
||||
<view class="content">
|
||||
<journal-list
|
||||
id="listRef"
|
||||
searchable="{{true}}"
|
||||
mode="navigate"
|
||||
bind:navigate="onNavigateItem"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -6,7 +6,7 @@ import Events from "../../../utils/Events";
|
||||
import Toolkit from "../../../utils/Toolkit";
|
||||
import { Journal, JournalPage, JournalPageType } from "../../../types/Journal";
|
||||
import { OrderType } from "../../../types/Model";
|
||||
import { ImageMetadata, MediaAttachExt } from "../../../types/Attachment";
|
||||
import { PreviewImageMetadata } from "../../../types/Attachment";
|
||||
import { MediaItem, MediaItemType } from "../../../types/UI";
|
||||
import { JournalApi } from "../../../api/JournalApi";
|
||||
|
||||
@ -163,12 +163,12 @@ Page({
|
||||
}
|
||||
list.forEach(journal => {
|
||||
const mediaItems = journal.items!.filter((item) => item.attachType === "THUMB").map((thumbItem, index) => {
|
||||
const metadata = thumbItem.metadata as ImageMetadata;
|
||||
const ext = thumbItem.ext = JSON.parse(thumbItem.ext!.toString()) as MediaAttachExt;
|
||||
const metadata = (typeof thumbItem.metadata === "string" ? JSON.parse(thumbItem.metadata) : thumbItem.metadata) as PreviewImageMetadata;
|
||||
const thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${metadata.sourceMongoId}`;
|
||||
const isVideo = metadata.sourceMimeType?.startsWith("video/");
|
||||
return {
|
||||
type: ext.isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
type: isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
thumbURL,
|
||||
sourceURL,
|
||||
size: thumbItem.size || 0,
|
||||
|
||||
@ -5,7 +5,7 @@ import IOSize, { Unit } from "../../../utils/IOSize";
|
||||
import Time from "../../../utils/Time";
|
||||
import Toolkit from "../../../utils/Toolkit";
|
||||
import { Location, MediaItemType } from "../../../types/UI";
|
||||
import { MediaAttachExt } from "../../../types/Attachment";
|
||||
import { PreviewImageMetadata } from "../../../types/Attachment";
|
||||
import { MomentApi } from "../../../api/MomentApi";
|
||||
import { JournalApi } from "../../../api/JournalApi";
|
||||
import { Network } from "../../../utils/Network";
|
||||
@ -134,12 +134,13 @@ Page({
|
||||
}
|
||||
this.setData({
|
||||
list: list.map((item: any) => {
|
||||
const ext = JSON.parse(item.ext) as MediaAttachExt;
|
||||
const metadata = (typeof item.metadata === "string" ? JSON.parse(item.metadata) : item.metadata) as PreviewImageMetadata;
|
||||
const thumbURL = `${config.url}/attachment/read/${item.mongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${metadata.sourceMongoId}`;
|
||||
const isImage = metadata.sourceMimeType?.startsWith("image/");
|
||||
return {
|
||||
id: item.id,
|
||||
type: ext.isImage ? MediaItemType.IMAGE : MediaItemType.VIDEO,
|
||||
type: isImage ? MediaItemType.IMAGE : MediaItemType.VIDEO,
|
||||
thumbURL,
|
||||
sourceURL,
|
||||
checked: false
|
||||
@ -259,12 +260,13 @@ Page({
|
||||
const list = await MomentApi.create(tempFileIds);
|
||||
wx.showToast({ title: "上传成功", icon: "success" });
|
||||
const added = list.map((item: any) => {
|
||||
const ext = JSON.parse(item.ext) as MediaAttachExt;
|
||||
const metadata = (typeof item.metadata === "string" ? JSON.parse(item.metadata) : item.metadata) as PreviewImageMetadata;
|
||||
const thumbURL = `${config.url}/attachment/read/${item.mongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${metadata.sourceMongoId}`;
|
||||
const isImage = item.mimeType?.startsWith("image/");
|
||||
return {
|
||||
id: item.id,
|
||||
type: ext.isImage ? MediaItemType.IMAGE : MediaItemType.VIDEO,
|
||||
type: isImage ? MediaItemType.IMAGE : MediaItemType.VIDEO,
|
||||
thumbURL,
|
||||
sourceURL,
|
||||
checked: false
|
||||
|
||||
@ -6,7 +6,7 @@ import Events from "../../../utils/Events";
|
||||
import Toolkit from "../../../utils/Toolkit";
|
||||
import { Journal, JournalPage, JournalPageType } from "../../../types/Journal";
|
||||
import { OrderType, } from "../../../types/Model";
|
||||
import { ImageMetadata, MediaAttachExt } from "../../../types/Attachment";
|
||||
import { PreviewImageMetadata } from "../../../types/Attachment";
|
||||
import { MediaItem, MediaItemType } from "../../../types/UI";
|
||||
import { JournalApi } from "../../../api/JournalApi";
|
||||
|
||||
@ -92,12 +92,12 @@ Page({
|
||||
}
|
||||
list.forEach(journal => {
|
||||
const mediaItems = journal.items!.filter((item) => item.attachType === "THUMB").map((thumbItem, index) => {
|
||||
const metadata = thumbItem.metadata as ImageMetadata;
|
||||
const ext = thumbItem.ext = JSON.parse(thumbItem.ext!.toString()) as MediaAttachExt;
|
||||
const metadata = (typeof thumbItem.metadata === "string" ? JSON.parse(thumbItem.metadata) : thumbItem.metadata) as PreviewImageMetadata;
|
||||
const thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${metadata.sourceMongoId}`;
|
||||
const isVideo = metadata.sourceMimeType?.startsWith("video/");
|
||||
return {
|
||||
type: ext.isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
type: isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
thumbURL,
|
||||
sourceURL,
|
||||
size: thumbItem.size || 0,
|
||||
|
||||
@ -42,9 +42,55 @@
|
||||
|
||||
.header {
|
||||
padding: 16rpx 32rpx;
|
||||
|
||||
.left-actions {
|
||||
gap: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.type-picker {
|
||||
|
||||
.picker-button {
|
||||
gap: 8rpx;
|
||||
color: var(--theme-wx);
|
||||
border: 1px solid var(--theme-wx);
|
||||
display: flex;
|
||||
padding: 14rpx 24rpx 14rpx 32rpx;
|
||||
font-size: 26rpx;
|
||||
background: var(--theme-bg-card);
|
||||
align-items: center;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.location {
|
||||
.thumb {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border: 1px solid var(--theme-border-light);
|
||||
overflow: hidden;
|
||||
background: var(--theme-bg-page);
|
||||
flex-shrink: 0;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.thumb-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.thumb-placeholder {
|
||||
color: var(--theme-text-secondary);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
font-size: 24rpx;
|
||||
background: var(--theme-bg-page);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.note {
|
||||
width: 2em;
|
||||
|
||||
@ -3,7 +3,13 @@
|
||||
import Time from "../../../utils/Time";
|
||||
import { TravelApi } from "../../../api/TravelApi";
|
||||
import { TravelLocationApi } from "../../../api/TravelLocationApi";
|
||||
import { Travel, TravelStatusLabel, TravelStatusIcon, TransportationTypeLabel, TravelLocation, TravelLocationTypeLabel, TravelLocationTypeIcon } from "../../../types/Travel";
|
||||
import config from "../../../config/index";
|
||||
import { Travel, TravelStatusLabel, TravelStatusIcon, TransportationTypeLabel, TravelLocation, TravelLocationTypeLabel, TravelLocationTypeIcon, TransportationTypeIcon, TravelLocationType } from "../../../types/Travel";
|
||||
|
||||
interface TravelLocationView extends TravelLocation {
|
||||
/** 预览图 */
|
||||
previewThumb?: string;
|
||||
}
|
||||
|
||||
interface TravelDetailData {
|
||||
/** 出行详情 */
|
||||
@ -13,7 +19,7 @@ interface TravelDetailData {
|
||||
/** 是否正在加载 */
|
||||
isLoading: boolean;
|
||||
/** 地点列表 */
|
||||
locations: TravelLocation[];
|
||||
locations: TravelLocationView[];
|
||||
/** 是否正在加载地点 */
|
||||
isLoadingLocations: boolean;
|
||||
/** 状态标签映射 */
|
||||
@ -22,10 +28,16 @@ interface TravelDetailData {
|
||||
statusIcons: typeof TravelStatusIcon;
|
||||
/** 交通类型标签映射 */
|
||||
transportLabels: typeof TransportationTypeLabel;
|
||||
/** 交通类型图标映射 */
|
||||
transportIcons: typeof TransportationTypeIcon;
|
||||
/** 地点类型标签映射 */
|
||||
locationTypeLabels: typeof TravelLocationTypeLabel;
|
||||
/** 地点类型图标映射 */
|
||||
locationTypeIcons: typeof TravelLocationTypeIcon;
|
||||
/** 地点类型选项 */
|
||||
locationTypes: string[];
|
||||
/** 选中的地点类型索引 */
|
||||
selectedLocationTypeIndex: number;
|
||||
/** 删除对话框可见性 */
|
||||
deleteDialogVisible: boolean;
|
||||
/** 删除确认文本 */
|
||||
@ -42,8 +54,11 @@ Page({
|
||||
statusLabels: TravelStatusLabel,
|
||||
statusIcons: TravelStatusIcon,
|
||||
transportLabels: TransportationTypeLabel,
|
||||
transportIcons: TransportationTypeIcon,
|
||||
locationTypeLabels: TravelLocationTypeLabel,
|
||||
locationTypeIcons: TravelLocationTypeIcon,
|
||||
locationTypes: ["全部", ...Object.values(TravelLocationTypeLabel)],
|
||||
selectedLocationTypeIndex: 0,
|
||||
deleteDialogVisible: false,
|
||||
deleteConfirmText: ""
|
||||
},
|
||||
@ -106,15 +121,39 @@ Page({
|
||||
this.setData({ isLoadingLocations: true });
|
||||
|
||||
try {
|
||||
const { selectedLocationTypeIndex, locationTypes } = this.data;
|
||||
|
||||
// 构建查询条件
|
||||
const equalsExample: { [key: string]: number | string } = {
|
||||
travelId: Number(travelId)
|
||||
};
|
||||
|
||||
// 添加类型过滤(索引 0 表示"全部")
|
||||
if (0 < selectedLocationTypeIndex) {
|
||||
const selectedTypeLabel = locationTypes[selectedLocationTypeIndex];
|
||||
const selectedType = Object.keys(TravelLocationTypeLabel).find(
|
||||
key => TravelLocationTypeLabel[key as TravelLocationType] === selectedTypeLabel
|
||||
) as TravelLocationType | undefined;
|
||||
|
||||
if (selectedType) {
|
||||
equalsExample.type = selectedType;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await TravelLocationApi.getList({
|
||||
index: 0,
|
||||
size: 100,
|
||||
equalsExample: {
|
||||
travelId: Number(travelId)
|
||||
}
|
||||
size: 999,
|
||||
equalsExample
|
||||
});
|
||||
|
||||
this.setData({ locations: result.list });
|
||||
const locations = result.list.map((location) => {
|
||||
const previewItem = location.items && 0 < location.items.length ? location.items[0] : undefined;
|
||||
return {
|
||||
...location,
|
||||
previewThumb: previewItem ? `${config.url}/attachment/read/${previewItem.mongoId}` : undefined
|
||||
};
|
||||
});
|
||||
this.setData({ locations });
|
||||
} catch (error) {
|
||||
console.error("获取地点列表失败:", error);
|
||||
} finally {
|
||||
@ -122,6 +161,14 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
/** 地点类型改变 */
|
||||
onLocationTypeChange(e: WechatMiniprogram.PickerChange) {
|
||||
const index = Number(e.detail.value);
|
||||
this.setData({ selectedLocationTypeIndex: index });
|
||||
// 重新从接口获取过滤后的数据
|
||||
this.fetchLocations(this.data.travelId);
|
||||
},
|
||||
|
||||
/** 编辑出行 */
|
||||
toEdit() {
|
||||
const { travel } = this.data;
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
<text wx:else class="undecided-value">未定</text>
|
||||
</view>
|
||||
</t-cell>
|
||||
<t-cell left-icon="{{travel.transportationType === 'PLANE' ? 'flight-takeoff' : travel.transportationType === 'TRAIN' ? 'map-route' : travel.transportationType === 'SELF_DRIVING' ? 'control-platform' : 'location'}}" title="交通方式">
|
||||
<t-cell left-icon="{{transportIcons[travel.transportationType]}}" title="交通方式">
|
||||
<view slot="note">{{transportLabels[travel.transportationType]}}</view>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
@ -52,7 +52,15 @@
|
||||
<t-cell-group class="section locations">
|
||||
<view slot="title" class="title">地点列表</view>
|
||||
<t-cell class="header">
|
||||
<t-button slot="left-icon" theme="primary" icon="map" size="small" bind:tap="toMap">地图浏览</t-button>
|
||||
<view slot="left-icon" class="left-actions">
|
||||
<t-button theme="primary" icon="map" size="small" bind:tap="toMap">地图浏览</t-button>
|
||||
<picker class="type-picker" mode="selector" range="{{locationTypes}}" value="{{selectedLocationTypeIndex}}" bind:change="onLocationTypeChange">
|
||||
<view class="picker-button">
|
||||
<text>{{locationTypes[selectedLocationTypeIndex]}}</text>
|
||||
<t-icon class="icon" name="chevron-down" size="16px" />
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<t-icon slot="right-icon" name="add" size="20px" color="var(--theme-wx)" bind:tap="toAddLocation" />
|
||||
</t-cell>
|
||||
<t-cell wx:if="{{isLoadingLocations}}" class="loading">
|
||||
@ -63,12 +71,17 @@
|
||||
class="location"
|
||||
wx:for="{{locations}}"
|
||||
wx:key="id"
|
||||
left-icon="{{locationTypeIcons[item.type]}}"
|
||||
title="{{item.title || '未命名地点'}}"
|
||||
bind:tap="toLocationDetail"
|
||||
data-id="{{item.id}}"
|
||||
arrow
|
||||
>
|
||||
<view slot="left-icon" class="thumb">
|
||||
<image wx:if="{{item.previewThumb}}" class="thumb-img" src="{{item.previewThumb}}" mode="aspectFill" />
|
||||
<view wx:else class="thumb-placeholder">
|
||||
<t-icon name="{{locationTypeIcons[item.type]}}" size="28px" color="var(--theme-wx)" />
|
||||
</view>
|
||||
</view>
|
||||
<view slot="note" class="note">{{locationTypeLabels[item.type]}}</view>
|
||||
<view slot="description" class="description">
|
||||
<view wx:if="{{item.requireIdCard}}" class="item">
|
||||
|
||||
@ -55,28 +55,21 @@
|
||||
|
||||
.map {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.mini-map {
|
||||
width: 100%;
|
||||
height: 520rpx;
|
||||
}
|
||||
.instance {
|
||||
width: 100%;
|
||||
height: 520rpx;
|
||||
|
||||
.custom-callout {
|
||||
width: fit-content;
|
||||
max-width: 400rpx;
|
||||
background: var(--theme-bg-card-secondary);
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, .15);
|
||||
border-radius: 8rpx;
|
||||
|
||||
.callout-content {
|
||||
padding: 12rpx 20rpx;
|
||||
|
||||
.callout-text {
|
||||
.marker {
|
||||
width: calc(var(--title-length) * 28rpx);
|
||||
color: var(--theme-text-primary);
|
||||
padding: 8rpx;
|
||||
overflow: hidden;
|
||||
font-size: 28rpx;
|
||||
background: var(--theme-bg-card);
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, .15);
|
||||
white-space: nowrap;
|
||||
border-radius: 8rpx;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
@ -91,11 +84,10 @@
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
|
||||
.media-item {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.thumbnail {
|
||||
width: 100%;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import config from "../../../config/index";
|
||||
import { TravelLocationApi } from "../../../api/TravelLocationApi";
|
||||
import { TravelLocation, TravelLocationTypeIcon, TravelLocationTypeLabel } from "../../../types/Travel";
|
||||
import { MediaAttachExt, MediaAttachType } from "../../../types/Attachment";
|
||||
import { MediaAttachType, PreviewImageMetadata } from "../../../types/Attachment";
|
||||
import { MapMarker, MediaItem, MediaItemType } from "../../../types/UI";
|
||||
import Toolkit from "../../../utils/Toolkit";
|
||||
|
||||
@ -95,18 +95,19 @@ Page({
|
||||
|
||||
thumbItems.forEach((thumbItem) => {
|
||||
try {
|
||||
const ext = JSON.parse(thumbItem.ext!.toString()) as MediaAttachExt;
|
||||
const metadata = (typeof thumbItem.metadata === "string" ? JSON.parse(thumbItem.metadata) : thumbItem.metadata) as PreviewImageMetadata;
|
||||
const thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${metadata.sourceMongoId}`;
|
||||
const isVideo = metadata.sourceMimeType?.startsWith("video/");
|
||||
mediaItems.push({
|
||||
type: ext.isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
type: isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
thumbURL,
|
||||
sourceURL,
|
||||
size: thumbItem.size || 0,
|
||||
attachmentId: thumbItem.id!
|
||||
});
|
||||
} catch (parseError) {
|
||||
console.warn("解析附件扩展信息失败", parseError);
|
||||
console.warn("解析附件元数据失败", parseError);
|
||||
}
|
||||
});
|
||||
|
||||
@ -120,15 +121,12 @@ Page({
|
||||
width: 24,
|
||||
height: 30,
|
||||
customCallout: {
|
||||
anchorY: 0,
|
||||
anchorY: -2,
|
||||
anchorX: 0,
|
||||
display: "ALWAYS"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log(mediaItems);
|
||||
|
||||
this.setData({
|
||||
location: {
|
||||
...location,
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
<t-cell class="map">
|
||||
<map
|
||||
slot="description"
|
||||
class="mini-map"
|
||||
class="instance"
|
||||
latitude="{{location.lat}}"
|
||||
longitude="{{location.lng}}"
|
||||
markers="{{mapMarkers}}"
|
||||
@ -43,10 +43,8 @@
|
||||
show-location
|
||||
>
|
||||
<cover-view slot="callout">
|
||||
<cover-view class="custom-callout" marker-id="0">
|
||||
<cover-view class="callout-content">
|
||||
<cover-view class="callout-text">{{location.title || '地点'}}</cover-view>
|
||||
</cover-view>
|
||||
<cover-view class="marker" marker-id="0" style="--title-length: {{location.title.length}}">
|
||||
{{location.title || '地点'}}
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
</map>
|
||||
@ -92,9 +90,9 @@
|
||||
</t-cell-group>
|
||||
<!-- 照片/视频 -->
|
||||
<t-cell-group wx:if="{{location.mediaItems && 0 < location.mediaItems.length}}" class="section media">
|
||||
<view slot="title" class="title">照片/视频</view>
|
||||
<view slot="title" class="title">照片视频</view>
|
||||
<t-cell>
|
||||
<view class="media-grid">
|
||||
<view slot="title" class="media-grid">
|
||||
<view
|
||||
wx:for="{{location.mediaItems}}"
|
||||
wx:key="attachmentId"
|
||||
|
||||
@ -7,13 +7,11 @@
|
||||
"t-input": "tdesign-miniprogram/input/input",
|
||||
"t-button": "tdesign-miniprogram/button/button",
|
||||
"t-dialog": "tdesign-miniprogram/dialog/dialog",
|
||||
"t-picker": "tdesign-miniprogram/picker/picker",
|
||||
"t-navbar": "tdesign-miniprogram/navbar/navbar",
|
||||
"t-loading": "tdesign-miniprogram/loading/loading",
|
||||
"t-stepper": "tdesign-miniprogram/stepper/stepper",
|
||||
"t-textarea": "tdesign-miniprogram/textarea/textarea",
|
||||
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
|
||||
"t-picker-item": "tdesign-miniprogram/picker-item/picker-item"
|
||||
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group"
|
||||
},
|
||||
"styleIsolation": "shared"
|
||||
}
|
||||
|
||||
@ -23,6 +23,34 @@
|
||||
.section {
|
||||
margin-top: 48rpx;
|
||||
|
||||
.rate-cell {
|
||||
|
||||
&.decided {
|
||||
|
||||
.rate {
|
||||
display: block;
|
||||
gap: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.undecided {
|
||||
|
||||
.rate {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.text {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .title {
|
||||
color: var(--theme-text-secondary);
|
||||
padding: 0 32rpx;
|
||||
@ -53,8 +81,8 @@
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
|
||||
.item {
|
||||
width: 240rpx;
|
||||
height: 240rpx;
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: var(--theme-bg-card);
|
||||
@ -149,11 +177,11 @@
|
||||
display: flex;
|
||||
padding: 24rpx 16rpx 48rpx 16rpx;
|
||||
|
||||
.delete-btn {
|
||||
.delete {
|
||||
flex: .6;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
.submit {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
import { Network, WechatMediaItem } from "../../../utils/Network";
|
||||
import { TravelLocationApi } from "../../../api/TravelLocationApi";
|
||||
import { TravelLocationType } from "../../../types/Travel";
|
||||
import { MediaAttachExt, MediaAttachType } from "../../../types/Attachment";
|
||||
import { TravelLocationType, TravelLocationTypeLabel } from "../../../types/Travel";
|
||||
import { MediaAttachType, PreviewImageMetadata } from "../../../types/Attachment";
|
||||
import config from "../../../config/index";
|
||||
import { MediaItem, MediaItemType } from "../../../types/UI";
|
||||
|
||||
@ -34,8 +34,12 @@ interface TravelLocationEditorData {
|
||||
requireAppointment: boolean;
|
||||
/** 评分 */
|
||||
score: number;
|
||||
/** 评分是否未定 */
|
||||
scoreUndecided: boolean;
|
||||
/** 重要程度 */
|
||||
importance: number;
|
||||
/** 重要程度是否未定 */
|
||||
importanceUndecided: boolean;
|
||||
/** 媒体列表(创建和编辑模式使用) */
|
||||
mediaList: (MediaItem | WechatMediaItem)[];
|
||||
/** 新媒体列表(编辑模式使用) */
|
||||
@ -67,7 +71,7 @@ Page({
|
||||
mode: "create",
|
||||
id: undefined,
|
||||
travelId: 0,
|
||||
type: TravelLocationType.ATTRACTION,
|
||||
type: TravelLocationType.FOOD,
|
||||
title: "",
|
||||
description: "",
|
||||
location: "",
|
||||
@ -77,7 +81,9 @@ Page({
|
||||
requireIdCard: false,
|
||||
requireAppointment: false,
|
||||
score: 3,
|
||||
scoreUndecided: true,
|
||||
importance: 1,
|
||||
importanceUndecided: true,
|
||||
mediaList: [],
|
||||
newMediaList: [],
|
||||
isLoading: false,
|
||||
@ -87,7 +93,7 @@ Page({
|
||||
mediaItemTypeEnum: {
|
||||
...MediaItemType
|
||||
},
|
||||
locationTypes: ["美食", "酒店", "交通", "景点", "购物", "玩乐", "生活"],
|
||||
locationTypes: Object.values(TravelLocationTypeLabel),
|
||||
locationTypeValues: [
|
||||
TravelLocationType.FOOD,
|
||||
TravelLocationType.HOTEL,
|
||||
@ -155,11 +161,12 @@ Page({
|
||||
const thumbItems = items.filter((item) => item.attachType === MediaAttachType.THUMB);
|
||||
|
||||
const mediaList: MediaItem[] = thumbItems.map((thumbItem) => {
|
||||
const ext = thumbItem.ext = JSON.parse(thumbItem.ext!.toString()) as MediaAttachExt;
|
||||
const metadata = (typeof thumbItem.metadata === "string" ? JSON.parse(thumbItem.metadata) : thumbItem.metadata) as PreviewImageMetadata;
|
||||
const thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
|
||||
const sourceURL = `${config.url}/attachment/read/${metadata.sourceMongoId}`;
|
||||
const isVideo = metadata.sourceMimeType?.startsWith("video/");
|
||||
return {
|
||||
type: ext.isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
type: isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
|
||||
thumbURL,
|
||||
sourceURL,
|
||||
size: thumbItem.size || 0,
|
||||
@ -167,6 +174,14 @@ Page({
|
||||
} as MediaItem;
|
||||
});
|
||||
|
||||
// 判断评分是否未定
|
||||
const scoreUndecided = location.score === null || location.score === undefined;
|
||||
const score = location.score !== null && location.score !== undefined ? location.score : 3;
|
||||
|
||||
// 判断重要程度是否未定
|
||||
const importanceUndecided = location.importance === null || location.importance === undefined;
|
||||
const importance = location.importance !== null && location.importance !== undefined ? location.importance : 1;
|
||||
|
||||
this.setData({
|
||||
type,
|
||||
locationTypeIndex: locationTypeIndex >= 0 ? locationTypeIndex : 0,
|
||||
@ -178,8 +193,10 @@ Page({
|
||||
amount: location.amount || 0,
|
||||
requireIdCard: location.requireIdCard || false,
|
||||
requireAppointment: location.requireAppointment || false,
|
||||
score: location.score !== undefined ? location.score : 3,
|
||||
importance: location.importance !== undefined ? location.importance : 1,
|
||||
score,
|
||||
scoreUndecided,
|
||||
importance,
|
||||
importanceUndecided,
|
||||
mediaList,
|
||||
isLoading: false
|
||||
});
|
||||
@ -225,6 +242,32 @@ Page({
|
||||
this.setData({ importance: e.detail.value });
|
||||
},
|
||||
|
||||
/** 清除评分 */
|
||||
clearScore() {
|
||||
this.setData({ scoreUndecided: true });
|
||||
},
|
||||
|
||||
/** 清除重要程度 */
|
||||
clearImportance() {
|
||||
this.setData({ importanceUndecided: true });
|
||||
},
|
||||
|
||||
/** 点击未定文字选择评分 */
|
||||
selectScore() {
|
||||
this.setData({
|
||||
scoreUndecided: false,
|
||||
score: 3
|
||||
});
|
||||
},
|
||||
|
||||
/** 点击未定文字选择重要程度 */
|
||||
selectImportance() {
|
||||
this.setData({
|
||||
importanceUndecided: false,
|
||||
importance: 1
|
||||
});
|
||||
},
|
||||
|
||||
/** 选择位置 */
|
||||
chooseLocation() {
|
||||
wx.chooseLocation({
|
||||
@ -483,8 +526,8 @@ Page({
|
||||
amount: this.data.amount,
|
||||
requireIdCard: this.data.requireIdCard,
|
||||
requireAppointment: this.data.requireAppointment,
|
||||
score: this.data.score,
|
||||
importance: this.data.importance,
|
||||
score: this.data.scoreUndecided ? null : this.data.score,
|
||||
importance: this.data.importanceUndecided ? null : this.data.importance,
|
||||
tempFileIds
|
||||
});
|
||||
wx.showToast({
|
||||
@ -539,8 +582,8 @@ Page({
|
||||
amount: this.data.amount,
|
||||
requireIdCard: this.data.requireIdCard,
|
||||
requireAppointment: this.data.requireAppointment,
|
||||
score: this.data.score,
|
||||
importance: this.data.importance,
|
||||
score: this.data.scoreUndecided ? null : this.data.score,
|
||||
importance: this.data.importanceUndecided ? null : this.data.importance,
|
||||
attachmentIds,
|
||||
tempFileIds
|
||||
});
|
||||
|
||||
@ -61,84 +61,98 @@
|
||||
<switch checked="{{requireAppointment}}" bindchange="onChangeRequireAppointment" />
|
||||
</view>
|
||||
</t-cell>
|
||||
<t-cell title="重要程度">
|
||||
<view slot="right-icon">
|
||||
<t-rate
|
||||
value="{{importance}}"
|
||||
count="{{5}}"
|
||||
size="24px"
|
||||
bind:change="onChangeImportance"
|
||||
/>
|
||||
<t-cell title="重要程度" class="rate-cell importance {{importanceUndecided ? 'undecided' : 'decided'}}">
|
||||
<view slot="note">
|
||||
<view class="rate">
|
||||
<t-rate
|
||||
value="{{importance}}"
|
||||
count="{{5}}"
|
||||
size="20px"
|
||||
bind:change="onChangeImportance"
|
||||
/>
|
||||
<t-icon
|
||||
name="close-circle-filled"
|
||||
size="20px"
|
||||
class="clear-icon"
|
||||
bindtap="clearImportance"
|
||||
/>
|
||||
</view>
|
||||
<view class="text" bindtap="selectImportance">
|
||||
未定
|
||||
</view>
|
||||
</view>
|
||||
</t-cell>
|
||||
<t-cell title="评分">
|
||||
<view slot="right-icon">
|
||||
<t-rate
|
||||
value="{{score}}"
|
||||
count="{{5}}"
|
||||
size="24px"
|
||||
bind:change="onChangeScore"
|
||||
/>
|
||||
<t-cell title="评分" class="rate-cell score {{scoreUndecided ? 'undecided' : 'decided'}}">
|
||||
<view slot="note">
|
||||
<view class="rate">
|
||||
<t-rate
|
||||
value="{{score}}"
|
||||
count="{{5}}"
|
||||
size="20px"
|
||||
bind:change="onChangeScore"
|
||||
/>
|
||||
<t-icon
|
||||
name="close-circle-filled"
|
||||
size="20px"
|
||||
class="clear-icon"
|
||||
bindtap="clearScore"
|
||||
/>
|
||||
</view>
|
||||
<view class="text" bindtap="selectScore">
|
||||
未定
|
||||
</view>
|
||||
</view>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
<view class="section media">
|
||||
<view class="gallery">
|
||||
<!-- 创建模式:mediaList 显示新选择的媒体 -->
|
||||
<block wx:if="{{mode === 'create'}}">
|
||||
<block wx:for="{{mediaList}}" wx:key="index">
|
||||
<view class="item">
|
||||
<!-- 图片 -->
|
||||
<image
|
||||
wx:if="{{item.type === mediaItemTypeEnum.IMAGE}}"
|
||||
src="{{item.path}}"
|
||||
class="thumbnail"
|
||||
mode="aspectFill"
|
||||
bindtap="preview"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{true}}"
|
||||
></image>
|
||||
<!-- 视频 -->
|
||||
<view wx:if="{{item.type === mediaItemTypeEnum.VIDEO}}" class="video-container">
|
||||
<t-cell-group class="section media">
|
||||
<view slot="title" class="title">图片视频</view>
|
||||
<t-cell>
|
||||
<view slot="title" class="gallery">
|
||||
<!-- 创建模式:mediaList 显示新选择的媒体 -->
|
||||
<block wx:if="{{mode === 'create'}}">
|
||||
<block wx:for="{{mediaList}}" wx:key="index">
|
||||
<view class="item">
|
||||
<!-- 图片 -->
|
||||
<image
|
||||
src="{{item.thumbPath}}"
|
||||
wx:if="{{item.type === mediaItemTypeEnum.IMAGE}}"
|
||||
src="{{item.path}}"
|
||||
class="thumbnail"
|
||||
mode="aspectFill"
|
||||
bindtap="preview"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{true}}"
|
||||
></image>
|
||||
<t-icon class="play-icon" name="play" />
|
||||
<!-- 视频 -->
|
||||
<view wx:if="{{item.type === mediaItemTypeEnum.VIDEO}}" class="video-container">
|
||||
<image
|
||||
src="{{item.thumbPath}}"
|
||||
class="thumbnail"
|
||||
mode="aspectFill"
|
||||
bindtap="preview"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{true}}"
|
||||
></image>
|
||||
<t-icon class="play-icon" name="play" />
|
||||
</view>
|
||||
<!-- 删除 -->
|
||||
<t-icon
|
||||
class="delete"
|
||||
name="close"
|
||||
bindtap="deleteMedia"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{true}}"
|
||||
/>
|
||||
</view>
|
||||
<!-- 删除 -->
|
||||
<t-icon
|
||||
class="delete"
|
||||
name="close"
|
||||
bindtap="deleteMedia"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{true}}"
|
||||
/>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
</block>
|
||||
<!-- 编辑模式:mediaList 显示现有附件,newMediaList 显示新添加的附件 -->
|
||||
<block wx:else>
|
||||
<!-- 现有附件 -->
|
||||
<block wx:for="{{mediaList}}" wx:key="attachmentId">
|
||||
<view class="item">
|
||||
<!-- 图片 -->
|
||||
<image
|
||||
wx:if="{{item.type === mediaItemTypeEnum.IMAGE}}"
|
||||
src="{{item.thumbURL}}"
|
||||
class="thumbnail"
|
||||
mode="aspectFill"
|
||||
bindtap="preview"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{false}}"
|
||||
></image>
|
||||
<!-- 视频 -->
|
||||
<view wx:if="{{item.type === mediaItemTypeEnum.VIDEO}}" class="video-container">
|
||||
<!-- 编辑模式:mediaList 显示现有附件,newMediaList 显示新添加的附件 -->
|
||||
<block wx:else>
|
||||
<!-- 现有附件 -->
|
||||
<block wx:for="{{mediaList}}" wx:key="attachmentId">
|
||||
<view class="item">
|
||||
<!-- 图片 -->
|
||||
<image
|
||||
wx:if="{{item.type === mediaItemTypeEnum.IMAGE}}"
|
||||
src="{{item.thumbURL}}"
|
||||
class="thumbnail"
|
||||
mode="aspectFill"
|
||||
@ -146,68 +160,79 @@
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{false}}"
|
||||
></image>
|
||||
<t-icon class="play-icon" name="play" />
|
||||
<!-- 视频 -->
|
||||
<view wx:if="{{item.type === mediaItemTypeEnum.VIDEO}}" class="video-container">
|
||||
<image
|
||||
src="{{item.thumbURL}}"
|
||||
class="thumbnail"
|
||||
mode="aspectFill"
|
||||
bindtap="preview"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{false}}"
|
||||
></image>
|
||||
<t-icon class="play-icon" name="play" />
|
||||
</view>
|
||||
<!-- 删除 -->
|
||||
<t-icon
|
||||
class="delete"
|
||||
name="close"
|
||||
bindtap="deleteMedia"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{false}}"
|
||||
/>
|
||||
</view>
|
||||
<!-- 删除 -->
|
||||
<t-icon
|
||||
class="delete"
|
||||
name="close"
|
||||
bindtap="deleteMedia"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{false}}"
|
||||
/>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 新选择附件 -->
|
||||
<block wx:for="{{newMediaList}}" wx:key="index">
|
||||
<view class="item new-item">
|
||||
<!-- 图片 -->
|
||||
<image
|
||||
wx:if="{{item.type === mediaItemTypeEnum.IMAGE}}"
|
||||
src="{{item.path}}"
|
||||
class="thumbnail"
|
||||
mode="aspectFill"
|
||||
bindtap="preview"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{true}}"
|
||||
></image>
|
||||
<!-- 视频 -->
|
||||
<view wx:if="{{item.type === mediaItemTypeEnum.VIDEO}}" class="video-container">
|
||||
</block>
|
||||
<!-- 新选择附件 -->
|
||||
<block wx:for="{{newMediaList}}" wx:key="index">
|
||||
<view class="item new-item">
|
||||
<!-- 图片 -->
|
||||
<image
|
||||
src="{{item.thumbPath}}"
|
||||
wx:if="{{item.type === mediaItemTypeEnum.IMAGE}}"
|
||||
src="{{item.path}}"
|
||||
class="thumbnail"
|
||||
mode="aspectFill"
|
||||
bindtap="preview"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{true}}"
|
||||
></image>
|
||||
<t-icon class="play-icon" name="play" />
|
||||
<!-- 视频 -->
|
||||
<view wx:if="{{item.type === mediaItemTypeEnum.VIDEO}}" class="video-container">
|
||||
<image
|
||||
src="{{item.thumbPath}}"
|
||||
class="thumbnail"
|
||||
mode="aspectFill"
|
||||
bindtap="preview"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{true}}"
|
||||
></image>
|
||||
<t-icon class="play-icon" name="play" />
|
||||
</view>
|
||||
<!-- 新增标识 -->
|
||||
<t-icon class="new-badge" name="add" />
|
||||
<!-- 删除 -->
|
||||
<t-icon
|
||||
class="delete"
|
||||
name="close"
|
||||
bindtap="deleteNewMedia"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{true}}"
|
||||
/>
|
||||
</view>
|
||||
<!-- 新增标识 -->
|
||||
<t-icon class="new-badge" name="add" />
|
||||
<!-- 删除 -->
|
||||
<t-icon
|
||||
class="delete"
|
||||
name="close"
|
||||
bindtap="deleteNewMedia"
|
||||
data-index="{{index}}"
|
||||
data-new-media="{{true}}"
|
||||
/>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
</block>
|
||||
<!-- 添加按钮 -->
|
||||
<t-button
|
||||
class="item add"
|
||||
theme="primary"
|
||||
plain="true"
|
||||
disabled="{{isSaving}}"
|
||||
bind:tap="addMedia"
|
||||
>
|
||||
<t-icon name="add" />
|
||||
</t-button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 添加按钮 -->
|
||||
<t-button
|
||||
class="item add"
|
||||
theme="primary"
|
||||
plain="true"
|
||||
disabled="{{isSaving}}"
|
||||
bind:tap="addMedia"
|
||||
>
|
||||
<t-icon name="add" />
|
||||
</t-button>
|
||||
</view>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
|
||||
<!-- 上传进度提示 -->
|
||||
<view wx:if="{{isUploading}}" class="section upload">
|
||||
|
||||
@ -9,20 +9,17 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.custom-callout {
|
||||
.marker {
|
||||
width: fit-content;
|
||||
padding: 12rpx 16rpx;
|
||||
display: flex;
|
||||
min-width: 300rpx;
|
||||
max-width: 400rpx;
|
||||
background: var(--theme-bg-card-secondary);
|
||||
background: var(--theme-bg-card);
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, .15);
|
||||
border-radius: 6rpx;
|
||||
flex-direction: column;
|
||||
|
||||
.location-item {
|
||||
.location {
|
||||
display: flex;
|
||||
padding: 6rpx 0;
|
||||
padding: 8rpx 16rpx 8rpx 8rpx;
|
||||
align-items: center;
|
||||
|
||||
.type {
|
||||
@ -37,6 +34,7 @@
|
||||
|
||||
.title {
|
||||
flex: 1;
|
||||
width: calc(var(--title-length) * 28rpx);
|
||||
color: var(--theme-text-primary, #333);
|
||||
overflow: hidden;
|
||||
font-size: 28rpx;
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import { TravelLocationApi } from "../../../api/TravelLocationApi";
|
||||
import { TravelLocation, TravelLocationTypeLabel } from "../../../types/Travel";
|
||||
import Toolkit from "../../../utils/Toolkit";
|
||||
|
||||
interface MapMarker {
|
||||
id: number;
|
||||
@ -110,7 +111,8 @@ Page({
|
||||
height: 30,
|
||||
customCallout: {
|
||||
anchorY: -2,
|
||||
anchorX: 0,
|
||||
// 随机错位避免近距离重叠
|
||||
anchorX: Toolkit.random(-10, 10),
|
||||
display: "ALWAYS"
|
||||
},
|
||||
locations: locs
|
||||
|
||||
@ -13,16 +13,25 @@
|
||||
bindcallouttap="onCalloutTap"
|
||||
>
|
||||
<cover-view slot="callout">
|
||||
<block wx:for="{{markers}}" wx:key="id" wx:for-index="markerIndex">
|
||||
<cover-view class="custom-callout" marker-id="{{markerIndex}}">
|
||||
<block wx:for="{{item.locations}}" wx:key="id" wx:for-item="location">
|
||||
<cover-view class="location-item">
|
||||
<cover-view wx:if="{{location.typeLabel}}" class="type">{{location.typeLabel}}</cover-view>
|
||||
<cover-view class="title">{{location.title || '未命名地点'}}</cover-view>
|
||||
</cover-view>
|
||||
</block>
|
||||
<cover-view
|
||||
class="marker"
|
||||
wx:for="{{markers}}"
|
||||
wx:key="id"
|
||||
wx:for-item="marker"
|
||||
wx:for-index="markerIndex"
|
||||
marker-id="{{markerIndex}}"
|
||||
>
|
||||
<cover-view
|
||||
class="location"
|
||||
wx:for="{{marker.locations}}"
|
||||
wx:key="id"
|
||||
wx:for-item="location"
|
||||
style="{{'--title-length: ' + location.title.length}}"
|
||||
>
|
||||
<cover-view wx:if="{{location.typeLabel}}" class="type">{{location.typeLabel}}</cover-view>
|
||||
<cover-view class="title">{{location.title || '未命名地点'}}</cover-view>
|
||||
</cover-view>
|
||||
</block>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
</map>
|
||||
<view wx:if="{{isLoading}}" class="loading">
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
import Time from "../../../utils/Time";
|
||||
import { TravelApi } from "../../../api/TravelApi";
|
||||
import { Travel, TravelPage, TravelStatus, TravelStatusLabel, TravelStatusIcon, TransportationTypeLabel, TransportationTypeIcon } from "../../../types/Travel";
|
||||
import { OrderType } from "../../../types/Model";
|
||||
|
||||
interface TravelData {
|
||||
/** 分页参数 */
|
||||
@ -35,10 +34,7 @@ Page({
|
||||
data: <TravelData>{
|
||||
page: {
|
||||
index: 0,
|
||||
size: 10,
|
||||
orderMap: {
|
||||
travelAt: OrderType.DESC
|
||||
}
|
||||
size: 10
|
||||
},
|
||||
list: [],
|
||||
currentStatus: "ALL",
|
||||
@ -79,9 +75,6 @@ Page({
|
||||
page: {
|
||||
index: 0,
|
||||
size: 10,
|
||||
orderMap: {
|
||||
travelAt: OrderType.DESC
|
||||
},
|
||||
equalsExample: this.data.currentStatus === "ALL" ? undefined : {
|
||||
status: this.data.currentStatus as TravelStatus
|
||||
}
|
||||
|
||||
@ -68,9 +68,9 @@ page {
|
||||
--theme-bg-wx: #111;
|
||||
--theme-bg-primary: #1A1A1A;
|
||||
--theme-bg-secondary: #2A2A2A;
|
||||
--theme-bg-card: #2C2C2C;
|
||||
--theme-bg-card-secondary: #404040;
|
||||
--theme-bg-journal: #404040;
|
||||
--theme-bg-card: #3D3D3D;
|
||||
--theme-bg-card-secondary: #525252;
|
||||
--theme-bg-journal: #4B4B4B;
|
||||
--theme-bg-overlay: rgba(0, 0, 0, .3);
|
||||
--theme-bg-menu: rgba(40, 40, 40, .95);
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ export type Attachment = {
|
||||
|
||||
mimeType?: string;
|
||||
|
||||
metadata?: string | ImageMetadata;
|
||||
metadata?: string | ImageMetadata | PreviewImageMetadata;
|
||||
|
||||
/** 文件 MD5 */
|
||||
md5: string;
|
||||
@ -27,9 +27,6 @@ export type Attachment = {
|
||||
|
||||
/** 大小 */
|
||||
size: number;
|
||||
|
||||
/** 扩展数据 */
|
||||
ext?: string | MediaAttachExt;
|
||||
} & Model;
|
||||
|
||||
/** 媒体附件类型 */
|
||||
@ -52,8 +49,7 @@ export type ImageMetadata = {
|
||||
height: number;
|
||||
}
|
||||
|
||||
/** 媒体附件扩展数据 */
|
||||
export type MediaAttachExt = {
|
||||
export type PreviewImageMetadata = {
|
||||
|
||||
/** 原文件附件 ID */
|
||||
sourceId: number;
|
||||
@ -61,15 +57,6 @@ export type MediaAttachExt = {
|
||||
/** 原文件访问 mongoId */
|
||||
sourceMongoId: string;
|
||||
|
||||
/** true 为图片 */
|
||||
isImage: boolean;
|
||||
|
||||
/** true 为视频 */
|
||||
isVideo: boolean;
|
||||
|
||||
/** 原图宽度(像素) */
|
||||
width?: number;
|
||||
|
||||
/** 原图高度(像素) */
|
||||
height?: number;
|
||||
}
|
||||
/** 原文件 MimeType */
|
||||
sourceMimeType: string;
|
||||
} & ImageMetadata;
|
||||
|
||||
@ -94,9 +94,10 @@ export enum TravelLocationType {
|
||||
HOTEL = "HOTEL",
|
||||
TRANSPORT = "TRANSPORT",
|
||||
ATTRACTION = "ATTRACTION",
|
||||
MALL = "MALL",
|
||||
SHOPPING = "SHOPPING",
|
||||
PLAY = "PLAY",
|
||||
LIFE = "LEFE"
|
||||
LIFE = "LIFE"
|
||||
}
|
||||
|
||||
/** 地点类型中文映射 */
|
||||
@ -105,6 +106,7 @@ export const TravelLocationTypeLabel: Record<TravelLocationType, string> = {
|
||||
[TravelLocationType.HOTEL]: "酒店",
|
||||
[TravelLocationType.TRANSPORT]: "交通",
|
||||
[TravelLocationType.ATTRACTION]: "景点",
|
||||
[TravelLocationType.MALL]: "商场",
|
||||
[TravelLocationType.SHOPPING]: "购物",
|
||||
[TravelLocationType.PLAY]: "玩乐",
|
||||
[TravelLocationType.LIFE]: "生活"
|
||||
@ -116,9 +118,10 @@ export const TravelLocationTypeIcon: Record<TravelLocationType, string> = {
|
||||
[TravelLocationType.HOTEL]: "city-8",
|
||||
[TravelLocationType.TRANSPORT]: "map-route-planning",
|
||||
[TravelLocationType.ATTRACTION]: "image-1",
|
||||
[TravelLocationType.MALL]: "chart-3d",
|
||||
[TravelLocationType.SHOPPING]: "shop",
|
||||
[TravelLocationType.PLAY]: "ferris-wheel",
|
||||
[TravelLocationType.LIFE]: "cart"
|
||||
[TravelLocationType.LIFE]: "location"
|
||||
};
|
||||
|
||||
/** 出行地点实体 */
|
||||
@ -154,10 +157,10 @@ export interface TravelLocation extends Model {
|
||||
requireAppointment?: boolean;
|
||||
|
||||
/** 评分 */
|
||||
score?: number;
|
||||
score?: number | null;
|
||||
|
||||
/** 重要程度 */
|
||||
importance?: number;
|
||||
importance?: number | null;
|
||||
|
||||
/** 附件 */
|
||||
items?: Attachment[];
|
||||
|
||||
Reference in New Issue
Block a user