Browse Source

订单

dev_des
1054425342@qq.com 4 weeks ago
parent
commit
d765a35a34
  1. 135
      components/WaterfallLayout.vue
  2. 6
      pages.json
  3. 18
      pages/index/iSoul.vue
  4. 194
      pages/index/timeShopBank.vue
  5. 262
      pages/notes/detail.vue
  6. 365
      pages/notes/publish.vue
  7. 2
      static/js/CommonFunction.js
  8. 4
      static/js/request.js
  9. 72
      subPackages/haveFeeling/detail.vue
  10. 1113
      subPackages/haveFeeling/detailAll.vue
  11. 37
      subPackages/haveFeeling/list.vue
  12. 479
      uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue

135
components/WaterfallLayout.vue

@ -1,24 +1,50 @@
<template>
<view class="waterfall-layout">
<view class="waterfall-container">
<!-- 空状态 -->
<view v-if="!leftItems.length && !rightItems.length" class="empty-state">
<text class="empty-title">暂无内容</text>
<text class="empty-desc">快来发布第一篇笔记吧</text>
</view>
<!-- 瀑布流内容 -->
<view v-else class="waterfall-container">
<!-- 左列 -->
<view class="column">
<view v-for="(item, index) in leftItems" :key="item.id || index" class="waterfall-item"
@click="handleItemClick(item)">
<image v-if="item.image" :src="showImg(item.image)" class="item-image" mode="aspectFill" />
<view
v-for="(item, index) in leftItems"
:key="item.id || index"
class="waterfall-item"
@click="handleItemClick(index, leftItems)"
>
<image
v-if="item.coverImage"
:src="item.coverImage && item.coverImage.split(',')[0]"
class="item-image"
mode="aspectFill"
/>
<view class="item-content">
<text v-if="item.title" class="item-title">{{ item.title }}</text>
<view class="item-footer">
<view class="user-info">
<image
src="https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?auto=format&fit=crop&w=100"
class="user-avatar" mode="aspectFill" />
<text class="username">风景之旅</text>
:src="item.headImg"
class="user-avatar"
mode="aspectFill"
/>
<text class="username">{{ item.nickname }}</text>
</view>
<view class="like-info">
<image :src="showImg('/uploads/20250731/0260884d7a44a483885a026da524e0b8.png')"
style="height: 22rpx;width: 25rpx;"></image>
<text class="like-count">100</text>
<image
v-if="!item.userLiked"
src="https://epic.js-dyyj.com/uploads/20250728/2f3ae212c01fa3b67be81abc5723cf5c.png"
style="height: 22rpx; width: 25rpx"
></image>
<image
v-else
src="https://epic.js-dyyj.com/uploads/20250728/dd7ed269b24e84a2dd141da6ab980fd6.png"
style="height: 22rpx; width: 25rpx"
></image>
<text class="like-count">{{ item.likeCount || 0 }}</text>
</view>
</view>
</view>
@ -27,22 +53,44 @@
<!-- 右列 -->
<view class="column">
<view v-for="(item, index) in rightItems" :key="item.id || index" class="waterfall-item"
@click="handleItemClick(item)">
<image v-if="item.image" :src="showImg(item.image)" class="item-image" mode="aspectFill" />
<view
v-for="(item, index) in rightItems"
:key="item.id || index"
class="waterfall-item"
@click="handleItemClick(index, rightItems)"
>
<image
v-if="item.coverImage"
:src="item.coverImage && item.coverImage.split(',')[0]"
class="item-image"
mode="aspectFill"
/>
<view class="item-content">
<text v-if="item.title" class="item-title">{{ item.title }}</text>
<view class="item-footer">
<view class="user-info">
<image
src="https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?auto=format&fit=crop&w=100"
class="user-avatar" mode="aspectFill" />
<text class="username">风景之旅</text>
:src="item.headImg"
class="user-avatar"
mode="aspectFill"
/>
<text class="username"
>{{ item.nickname }}{{ item.nickname
}}{{ item.nickname }}</text
>
</view>
<view class="like-info">
<image :src="showImg('/uploads/20250731/0260884d7a44a483885a026da524e0b8.png')"
style="height: 22rpx;width: 25rpx;"></image>
<text class="like-count">120</text>
<image
v-if="!item.userLiked"
src="https://epic.js-dyyj.com/uploads/20250728/2f3ae212c01fa3b67be81abc5723cf5c.png"
style="height: 22rpx; width: 25rpx"
></image>
<image
v-else
src="https://epic.js-dyyj.com/uploads/20250728/dd7ed269b24e84a2dd141da6ab980fd6.png"
style="height: 22rpx; width: 25rpx"
></image>
<text class="like-count">{{ item.likeCount || 0 }}</text>
</view>
</view>
</view>
@ -101,9 +149,12 @@
if (!columnRef) return 0;
const query = uni.createSelectorQuery().in(this);
return new Promise((resolve) => {
query.select(columnRef).boundingClientRect((data) => {
query
.select(columnRef)
.boundingClientRect((data) => {
resolve(data ? data.height : 0);
}).exec();
})
.exec();
});
},
@ -143,8 +194,8 @@
},
//
handleItemClick(item) {
this.$emit("item-click", item);
handleItemClick(index, list) {
this.$emit("item-click", list[index]);
},
//
@ -155,7 +206,7 @@
//
removeItem(itemId) {
//
let index = this.leftItems.findIndex(item => item.id === itemId);
let index = this.leftItems.findIndex((item) => item.id === itemId);
if (index !== -1) {
this.leftItems.splice(index, 1);
this.$emit("item-removed", itemId);
@ -163,7 +214,7 @@
}
//
index = this.rightItems.findIndex(item => item.id === itemId);
index = this.rightItems.findIndex((item) => item.id === itemId);
if (index !== -1) {
this.rightItems.splice(index, 1);
this.$emit("item-removed", itemId);
@ -179,6 +230,36 @@
box-sizing: border-box;
}
/* 空状态样式 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 160rpx 40rpx;
text-align: center;
}
.empty-icon {
width: 240rpx;
height: 240rpx;
margin-bottom: 40rpx;
opacity: 0.6;
}
.empty-title {
font-size: 32rpx;
color: #666;
margin-bottom: 16rpx;
font-weight: 500;
}
.empty-desc {
font-size: 28rpx;
color: #999;
line-height: 1.4;
}
.waterfall-container {
display: flex;
gap: 16rpx;
@ -279,6 +360,10 @@
.username {
font-size: 22rpx;
color: #666;
width: 160rpx;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.like-info {

6
pages.json

@ -272,6 +272,12 @@
"navigationBarTitleText": "订单详情"
}
},
{
"path": "haveFeeling/detailAll",
"style": {
"navigationBarTitleText": "订单详情"
}
},
{
"path": "haveFeeling/logistics",
"style": {

18
pages/index/iSoul.vue

@ -234,6 +234,7 @@
</view>
<view class="goods-card">
<image
@click="gotoPath('/subPackages/haveFeeling/list')"
:src="
showImg('/uploads/20250729/a010feb51e3f195563fb440f9235cc8b.png')
"
@ -716,19 +717,14 @@ export default {
break;
case "待发货":
uni.navigateTo({
url: "/subPackages/order/trades?status=POST",
});
break;
case "退货/退款":
uni.navigateTo({
url: "/subPackages/order/trades?status=WAIT_REFUND",
});
break;
case "评价":
uni.navigateTo({
url: "/subPackages/order/trades?status=WAIT_COMMENT",
url: "/subPackages/haveFeeling/list?status=2",
});
break;
default:
uni.showToast({
title:'功能暂未开放',
icon:'none'
})
}
},
//

194
pages/index/timeShopBank.vue

@ -10,21 +10,20 @@
:style-type="'timeShop'"
/>
<view class="desc-box">
<view class="">
欢迎来到旅行时间行你的精神财富储蓄所
</view>
<view class=""> 欢迎来到旅行时间行你的精神财富储蓄所 </view>
<view class="">
在这里你的每一次人文漫游每一次灵感闪现都值得被郑重记录我们鼓励你分享高质量的图文笔记将旅途中的美与感动化为这座精神星球上的璀璨星辰
</view>
<view class="">
为他人的美好驻足点赞留言每一次真诚的互动都是在为你的时间银行存入一笔宝贵的精神货币这些资产不仅可以兑换独家福利与实体好物更能为你解锁专属的荣誉身份让你成为这座星球上最闪耀的共创者
</view>
<view class="">
即刻发布你的第一篇笔记开启你的财富积累之旅吧
</view>
<view class=""> 即刻发布你的第一篇笔记开启你的财富积累之旅吧 </view>
</view>
<image style="width: 700rpx;height: 14rpx;margin: 20rpx auto;display: block;" :src="showImg('/uploads/20250829/f7214bc2a4f4e236561de893ca7b9113.png')"></image>
<image
style="width: 700rpx; height: 14rpx; margin: 20rpx auto; display: block"
:src="showImg('/uploads/20250829/f7214bc2a4f4e236561de893ca7b9113.png')"
></image>
<!-- Tab切换组件 -->
<view class="tab-container">
@ -75,6 +74,7 @@
<view v-if="currentTab == 2" class="notes-content">
<view class="fab-container">
<image
@click="goToPublish"
:src="
showImg('/uploads/20250825/7ea7864b8abb89c3dd7834f025e49b3f.png')
"
@ -126,94 +126,29 @@ export default {
},
data() {
return {
currentTab: 0, // ""
currentTab: 2, // ""
tabs: [
{ name: "笔记", id: "notes" },
{ name: "关注", id: "follow" },
{ name: "推荐", id: "recommend" },
],
waterfallItems: [
{
title: "生命的扶持|风景之旅",
image: "/uploads/20250824/2cf1f49920b911c9d14e4abf3b67a59c.png",
},
{
title: "苏州丨园林之美 鱼戏莲叶间心随鱼鸟闲",
image: "/uploads/20250824/c209044a821630158f6e6771805682a7.png",
},
{
title: "蘇州|你一句春不晚,我便出现在真江南",
image: "/uploads/20250824/74101a77233375625282209392dc69e3.png",
},
{
title: "收好这天然氧吧",
image: "/uploads/20250824/e0ab7fd0483d05742451e8f10ab3ce24.png",
},
],
waterfallItems: [],
//
pageNum: 1,
pageSize: 10,
loading: false,
hasMore: true,
autoAddEnabled: false,
// URL
images: [
"/uploads/20250824/2cf1f49920b911c9d14e4abf3b67a59c.png",
"https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?auto=format&fit=crop&w=800",
"https://images.unsplash.com/photo-1441974231531-c6227db76b6e?auto=format&fit=crop&w=800",
"https://images.unsplash.com/photo-1469474968028-56623f02e42e?auto=format&fit=crop&w=800",
"https://images.unsplash.com/photo-1418065460487-3e41a6c84dc5?auto=format&fit=crop&w=800",
],
//
avatars: [
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?auto=format&fit=crop&w=100",
"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?auto=format&fit=crop&w=100",
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?auto=format&fit=crop&w=100",
"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?auto=format&fit=crop&w=100",
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?auto=format&fit=crop&w=100",
],
//
usernames: [
"杨璐摄影",
"旅行者小王",
"风景猎人",
"自然探索者",
"摄影师阿明",
],
//
shortTitles: ["城市风光", "自然美景", "湖光山色", "森林小径", "日出东方"],
mediumTitles: [
"繁华都市的夜景与灯光",
"壮丽的山脉与蓝天白云",
"宁静的湖泊与秋日树木",
"蜿蜒的小路穿过金色森林",
"清晨第一缕阳光照亮大地",
],
longTitles: [
"现代都市天际线在黄昏时分展现出迷人的轮廓,灯火辉煌的建筑倒映在水面上",
"雄伟的山脉在云雾缭绕中若隐若现,展现出大自然的壮丽与神秘",
"平静如镜的湖面倒映着五彩斑斓的秋叶,构成一幅完美的自然画卷",
"阳光透过茂密的树叶,在铺满落叶的林间小路上投下斑驳的光影",
"清晨的太阳从地平线缓缓升起,金色的光芒洒满整个大地,带来新的一天",
],
descriptions: [
"探索城市中隐藏的美丽角落,发现不为人知的风景",
"大自然的鬼斧神工创造了无数令人惊叹的景观",
"水与山的完美结合,创造出宁静致远的意境",
"漫步在森林中,感受大自然的清新与宁静",
"日出时分的美景总能带给人们希望和力量",
],
tagsList: [
["城市", "摄影", "旅行"],
["自然", "山脉", "探险"],
["湖泊", "秋天", "风景"],
["森林", "小路", "自然"],
["日出", "早晨", "美景"],
],
};
},
onLoad() {
this.initializeData();
this.userInfo =
(uni.getStorageSync("userInfo") &&
JSON.parse(uni.getStorageSync("userInfo"))) ||
this.$store.state.user.userInfo ||
{};
//
this.getRecommendList(1);
},
onShow() {
if (this.userInfo && this.userInfo.id) {
@ -242,50 +177,46 @@ export default {
});
});
},
//
getRandomItem() {
const titleType =
Math.random() > 0.5
? Math.random() > 0.5
? this.longTitles
: this.mediumTitles
: this.shortTitles;
return {
id: Date.now() + Math.floor(Math.random() * 1000),
title: titleType[Math.floor(Math.random() * titleType.length)],
image: this.images[Math.floor(Math.random() * this.images.length)],
// description:
// this.descriptions[
// Math.floor(Math.random() * this.descriptions.length)
// ],
// tags: this.tagsList[Math.floor(Math.random() * this.tagsList.length)],
//
user: {
avatar: this.avatars[Math.floor(Math.random() * this.avatars.length)],
name: this.usernames[
Math.floor(Math.random() * this.usernames.length)
],
},
likes: Math.floor(Math.random() * 999) + 1, // 1-999
//
getRecommendList(type = 1) {
if (this.loading) return;
this.loading = true;
const params = {
pageNum: this.pageNum,
pageSize: this.pageSize,
type: type, // 0: tab, 1: tab
};
},
//
initializeData() {
const initialItems = [];
for (let i = 0; i < 20; i++) {
initialItems.push(this.getRandomItem());
this.Post(params, "/framework/note/list", "DES")
.then((res) => {
if (res.code === 200 && res.rows) {
const newItems = res.rows || [];
if (this.pageNum === 1) {
//
this.waterfallItems = newItems;
} else {
//
this.waterfallItems.push(...newItems);
}
// this.waterfallItems = initialItems;
},
//
addRandomItem() {
const newItem = this.getRandomItem();
// this.waterfallItems.push(newItem);
//
this.hasMore = newItems.length === this.pageSize;
}
})
.catch((error) => {
console.error("获取推荐列表失败:", error);
uni.showToast({
title: "加载失败,请重试",
icon: "none",
});
})
.finally(() => {
this.loading = false;
});
},
//
clearAllItems() {
uni.showModal({
@ -301,9 +232,10 @@ export default {
//
handleItemClick(item) {
console.log(item);
//
uni.navigateTo({
url: `/pages/notes/detail`,
url: `/pages/notes/detail?id=${item.id}`,
});
},
@ -319,16 +251,17 @@ export default {
//
handleAutoAddRequest() {
this.addRandomItem();
this.loadMoreItems();
},
//
loadMoreItems() {
const newItems = [];
for (let i = 0; i < 10; i++) {
newItems.push(this.getRandomItem());
if (!this.loading && this.hasMore) {
this.pageNum++;
// tabtype: 0-, 1-
const type = this.currentTab === 0 ? 0 : 1;
this.getRecommendList(type);
}
// this.waterfallItems.push(...newItems);
},
// Tab
@ -341,8 +274,14 @@ export default {
// tab
loadTabContent(tabId) {
// tabId
//
this.initializeData();
if (tabId === "notes") {
// tab:
this.pageNum = 1;
this.waterfallItems = [];
this.hasMore = true;
this.getRecommendList(0);
}
// tab
},
},
};
@ -498,6 +437,7 @@ page {
.content-area {
flex: 1;
overflow: hidden;
padding: 10rpx 0;
}
.notes-content {

262
pages/notes/detail.vue

@ -14,11 +14,7 @@
@change="swiperChange"
>
<swiper-item v-for="(item, index) in topBanner" :key="index">
<image
class="top-banner"
:src="showImg(item)"
mode="aspectFill"
></image>
<image class="top-banner" :src="item" mode="aspectFill" @click="previewImage(item)"></image>
</swiper-item>
</swiper>
@ -31,27 +27,25 @@
</view>
</view>
<!-- 内容卡片 -->
<view class="content-card">
<!-- 作者信息 -->
<view class="author-section">
<image
class="author-avatar"
:src="showImg(noteDetail.user.avatar)"
:src="noteDetail.headImg"
mode="aspectFill"
/>
<view class="author-info">
<text class="author-name">{{ noteDetail.user.name }}</text>
<text class="author-name">{{ noteDetail.nickname }}</text>
</view>
<view
<!-- <view
class="follow-btn"
:class="{ followed: noteDetail.user.isFollowed }"
@click="toggleFollow"
>
{{ noteDetail.user.isFollowed ? "已关注" : "关注" }}
</view>
</view> -->
</view>
<!-- 笔记标题 -->
<view class="note-title">
@ -70,13 +64,29 @@
<!-- 笔记内容 -->
<view class="note-content">
<text class="content-text">{{ noteDetail.content }}</text>
<view class="content-text">{{ noteDetail.content }}</view>
<!-- 标签名称 -->
<view class="tag-names" v-if="noteDetail.tagNames">
<text
v-for="(tag, index) in noteDetail.tagNames"
:key="index"
class="tag-link"
@click="handleTagClick(tag)"
>#{{ tag }}</text
>
</view>
</view>
<view class="" style="width: auto;height: 1rpx;background-color: #999999;margin: 60rpx 32rpx;">
</view>
<view
class=""
style="
width: auto;
height: 1rpx;
background-color: #999999;
margin: 60rpx 32rpx;
"
>
</view>
<!-- 评论区域 -->
<view class="comments-section">
<view class="comments-header">
@ -127,15 +137,25 @@
>
发送
</button>
<view class="like-section">
<image class="like-icon" mode="widthFix" :src="showImg('/uploads/20250826/3f4c0ccaaaccbef50bf8c5c27ff6a87b.png')"></image>
<!-- <text
<view class="like-section" @click="toggleLike">
<image
class="like-icon"
:class="{ liked: noteDetail.isLiked }"
@click="toggleLike"
></text
> -->
<text class="like-count">{{ noteDetail.likes }}</text>
mode="widthFix"
:src="
!noteDetail.userLiked
? showImg(
'https://epic.js-dyyj.com/uploads/20250728/2f3ae212c01fa3b67be81abc5723cf5c.png'
)
: showImg(
'https://epic.js-dyyj.com/uploads/20250728/dd7ed269b24e84a2dd141da6ab980fd6.png'
)
"
></image>
<text
class="like-count"
:class="{ 'liked-text': noteDetail.userLiked }"
>{{ noteDetail.likeCount }}</text
>
</view>
</view>
</view>
@ -154,7 +174,7 @@ export default {
return {
noteId: "",
commentText: "",
topBanner: ["/uploads/20250826/17356ed4dcdeedcd13de60ca28909d22.png"],
topBanner: [],
swiperIndex: 0,
noteDetail: {
id: "",
@ -165,12 +185,8 @@ export default {
likes: 0,
isLiked: false,
createTime: "",
user: {
id: "",
name: "",
avatar: "",
isFollowed: false,
},
tagNames: [],
comments: [],
},
};
@ -180,7 +196,13 @@ export default {
this.noteId = options.id;
this.loadNoteDetail();
} else {
this.loadMockData();
uni.showToast({
title: "笔记ID不存在",
icon: "none",
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
},
methods: {
@ -191,9 +213,24 @@ export default {
async loadNoteDetail() {
try {
uni.showLoading({ title: "加载中..." });
// API
const res = await this.getNoteDetail(this.noteId);
// API
const res = await this.Post(
{ noteId: this.noteId },
"/framework/note/getInfo/" + this.noteId,
"DES"
);
if (res.code === 200) {
this.noteDetail = res.data;
this.noteDetail.tagNames = this.noteDetail.tagNames.split(",");
console.log(this.noteDetail.tagNames);
//
this.topBanner = this.noteDetail.coverImage.split(",");
} else {
uni.showToast({
title: res.msg || "加载失败",
icon: "none",
});
}
} catch (error) {
console.error("加载笔记详情失败:", error);
uni.showToast({
@ -232,8 +269,7 @@ export default {
user: {
id: "user002",
name: "@小新",
avatar:
"/uploads/20250826/d68433653e8b8cceba9bc4a6ab2a394d.png",
avatar: "/uploads/20250826/d68433653e8b8cceba9bc4a6ab2a394d.png",
},
},
{
@ -244,8 +280,7 @@ export default {
user: {
id: "user003",
name: "Mr.曾",
avatar:
"/uploads/20250826/d68433653e8b8cceba9bc4a6ab2a394d.png",
avatar: "/uploads/20250826/d68433653e8b8cceba9bc4a6ab2a394d.png",
},
},
],
@ -255,24 +290,82 @@ export default {
//
previewImage(imageUrl) {
uni.previewImage({
urls: [imageUrl],
urls: this.topBanner,
current: imageUrl,
});
},
//
toggleFollow() {
async toggleFollow() {
try {
const action = this.noteDetail.user.isFollowed ? "cancel" : "follow";
const res = await this.Post(
{
method: "POST",
userId: this.noteDetail.user.id,
action: action,
},
"/framework/user/follow",
"DES"
);
if (res.code === 200) {
this.noteDetail.user.isFollowed = !this.noteDetail.user.isFollowed;
uni.showToast({
title: this.noteDetail.user.isFollowed ? "已关注" : "取消关注",
icon: "success",
});
} else {
uni.showToast({
title:
res.msg || (action === "follow" ? "关注失败" : "取消关注失败"),
icon: "none",
});
}
} catch (error) {
console.error("关注操作失败:", error);
uni.showToast({
title: "操作失败",
icon: "none",
});
}
},
//
toggleLike() {
async toggleLike() {
try {
const action = this.noteDetail.isLiked ? "cancel" : "like";
const res = await this.Post(
{
method: "POST",
noteId: this.noteId,
action: action,
},
"/framework/note/like",
"DES"
);
if (res.code === 200) {
this.noteDetail.isLiked = !this.noteDetail.isLiked;
this.noteDetail.likes += this.noteDetail.isLiked ? 1 : -1;
//
if (res.data && res.data.likeCount !== undefined) {
this.noteDetail.likes = res.data.likeCount;
}
} else {
uni.showToast({
title: res.msg || (action === "like" ? "点赞失败" : "取消点赞失败"),
icon: "none",
});
}
} catch (error) {
console.error("点赞操作失败:", error);
uni.showToast({
title: "操作失败",
icon: "none",
});
}
},
//
@ -281,25 +374,42 @@ export default {
return;
}
const newComment = {
id: "comment" + Date.now(),
try {
uni.showLoading({ title: "提交中..." });
const res = await this.Post(
{
method: "POST",
noteId: this.noteId,
content: this.commentText,
createTime: new Date().toISOString(),
user: {
id: "current_user",
name: "我",
avatar:
"https://des.dayunyuanjian.cn/epicSoul/avatar-current.png",
},
};
"/framework/note/comment/add",
"DES"
);
this.noteDetail.comments.unshift(newComment);
if (res.code === 200) {
//
this.loadNoteDetail();
this.commentText = "";
uni.showToast({
title: "评论成功",
icon: "success",
});
} else {
uni.showToast({
title: res.msg || "评论失败",
icon: "none",
});
}
} catch (error) {
console.error("提交评论失败:", error);
uni.showToast({
title: "评论失败",
icon: "none",
});
} finally {
uni.hideLoading();
}
},
//
@ -311,17 +421,29 @@ export default {
return `${year}/${month}/${day}`;
},
// API -
async getNoteDetail(noteId) {
return new Promise((resolve) => {
setTimeout(() => {
this.loadMockData();
resolve({
code: 200,
data: this.noteDetail,
});
}, 100);
});
//
showImg(img) {
if (!img) return "/static/image/default-avatar.png";
if (img.indexOf("https://") !== -1 || img.indexOf("http://") !== -1) {
return img;
} else {
return this.NEWAPIURLIMG + img;
}
},
//
handleTagClick(tag) {
console.log("标签点击:", tag);
//
// uni.showToast({
// title: `: ${tag}`,
// icon: "none",
// });
// 使
// uni.navigateTo({
// url: `/pages/tags/detail?tag=${encodeURIComponent(tag)}`
// });
},
},
};
@ -434,6 +556,20 @@ padding: 12rpx 30rpx;
line-height: 1.6;
color: #333;
font-weight: 500;
word-break: break-all;
}
.tag-names {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
margin-top: 24rpx;
.tag-link {
font-size: 28rpx;
color: #0066cc;
font-weight: 500;
}
}
}
@ -553,6 +689,10 @@ padding: 12rpx 30rpx;
font-size: 30rpx;
color: #666;
font-weight: bold;
&.liked-text {
color: #ff4757;
}
}
}
}

365
pages/notes/publish.vue

@ -4,32 +4,20 @@
<view class="content-scroll">
<!-- 图片区域 -->
<view class="image-section">
<view class="image-grid">
<!-- 已选图片 -->
<view
class="image-item"
v-for="(image, index) in selectedImages"
:key="index"
>
<image class="image-preview" :src="image" mode="aspectFill" />
<view class="image-delete" @click="removeImage(index)">
<text class="delete-icon">×</text>
</view>
</view>
<!-- 添加图片按钮 -->
<view
class="add-image-btn"
@click="chooseImage"
v-if="selectedImages.length < 9"
>
<text class="add-icon">+</text>
</view>
</view>
<uni-file-picker
v-model="selectedImages"
mode="grid"
file-mediatype="image"
file-extname="png,jpg,jpeg"
:auto-upload="false"
@select="onImageSelect"
@delete="onImageDelete"
></uni-file-picker>
</view>
<!-- 标题区域 -->
<view class="title-section">
<textarea
<input
class="title-input"
v-model="noteForm.title"
placeholder="请输入标题..."
@ -51,6 +39,12 @@
<!-- 快速标签区域 -->
<view class="quick-tags-section">
<view class="section-title">快速标签</view>
<scroll-view
class="quick-tags-scroll"
scroll-x="true"
show-scrollbar="false"
>
<view class="quick-tags-list">
<view
class="quick-tag-item"
@ -58,13 +52,19 @@
:key="tag"
@click="insertQuickTag(tag)"
>
#{{ tag }}
#{{ tag.name }}
</view>
</view>
</scroll-view>
</view>
<!-- 已添加标签 -->
<view class="selected-tags-section" v-if="noteForm.tags.length">
<scroll-view
class="selected-tags-scroll"
scroll-x
:show-scrollbar="false"
>
<view class="selected-tags-list">
<view
class="selected-tag-item"
@ -72,10 +72,11 @@
:key="index"
@click="removeTag(index)"
>
<text class="tag-text">#{{ tag }}</text>
<text class="tag-close">×</text>
<view class="tag-text">#{{ tag.name }}</view>
<view class="tag-close">×</view>
</view>
</view>
</scroll-view>
</view>
<!-- 底部占位 -->
@ -84,9 +85,7 @@
<!-- 底部发布按钮 -->
<view class="bottom-publish">
<button class="publish-btn" @click="publishNote" :disabled="!canPublish">
发布笔记
</button>
<button class="publish-btn" @click="publishNote">发布笔记</button>
</view>
</view>
</template>
@ -100,29 +99,48 @@ export default {
noteForm: {
title: "",
content: "",
tags: [],
tags: [], // {id, name}
images: [],
},
quickTags: [
"阅读体验",
"时间力",
"读书笔记",
"生活感悟",
"学习心得",
"思考",
],
quickTags: [], //
imageStyles: {
height: "200rpx", //
width: "200rpx", //
},
};
},
computed: {
canPublish() {
return (
this.noteForm.title.trim() ||
this.noteForm.content.trim() ||
(this.noteForm.title.trim() || this.noteForm.content.trim()) &&
this.selectedImages &&
this.selectedImages.length > 0
);
},
},
mounted() {
this.getTagList();
},
methods: {
//
async getTagList() {
try {
const res = await this.Post({}, "/framework/tag/list", "DES");
if (res && res.data) {
this.quickTags = res.data;
}
} catch (error) {
console.error("获取标签列表失败:", error);
//
this.quickTags = [
{ id: "1", name: "DES" },
{ id: "2", name: "AGENT" },
{ id: "3", name: "时间银行" },
{ id: "4", name: "阅读体验" },
{ id: "5", name: "时间力" },
];
}
},
//
goBack() {
if (this.canPublish) {
@ -140,33 +158,29 @@ export default {
}
},
//
chooseImage() {
const remainingCount = 9 - this.selectedImages.length;
uni.chooseImage({
count: remainingCount,
sizeType: ["original", "compressed"],
sourceType: ["album", "camera"],
success: (res) => {
this.selectedImages.push(...res.tempFilePaths);
this.noteForm.images = [...this.selectedImages];
},
fail: (err) => {
console.error("选择图片失败:", err);
},
});
//
onImageSelect(e) {
console.log("选择图片·:", e);
// selectedImagesv-model
this.uploadNewImages(e.tempFiles || []);
},
//
removeImage(index) {
this.selectedImages.splice(index, 1);
this.noteForm.images = [...this.selectedImages];
//
onImageDelete(e) {
let index = e.index;
// selectedImagesv-modelnoteForm.images
// selectedImagesnoteForm.images
if (this.noteForm.images && this.noteForm.images.length > index) {
this.noteForm.images.splice(index, 1);
}
},
//
insertQuickTag(tag) {
if (!this.noteForm.tags.includes(tag)) {
this.noteForm.tags.push(tag);
//
const existingTag = this.noteForm.tags.find((t) => t.id === tag.id);
if (!existingTag) {
this.noteForm.tags.unshift(tag);
}
},
@ -175,11 +189,100 @@ export default {
this.noteForm.tags.splice(index, 1);
},
//
async uploadSingleImage(file) {
return new Promise((resolve, reject) => {
const token = uni.getStorageSync("userInfo")
? JSON.parse(uni.getStorageSync("userInfo")).token
: "";
uni.uploadFile({
url: this.NEWAPIURL_DES + "/system/oss/upload", //
filePath: file.url || file.path,
name: "file",
header: {
token: token || "",
// token
},
success: (res) => {
console.log(res);
try {
const data = JSON.parse(res.data);
if (data.code === 200) {
resolve(data.data.url); // URL
} else {
reject(new Error(data.message || "上传失败"));
}
} catch (e) {
reject(new Error("解析响应失败"));
}
},
fail: (err) => {
reject(err);
},
});
});
},
//
async uploadNewImages(newFiles) {
if (!newFiles || newFiles.length === 0) {
return;
}
try {
uni.showLoading({ title: "上传图片中..." });
const uploadPromises = newFiles.map((file) =>
this.uploadSingleImage(file)
);
const uploadedUrls = await Promise.all(uploadPromises);
// URLnoteForm.images
this.noteForm.images = [
...(this.noteForm.images || []),
...uploadedUrls,
];
uni.hideLoading();
} catch (error) {
uni.hideLoading();
uni.showToast({
title: error.message || "图片上传失败",
icon: "none",
});
}
},
//
async uploadImages() {
if (!this.selectedImages || this.selectedImages.length === 0) {
return [];
}
const uploadPromises = this.selectedImages.map((file) =>
this.uploadSingleImage(file)
);
try {
const uploadedUrls = await Promise.all(uploadPromises);
return uploadedUrls;
} catch (error) {
throw new Error("图片上传失败: " + error.message);
}
},
//
async publishNote() {
if (!this.canPublish) {
console.log(this.noteForm, "0000");
if (!this.noteForm.images || this.noteForm.images.length === 0) {
uni.showToast({
title: "请添加内容",
title: "请至少选择一张图片",
icon: "none",
});
return;
}
if (!this.noteForm.title.trim() && !this.noteForm.content.trim()) {
uni.showToast({
title: "请添加标题或内容",
icon: "none",
});
return;
@ -187,45 +290,41 @@ export default {
try {
uni.showLoading({ title: "发布中..." });
// API
//
await this.submitNote(this.noteForm);
uni.hideLoading();
uni.showToast({
title: "发布成功",
icon: "success",
icon: "none",
duration: 2000,
});
//
setTimeout(() => {
uni.navigateBack();
}, 1500);
}, 800);
} catch (error) {
uni.hideLoading();
uni.showToast({
title: "发布失败,请重试",
title: error.message || "发布失败,请重试",
icon: "none",
});
}
},
// API
//
async submitNote(noteData) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("发布笔记数据:", noteData);
resolve({
code: 200,
message: "发布成功",
data: {
id: "note_" + Date.now(),
...noteData,
},
});
}, 1500);
});
// API
const formData = {
title: noteData.title,
content: noteData.content,
tags: noteData.tags.map((tag) => tag.id).join(","), // ID
coverImage: noteData.images.join(","), // URL
method: "POST",
};
return this.Post(formData, "/framework/note/addNote", "DES");
},
},
};
@ -243,7 +342,6 @@ export default {
.content-scroll {
flex: 1;
padding: 0 30rpx;
width: 690rpx;
}
//
@ -251,59 +349,6 @@ export default {
margin: 32rpx 0;
padding-bottom: 32rpx;
border-bottom: 1rpx solid #f0f0f0;
.image-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16rpx;
}
.image-item {
position: relative;
width: 100%;
aspect-ratio: 1;
border-radius: 16rpx;
overflow: hidden;
.image-preview {
width: 100%;
height: 100%;
}
.image-delete {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 48rpx;
height: 48rpx;
background: rgba(0, 0, 0, 0.6);
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
.delete-icon {
font-size: 32rpx;
color: #fff;
}
}
}
.add-image-btn {
width: 100%;
aspect-ratio: 1;
border: 2rpx dashed #ddd;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
background: #fafafa;
.add-icon {
font-size: 48rpx;
color: #999;
}
}
}
//
@ -334,7 +379,7 @@ export default {
.content-input {
width: 100%;
min-height: 300rpx;
font-size: 32rpx;
font-size: 28rpx;
line-height: 1.6;
color: #333;
border: none;
@ -347,20 +392,33 @@ export default {
.quick-tags-section {
margin: 24rpx 0;
.section-title {
font-size: 28rpx;
color: #666;
margin-bottom: 25rpx;
}
.quick-tags-scroll {
width: 100%;
white-space: nowrap;
}
.quick-tags-list {
display: flex;
flex-wrap: wrap;
display: inline-flex;
gap: 16rpx;
padding-right: 32rpx;
width: max-content;
.quick-tag-item {
flex-shrink: 0;
padding: 16rpx 24rpx;
background: #f8f9fa;
border: 1rpx solid #e0e0e0;
border-radius: 32rpx;
font-size: 28rpx;
color: #666;
cursor: pointer;
transition: all 0.3s ease;
white-space: nowrap;
&:active {
background: #e9ecef;
@ -376,28 +434,36 @@ export default {
padding-top: 24rpx;
border-top: 1rpx solid #f0f0f0;
.selected-tags-scroll {
width: 100%;
white-space: nowrap;
}
.selected-tags-list {
display: flex;
flex-wrap: wrap;
display: inline-flex;
gap: 16rpx;
padding-right: 32rpx;
width: max-content;
.selected-tag-item {
flex-shrink: 0;
display: flex;
align-items: center;
gap: 8rpx;
padding: 12rpx 20rpx;
background: #ff4757;
background: linear-gradient(135deg, #fffdb7e6 0%, #97fffab5 100%);
border-radius: 32rpx;
cursor: pointer;
white-space: nowrap;
.tag-text {
font-size: 26rpx;
color: #fff;
color: #333;
}
.tag-close {
font-size: 24rpx;
color: #fff;
color: #333;
opacity: 0.8;
margin-left: 8rpx;
}
@ -410,7 +476,7 @@ export default {
padding: 24rpx 32rpx;
background: #fff;
border-top: 1rpx solid #f0f0f0;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
padding-bottom: max(24rpx, env(safe-area-inset-bottom));
position: fixed;
bottom: 0;
left: 0;
@ -419,16 +485,15 @@ export default {
.publish-btn {
width: 100%;
height: 88rpx;
background: #ff4757;
color: #fff;
color: #333333;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
border: none;
transition: all 0.3s ease;
background: linear-gradient(135deg, #fffdb7e6 0%, #97fffab5 100%);
&:disabled {
background: #ccc;
background: linear-gradient(135deg, #ccc 0%, #ccc 100%);
}
&:not(:disabled):active {
@ -438,7 +503,7 @@ export default {
}
.bottom-placeholder {
height: 140rpx;
height: 180rpx;
padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
}

2
static/js/CommonFunction.js

@ -356,7 +356,7 @@ Vue.prototype.getSubscribeMessage = () => {
tmplIds: templateIds,
complete(res) {
uni.navigateTo({
url: '/subPackages/order/trades'
url: '/subPackages/haveFeeling/list'
})
}
})

4
static/js/request.js

@ -8,9 +8,9 @@ const DEV_API_URL = 'http://1.13.193.49';
// const PROD_API_URL = 'https://epic.js-dyyj.com';
const PROD_API_URL = 'http://1.13.193.49';
const NEWAPIURL = process.env.NODE_ENV === 'development' ? DEV_API_URL : PROD_API_URL;
const DEV_API_URL_DES = 'https://des.dayunyuanjian.cn/xcx';
// const DEV_API_URL_DES = 'https://des.dayunyuanjian.cn/xcx';
// const DEV_API_URL_DES = 'http://1.13.193.49:8083/xcx';
// const DEV_API_URL_DES = 'https://des.dayunyuanjian.cn/xcx';
const DEV_API_URL_DES = 'http://192.168.124.8:8083/xcx';
// const PROD_API_URL_DES = 'https://des.dayunyuanjian.cn/xcx';
const PROD_API_URL_DES = 'https://des.dayunyuanjian.cn/xcx';
const NEWAPIURL_DES = process.env.NODE_ENV === 'development' ? DEV_API_URL_DES : PROD_API_URL_DES;

72
subPackages/haveFeeling/detail.vue

@ -67,7 +67,7 @@
>
</view>
<view class="summary-row">
<text class="summary-label">应付金额</text>
<text class="summary-label">金额</text>
<text class="summary-value"
>¥{{ orderDetail.totalAmount || "0.00" }}</text
>
@ -79,7 +79,7 @@
>
</view>
<view class="summary-row total-row">
<text class="summary-label">付金额</text>
<text class="summary-label">付金额</text>
<text class="summary-value total-amount"
>¥{{ orderDetail.payAmount || "0.00" }}</text
>
@ -159,6 +159,13 @@
>
去支付
</button>
<button
class="action-btn-bottom cancel-btn"
@click="handleOrderAction('cancel')"
v-if="orderDetail.status === 0"
>
取消订单
</button>
<!-- <button
class="action-btn-bottom"
@click="handleOrderAction('logistics')"
@ -370,7 +377,52 @@ export default {
case "aftersale":
this.applyAfterSale();
break;
case "cancel":
this.cancelOrder();
break;
}
},
//
cancelOrder() {
uni.showModal({
title: "取消订单",
content: "确定要取消该订单吗?",
success: (res) => {
if (res.confirm) {
this.cancelOrderApi();
}
},
});
},
// API
cancelOrderApi() {
uni.showLoading({
title: "取消中...",
});
this.Post(
{ parentOrderNo: this.orderDetail.orderNo },
`/framework/ygOrder/cancel`,
"DES"
).then((res) => {
uni.hideLoading();
if (res.code == 200) {
uni.showToast({
title: "订单已取消",
icon: "success",
});
setTimeout(() => {
this.loadOrderDetail();
}, 800);
} else {
uni.showToast({
title: res.msg || "取消订单失败",
icon: "none",
});
}
});
},
//
@ -393,7 +445,16 @@ export default {
signType: res.data.wxInfo.signType,
timeStamp: res.data.wxInfo.timeStamp,
success: () => {
this.loadOrderDetail();
// this.loadOrderDetail();
uni.showToast({
title: "支付成功",
icon: "success",
});
setTimeout(() => {
uni.navigateBack({
delta: 1,
});
}, 800);
},
fail() {
// uni.navigateTo({
@ -876,6 +937,11 @@ export default {
color: #ffffff;
}
&.cancel-btn {
background-color: #ff3b30;
color: #ffffff;
}
&.contact-btn {
background-color: #007aff;
color: #ffffff;

1113
subPackages/haveFeeling/detailAll.vue

File diff suppressed because it is too large

37
subPackages/haveFeeling/list.vue

@ -25,16 +25,16 @@
<view
class="order-item"
v-for="order in orderList"
:key="order.id"
@click="goToOrderDetail(order)"
>
<!-- 订单头部 - 供应商信息 -->
<view class="order-header">
<view class="supplier-info">
<view class="supplier-name">{{
<view class="supplier-name" v-if="order.status==0||order.status==-1">有感商品订单</view>
<view class="supplier-name" v-else>{{
order.supplierName || "默认供应商"
}}</view>
<view class="order-number">订单号{{ order.orderNo }}</view>
<view class="order-number">订单号{{ ( order.status==0||order.status==-1)?order.parentOrderNo:order.orderNo }}</view>
</view>
<view class="order-status-wrapper">
<text class="status-name" :class="[getStatusClass(order.status)]">{{
@ -42,7 +42,6 @@
}}</text>
</view>
</view>
<!-- 商品列表 -->
<view class="goods-section">
<view class="goods-list">
@ -80,7 +79,7 @@
<view class="order-footer">
<view class="order-info" >
<view class="order-total-section">
<text class="total-label">实付</text>
<!-- <text class="total-label">{{(order.status==0)?'':'实付'}}</text> -->
<text class="order-total">¥{{ order.payAmount || 0 }}</text>
</view>
<view class="order-time">{{ order.createTime }}</view>
@ -222,15 +221,29 @@ export default {
pageSize: this.pageSize,
status: this.tabs[this.currentTab].status,
};
let url = ''
if(this.currentTab==0||this.currentTab==1){
url = "/framework/ygOrder/parent/pageList"
}else{
url = "/framework/ygOrder/pageList"
}
this.Post(
{
...params,
},
"/framework/ygOrder/pageList",
url,
"DES"
).then((res) => {
if (res.code == 200) {
if(this.currentTab==0||this.currentTab==1){
let orderItems = []
res.rows.forEach(item =>{
item.orderItems = []
item.orders.forEach(_item =>{
item.orderItems.push(..._item.orderItems)
})
})
}
this.orderList.push(...res.rows);
console.log(this.orderList);
this.hasMore = res.rows.length === this.pageSize;
@ -426,9 +439,17 @@ export default {
//
goToOrderDetail(order) {
console.log(order)
if(order.status==-1||order.status==0){
uni.navigateTo({
url: `/subPackages/haveFeeling/detail?id=${order.id}`,
url: `/subPackages/haveFeeling/detailAll?id=${order.orderNo}`,
});
}else{
uni.navigateTo({
url: `/subPackages/haveFeeling/detail?id=${order.orderNo}`,
});
}
},
},
};

479
uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue

@ -4,9 +4,18 @@
<text class="file-title">{{ title }}</text>
<text class="file-count">{{ filesList.length }}/{{ limitLength }}</text>
</view>
<upload-image v-if="fileMediatype === 'image' && showType === 'grid'" :readonly="readonly"
:image-styles="imageStyles" :files-list="filesList" :limit="limitLength" :disablePreview="disablePreview"
:delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
<upload-image
v-if="fileMediatype === 'image' && showType === 'grid'"
:readonly="readonly"
:image-styles="imageStyles"
:files-list="filesList"
:limit="limitLength"
:disablePreview="disablePreview"
:delIcon="delIcon"
@uploadFiles="uploadFiles"
@choose="choose"
@delFile="delFile"
>
<slot>
<view class="is-add">
<view class="icon-add"></view>
@ -14,9 +23,17 @@
</view>
</slot>
</upload-image>
<upload-file v-if="fileMediatype !== 'image' || showType !== 'grid'" :readonly="readonly"
:list-styles="listStyles" :files-list="filesList" :showType="showType" :delIcon="delIcon"
@uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
<upload-file
v-if="fileMediatype !== 'image' || showType !== 'grid'"
:readonly="readonly"
:list-styles="listStyles"
:files-list="filesList"
:showType="showType"
:delIcon="delIcon"
@uploadFiles="uploadFiles"
@choose="choose"
@delFile="delFile"
>
<slot><button type="primary" size="mini">选择文件</button></slot>
</upload-file>
</view>
@ -25,18 +42,18 @@
<script>
import {
chooseAndUploadFile,
uploadCloudFiles
} from './choose-and-upload-file.js'
uploadCloudFiles,
} from "./choose-and-upload-file.js";
import {
get_file_ext,
get_extname,
get_files_and_is_max,
get_file_info,
get_file_data
} from './utils.js'
import uploadImage from './upload-image.vue'
import uploadFile from './upload-file.vue'
let fileInput = null
get_file_data,
} from "./utils.js";
import uploadImage from "./upload-image.vue";
import uploadFile from "./upload-file.vue";
let fileInput = null;
/**
* FilePicker 文件选择上传
* @description 文件选择上传组件可以选择图片视频等任意文件并上传到当前绑定的服务空间
@ -79,22 +96,30 @@
* @event {Function} delete 文件从列表移除时触发
*/
export default {
name: 'uniFilePicker',
name: "uniFilePicker",
components: {
uploadImage,
uploadFile
uploadFile,
},
options: {
virtualHost: true
},
emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'input'],
virtualHost: true,
},
emits: [
"select",
"success",
"fail",
"progress",
"delete",
"update:modelValue",
"input",
],
props: {
// #ifdef VUE3
modelValue: {
type: [Array, Object],
default() {
return []
}
return [];
},
},
// #endif
@ -102,53 +127,53 @@
value: {
type: [Array, Object],
default() {
return []
}
return [];
},
},
// #endif
disabled: {
type: Boolean,
default: false
default: false,
},
disablePreview: {
type: Boolean,
default: false
default: false,
},
delIcon: {
type: Boolean,
default: true
default: true,
},
//
autoUpload: {
type: Boolean,
default: true
default: true,
},
// h5
limit: {
type: [Number, String],
default: 9
default: 9,
},
// grid | list | list-card
mode: {
type: String,
default: 'grid'
default: "grid",
},
// image/video/all
fileMediatype: {
type: String,
default: 'image'
default: "image",
},
//
fileExtname: {
type: [Array, String],
default() {
return []
}
return [];
},
},
title: {
type: String,
default: ''
default: "",
},
listStyles: {
type: Object,
@ -159,103 +184,103 @@
// 线
dividline: true,
// 线
borderStyle: {}
}
}
borderStyle: {},
};
},
},
imageStyles: {
type: Object,
default() {
return {
width: 'auto',
height: 'auto'
}
}
width: "auto",
height: "auto",
};
},
},
readonly: {
type: Boolean,
default: false
default: false,
},
returnType: {
type: String,
default: 'array'
default: "array",
},
sizeType: {
type: Array,
default() {
return ['original', 'compressed']
}
return ["original", "compressed"];
},
},
sourceType: {
type: Array,
default() {
return ['album', 'camera']
}
}
return ["album", "camera"];
},
},
},
data() {
return {
files: [],
localValue: []
}
localValue: [],
};
},
watch: {
// #ifndef VUE3
value: {
handler(newVal, oldVal) {
this.setValue(newVal, oldVal)
this.setValue(newVal, oldVal);
},
immediate: true
immediate: true,
},
// #endif
// #ifdef VUE3
modelValue: {
handler(newVal, oldVal) {
this.setValue(newVal, oldVal)
this.setValue(newVal, oldVal);
},
immediate: true
immediate: true,
},
// #endif
},
computed: {
filesList() {
let files = []
this.files.forEach(v => {
files.push(v)
})
return files
let files = [];
this.files.forEach((v) => {
files.push(v);
});
return files;
},
showType() {
if (this.fileMediatype === 'image') {
return this.mode
if (this.fileMediatype === "image") {
return this.mode;
}
return 'list'
return "list";
},
limitLength() {
if (this.returnType === 'object') {
return 1
if (this.returnType === "object") {
return 1;
}
if (!this.limit) {
return 1
return 1;
}
if (this.limit >= 9) {
return 9
}
return this.limit
return 9;
}
return this.limit;
},
},
created() {
// TODO
if (!(uniCloud.config && uniCloud.config.provider)) {
this.noSpace = true
uniCloud.chooseAndUploadFile = chooseAndUploadFile
this.noSpace = true;
uniCloud.chooseAndUploadFile = chooseAndUploadFile;
}
this.form = this.getForm('uniForms')
this.formItem = this.getForm('uniFormsItem')
this.form = this.getForm("uniForms");
this.formItem = this.getForm("uniFormsItem");
if (this.form && this.formItem) {
if (this.formItem.name) {
this.rename = this.formItem.name
this.form.inputChildrens.push(this)
this.rename = this.formItem.name;
this.form.inputChildrens.push(this);
}
}
},
@ -266,89 +291,91 @@
*/
clearFiles(index) {
if (index !== 0 && !index) {
this.files = []
this.files = [];
this.$nextTick(() => {
this.setEmit()
})
this.setEmit();
});
} else {
this.files.splice(index, 1)
this.files.splice(index, 1);
}
this.$nextTick(() => {
this.setEmit()
})
this.setEmit();
});
},
/**
* 公开用户使用继续上传
*/
upload() {
let files = []
let files = [];
this.files.forEach((v, index) => {
if (v.status === 'ready' || v.status === 'error') {
files.push(Object.assign({}, v))
if (v.status === "ready" || v.status === "error") {
files.push(Object.assign({}, v));
}
})
return this.uploadFiles(files)
});
return this.uploadFiles(files);
},
async setValue(newVal, oldVal) {
const newData = async (v) => {
const reg = /cloud:\/\/([\w.]+\/?)\S*/
let url = ''
const reg = /cloud:\/\/([\w.]+\/?)\S*/;
let url = "";
if (v.fileID) {
url = v.fileID
url = v.fileID;
} else {
url = v.url
url = v.url;
}
if (reg.test(url)) {
v.fileID = url
v.url = await this.getTempFileURL(url)
}
if(v.url) v.path = v.url
return v
v.fileID = url;
v.url = await this.getTempFileURL(url);
}
if (this.returnType === 'object') {
if (v.url) v.path = v.url;
return v;
};
if (this.returnType === "object") {
if (newVal) {
await newData(newVal)
await newData(newVal);
} else {
newVal = {}
newVal = {};
}
} else {
if (!newVal) newVal = []
if (!newVal) newVal = [];
for (let i = 0; i < newVal.length; i++) {
let v = newVal[i]
await newData(v)
let v = newVal[i];
await newData(v);
}
}
this.localValue = newVal
this.localValue = newVal;
if (this.form && this.formItem && !this.is_reset) {
this.is_reset = false
this.formItem.setValue(this.localValue)
this.is_reset = false;
this.formItem.setValue(this.localValue);
}
let filesData = Object.keys(newVal).length > 0 ? newVal : [];
this.files = [].concat(filesData)
this.files = [].concat(filesData);
},
/**
* 选择文件
*/
choose() {
if (this.disabled) return
if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType ===
'array') {
if (this.disabled) return;
if (
this.files.length >= Number(this.limitLength) &&
this.showType !== "grid" &&
this.returnType === "array"
) {
uni.showToast({
title: `您最多选择 ${this.limitLength} 个文件`,
icon: 'none'
})
return
icon: "none",
});
return;
}
this.chooseFiles()
this.chooseFiles();
},
/**
* 选择文件并上传
*/
chooseFiles() {
const _extname = get_extname(this.fileExtname)
const _extname = get_extname(this.fileExtname);
//
uniCloud
.chooseAndUploadFile({
@ -360,16 +387,16 @@
extension: _extname.length > 0 ? _extname : undefined,
count: this.limitLength - this.files.length, //9
onChooseFile: this.chooseFileCallback,
onUploadProgress: progressEvent => {
this.setProgress(progressEvent, progressEvent.index)
}
})
.then(result => {
this.setSuccessAndError(result.tempFiles)
onUploadProgress: (progressEvent) => {
this.setProgress(progressEvent, progressEvent.index);
},
})
.catch(err => {
console.log('选择失败', err)
.then((result) => {
this.setSuccessAndError(result.tempFiles);
})
.catch((err) => {
console.log("选择失败", err);
});
},
/**
@ -377,46 +404,44 @@
* @param {Object} res
*/
async chooseFileCallback(res) {
const _extname = get_extname(this.fileExtname)
const is_one = (Number(this.limitLength) === 1 &&
const _extname = get_extname(this.fileExtname);
const is_one =
(Number(this.limitLength) === 1 &&
this.disablePreview &&
!this.disabled) ||
this.returnType === 'object'
this.returnType === "object";
//
if (is_one) {
this.files = []
this.files = [];
}
let {
filePaths,
files
} = get_files_and_is_max(res, _extname)
let { filePaths, files } = get_files_and_is_max(res, _extname);
if (!(_extname && _extname.length > 0)) {
filePaths = res.tempFilePaths
files = res.tempFiles
filePaths = res.tempFilePaths;
files = res.tempFiles;
}
let currentData = []
let currentData = [];
for (let i = 0; i < files.length; i++) {
if (this.limitLength - this.files.length <= 0) break
files[i].uuid = Date.now()
let filedata = await get_file_data(files[i], this.fileMediatype)
filedata.progress = 0
filedata.status = 'ready'
this.files.push(filedata)
if (this.limitLength - this.files.length <= 0) break;
files[i].uuid = Date.now();
let filedata = await get_file_data(files[i], this.fileMediatype);
filedata.progress = 0;
filedata.status = "ready";
this.files.push(filedata);
currentData.push({
...filedata,
file: files[i]
})
file: files[i],
});
}
this.$emit('select', {
this.$emit("select", {
tempFiles: currentData,
tempFilePaths: filePaths
})
res.tempFiles = files
tempFilePaths: filePaths,
});
res.tempFiles = files;
//
if (!this.autoUpload || this.noSpace) {
res.tempFiles = []
res.tempFiles = [];
}
},
@ -425,70 +450,73 @@
* @param {Object} e
*/
uploadFiles(files) {
files = [].concat(files)
return uploadCloudFiles.call(this, files, 5, res => {
this.setProgress(res, res.index, true)
files = [].concat(files);
return uploadCloudFiles
.call(this, files, 5, (res) => {
this.setProgress(res, res.index, true);
})
.then(result => {
this.setSuccessAndError(result)
.then((result) => {
this.setSuccessAndError(result);
return result;
})
.catch(err => {
console.log(err)
})
.catch((err) => {
console.log(err);
});
},
/**
* 成功或失败
*/
async setSuccessAndError(res, fn) {
let successData = []
let errorData = []
let tempFilePath = []
let errorTempFilePath = []
let successData = [];
let errorData = [];
let tempFilePath = [];
let errorTempFilePath = [];
for (let i = 0; i < res.length; i++) {
const item = res[i]
const index = item.uuid ? this.files.findIndex(p => p.uuid === item.uuid) : item.index
const item = res[i];
const index = item.uuid
? this.files.findIndex((p) => p.uuid === item.uuid)
: item.index;
if (index === -1 || !this.files) break
if (item.errMsg === 'request:fail') {
this.files[index].url = item.path
this.files[index].status = 'error'
this.files[index].errMsg = item.errMsg
if (index === -1 || !this.files) break;
if (item.errMsg === "request:fail") {
this.files[index].url = item.path;
this.files[index].status = "error";
this.files[index].errMsg = item.errMsg;
// this.files[index].progress = -1
errorData.push(this.files[index])
errorTempFilePath.push(this.files[index].url)
errorData.push(this.files[index]);
errorTempFilePath.push(this.files[index].url);
} else {
this.files[index].errMsg = ''
this.files[index].fileID = item.url
const reg = /cloud:\/\/([\w.]+\/?)\S*/
this.files[index].errMsg = "";
this.files[index].fileID = item.url;
const reg = /cloud:\/\/([\w.]+\/?)\S*/;
if (reg.test(item.url)) {
this.files[index].url = await this.getTempFileURL(item.url)
this.files[index].url = await this.getTempFileURL(item.url);
} else {
this.files[index].url = item.url
this.files[index].url = item.url;
}
this.files[index].status = 'success'
this.files[index].progress += 1
successData.push(this.files[index])
tempFilePath.push(this.files[index].fileID)
this.files[index].status = "success";
this.files[index].progress += 1;
successData.push(this.files[index]);
tempFilePath.push(this.files[index].fileID);
}
}
if (successData.length > 0) {
this.setEmit()
this.setEmit();
//
this.$emit('success', {
this.$emit("success", {
tempFiles: this.backObject(successData),
tempFilePaths: tempFilePath
})
tempFilePaths: tempFilePath,
});
}
if (errorData.length > 0) {
this.$emit('fail', {
this.$emit("fail", {
tempFiles: this.backObject(errorData),
tempFilePaths: errorTempFilePath
})
tempFilePaths: errorTempFilePath,
});
}
},
@ -499,22 +527,26 @@
* @param {Object} type
*/
setProgress(progressEvent, index, type) {
const fileLenth = this.files.length
const percentNum = (index / fileLenth) * 100
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
let idx = index
const fileLenth = this.files.length;
const percentNum = (index / fileLenth) * 100;
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
let idx = index;
if (!type) {
idx = this.files.findIndex(p => p.uuid === progressEvent.tempFile.uuid)
idx = this.files.findIndex(
(p) => p.uuid === progressEvent.tempFile.uuid
);
}
if (idx === -1 || !this.files[idx]) return
if (idx === -1 || !this.files[idx]) return;
// fix by mehaotian 100 -1
this.files[idx].progress = percentCompleted - 1
this.files[idx].progress = percentCompleted - 1;
//
this.$emit('progress', {
this.$emit("progress", {
index: idx,
progress: parseInt(percentCompleted),
tempFile: this.files[idx]
})
tempFile: this.files[idx],
});
},
/**
@ -522,14 +554,15 @@
* @param {Object} index
*/
delFile(index) {
this.$emit('delete', {
this.$emit("delete", {
tempFile: this.files[index],
tempFilePath: this.files[index].url
})
this.files.splice(index, 1)
tempFilePath: this.files[index].url,
index,
});
this.files.splice(index, 1);
this.$nextTick(() => {
this.setEmit()
})
this.setEmit();
});
},
/**
@ -537,34 +570,34 @@
* @param {Object} name
*/
getFileExt(name) {
const last_len = name.lastIndexOf('.')
const len = name.length
const last_len = name.lastIndexOf(".");
const len = name.length;
return {
name: name.substring(0, last_len),
ext: name.substring(last_len + 1, len)
}
ext: name.substring(last_len + 1, len),
};
},
/**
* 处理返回事件
*/
setEmit() {
let data = []
if (this.returnType === 'object') {
data = this.backObject(this.files)[0]
this.localValue = data?data:null
let data = [];
if (this.returnType === "object") {
data = this.backObject(this.files)[0];
this.localValue = data ? data : null;
} else {
data = this.backObject(this.files)
data = this.backObject(this.files);
if (!this.localValue) {
this.localValue = []
this.localValue = [];
}
this.localValue = [...data]
this.localValue = [...data];
}
// #ifdef VUE3
this.$emit('update:modelValue', this.localValue)
this.$emit("update:modelValue", this.localValue);
// #endif
// #ifndef VUE3
this.$emit('input', this.localValue)
this.$emit("input", this.localValue);
// #endif
},
@ -573,8 +606,8 @@
* @param {Object} files
*/
backObject(files) {
let newFilesData = []
files.forEach(v => {
let newFilesData = [];
files.forEach((v) => {
newFilesData.push({
extname: v.extname,
fileType: v.fileType,
@ -583,22 +616,22 @@
path: v.path,
size: v.size,
fileID: v.fileID,
url: v.url
})
})
return newFilesData
url: v.url,
});
});
return newFilesData;
},
async getTempFileURL(fileList) {
fileList = {
fileList: [].concat(fileList)
}
const urls = await uniCloud.getTempFileURL(fileList)
return urls.fileList[0].tempFileURL || ''
fileList: [].concat(fileList),
};
const urls = await uniCloud.getTempFileURL(fileList);
return urls.fileList[0].tempFileURL || "";
},
/**
* 获取父元素实例
*/
getForm(name = 'uniForms') {
getForm(name = "uniForms") {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
@ -607,9 +640,9 @@
parentName = parent.$options.name;
}
return parent;
}
}
}
},
},
};
</script>
<style>

Loading…
Cancel
Save