apply Network.ts

This commit is contained in:
Timi
2025-12-17 16:56:33 +08:00
parent 369cfe2bf2
commit 62186abdb8
13 changed files with 584 additions and 696 deletions

View File

@ -0,0 +1,89 @@
import { Network } from "../utils/Network";
import { Journal, JournalPage } from "../types/Journal";
import { QueryPageResult } from "../types/Model";
/**
* Journal 日记 API
*
* 按业务模块封装网络请求,使代码更清晰、可维护
*/
export class JournalApi {
/**
* 获取日记详情
*
* @param id - 日记 ID
*/
static getDetail(id: number | string): Promise<Journal> {
return Network.post<Journal>(`/journal/${id}`);
}
/**
* 日记分页列表
*
* @param pageParams - 分页参数
*/
static getList(pageParams: JournalPage): Promise<QueryPageResult<Journal>> {
return Network.post<QueryPageResult<Journal>>("/journal/list", pageParams);
}
/**
* 创建日记
*
* @param data - 日记数据
*/
static create(data: Partial<Journal> & {
pusher?: string;
tempFileIds?: string[];
}): Promise<Journal> {
return Network.post<Journal>("/journal/create", data, {
showLoading: true,
loadingText: "正在保存.."
});
}
/**
* 更新日记
*
* @param data - 日记数据(必须包含 id
*/
static update(data: Partial<Journal> & {
id: number;
attachmentIds?: number[];
tempFileIds?: string[];
}): Promise<Journal> {
return Network.post<Journal>("/journal/update", data, {
showLoading: true,
loadingText: "正在保存.."
});
}
/**
* 删除日记
*
* @param id - 日记 ID
*/
static delete(id: number): Promise<void> {
return Network.post<void>("/journal/delete", { id }, {
showLoading: true,
loadingText: "删除中..."
});
}
/**
* 获取 openId用于推送
*
* @param code - 微信登录 code
*/
static getOpenId(code: string): Promise<string> {
return Network.post<string>("/journal/openid", { code });
}
/**
* 根据 ID 列表获取日记
*
* @param ids - 日记 ID 列表
*/
static getListByIds(ids: number[]): Promise<Journal[]> {
return Network.post<Journal[]>("/journal/list/ids", ids);
}
}

View File

@ -0,0 +1,69 @@
import { Network } from "../utils/Network";
import { Attachment } from "../types/Attachment";
/**
* Moment 瞬间 API
*
* 管理临时照片/视频上传、归档到日记等操作
*/
export class MomentApi {
/**
* 获取 moment 列表
*/
static getList(): Promise<Attachment[]> {
return Network.post<Attachment[]>("/journal/moment/list");
}
/**
* MD5 查重过滤
*
* @param md5s - MD5 值数组
* @returns 未重复的 MD5 数组
*/
static filterByMD5(md5s: string[]): Promise<string[]> {
return Network.post<string[]>("/journal/moment/filter", md5s);
}
/**
* 创建 moment 附件
*
* @param tempFileIds - 临时文件 ID 数组
* @returns 创建的附件列表
*/
static create(tempFileIds: string[]): Promise<Attachment[]> {
return Network.post<Attachment[]>("/journal/moment/create", tempFileIds, {
showLoading: true,
loadingText: "正在保存.."
});
}
/**
* 归档 moments 到日记
*
* @param data - 归档数据
*/
static archive(data: {
id?: number;
type: string;
idea: string;
lat?: number;
lng?: number;
location?: string;
pusher: string;
thumbIds: number[];
}): Promise<void> {
return Network.post<void>("/journal/moment/archive", data, {
showLoading: true,
loadingText: "正在归档.."
});
}
/**
* 删除 moments
*
* @param ids - 附件 ID 数组
*/
static delete(ids: number[]): Promise<void> {
return Network.post<void>("/journal/moment/delete", ids);
}
}

View File

@ -5,6 +5,7 @@ import Toolkit from "../../utils/Toolkit";
import { ImageMetadata, MediaAttachExt, MediaAttachType } from "../../types/Attachment"; import { ImageMetadata, MediaAttachExt, MediaAttachType } from "../../types/Attachment";
import { MediaItem, MediaItemType } from "../../types/UI"; import { MediaItem, MediaItemType } from "../../types/UI";
import Time from "../../utils/Time"; import Time from "../../utils/Time";
import { JournalApi } from "../../api/JournalApi";
interface JournalDetailPanelData { interface JournalDetailPanelData {
journals: Journal[]; journals: Journal[];
@ -35,24 +36,7 @@ Component({
if (visible && ids && 0 < ids.length) { if (visible && ids && 0 < ids.length) {
wx.showLoading({ title: "加载中...", mask: true }); wx.showLoading({ title: "加载中...", mask: true });
try { try {
const journals: Journal[] = await new Promise((resolve, reject) => { const journals = await JournalApi.getListByIds(ids);
wx.request({
url: `${config.url}/journal/list/ids`,
method: "POST",
header: {
Key: wx.getStorageSync("key")
},
data: ids,
success: (resp: any) => {
if (resp.data.code === 20000) {
resolve(resp.data.data);
} else {
reject(new Error(resp.data.message || "加载失败"));
}
},
fail: reject
});
}) || [];
journals.forEach(journal => { journals.forEach(journal => {
journal.date = Time.toPassedDate(journal.createdAt); journal.date = Time.toPassedDate(journal.createdAt);
journal.time = Time.toTime(journal.createdAt); journal.time = Time.toTime(journal.createdAt);

View File

@ -4,6 +4,7 @@ import { JournalPage, JournalPageType } from "../../types/Journal";
import { OrderType } from "../../types/Model"; import { OrderType } from "../../types/Model";
import Time from "../../utils/Time"; import Time from "../../utils/Time";
import Toolkit from "../../utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
import { JournalApi } from "../../api/JournalApi";
export type JournalListItem = { export type JournalListItem = {
id: number; id: number;
@ -19,14 +20,10 @@ interface JournalListData {
isFinished: boolean; isFinished: boolean;
page: JournalPage; page: JournalPage;
searchValue: string; searchValue: string;
debouncedSearch?: any;
} }
// 组件实例类型扩展 Component({
interface ComponentInstance {
debouncedSearch?: ((keyword: string) => void) & { cancel(): void };
}
Component<JournalListData, {}, {}, ComponentInstance>({
options: { options: {
styleIsolation: 'apply-shared' styleIsolation: 'apply-shared'
}, },
@ -64,25 +61,28 @@ Component<JournalListData, {}, {}, ComponentInstance>({
createdAt: OrderType.DESC createdAt: OrderType.DESC
} }
}, },
searchValue: "" searchValue: "",
debouncedSearch: undefined
}, },
lifetimes: { lifetimes: {
ready() { ready() {
// 创建防抖搜索函数 // 创建防抖搜索函数
this.debouncedSearch = Toolkit.debounce( this.setData({
(keyword: string) => { debouncedSearch: Toolkit.debounce(
this.resetAndSearch(keyword); (keyword: string) => {
}, this.resetAndSearch(keyword);
false, // 不立即执行,等待输入停止 },
400 // 400ms 延迟 false, // 不立即执行,等待输入停止
); 400 // 400ms 延迟
)
})
// 组件加载时就获取数据 // 组件加载时就获取数据
this.fetch(); this.fetch();
}, },
detached() { detached() {
// 组件销毁时取消防抖 // 组件销毁时取消防抖
if (this.debouncedSearch) { if (this.data.debouncedSearch) {
this.debouncedSearch.cancel(); this.data.debouncedSearch.cancel();
} }
} }
}, },
@ -125,77 +125,74 @@ Component<JournalListData, {}, {}, ComponentInstance>({
}); });
}, },
/** 获取数据 */ /** 获取数据 */
fetch() { async fetch() {
if (this.data.isFetching || this.data.isFinished) { if (this.data.isFetching || this.data.isFinished) {
return; return;
} }
this.setData({ isFetching: true }); this.setData({ isFetching: true });
wx.request({ try {
url: `${config.url}/journal/list`, const pageResult = await JournalApi.getList(this.data.page);
method: "POST", const list = pageResult.list;
header: { if (!list || list.length === 0) {
Key: wx.getStorageSync("key")
},
data: this.data.page,
success: (resp: any) => {
const list = resp.data.data.list;
if (!list || list.length === 0) {
this.setData({ isFinished: true });
return;
}
const result = list.map((journal: any) => {
const firstThumb = journal.items.find((item: any) => item.attachType === "THUMB");
return {
id: journal.id,
date: Time.toPassedDate(journal.createdAt),
idea: journal.idea,
location: journal.location,
thumbUrl: firstThumb ? `${config.url}/attachment/read/${firstThumb.mongoId}` : undefined
} as JournalListItem;
});
this.setData({ this.setData({
page: { isFinished: true,
...this.data.page, isFetching: false
index: this.data.page.index + 1,
type: JournalPageType.PREVIEW,
equalsExample: this.data.page.equalsExample,
likeExample: this.data.page.likeExample,
orderMap: {
createdAt: OrderType.DESC
}
},
list: this.data.list.concat(result),
isFinished: list.length < this.data.page.size
}); });
}, return;
complete: () => {
this.setData({ isFetching: false });
} }
}); const result = list.map((journal: any) => {
const firstThumb = journal.items.find((item: any) => item.attachType === "THUMB");
return {
id: journal.id,
date: Time.toPassedDate(journal.createdAt),
idea: journal.idea,
location: journal.location,
thumbUrl: firstThumb ? `${config.url}/attachment/read/${firstThumb.mongoId}` : undefined
} as JournalListItem;
});
this.setData({
page: {
...this.data.page,
index: this.data.page.index + 1,
type: JournalPageType.PREVIEW,
equalsExample: this.data.page.equalsExample,
likeExample: this.data.page.likeExample,
orderMap: {
createdAt: OrderType.DESC
}
},
list: this.data.list.concat(result),
isFinished: list.length < this.data.page.size,
isFetching: false
});
} catch (error) {
console.error("加载日记列表失败:", error);
this.setData({ isFetching: false });
}
}, },
/** 输入搜索 */ /** 输入搜索 */
onSearchChange(e: WechatMiniprogram.CustomEvent) { onSearchChange(e: WechatMiniprogram.CustomEvent) {
const value = e.detail.value.trim(); const value = e.detail.value.trim();
this.setData({ searchValue: value }); this.setData({ searchValue: value });
// 使用防抖自动搜索(包括清空的情况) // 使用防抖自动搜索(包括清空的情况)
if (this.debouncedSearch) { if (this.data.debouncedSearch) {
this.debouncedSearch(value); this.data.debouncedSearch(value);
} }
}, },
/** 提交搜索 */ /** 提交搜索 */
onSearchSubmit(e: WechatMiniprogram.CustomEvent) { onSearchSubmit(e: WechatMiniprogram.CustomEvent) {
const value = e.detail.value.trim(); const value = e.detail.value.trim();
// 立即搜索,取消防抖 // 立即搜索,取消防抖
if (this.debouncedSearch) { if (this.data.debouncedSearch) {
this.debouncedSearch.cancel(); this.data.debouncedSearch.cancel();
} }
this.resetAndSearch(value); this.resetAndSearch(value);
}, },
/** 清空搜索 */ /** 清空搜索 */
onSearchClear() { onSearchClear() {
// 取消防抖,立即搜索 // 取消防抖,立即搜索
if (this.debouncedSearch) { if (this.data.debouncedSearch) {
this.debouncedSearch.cancel(); this.data.debouncedSearch.cancel();
} }
this.resetAndSearch(""); this.resetAndSearch("");
}, },

View File

@ -1,7 +1,7 @@
// components/travel-location-popup/index.ts // components/travel-location-popup/index.ts
import { TravelLocation, TravelLocationTypeLabel, TravelLocationTypeIcon } from "../../types/Travel"; import { TravelLocation, TravelLocationTypeLabel, TravelLocationTypeIcon } from "../../types/Travel";
import { TravelLocationApi } from "../../api/TravelLocationApi"; import { TravelLocationApi } from "../../api/TravelLocationApi";
import { MediaAttachExt, MediaAttachType } from "../../types/Attachment"; import { ImageMetadata, MediaAttachType } from "../../types/Attachment";
import { MediaItem, MediaItemType } from "../../types/UI"; import { MediaItem, MediaItemType } from "../../types/UI";
import config from "../../config/index"; import config from "../../config/index";
import Toolkit from "../../utils/Toolkit"; import Toolkit from "../../utils/Toolkit";
@ -39,11 +39,11 @@ Component({
// 处理附件数据 // 处理附件数据
const attachments = location.items || []; const attachments = location.items || [];
const thumbItems = attachments.filter((item: any) => item.attachType === MediaAttachType.THUMB); const thumbItems = attachments.filter(item => item.attachType === MediaAttachType.THUMB);
if (0 < thumbItems.length) { if (0 < thumbItems.length) {
const mediaItems: MediaItem[] = thumbItems.map((thumbItem: any, index: number) => { const mediaItems: MediaItem[] = thumbItems.map((thumbItem, index) => {
const metadata = thumbItem.metadata; const metadata = thumbItem.metadata as ImageMetadata;
const ext = typeof thumbItem.ext === "string" ? JSON.parse(thumbItem.ext) : thumbItem.ext; const ext = typeof thumbItem.ext === "string" ? JSON.parse(thumbItem.ext) : thumbItem.ext;
const thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`; const thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`; const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;

View File

@ -2,6 +2,7 @@
import config from "../../config/index" import config from "../../config/index"
import { JournalPage, JournalPageType } from "../../types/Journal"; import { JournalPage, JournalPageType } from "../../types/Journal";
import { JournalApi } from "../../api/JournalApi";
interface IndexData { interface IndexData {
key: string; key: string;
@ -19,32 +20,23 @@ Page({
}); });
} }
}, },
navigateToMain() { async navigateToMain() {
wx.request({ try {
url: `${config.url}/journal/list`, await JournalApi.getList({
method: "POST",
header: {
Key: this.data.key
},
data: <JournalPage> {
index: 0, index: 0,
size: 1, size: 1,
type: JournalPageType.PREVIEW type: JournalPageType.PREVIEW
}, });
success: (resp) => { wx.setStorageSync("key", this.data.key);
const data = resp.data as any; wx.switchTab({
if (data.code === 20000) { url: "/pages/main/journal/index",
wx.setStorageSync("key", this.data.key); })
wx.switchTab({ } catch (error: any) {
url: "/pages/main/journal/index", if (error?.code === 40100) {
}) wx.showToast({ title: "密码错误", icon: "error" });
} else if (data.code === 40100) { } else {
wx.showToast({ title: "密码错误", icon: "error" }); wx.showToast({ title: "验证失败", icon: "error" });
} else { }
wx.showToast({ title: "服务异常", icon: "error" }); }
}
},
fail: () => wx.showToast({ title: "验证失败", icon: "error" })
});
} }
}) })

View File

@ -1,7 +1,7 @@
// pages/main/journal-date/index.ts // pages/main/journal-date/index.ts
import config from "../../../config/index";
import { Journal, JournalPageType } from "../../../types/Journal"; import { Journal, JournalPageType } from "../../../types/Journal";
import Time from "../../../utils/Time"; import Time from "../../../utils/Time";
import { JournalApi } from "../../../api/JournalApi";
interface JournalDateData { interface JournalDateData {
// 存储每个日期的日记 id 列表 // 存储每个日期的日记 id 列表
@ -27,28 +27,12 @@ Page({
async loadJournals() { async loadJournals() {
this.setData({ isLoading: true }); this.setData({ isLoading: true });
try { try {
const list: Journal[] = await new Promise((resolve, reject) => { const pageResult = await JournalApi.getList({
wx.request({ index: 0,
url: `${config.url}/journal/list`, size: 9007199254740992,
method: "POST", type: JournalPageType.PREVIEW
header: { });
Key: wx.getStorageSync("key") const list: Journal[] = pageResult.list || [];
},
data: {
page: 0,
size: 9007199254740992,
type: JournalPageType.PREVIEW
},
success: (resp: any) => {
if (resp.data.code === 20000) {
resolve(resp.data.data.list);
} else {
reject(new Error(resp.data.message || "加载失败"));
}
},
fail: reject
});
}) || [];
// 按日期分组,只存储 id // 按日期分组,只存储 id
const journalMap: Record<string, number[]> = {}; const journalMap: Record<string, number[]> = {};
list.forEach((journal: any) => { list.forEach((journal: any) => {

View File

@ -4,9 +4,10 @@ import Time from "../../../utils/Time";
import Toolkit from "../../../utils/Toolkit"; import Toolkit from "../../../utils/Toolkit";
import config from "../../../config/index"; import config from "../../../config/index";
import { Location, MediaItem, MediaItemType, WechatMediaItem } from "../../../types/UI"; import { Location, MediaItem, MediaItemType, WechatMediaItem } from "../../../types/UI";
import { Journal, JournalType } from "../../../types/Journal"; import { JournalType } from "../../../types/Journal";
import { MediaAttachExt, MediaAttachType } from "../../../types/Attachment"; import { MediaAttachExt, MediaAttachType } from "../../../types/Attachment";
import IOSize, { Unit } from "../../../utils/IOSize"; import IOSize, { Unit } from "../../../utils/IOSize";
import { JournalApi } from "../../../api/JournalApi";
interface JournalEditorData { interface JournalEditorData {
/** 模式create 或 edit */ /** 模式create 或 edit */
@ -148,25 +149,8 @@ Page({
}, },
/** 加载日记详情(编辑模式) */ /** 加载日记详情(编辑模式) */
async loadJournalDetail(id: number) { async loadJournalDetail(id: number) {
wx.showLoading({ title: "加载中...", mask: true });
try { try {
const journal: Journal = await new Promise((resolve, reject) => { const journal = await JournalApi.getDetail(id);
wx.request({
url: `${config.url}/journal/${id}`,
method: "POST",
header: {
Key: wx.getStorageSync("key")
},
success: (res: any) => {
if (res.data.code === 20000) {
resolve(res.data.data);
} else {
reject(new Error(res.data.message || "加载失败"));
}
},
fail: reject
});
});
const items = journal.items || []; const items = journal.items || [];
const thumbItems = items.filter((item) => item.attachType === MediaAttachType.THUMB); const thumbItems = items.filter((item) => item.attachType === MediaAttachType.THUMB);
@ -309,11 +293,11 @@ Page({
// 编辑模式mediaList + newMediaList // 编辑模式mediaList + newMediaList
const sources = (this.data.mediaList as MediaItem[]).map(item => ({ const sources = (this.data.mediaList as MediaItem[]).map(item => ({
url: item.sourceURL, url: item.sourceURL,
type: MediaItemType[item.type].toLowerCase() type: item.type
})); }));
const newSources = this.data.newMediaList.map(item => ({ const newSources = this.data.newMediaList.map(item => ({
url: item.path, url: item.path,
type: MediaItemType[item.type].toLowerCase() type: item.type
})); }));
const allSources = [...sources, ...newSources]; const allSources = [...sources, ...newSources];
const itemIndex = isNewMedia ? this.data.mediaList.length + index : index; const itemIndex = isNewMedia ? this.data.mediaList.length + index : index;
@ -392,45 +376,21 @@ Page({
this.executeDelete(); this.executeDelete();
}, },
/** 执行删除 */ /** 执行删除 */
executeDelete() { async executeDelete() {
wx.showLoading({ title: "删除中...", mask: true }); try {
wx.request({ await JournalApi.delete(this.data.id!);
url: `${config.url}/journal/delete`, Events.emit("JOURNAL_REFRESH");
method: "POST", Events.emit("JOURNAL_LIST_REFRESH");
header: { wx.showToast({
Key: wx.getStorageSync("key"), title: "删除成功",
"Content-Type": "application/json" icon: "success"
}, });
data: { setTimeout(() => {
id: this.data.id wx.navigateBack();
}, }, 1000);
success: (res: any) => { } catch (error) {
wx.hideLoading(); console.error("删除日记失败:", error);
if (res.data.code === 20000 || res.statusCode === 200) { }
Events.emit("JOURNAL_REFRESH");
Events.emit("JOURNAL_LIST_REFRESH");
wx.showToast({
title: "删除成功",
icon: "success"
});
setTimeout(() => {
wx.navigateBack();
}, 1000);
} else {
wx.showToast({
title: res.data.message || "删除失败",
icon: "error"
});
}
},
fail: () => {
wx.hideLoading();
wx.showToast({
title: "删除失败",
icon: "error"
});
}
});
}, },
/** 提交/保存 */ /** 提交/保存 */
submit() { submit() {
@ -441,10 +401,9 @@ Page({
} }
}, },
/** 创建日记 */ /** 创建日记 */
createJournal() { async createJournal() {
const handleFail = () => { const handleFail = () => {
wx.showToast({ title: "上传失败", icon: "error" }); wx.showToast({ title: "上传失败", icon: "error" });
wx.hideLoading();
this.setData({ this.setData({
isSaving: false isSaving: false
}); });
@ -452,83 +411,64 @@ Page({
this.setData({ this.setData({
isSaving: true isSaving: true
}); });
// 获取 openId try {
const getOpenId = new Promise<string>((resolve, reject) => { // 获取 openId
wx.login({ const getOpenId = new Promise<string>((resolve, reject) => {
success: (res) => { wx.login({
if (res.code) { success: async (res) => {
wx.request({ if (res.code) {
url: `${config.url}/journal/openid`, try {
method: "POST", const openId = await JournalApi.getOpenId(res.code);
header: { resolve(openId);
Key: wx.getStorageSync("key") } catch (error) {
}, reject(new Error("获取 openId 失败"));
data: { }
code: res.code } else {
}, reject(new Error("获取登录凭证失败"));
success: (resp) => { }
const data = resp.data as any; },
if (data.code === 20000) { fail: () => reject(new Error("登录失败"))
resolve(data.data); });
} else {
reject(new Error("获取 openId 失败"));
}
},
fail: () => reject(new Error("获取 openId 请求失败"))
});
} else {
reject(new Error("获取登录凭证失败"));
}
},
fail: () => reject(new Error("登录失败"))
}); });
}); // 文件上传
// 文件上传 const uploadFiles = this.uploadMediaFiles(this.data.mediaList as WechatMediaItem[]);
const uploadFiles = this.uploadMediaFiles(this.data.mediaList as WechatMediaItem[]); // 并行执行获取 openId 和文件上传
// 并行执行获取 openId 和文件上传 const [openId, tempFileIds] = await Promise.all([getOpenId, uploadFiles]);
Promise.all([getOpenId, uploadFiles]).then(([openId, tempFileIds]) => {
wx.showLoading({ title: "正在保存..", mask: true }); await JournalApi.create({
wx.request({ idea: this.data.idea,
url: `${config.url}/journal/create`, type: this.data.type,
method: "POST", lat: this.data.location?.lat,
header: { lng: this.data.location?.lng,
Key: wx.getStorageSync("key") location: this.data.location?.text,
}, pusher: openId,
data: { createdAt: Date.parse(`${this.data.date} ${this.data.time}`),
idea: this.data.idea, tempFileIds
type: this.data.type,
lat: this.data.location?.lat,
lng: this.data.location?.lng,
location: this.data.location?.text,
pusher: openId,
createdAt: Date.parse(`${this.data.date} ${this.data.time}`),
tempFileIds
},
success: async () => {
Events.emit("JOURNAL_REFRESH");
wx.showToast({ title: "提交成功", icon: "success" });
this.setData({
idea: "",
mediaList: [],
isSaving: false,
uploaded: "0",
uploadTotal: "0 MB",
uploadProgress: 0
});
await Toolkit.sleep(1000);
wx.switchTab({
url: "/pages/main/journal/index"
});
},
fail: handleFail
}); });
}).catch(handleFail);
Events.emit("JOURNAL_REFRESH");
wx.showToast({ title: "提交成功", icon: "success" });
this.setData({
idea: "",
mediaList: [],
isSaving: false,
uploaded: "0",
uploadTotal: "0 MB",
uploadProgress: 0
});
await Toolkit.sleep(1000);
wx.switchTab({
url: "/pages/main/journal/index"
});
} catch (error) {
console.error("创建日记失败:", error);
handleFail();
}
}, },
/** 更新日记 */ /** 更新日记 */
updateJournal() { async updateJournal() {
const handleFail = () => { const handleFail = () => {
wx.showToast({ title: "保存失败", icon: "error" }); wx.showToast({ title: "保存失败", icon: "error" });
wx.hideLoading();
this.setData({ this.setData({
isSaving: false isSaving: false
}); });
@ -536,53 +476,39 @@ Page({
this.setData({ this.setData({
isSaving: true isSaving: true
}); });
// 收集保留的附件 ID缩略图 ID try {
const attachmentIds = (this.data.mediaList as MediaItem[]).map(item => item.attachmentId); // 收集保留的附件 ID缩略图 ID
// 上传新媒体文件 const attachmentIds = (this.data.mediaList as MediaItem[]).map(item => item.attachmentId);
const uploadFiles = this.uploadMediaFiles(this.data.newMediaList); // 上传新媒体文件
const tempFileIds = await this.uploadMediaFiles(this.data.newMediaList);
// 提交保存 await JournalApi.update({
uploadFiles.then((tempFileIds) => { id: this.data.id!,
wx.showLoading({ title: "正在保存..", mask: true }); idea: this.data.idea,
wx.request({ type: this.data.type,
url: `${config.url}/journal/update`, lat: this.data.location?.lat,
method: "POST", lng: this.data.location?.lng,
header: { location: this.data.location?.text,
Key: wx.getStorageSync("key") createdAt: new Date(`${this.data.date}T${this.data.time}:00`).getTime(),
}, attachmentIds,
data: { tempFileIds
id: this.data.id,
idea: this.data.idea,
type: this.data.type,
lat: this.data.location?.lat,
lng: this.data.location?.lng,
location: this.data.location?.text,
createdAt: new Date(`${this.data.date}T${this.data.time}:00`).getTime(),
// 保留的现有附件 ID
attachmentIds,
// 新上传的临时文件 ID
tempFileIds
},
success: async (resp: any) => {
if (resp.data.code === 20000 || resp.statusCode === 200) {
Events.emit("JOURNAL_REFRESH");
Events.emit("JOURNAL_LIST_REFRESH");
wx.showToast({ title: "保存成功", icon: "success" });
this.setData({
isSaving: false,
uploaded: "0",
uploadTotal: "0 MB",
uploadProgress: 0
});
await Toolkit.sleep(1000);
wx.navigateBack();
} else {
handleFail();
}
},
fail: handleFail
}); });
}).catch(handleFail);
Events.emit("JOURNAL_REFRESH");
Events.emit("JOURNAL_LIST_REFRESH");
wx.showToast({ title: "保存成功", icon: "success" });
this.setData({
isSaving: false,
uploaded: "0",
uploadTotal: "0 MB",
uploadProgress: 0
});
await Toolkit.sleep(1000);
wx.navigateBack();
} catch (error) {
console.error("更新日记失败:", error);
handleFail();
}
}, },
/** 上传媒体文件 */ /** 上传媒体文件 */
uploadMediaFiles(mediaList: WechatMediaItem[]): Promise<string[]> { uploadMediaFiles(mediaList: WechatMediaItem[]): Promise<string[]> {

View File

@ -1,9 +1,10 @@
// pages/main/journal-map/index.ts // pages/main/journal-map/index.ts
import config from "../../../config/index"; import config from "../../../config/index";
import Time from "../../../utils/Time"; import Time from "../../../utils/Time";
import { Journal, JournalPageType } from "../../../types/Journal"; import { JournalPageType } from "../../../types/Journal";
import Toolkit from "../../../utils/Toolkit"; import Toolkit from "../../../utils/Toolkit";
import { MapMarker } from "../../../types/UI"; import { MapMarker } from "../../../types/UI";
import { JournalApi } from "../../../api/JournalApi";
interface LocationMarker { interface LocationMarker {
locationKey: string; // 位置键 "lat,lng" locationKey: string; // 位置键 "lat,lng"
@ -50,28 +51,12 @@ Page({
async loadJournals() { async loadJournals() {
this.setData({ isLoading: true }); this.setData({ isLoading: true });
try { try {
const list: Journal[] = await new Promise((resolve, reject) => { const result = await JournalApi.getList({
wx.request({ index: 0,
url: `${config.url}/journal/list`, size: 9007199254740992,
method: "POST", type: JournalPageType.PREVIEW
header: { });
Key: wx.getStorageSync("key") const list = result.list || [];
},
data: {
page: 0,
size: 9007199254740992,
type: JournalPageType.PREVIEW
},
success: (resp: any) => {
if (resp.data.code === 20000) {
resolve(resp.data.data.list);
} else {
reject(new Error(resp.data.message || "加载失败"));
}
},
fail: reject
});
}) || [];
// 过滤有位置信息的记录,并按位置分组 // 过滤有位置信息的记录,并按位置分组
const locationMap = new Map<string, LocationMarker>(); const locationMap = new Map<string, LocationMarker>();
list.filter((journal: any) => journal.lat && journal.lng).forEach((journal: any) => { list.filter((journal: any) => journal.lat && journal.lng).forEach((journal: any) => {

View File

@ -5,9 +5,10 @@ import config from "../../../config/index"
import Events from "../../../utils/Events"; import Events from "../../../utils/Events";
import Toolkit from "../../../utils/Toolkit"; import Toolkit from "../../../utils/Toolkit";
import { Journal, JournalPage, JournalPageType } from "../../../types/Journal"; import { Journal, JournalPage, JournalPageType } from "../../../types/Journal";
import { OrderType, QueryPageResult } from "../../../types/Model"; import { OrderType } from "../../../types/Model";
import { ImageMetadata, MediaAttachExt } from "../../../types/Attachment"; import { ImageMetadata, MediaAttachExt } from "../../../types/Attachment";
import { MediaItem, MediaItemType } from "../../../types/UI"; import { MediaItem, MediaItemType } from "../../../types/UI";
import { JournalApi } from "../../../api/JournalApi";
interface JournalData { interface JournalData {
page: JournalPage; page: JournalPage;
@ -143,79 +144,71 @@ Page({
url: "/pages/main/journal-date/index" url: "/pages/main/journal-date/index"
}) })
}, },
fetch() { async fetch() {
if (this.data.isFetching || this.data.isFinished) { if (this.data.isFetching || this.data.isFinished) {
return; return;
} }
this.setData({ this.setData({
isFetching: true isFetching: true
}); });
wx.request({ try {
url: `${config.url}/journal/list`, const pageResult = await JournalApi.getList(this.data.page);
method: "POST", const list = pageResult.list;
header: { if (!list || list.length === 0) {
Key: wx.getStorageSync("key")
},
data: this.data.page,
success: async (resp: any) => {
const pageResult = resp.data.data as QueryPageResult<Journal>;
const list = pageResult.list;
if (!list || list.length === 0) {
this.setData({
isFinished: true
})
return;
}
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 thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
return {
type: ext.isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
thumbURL,
sourceURL,
size: thumbItem.size || 0,
attachmentId: thumbItem.id,
width: metadata.width,
height: metadata.height,
originalIndex: index
} as MediaItem;
});
journal.date = Time.toDate(journal.createdAt);
journal.time = Time.toTime(journal.createdAt);
journal.datetime = Time.toPassedDateTime(journal.createdAt);
journal.mediaItems = mediaItems;
journal.columnedItems = Toolkit.splitItemsIntoColumns(mediaItems, 3, (item) => {
if (item.width && item.height && 0 < item.width) {
return item.height / item.width;
}
return 1;
})
})
this.setData({
page: {
index: this.data.page.index + 1,
size: 8,
type: JournalPageType.NORMAL,
equalsExample: {
type: "NORMAL"
},
orderMap: {
createdAt: OrderType.DESC
}
},
list: this.data.list.concat(list),
isFinished: list.length < this.data.page.size
});
},
complete: () => {
this.setData({ this.setData({
isFinished: true,
isFetching: false isFetching: false
}); });
return;
} }
}); 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 thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
return {
type: ext.isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
thumbURL,
sourceURL,
size: thumbItem.size || 0,
attachmentId: thumbItem.id,
width: metadata.width,
height: metadata.height,
originalIndex: index
} as MediaItem;
});
journal.date = Time.toDate(journal.createdAt);
journal.time = Time.toTime(journal.createdAt);
journal.datetime = Time.toPassedDateTime(journal.createdAt);
journal.mediaItems = mediaItems;
journal.columnedItems = Toolkit.splitItemsIntoColumns(mediaItems, 3, (item) => {
if (item.width && item.height && 0 < item.width) {
return item.height / item.width;
}
return 1;
})
});
this.setData({
page: {
index: this.data.page.index + 1,
size: 8,
type: JournalPageType.NORMAL,
equalsExample: {
type: "NORMAL"
},
orderMap: {
createdAt: OrderType.DESC
}
},
list: this.data.list.concat(list),
isFinished: list.length < this.data.page.size,
isFetching: false
});
} catch (error) {
console.error("加载日记失败:", error);
this.setData({ isFetching: false });
}
}, },
preview(e: WechatMiniprogram.BaseEvent) { preview(e: WechatMiniprogram.BaseEvent) {
const { journalIndex, itemIndex } = e.currentTarget.dataset; const { journalIndex, itemIndex } = e.currentTarget.dataset;

View File

@ -6,6 +6,9 @@ import Time from "../../../utils/Time";
import Toolkit from "../../../utils/Toolkit"; import Toolkit from "../../../utils/Toolkit";
import { Location, MediaItemType } from "../../../types/UI"; import { Location, MediaItemType } from "../../../types/UI";
import { MediaAttachExt } from "../../../types/Attachment"; import { MediaAttachExt } from "../../../types/Attachment";
import { MomentApi } from "../../../api/MomentApi";
import { JournalApi } from "../../../api/JournalApi";
import { Network } from "../../../utils/Network";
type Item = { type Item = {
id: number; id: number;
@ -123,34 +126,29 @@ Page({
} }
}); });
}, },
fetch() { async fetch() {
wx.request({ try {
url: `${config.url}/journal/moment/list`, const list = await MomentApi.getList();
method: "POST", if (!list || list.length === 0) {
header: { return;
Key: wx.getStorageSync("key")
},
success: async (resp: any) => {
const list = resp.data.data;
if (!list || list.length === 0) {
return;
}
this.setData({
list: list.map((item: any) => {
const ext = JSON.parse(item.ext) as MediaAttachExt;
const thumbURL = `${config.url}/attachment/read/${item.mongoId}`;
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
return {
id: item.id,
type: ext.isImage ? MediaItemType.IMAGE : MediaItemType.VIDEO,
thumbURL,
sourceURL,
checked: false
} as Item;
})
});
} }
}); this.setData({
list: list.map((item: any) => {
const ext = JSON.parse(item.ext) as MediaAttachExt;
const thumbURL = `${config.url}/attachment/read/${item.mongoId}`;
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
return {
id: item.id,
type: ext.isImage ? MediaItemType.IMAGE : MediaItemType.VIDEO,
thumbURL,
sourceURL,
checked: false
} as Item;
})
});
} catch (error) {
console.error("加载 moment 列表失败:", error);
}
}, },
updateHasChecked() { updateHasChecked() {
this.setData({ hasChecked: this.data.list.some(item => item.checked) }); this.setData({ hasChecked: this.data.list.some(item => item.checked) });
@ -226,155 +224,65 @@ Page({
} as MD5Result); } as MD5Result);
})); }));
// 查重 // 查重
const filterMD5Result: string[] = await new Promise((resolve, reject) => { const filterMD5Result: string[] = await MomentApi.filterByMD5(
wx.request({ md5Results.map(item => item.md5)
url: `${config.url}/journal/moment/filter`, );
method: "POST",
header: {
Key: wx.getStorageSync("key")
},
data: md5Results.map(item => item.md5),
success: async (resp: any) => {
resolve(resp.data.data);
},
fail: reject
});
});
// 过滤文件 // 过滤文件
const filterPath = md5Results.filter(item => filterMD5Result.indexOf(item.md5) !== -1) const filterPath = md5Results.filter(item => filterMD5Result.indexOf(item.md5) !== -1)
.map(item => item.path); .map(item => item.path);
files = files.filter(file => filterPath.indexOf(file.tempFilePath) !== -1); files = files.filter(file => filterPath.indexOf(file.tempFilePath) !== -1);
if (files.length === 0) { if (files.length === 0) {
wx.hideLoading(); wx.hideLoading();
that.setData({ isUploading: false });
return; return;
} }
wx.showLoading({
title: "正在上传..",
mask: true
})
// 计算上传大小
const sizePromises = files.map(file => {
return new Promise<number>((sizeResolve, sizeReject) => {
wx.getFileSystemManager().getFileInfo({
filePath: file.tempFilePath,
success: (res) => sizeResolve(res.size),
fail: (err) => sizeReject(err)
});
});
});
Promise.all(sizePromises).then(fileSizes => {
const totalSize = fileSizes.reduce((acc, size) => acc + size, 0);
const uploadTasks: WechatMiniprogram.UploadTask[] = [];
let uploadedSize = 0;
let lastUploadedSize = 0;
// 使用 Network.uploadFiles 上传文件
try {
const tempFileIds = await Network.uploadFiles({
mediaList: files.map(file => ({
path: file.tempFilePath,
size: file.size
})),
onProgress: (progress) => {
that.setData({
uploaded: IOSize.formatWithoutUnit(progress.uploaded, 2, Unit.MB),
uploadTotal: IOSize.format(progress.total, 2, Unit.MB),
uploadSpeed: `${IOSize.format(progress.speed)} / s`,
uploadProgress: progress.percent
});
},
showLoading: true
});
// 上传完成转附件
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 thumbURL = `${config.url}/attachment/read/${item.mongoId}`;
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
return {
id: item.id,
type: ext.isImage ? MediaItemType.IMAGE : MediaItemType.VIDEO,
thumbURL,
sourceURL,
checked: false
} as Item;
});
// 前插列表
that.data.list.unshift(...added);
that.setData({ that.setData({
uploadTotal: IOSize.format(totalSize, 2, Unit.MB) list: that.data.list,
isUploading: false,
uploaded: "0",
uploadTotal: "0 MB",
uploadProgress: 0
}); });
that.updateHasChecked();
// 计算上传速度 } catch (error) {
const speedUpdateInterval = setInterval(() => { handleFail(error);
const chunkSize = uploadedSize - lastUploadedSize; }
that.setData({
uploadSpeed: `${IOSize.format(chunkSize)} / s`
});
lastUploadedSize = uploadedSize;
}, 1000);
// 上传文件
const uploadPromises = files.map(file => {
return new Promise<string>((uploadResolve, uploadReject) => {
const task = wx.uploadFile({
url: `${config.url}/temp/file/upload`,
filePath: file.tempFilePath,
name: "file",
success: (resp) => {
const result = JSON.parse(resp.data);
if (result && result.code === 20000) {
// 更新进度
const progress = totalSize > 0 ? uploadedSize / totalSize : 1;
that.setData({
uploadProgress: Math.round(progress * 10000) / 100
});
uploadResolve(result.data[0].id);
} else {
uploadReject(new Error(`文件上传失败: ${result?.message || "未知错误"}`));
}
},
fail: (err) => uploadReject(new Error(`文件上传失败: ${err.errMsg}`))
});
// 监听上传进度事件
let prevProgress = 0;
task.onProgressUpdate((res) => {
const fileUploaded = (res.totalBytesExpectedToSend * res.progress) / 100;
const delta = fileUploaded - prevProgress;
uploadedSize += delta;
// 保存当前进度
prevProgress = fileUploaded;
// 更新进度条
that.setData({
uploaded: IOSize.formatWithoutUnit(uploadedSize, 2, Unit.MB),
uploadProgress: Math.round((uploadedSize / totalSize) * 10000) / 100
});
});
uploadTasks.push(task);
});
});
Promise.all(uploadPromises).then((tempFileIds) => {
wx.showLoading({
title: "正在保存..",
mask: true
})
// 清除定时器
clearInterval(speedUpdateInterval);
uploadTasks.forEach(task => task.offProgressUpdate());
that.setData({
uploadProgress: 100,
uploadSpeed: "0 MB / s"
});
// 上传完成转附件
wx.request({
url: `${config.url}/journal/moment/create`,
method: "POST",
header: {
Key: wx.getStorageSync("key")
},
data: tempFileIds,
success: async (resp: any) => {
wx.showToast({ title: "上传成功", icon: "success" });
const list = resp.data.data;
const added = list.map((item: any) => {
const ext = JSON.parse(item.ext) as MediaAttachExt;
const thumbURL = `${config.url}/attachment/read/${item.mongoId}`;
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
return {
id: item.id,
type: ext.isImage ? MediaItemType.IMAGE : MediaItemType.VIDEO,
thumbURL,
sourceURL,
checked: false
} as Item;
});
// 前插列表
that.data.list.unshift(...added);
that.setData({
list: that.data.list,
isUploading: false,
uploaded: "0",
uploadTotal: "0 MB",
uploadProgress: 0
});
that.updateHasChecked();
wx.hideLoading();
},
fail: handleFail
});
}).catch((e: Error) => {
// 取消所有上传任务
uploadTasks.forEach(task => task.abort());
that.updateHasChecked();
handleFail(e);
});
}).catch(handleFail);
} }
}) })
}, },
@ -450,27 +358,14 @@ Page({
}) })
const openId = await new Promise<string>((resolve, reject) => { const openId = await new Promise<string>((resolve, reject) => {
wx.login({ wx.login({
success: (res) => { success: async (res) => {
if (res.code) { if (res.code) {
wx.request({ try {
url: `${config.url}/journal/openid`, const openId = await JournalApi.getOpenId(res.code);
method: "POST", resolve(openId);
header: { } catch (error) {
Key: wx.getStorageSync("key") reject(new Error("获取 openId 失败"));
}, }
data: {
code: res.code
},
success: (resp) => {
const data = resp.data as any;
if (data.code === 20000) {
resolve(data.data);
} else {
reject(new Error("获取 openId 失败"));
}
},
fail: () => reject(new Error("获取 openId 请求失败"))
});
} else { } else {
reject(new Error("获取登录凭证失败")); reject(new Error("获取登录凭证失败"));
} }
@ -491,36 +386,23 @@ Page({
if (this.data.selectedJournalId) { if (this.data.selectedJournalId) {
archiveData.id = this.data.selectedJournalId; archiveData.id = this.data.selectedJournalId;
} }
wx.request({ try {
url: `${config.url}/journal/moment/archive`, await MomentApi.archive(archiveData);
method: "POST", Events.emit("JOURNAL_REFRESH");
header: { wx.showToast({ title: "归档成功", icon: "success" });
Key: wx.getStorageSync("key") this.setData({
}, idea: "",
data: archiveData, list: [],
success: async (resp: any) => { hasChecked: false,
if (resp.data && resp.data.code === 20000) { isArchiving: false,
Events.emit("JOURNAL_REFRESH"); selectedJournalId: undefined,
wx.showToast({ title: "归档成功", icon: "success" }); isVisibleArchivePopup: false
this.setData({ });
idea: "", await Toolkit.sleep(1000);
list: [], this.fetch();
hasChecked: false, } catch (error) {
isArchiving: false, handleFail();
selectedJournalId: undefined, }
isVisibleArchivePopup: false
});
await Toolkit.sleep(1000);
this.fetch();
} else {
wx.showToast({ title: "归档失败", icon: "error" });
this.setData({
isArchiving: false
});
}
},
fail: handleFail
});
}, },
allChecked() { allChecked() {
this.data.list.forEach(item => item.checked = true); this.data.list.forEach(item => item.checked = true);
@ -556,27 +438,21 @@ Page({
confirmText: "删除已选", confirmText: "删除已选",
confirmColor: "#E64340", confirmColor: "#E64340",
cancelText: "取消", cancelText: "取消",
success: res => { success: async (res) => {
if (res.confirm) { if (res.confirm) {
const selected = this.data.list.filter(item => item.checked); const selected = this.data.list.filter(item => item.checked);
wx.request({ try {
url: `${config.url}/journal/moment/delete`, await MomentApi.delete(selected.map(item => item.id));
method: "POST", wx.showToast({ title: "删除成功", icon: "success" });
header: { const list = this.data.list.filter(item => !item.checked);
Key: wx.getStorageSync("key") this.setData({
}, list
data: selected.map(item => item.id), });
success: async (resp: any) => { this.updateHasChecked();
if (resp.data && resp.data.code === 20000) { } catch (error) {
wx.showToast({ title: "删除成功", icon: "success" }); console.error("删除 moment 失败:", error);
const list = this.data.list.filter(item => !item.checked); wx.showToast({ title: "删除失败", icon: "error" });
this.setData({ }
list
});
this.updateHasChecked();
}
},
});
} }
} }
}) })

View File

@ -5,9 +5,10 @@ import config from "../../../config/index"
import Events from "../../../utils/Events"; import Events from "../../../utils/Events";
import Toolkit from "../../../utils/Toolkit"; import Toolkit from "../../../utils/Toolkit";
import { Journal, JournalPage, JournalPageType } from "../../../types/Journal"; import { Journal, JournalPage, JournalPageType } from "../../../types/Journal";
import { OrderType, QueryPageResult } from "../../../types/Model"; import { OrderType, } from "../../../types/Model";
import { ImageMetadata, MediaAttachExt } from "../../../types/Attachment"; import { ImageMetadata, MediaAttachExt } from "../../../types/Attachment";
import { MediaItem, MediaItemType } from "../../../types/UI"; import { MediaItem, MediaItemType } from "../../../types/UI";
import { JournalApi } from "../../../api/JournalApi";
interface IPortfolioData { interface IPortfolioData {
page: JournalPage; page: JournalPage;
@ -72,79 +73,71 @@ Page({
this.setData({ stickyOffset: height }); this.setData({ stickyOffset: height });
}); });
}, },
fetch() { async fetch() {
if (this.data.isFetching || this.data.isFinished) { if (this.data.isFetching || this.data.isFinished) {
return; return;
} }
this.setData({ this.setData({
isFetching: true isFetching: true
}); });
wx.request({ try {
url: `${config.url}/journal/list`, const pageResult = await JournalApi.getList(this.data.page);
method: "POST", const list = pageResult.list;
header: { if (!list || list.length === 0) {
Key: wx.getStorageSync("key")
},
data: this.data.page,
success: async (resp: any) => {
const pageResult = resp.data.data as QueryPageResult<Journal>;
const list = pageResult.list;
if (!list || list.length === 0) {
this.setData({
isFinished: true
})
return;
}
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 thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
return {
type: ext.isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
thumbURL,
sourceURL,
size: thumbItem.size || 0,
attachmentId: thumbItem.id,
width: metadata.width,
height: metadata.height,
originalIndex: index
} as MediaItem;
});
journal.date = Time.toDate(journal.createdAt);
journal.time = Time.toTime(journal.createdAt);
journal.datetime = Time.toPassedDateTime(journal.createdAt);
journal.mediaItems = mediaItems;
journal.columnedItems = Toolkit.splitItemsIntoColumns(mediaItems, 3, (item) => {
if (item.width && item.height && 0 < item.width) {
return item.height / item.width;
}
return 1;
})
})
this.setData({
page: {
index: this.data.page.index + 1,
size: 8,
type: JournalPageType.NORMAL,
equalsExample: {
type: "PORTFOLIO"
},
orderMap: {
createdAt: OrderType.DESC
}
},
list: this.data.list.concat(list),
isFinished: list.length < this.data.page.size
});
},
complete: () => {
this.setData({ this.setData({
isFinished: true,
isFetching: false isFetching: false
}); })
return;
} }
}); 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 thumbURL = `${config.url}/attachment/read/${thumbItem.mongoId}`;
const sourceURL = `${config.url}/attachment/read/${ext.sourceMongoId}`;
return {
type: ext.isVideo ? MediaItemType.VIDEO : MediaItemType.IMAGE,
thumbURL,
sourceURL,
size: thumbItem.size || 0,
attachmentId: thumbItem.id,
width: metadata.width,
height: metadata.height,
originalIndex: index
} as MediaItem;
});
journal.date = Time.toDate(journal.createdAt);
journal.time = Time.toTime(journal.createdAt);
journal.datetime = Time.toPassedDateTime(journal.createdAt);
journal.mediaItems = mediaItems;
journal.columnedItems = Toolkit.splitItemsIntoColumns(mediaItems, 3, (item) => {
if (item.width && item.height && 0 < item.width) {
return item.height / item.width;
}
return 1;
})
})
this.setData({
page: {
index: this.data.page.index + 1,
size: 8,
type: JournalPageType.NORMAL,
equalsExample: {
type: "PORTFOLIO"
},
orderMap: {
createdAt: OrderType.DESC
}
},
list: this.data.list.concat(list),
isFinished: list.length < this.data.page.size,
isFetching: false
});
} catch (error) {
console.error("加载 portfolio 列表失败:", error);
this.setData({ isFetching: false });
}
}, },
preview(e: WechatMiniprogram.BaseEvent) { preview(e: WechatMiniprogram.BaseEvent) {
const { journalIndex, itemIndex } = e.currentTarget.dataset; const { journalIndex, itemIndex } = e.currentTarget.dataset;

View File

@ -198,7 +198,7 @@ Page({
const sources = location.mediaItems.map(item => ({ const sources = location.mediaItems.map(item => ({
url: item.sourceURL, url: item.sourceURL,
type: MediaItemType[item.type].toLowerCase() type: item.type
})); }));
const total = sources.length; const total = sources.length;