初始化仓库

This commit is contained in:
wangxiaowei
2026-04-14 16:54:04 +08:00
commit 967c25b397
553 changed files with 106514 additions and 0 deletions

BIN
bundle_b/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,278 @@
<template>
<view class="city">
<!-- 当前定位城市 -->
<view class="city-title xs">当前定位城市</view>
<view class="city-current bg-white flex row-between">
<view>
<u-icon name="map-fill" size="34"></u-icon>
<text class="m-l-8 nr" v-if="!isLoading">{{ cityInfo.name || '城市' }}</text>
<text class="m-l-8 nr" v-else>定位中...</text>
</view>
<view class="reselect nr flex" @click="reLocationFunc">
<u-loading mode="flower" color="#528FFF" size="28" v-if="isLoading"></u-loading>
<text class="m-l-8">重新定位</text>
</view>
</view>
<!-- 历史浏览城市 -->
<view class="city-title xs" v-if="historyList.length">历史浏览城市</view>
<view class="city-list bg-white">
<block v-for="(hisItem, hisIndex) in historyList" :key="hisIndex">
<view class="city-list-item line-1 nr" @click="chooseCity(hisItem)">{{ hisItem.name }}</view>
</block>
</view>
<!-- 热门城市 -->
<view class="city-title xs">热门城市</view>
<view class="city-list bg-white">
<block v-for="(hotItem, hotIndex) in hotList" :key="hotIndex">
<view class="city-list-item line-1 nr" @click="chooseCity(hotItem)">{{ hotItem.name }}</view>
</block>
</view>
<!-- 普通城市列表 -->
<view v-for="(cityItem, cityIndex) in cityList" :key="cityIndex">
<view class="city-title anchor xs">{{ cityIndex }}</view>
<view class="city-list bg-white">
<block v-for="(cityItem2, cityIndex2) in cityItem" :key="cityIndex2">
<view class="city-list-item line-1 nr" @click="chooseCity(cityItem2)">
{{ cityItem2.name }}
</view>
</block>
</view>
</view>
<!-- 侧边导航条 -->
<view class="city-bar__sidebar" @touchstart.stop.prevent="onTouchMove" @touchmove.stop.prevent="onTouchMove"
@touchend.stop.prevent="onTouchStop" @touchcancel.stop.prevent="onTouchStop">
<view v-for="(barItem, barIndex) in labelList" :key="barIndex" class="city-bar__index"
:style="{color: touchmoveIndex === barIndex ? '#528FFF' : ''}">
{{ barItem }}
</view>
</view>
<!-- 侧边弹窗 -->
<view class="city-list-alert" v-if="touchmove && labelList[touchmoveIndex+'']">
<text>{{labelList[touchmoveIndex]}}</text>
</view>
<loading-view v-if="isFirstLoading"></loading-view>
</view>
</template>
<script>
import { mapActions, mapMutations, mapGetters } from 'vuex'
import { getCityLists } from "@/api/store.js"
import { currentPage, toast } from "@/utils/tools.js"
import cache from "@/utils/cache"
export default {
data() {
return {
isLoading: false,
cityList: [],
labelList: [],
historyList: [],
hotList: [
{ name: '北京市', gcj02_lat: "39.929986", gcj02_lng: "116.395645", id: 110100 },
{ name: '上海市', gcj02_lat: "31.249162", gcj02_lng: "121.487899", id: 310100 },
{ name: '广州市', gcj02_lat: "23.120049", gcj02_lng: "113.30765", id: 440100 },
{ name: '深圳市', gcj02_lat: "22.546054", gcj02_lng: "114.025974", id: 440300 },
{ name: '重庆市', gcj02_lat: "29.544606", gcj02_lng: "106.530635", id: 110100 },
{ name: '成都市', gcj02_lat: "30.679943", gcj02_lng: "104.067923", id: 510100 },
{ name: '杭州市', gcj02_lat: "30.259244", gcj02_lng: "120.219375", id: 110100 },
{ name: '苏州市', gcj02_lat: "31.317987", gcj02_lng: "120.619907", id: 320500 },
{ name: '武汉市', gcj02_lat: "30.581084", gcj02_lng: "114.3162", id: 420100 },
{ name: '沈阳市', gcj02_lat: "41.808645", gcj02_lng: "123.432791", id: 210100 }
],
isFirstLoading: true,
touchmove: false,
touchmoveIndex: 0,
}
},
onLoad() {
this.getCityListsFunc()
this.historyList = cache.get('HISTORY') || []
},
onPageScroll({ scrollTop }) {
const anchor = this.anchor;
const index = anchor.findIndex(item => item >= scrollTop);
const isLessTop = index !== -1;
if (isLessTop && !this.touchmmove) this.touchmoveIndex = index;
},
methods: {
...mapActions(['initLocationFunc']),
...mapMutations(['setCityInfo']),
async reLocationFunc() {
this.isLoading = true;
await this.initLocationFunc()
this.isLoading = false;
},
chooseCity(city) {
try{
this.setCityInfo(city);
// 返回上一页
this.$Router.back(1, {
success: () => {
const {
onLoad,
options
} = currentPage()
// 刷新上一个页面
onLoad && onLoad({ ...options, refresh: true })
}
})
console.log(city)
const historyList = this.historyList;
const result = historyList.filter(item => item.name == city.name);
const isLessTop = result.length === 0;
if ( isLessTop ) {
historyList.unshift(city)
cache.set('HISTORY', historyList)
}
}catch(e){
console.log(e)
toast({ title: '选择有误,请联系管理员' })
//TODO handle the exception
}
},
getCityListsFunc() {
getCityLists()
.then(res => {
if (res.code == 1) {
this.cityList = res.data;
this.labelList = Object.keys(res.data ?? {});
this.setRect()
}
this.isFirstLoading = false;
})
},
onTouchMove(event) {
const y = parseInt(event.changedTouches[0].clientY)
const len = this.labelList.length;
const itemHeight = parseInt(this.sidebar.height / len);
let index = Math.floor((y - this.sidebar.top) / itemHeight);
if (index < 0) {
index = 0;
} else if (index > len - 1) {
index = len - 1;
}
if (this.touchmoveIndex != index) {
this.touchmove = true;
this.touchmoveIndex = index
uni.pageScrollTo({
duration: 0,
scrollTop: (this.anchor[index])
})
}
},
onTouchStop() {
this.touchmove = false;
this.scrollToAnchorIndex = null;
},
async setRect() {
await this.$nextTick()
const sidebar = uni.createSelectorQuery().selectAll(".city-bar__sidebar");
sidebar.boundingClientRect(res => {
this.sidebar = {
height: res[0].height,
top: res[0].top
}
}).exec();
const anchor = uni.createSelectorQuery().selectAll(".anchor");
anchor.boundingClientRect(res => {
res.top = parseInt(res.top)
this.anchor = res.map(item => parseInt(item.top));
}).exec();
}
},
computed: {
...mapGetters(['cityInfo'])
}
}
</script>
<style lang="scss" scoped>
.city {
padding: 12rpx 0;
&-title {
color: #AAAAAA;
padding: 12rpx 34rpx;
}
.city-current {
color: #575757;
padding: 0 36rpx;
.reselect {
color: #528FFF;
padding: 26rpx 10rpx;
}
}
&-list {
color: #575757;
padding: 0 40rpx;
display: flex;
flex-wrap: wrap;
&-item {
width: 25%;
height: 96rpx;
line-height: 96rpx;
padding-left: 34rpx;
}
}
&-bar__sidebar {
position: fixed;
top: 50%;
right: 0;
display: flex;
flex-direction: column;
text-align: center;
transform: translateY(-50%);
user-select: none;
z-index: 99;
}
&-bar__index {
font-weight: 500;
padding: 8rpx 18rpx;
font-size: 22rpx;
line-height: 1
}
&-list-alert {
position: fixed;
width: 120rpx;
height: 120rpx;
right: 90rpx;
top: 50%;
margin-top: -60rpx;
border-radius: 24rpx;
font-size: 50rpx;
color: #fff;
background-color: rgba(0, 0, 0, 0.65);
display: flex;
justify-content: center;
align-items: center;
padding: 0;
z-index: 9999999;
text {
line-height: 50rpx;
}
}
}
</style>

View File

@ -0,0 +1,789 @@
<template>
<view>
<!-- Main Start -->
<view class="content-box">
<!-- 审核失败 -->
<view class="audit flex" v-if="communityInfo.status == 2">
<image src="/bundle_b/static/icon_audit_fail.png"></image>
<view class="m-l-16 primary">
<view class="bold md">审核失败</view>
<view class="xs m-t-6">{{ communityInfo.audit_remark_desc }}</view>
</view>
</view>
<!-- 审核中 -->
<view class="audit flex" v-if="communityInfo.status == 0">
<image src="/bundle_b/static/icon_under_review.png"></image>
<view class="m-l-16 primary">
<view class="bold md">审核中</view>
<view class="xs m-t-6">{{ communityInfo.audit_remark_desc }}</view>
</view>
</view>
<!-- 内容头部信息 -->
<view class="header flex row-between">
<router-link
:to="
'/bundle_b/pages/community_user/community_user?id=' +
communityInfo.user.id
"
>
<view class="flex">
<!-- 头像 -->
<u-image
width="70"
height="70"
:src="communityInfo.user.avatar"
borderRadius="50%"
></u-image>
<!-- 昵称 -->
<text class="normal bold m-l-16">{{
communityInfo.user.nickname
}}</text>
</view>
</router-link>
<view class="flex">
<!-- 关注 -->
<button
class="follow-btn br60"
size="mini"
@click="handleFollow(1)"
v-if="communityInfo.is_author == 0 && communityInfo.is_follow == 0"
>
<image src="/bundle_b/static/icon_follow.png"></image>
<text class="m-l-6 xs">关注</text>
</button>
<!-- 已关注 -->
<button
class="followed-btn br60 sm"
size="mini"
@click="showFollowTips = true"
v-if="communityInfo.is_author == 0 && communityInfo.is_follow == 1"
>
已关注
</button>
<!-- 管理 -->
<button
class="manage-btn br60"
size="mini"
@click="showManage = true"
v-if="communityInfo.is_author"
>
管理
</button>
<!-- 转发 -->
<button
open-type="share"
@click="handleShare"
class="flex-col col--center"
hover-class="none"
>
<image
src="/bundle_b/static/icon_forward.png"
class="share-img m-l-12"
></image>
</button>
</view>
</view>
<!-- 内容媒体信息 -->
<view class="swiper-container">
<product-swiper
:imgUrls="communityInfo.images"
:autoplay="false"
borderRadius="14"
></product-swiper>
</view>
<!-- 提到的宝贝( 商品 ) -->
<view
class="goods-box bb flex row-between"
@click="showGoodsPopup = true"
v-if="communityInfo.goods_data.length"
>
<text class="nr lighter"
>查看TA提到的宝贝({{ communityInfo.goods_data.length }})</text
>
<!-- <text class="tips xs">300+人评价</text> -->
<view class="goods flex">
<block
v-for="(goodsItem, goodsIndex) in communityInfo.goods_data"
:key="goodsIndex"
>
<u-image
v-if="goodsIndex <= 2"
:src="goodsItem.image"
width="58"
height="58"
class="m-l-6"
>
</u-image>
</block>
<u-icon name="arrow-right" class="m-l-10"></u-icon>
</view>
</view>
<!-- 提到的店铺( 店铺 ) -->
<view
class="goods-box bb flex row-between"
@click="showShopPopup = true"
v-if="communityInfo.shop_data.length"
>
<text class="nr lighter"
>查看TA提到的店铺({{ communityInfo.shop_data.length }})</text
>
<!-- <text class="tips xs">300+人评价</text> -->
<view class="goods flex">
<block
v-for="(shopItem, shopIndex) in communityInfo.shop_data"
:key="shopIndex"
>
<u-image
v-if="shopIndex <= 2"
:src="shopItem.logo"
width="58"
height="58"
class="m-l-6"
>
</u-image>
</block>
<u-icon name="arrow-right" class="m-l-10"></u-icon>
</view>
</view>
<!-- 内容文字 -->
<view class="content">
<view class="text">
{{ communityInfo.content }}
</view>
<view class="tags" v-if="communityInfo.topic">
<navigator
hover-class="none"
:url="
'/bundle_b/pages/community_topic/community_topic?id=' +
communityInfo.topic.id +
'&name=' +
communityInfo.topic.name
"
>
<text># {{ communityInfo.topic.name }}</text>
</navigator>
</view>
<view class="xs muted">
{{ communityInfo.create_time || "2022-05-20 05:13:14" }}
</view>
</view>
</view>
<!-- Main End -->
<!-- Comment Start -->
<view class="comment-box m-t-20 bg-white">
<view class="comment-title bb" id="comment">
评价 ({{ communityInfo.total_comment || "0" }})
</view>
<template v-if="isCommentLoading">
<view class="text-center flex row-center p-50">
<u-loading
:color="colorConfig.primary"
:size="40"
mode="circle"
></u-loading>
<text class="m-l-20">加载中</text>
</view>
</template>
<template v-else>
<view class="text-center p-50" v-if="!commentData.length">
<view class="flex row-center">
<u-image
:src="'/static/images/news_null.png'"
width="300"
height="300"
></u-image>
</view>
<view class="muted m-t-40"> 还没有人评论呢, 快来抢沙发 </view>
</view>
<template v-else>
<view
v-for="(commentItem, commentIndex) in commentData"
:key="commentItem.id"
@click="onSelectComment(commentIndex)"
>
<community-comment-list :comment="commentItem" @reply="onReply">
</community-comment-list>
</view>
<!-- 加载 -->
<view
class="flex row-center primary nd p-50"
v-if="more === 1 && loading"
>
<u-loading
:color="colorConfig.primary"
:size="40"
mode="circle"
></u-loading>
<text class="m-l-20">加载中</text>
</view>
<!-- 加载底部 -->
<view class="text-center muted nd p-50" v-else>
<text>没有更多了~</text>
</view>
</template>
</template>
</view>
<!-- Comment End -->
<!-- Footer Start -->
<view class="footer flex bg-white">
<view class="content-wrapper flex row-between">
<view class="flex-1 flex" @click="handleComment">
<view class="input nr muted">发表你的想法吧</view>
</view>
<view class="flex likes-box">
<view
class="likes"
:class="communityInfo.is_like ? 'entry' : 'leave'"
@click="handleLike(communityInfo.is_like)"
></view>
<image class="m-l-30"></image>
<text>{{ communityInfo.like || "0" }}</text>
</view>
<view class="flex" @click="toComment">
<image src="/static/images/icon_evaluate.png" class="m-l-30"></image>
<text>{{ communityInfo.total_comment || "0" }}</text>
</view>
</view>
</view>
<!-- Footer End -->
<!-- 管理 -->
<u-action-sheet
:list="manageList"
v-model="showManage"
@click="handleManage"
safe-area-inset-bottom
>
</u-action-sheet>
<!-- 取消关注 -->
<u-modal
v-model="showFollowTips"
:show-cancel-button="true"
comfirm-text="取消关注"
cancelText="再想想"
:confirm-color="colorConfig.primary"
:show-title="false"
@confirm="handleFollow(0)"
>
<view class="flex-col col-center tips-dialog" style="padding-top: 40rpx">
<image class="icon-lg" src="/static/images/icon_warning.png" />
<view style="margin: 30rpx 0">确认取消关注</view>
</view>
</u-modal>
<!-- 提到的商品 -->
<community-goods
v-model="showGoodsPopup"
:communityId="communityInfo.id"
></community-goods>
<!-- 提到的店铺 -->
<community-shop
v-model="showShopPopup"
:communityId="communityInfo.id"
></community-shop>
<!-- 一级回复评论 -->
<community-comment
v-model="showCommentPopup"
:communityId="communityId"
@change="changeComment"
:safeAreaInsetBottom="false"
>
</community-comment>
<!-- 二级回复评论 -->
<community-comment
v-model="showCommentChildPopup"
:communityId="communityId"
:pid="childPid"
@change="changeCommentChild"
:placeholder="childPlaceholder"
:safeAreaInsetBottom="false"
>
</community-comment>
<!-- 加载 -->
<loading-view v-if="isFirstLoading"></loading-view>
<!-- #ifdef H5 -->
<u-popup
:custom-style="{ background: 'none' }"
class="share-tips"
v-model="showTips"
mode="top"
>
<view style="overflow: hidden">
<image src="/static/images/share_arrow.png" class="share-arrow" />
<view class="white" style="text-align: center; margin-top: 280rpx">
<view class="bold lg">立即分享给好友吧</view>
<view class="sm m-t-10">点击屏幕右上角将本页面分享给好友</view>
</view>
</view>
</u-popup>
<!-- #endif -->
</view>
</template>
<script>
import {
apiCommunityDel,
getCommunityDetail,
apiCommunityFollow,
getCommunityCommentLists,
apiCommunityCommentLike,
} from "@/api/community.js";
import { getRect } from "@/utils/tools";
export default {
data() {
return {
globalLike: false,
communityId: "",
communityInfo: {
user: {
avatar: "",
nickname: "",
id: null,
},
goods_data: [],
shop_data: [],
topic: {
id: false,
name: "",
},
},
isFirstLoading: true, // 详情加载
isCommentLoading: true, // 评论加载
showManage: false, // 管理操作
manageList: [
{
// 管理操作列表
text: "编辑",
color: "#333333",
fontSize: 32,
},
{
text: "删除",
color: "#333333",
fontSize: 32,
},
],
showFollowTips: false, // 取消关注
showGoodsPopup: false, // 商品推荐弹窗
showShopPopup: false, // 店铺推荐弹窗
showTips: false, // 分享弹窗H5
showCommentPopup: false, // 评论弹窗
showCommentChildPopup: false, // 二级评论
childPid: "", // 选择二级评论回复ID
childIndex: "", // 选择的二级评论第几条
childPlaceholder: "", // 选择的二级评论回复人名字: 回复@xxx
toScrollArr: [], // 存储需要跳转到的滚动条高度 0-评论
commentData: [], // 评论数据
page: 1, // 评论分页页数
pageSize: 10, // 评论分页数量
more: 0, // 是否有下一页分页
loading: false, // 加载动画
};
},
onLoad() {
this.$nextTick(() => {
// 滚动到顶部防止h5端出现问题
uni.pageScrollTo({
scrollTop: 0,
duration: 0,
});
getRect("#comment").then((res) => {
this.toScrollArr[0] = res.top;
});
});
const options = this.$Route.query;
if (options && options.scene) {
const scene = strToParams(decodeURIComponent(options.scene));
options.id = scene.id;
}
this.communityId = options.id || "";
this.initCommunityDetail();
},
onShow() {},
// 触底加载
onReachBottom() {
console.log("触底");
if (this.more) {
this.loading = true;
this.getCommentData();
}
},
// 分享朋友
onShareAppMessage() {
return {
title: `${this.communityInfo.user.nickname}TA的内容超级棒`,
path:
"/bundle_b/pages/community_detail/community_detail?id=" +
this.communityId,
imageUrl: this.communityInfo.image,
};
},
methods: {
// 初始化详情
initCommunityDetail() {
getCommunityDetail({
id: this.communityId,
}).then((res) => {
if (res.code == 1) {
this.communityInfo = res.data;
setTimeout(() => {
this.isFirstLoading && this.getCommentData();
this.isFirstLoading = false;
}, 600);
} else {
this.$toast({
title: res.msg,
});
setTimeout(() => this.$Router.back(), 2000);
}
});
},
// 分享处理
handleShare() {
// #ifdef H5
this.showTips = true;
// #endif
// #ifndef MP
this.$store.commit("setCommunity", {
...this.communityInfo,
url: "bundle_b/pages/community_detail/community_detail",
});
this.$store.dispatch("communityShare");
// #endif
},
// 刷新评论 event 是子组件传参是否评论
changeComment(event) {
this.communityInfo.total_comment += 1;
if (!event.hasOwnProperty("child")) {
event.child = [];
}
this.commentData.unshift(event);
},
// 选择第几条评论区回复
onSelectComment(index) {
this.childIndex = index;
},
// 回复评论
onReply(event) {
// console.log(event)
this.childPid = event.parentId;
this.childPlaceholder = "回复@" + event.commentUserName;
this.showCommentChildPopup = true;
},
// 改变添加子评论
changeCommentChild(event) {
this.commentData[this.childIndex].child.push(event);
},
// 获取评论
getCommentData() {
getCommunityCommentLists({
article_id: this.communityId,
page_no: this.page,
page_size: this.pageSize,
}).then((res) => {
setTimeout(() => (this.isCommentLoading = false), 500);
if (res.code == 1) {
res.data.list.forEach((item) => {
item.loading = false;
this.commentData.push(item);
});
if (res.data.more === 1) {
this.page += 1;
}
this.more = res.data.more;
this.loading = false;
} else {
this.$toast({ title: res.msg });
}
});
},
// 处理需要回复的评论
handleComment() {
this.showCommentPopup = true;
},
// 管理文章
handleManage(index) {
// 编辑
switch (index) {
case 0:
this.$Router.push({
path: "/bundle_b/pages/published_works/published_works",
query: {
id: this.communityId,
},
});
break;
case 1:
apiCommunityDel({
id: this.communityId,
}).then((res) => {
if (res.code == 1) {
this.$toast({
title: "删除成功",
});
setTimeout(() => this.$Router.back(), 1000);
}
});
break;
}
},
// 关注 取消关注
handleFollow(event) {
if (!this.isLogin) return this.$Router.push("/pages/login/login");
apiCommunityFollow({
follow_id: this.communityInfo.user.id,
status: event,
}).then((res) => {
this.$toast({
title: res.msg,
});
this.initCommunityDetail();
});
},
// 点赞
handleLike(status) {
if (!this.isLogin) return this.$Router.push("/pages/login/login");
this.communityInfo.is_like = !status;
apiCommunityCommentLike({
id: this.communityId,
status: status ? 0 : 1,
type: 1,
}).then((res) => {
if (res.code === 1) {
if (status) this.communityInfo.like -= 1;
else this.communityInfo.like += 1;
// 点赞文章时 其他页面的状态也需要更改
uni.$emit("changeItem", {
like: this.communityInfo.like,
is_like: this.communityInfo.is_like,
id: this.communityId,
});
} else {
this.communityInfo.is_like = status;
this.$toast({
title: res.msg,
});
}
});
},
// 跳转到评论
toComment() {
uni.pageScrollTo({
scrollTop: this.toScrollArr[0],
duration: 200,
});
},
},
};
</script>
<style lang="scss">
.bb {
border-bottom: 1px solid $-color-border;
}
.content-box {
margin-bottom: 20rpx;
background-color: $-color-white;
// 审核
.audit {
padding: 20rpx 24rpx;
background-color: rgba($-color-primary, 0.1);
image {
width: 60rpx;
height: 60rpx;
}
}
// 头部-作者
.header {
font-size: 28rpx;
padding: 20rpx 24rpx;
// 更多
.share-img {
width: 60rpx;
height: 60rpx;
}
// 关注-按钮
.follow-btn {
width: 118rpx;
height: 48rpx;
line-height: 44rpx;
color: #ffffff;
padding: 0;
background: linear-gradient(90deg, #f95f2f 0%, #ff2c3c 100%);
image {
width: 30rpx;
height: 30rpx;
vertical-align: middle;
}
}
// 已关注-按钮
.followed-btn {
width: 118rpx;
height: 48rpx;
line-height: 44rpx;
color: #ffffff;
padding: 0;
background-color: #cccccc;
}
// 管理-按钮
.manage-btn {
width: 92rpx;
height: 48rpx;
line-height: 44rpx;
padding: 0;
border: 1px solid $-color-primary;
color: $-color-primary;
}
}
.swiper-container {
padding: 0 20rpx;
}
// 推荐的商品或店铺
.goods-box {
padding: 15rpx 24rpx;
.tips {
padding: 0 10rpx;
color: $-color-primary;
border-radius: 20rpx;
border: 1px solid $-color-primary;
}
}
.content {
padding: 24rpx;
// 内容文字
.text {
white-space: pre-line;
line-height: 48rpx;
font-size: 28rpx;
color: $-color-normal;
}
// 标签
.tags {
padding: 24rpx 0;
text {
margin-right: 20rpx;
border-radius: 26rpx;
padding: 8rpx 24rpx;
color: $-color-primary;
background: rgba($-color-primary, 0.1);
}
}
}
}
.comment-box {
overflow: hidden;
padding-bottom: 140rpx;
.comment-title {
color: $-color-black;
padding: 28rpx 24rpx;
font-size: 30rpx;
}
}
// 底部
.footer {
width: 100%;
height: 92rpx;
z-index: 99;
position: fixed;
left: 0;
bottom: 0;
box-sizing: content-box;
padding-bottom: env(safe-area-inset-bottom);
box-shadow: 0 -4rpx 10rpx rgba(#000000, 0.1);
.content-wrapper {
width: 100%;
padding: 0 30rpx;
color: $-color-lighter;
font-size: 28rpx;
.input {
width: 100%;
border-radius: 30rpx;
background: #f8f8f8;
padding: 10rpx 30rpx;
}
image {
width: 44rpx;
height: 44rpx;
margin-right: 6rpx;
}
// 点赞动画
.likes-box {
position: relative;
.likes {
z-index: 999;
left: -10rpx;
width: 120rpx;
height: 120rpx;
margin-right: 6rpx;
position: absolute;
background: url("@/static/images/likes.png") no-repeat;
background-position: left;
background-size: cover;
}
// 没点赞
.leave {
background-position: left;
}
// 点赞
.entry {
background-position: right;
transition: background 0.6s steps(28);
}
}
}
}
.share-tips .share-arrow {
width: 140rpx;
height: 250rpx;
float: right;
margin: 15rpx 31rpx 0 0;
}
</style>

View File

@ -0,0 +1,307 @@
<template>
<view class="goods-search flex-col">
<view class="header-wrap">
<view class="search">
<u-search v-model="keyword" @focus="showHistory = true" :focus="showHistory" @search="onSearch"
bg-color="#F4F4F4"></u-search>
</view>
</view>
<view v-show="showHistory" class="search-content bg-white">
<scroll-view :scroll-y="true" style="height: 100%">
<view v-if="hotList.length" class="search-words">
<view class="title">热门搜索</view>
<view class="words flex flex-wrap">
<block v-for="(item, index) in hotList" :key="index">
<navigator hover-class="none" class=""
:url="'/bundle_b/pages/community_topic/community_topic?id=' + item.id +'&name=' + item.name">
<view class="item br60 m-r-20 m-b-20 lighter sm line-1">
{{item.name}}
</view>
</navigator>
</block>
</view>
</view>
<view v-if="historyList.length" class="search-words">
<view class="title flex row-between">
<view>历史搜索</view>
<view class="xs muted m-r-20" style="padding: 10rpx 20rpx" @tap="clearSearchFun">清空</view>
</view>
<view class="words flex flex-wrap">
<view v-for="(item, index) in historyList" :key="index"
class="item br60 m-r-20 m-b-20 lighter sm line-1" @tap="onChangeKeyword(item)">{{item}}
</view>
</view>
</view>
</scroll-view>
</view>
<view class="content">
<mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :up="upOption"
:down="{auto: false}" :fixed="false">
<view class="goods-list" v-if="goodsList.length">
<u-waterfall ref="uWaterfall" v-model="goodsList" :add-time="20">
<template v-slot:left="{leftList}">
<view style="padding:0 9rpx 0 30rpx">
<community-list width="336rpx" type="waterfall" :list="leftList"></community-list>
</view>
</template>
<template v-slot:right="{rightList}">
<view style="padding:0 30rpx 0 9rpx;">
<!-- {{ JSON.stringify(rightList) }} -->
<community-list width="336rpx" type="waterfall" :list="rightList"></community-list>
</view>
</template>
</u-waterfall>
</view>
</mescroll-uni>
</view>
</view>
</template>
<script>
import {
getCommunityArticleLists,
getCommunitySearchHistory,
apiCommunityClearSearchHistory
} from '@/api/community.js';
import {
mapGetters
} from 'vuex';
import {
getRect,
trottle
} from '@/utils/tools';
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins";
export default {
mixins: [MescrollMixin],
data() {
return {
upOption: {
auto: false,
empty: {
icon: '/static/images/news_null.png',
tip: "暂无种草文章",
}
},
keyword: '',
sortConfig: {
goodsType: 'double',
priceSort: '',
saleSort: '',
},
goodsList: [],
showHistory: false,
hotList: [],
historyList: []
};
},
watch: {
// 监听属性
keyword(value, old) {
if (!value && !this.id) {
this.showHistory = true
}
},
showHistory(value) {
if (value) {
this.getSearchpageFun();
}
},
'sortConfig.saleSort'() {
this.onSearch()
},
'sortConfig.priceSort'() {
this.onSearch()
}
},
onLoad(options) {
this.onSearch = trottle(this.onSearch, 500, this);
this.init(options);
uni.$on('changeItem', (event) => {
const index = this.goodsList.findIndex(el => el.id == event.id)
if (index == -1) return
this.$refs.uWaterfall.modify(event.id, 'like', event.like)
this.$refs.uWaterfall.modify(event.id, 'is_like', event.is_like)
})
},
onUnload() {
uni.$off("changeItem")
},
computed: {
...mapGetters(['sysInfo'])
},
methods: {
downCallback() {
this.onRefresh()
},
upCallback(page) {
let pageNum = page.num; // 页码, 默认从1开始
let pageSize = page.size; // 页长, 默认每页10条
let {
goodsList,
keyword,
sortConfig: {
priceSort,
saleSort,
}
} = this;
const params = {
page_size: pageSize,
page_no: pageNum,
platform_cate_id: this.type == 1 ? this.id : '',
brand_id: this.type == 0 ? this.id : '',
keyword,
sort_by_price: priceSort,
sort_by_sales: saleSort
}
getCommunityArticleLists(params).then(({
data
}) => {
if (page.num == 1) this.goodsList = [];
let curPageData = data.list;
let curPageLen = curPageData.length;
let hasNext = !!data.more;
this.goodsList = this.goodsList.concat(curPageData);
this.mescroll.endSuccess(curPageLen, hasNext);
}).catch(() => {
this.mescroll.endErr()
})
},
onChange(e) {
this.keyword = e.value
},
clearSearchFun() {
apiCommunityClearSearchHistory().then(res => {
if (res.code == 1) {
this.getSearchpageFun();
}
});
},
init(option) {
const {
id,
name,
type
} = this.$Route.query
this.type = type
if (id) {
uni.setNavigationBarTitle({
title: name
});
this.id = id;
this.$nextTick(() => {
this.onRefresh()
})
} else {
uni.setNavigationBarTitle({
title: '种草搜索'
});
this.showHistory = true
}
},
getSearchpageFun() {
getCommunitySearchHistory().then(res => {
if (res.code == 1) {
let {
history,
topic
} = res.data;
this.hotList = topic
this.historyList = history
}
});
},
onClear() {
if (this.id) {
this.onRefresh();
}
},
onSearch() {
this.showHistory = false
this.$nextTick(() => {
this.onRefresh()
})
},
onRefresh() {
this.goodsList = []
this.mescroll.resetUpScroll();
},
onChangeKeyword(item) {
this.keyword = item
this.showHistory = false
this.onRefresh();
},
}
};
</script>
<style lang="scss">
page {
height: 100%;
padding: 0;
}
.goods-search {
height: 100%;
position: relative;
.header-wrap {
position: relative;
z-index: 999;
.search {
box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.03);
position: relative;
z-index: 1;
}
}
.search-content {
position: absolute;
width: 100%;
height: 100%;
padding-top: 100rpx;
z-index: 100;
.search-words {
padding-left: 24rpx;
padding-bottom: 20rpx;
.title {
padding: 26rpx 0;
}
.words {
.item {
line-height: 52rpx;
height: 52rpx;
padding: 0 24rpx;
background-color: #F5F5F5;
}
}
}
}
.content {
flex: 1;
min-height: 0;
.goods-list {
overflow: hidden;
}
}
}
</style>

View File

@ -0,0 +1,150 @@
<template>
<view class="topic">
<view class="header flex row-between">
<view class="normal lg bold"># {{ topicName }}</view>
<view class="flex">
<image src="/bundle_b/static/icon_see.png"></image>
<text class="lighter sm m-l-10">{{ click }}</text>
</view>
</view>
<!-- Menu -->
<view class="menu flex">
<view class="menu--item" :class="{'active': sortHot}" @click="handleSortHot">最热</view>
<view class="menu--item" :class="{'active': sortNew}" @click="handleSortNew">最新</view>
</view>
<view>
<view v-if="lists.length">
<u-waterfall ref="uWaterfall" v-model="lists" :add-time="200">
<template v-slot:left="{leftList}">
<view style="padding:0 9rpx 0 30rpx">
<community-list width="336rpx" type="waterfall" :list="leftList"></community-list>
</view>
</template>
<template v-slot:right="{rightList}">
<view style="padding:0 30rpx 0 9rpx;">
<community-list width="336rpx" type="waterfall" :list="rightList"></community-list>
</view>
</template>
</u-waterfall>
</view>
<view class="p-t-60" v-else>
<view class="flex row-center m-t-40">
<u-image width="300" height="300" :src="'/bundle_b/static/works_null.png'"></u-image>
</view>
<view class="text-center muted m-t-40">
暂无话题文章
</view>
</view>
</view>
</view>
</template>
<script>
import {
getCommunityTopicArticle
} from "@/api/community.js"
export default {
data() {
return {
topicId: '',
topicName: '',
sortHot: true,
sortNew: false,
lists: [],
click: 0, //查看|点击数
page: 1,
more: 1,
pageSize: 10
}
},
onLoad() {
const options = this.$Route.query;
this.topicId = options.id || '';
this.topicName = options.name || '';
},
onShow() {
this.getCommunityTopic()
},
// 触底加载
onReachBottom() {
console.log('触底')
if (this.more) {
this.getCommunityTopic()
}
},
methods: {
handleSortHot() {
this.sortHot = !this.sortHot
this.page = 1;
this.getCommunityTopic()
},
handleSortNew() {
this.sortNew = !this.sortNew
this.page = 1;
this.getCommunityTopic()
},
// 获取
getCommunityTopic() {
getCommunityTopicArticle({
topic_id: this.topicId,
sort_hot: this.sortHot ? 'desc' : 'asc',
sort_new: this.sortNew ? 'desc' : 'asc',
page_no: this.page,
page_size: this.pageSize,
}).then(res => {
// 如果是第一页需手动置空列表
if (this.page == 1) {
if('uWaterfall' in this.$refs) this.$refs.uWaterfall.clear()
this.lists = []
}
if (res.data.more === 1) {
this.page += 1
}
this.click = res.data.click
this.more = res.data.lists.more;
// 异步让vue能够监听到数据改变过了
setTimeout(() => {
this.lists = [...this.lists, ...res.data.lists.list]
}, 0)
})
},
}
}
</script>
<style lang="scss">
page {
background-color: #FFFFFF;
}
.topic {
.header {
padding: 40rpx 40rpx 30rpx 30rpx;
border-top: 1px solid $-color-body;
border-bottom: 1px solid $-color-body;
image {
width: 42rpx;
height: 42rpx;
}
}
.menu {
padding: 0 30rpx;
&--item {
margin: 20rpx 0;
font-size: 28rpx;
color: $-color-muted;
margin-right: 50rpx;
}
.active {
color: $-color-primary;
font-weight: 500;
}
}
}
</style>

View File

@ -0,0 +1,381 @@
<template>
<view class="user" :style="[background]">
<u-navbar id="navbar" :border-bottom="false"
:background="{ background: 'rgba(256,256,256, '+ navStyle.backgroundBg +')', }"
:back-bg="'rgba(0,0,0,' + navStyle.backBg + ')'" :back-icon-color="navStyle.backColor" :immersive="false">
</u-navbar>
<!-- Header Start -->
<view class="header">
<!-- 用户信息 -->
<view class="user_info flex row-between">
<view class="flex">
<view class="user-avatar flex row-center">
<u-image :src="userInfo.avatar" width="106" height="106" borderRadius="50%"></u-image>
</view>
<view class="m-l-30">
<view class="user-name xxl white bold line-1">{{ userInfo.nickname }}</view>
<view class="user-id m-t-14 xs white">会员ID: {{ userInfo.sn }}</view>
</view>
</view>
<!-- 去用户设置 -->
<navigator v-if="userInfo.is_self" url="/bundle_b/pages/community_user_profile/community_user_profile"
hover-class="none">
<image src="/bundle_b/static/icon_my_setting.png" class="user-setting"></image>
</navigator>
</view>
<!-- 用户简介 -->
<view class="user-intro line-3 white sm">
{{ userInfo.signature || '暂无简介~' }}
</view>
<!-- 用户数据底部 -->
<view class="user-footer flex">
<!-- 统计 -->
<view class="user-statistics">
<view class="user-statistics--item">
<view class="xl bold">{{ userInfo.follow }}</view>
<view class="sm">关注</view>
</view>
<view class="user-statistics--item">
<view class="xl bold">{{ userInfo.fans }}</view>
<view class="sm">粉丝</view>
</view>
<view class="user-statistics--item">
<view class="xl bold">{{ userInfo.like }}</view>
<view class="sm">获赞</view>
</view>
</view>
<!-- 操作 -->
<view class="user-operation flex">
<!-- 发布 -->
<navigator url="/bundle_b/pages/published_works/published_works" hover-class="none">
<button class="btn-primary br60 xs white" v-if="userInfo.is_self">
<image src="/bundle_b/static/icon_publish.png"></image>
<text class="m-l-6">发布</text>
</button>
</navigator>
<!-- 关注 -->
<button class="btn-primary br60 xs white" v-if="userInfo.is_self==0 && userInfo.is_follow==0"
@click="handleCommunityFollow(1)">
<image src="/bundle_b/static/icon_follow.png"></image>
<text class="m-l-6">关注</text>
</button>
<!-- 已关注 -->
<button class="followed-btn br60 xs primary bold"
v-if="userInfo.is_self==0 && userInfo.is_follow==1" @click="showFollowTips = true">已关注</button>
<button open-type="share" @click="handleShare" class="flex-col col--center" hover-class="none">
<image src="/bundle_b/static/icon_share.png" class="share"></image>
</button>
</view>
</view>
</view>
<!-- Header End -->
<!-- Main Start -->
<view class="main">
<view class="nav-title">
<tabs :current="active" @change="changeTabs" bgColor="transparent" stickyBgColor="transparent"
height="100" fontSize="34" :is-scroll="false" width="400rpx" borderRadius="20rpx 20rpx 0 0">
<tab name="作品">
<community-works v-if="!isFirstLoading" :worksNum="worksNum" :i="0" :index="active"
ref="mescoll1" :userId="userId"></community-works>
</tab>
<tab name="赞过">
<community-like v-if="!isFirstLoading" :likeNum="likeNum" :i="1" :index="active" ref="mescoll2"
:userId="userId"></community-like>
</tab>
</tabs>
</view>
<!-- </u-sticky> -->
</view>
<!-- Main End -->
<!-- 加载 -->
<loading-view v-if="isFirstLoading"></loading-view>
<!-- 取消关注 -->
<u-modal v-model="showFollowTips" :show-cancel-button="true" comfirm-text="取消关注" cancelText="再想想"
:confirm-color="colorConfig.primary" :show-title="false" @confirm="handleCommunityFollow(0)">
<view class="flex-col col-center tips-dialog" style="padding-top: 40rpx">
<image class="icon-lg" src="/static/images/icon_warning.png" />
<view style="margin:30rpx 0;">确认取消关注</view>
</view>
</u-modal>
<!-- #ifdef H5 -->
<u-popup :custom-style="{'background': 'none'}" class="share-tips" v-model="showTips" mode="top">
<view style="overflow: hidden;">
<image src="/static/images/share_arrow.png" class="share-arrow" />
<view class="white" style="text-align: center;margin-top: 280rpx;">
<view class="bold lg">立即分享给好友吧</view>
<view class="sm m-t-10">点击屏幕右上角将本页面分享给好友</view>
</view>
</view>
</u-popup>
<!-- #endif -->
</view>
</template>
<script>
import {
getCommunityUserCenter,
apiCommunityFollow,
} from "@/api/community.js"
import CommunityLike from "./components/community-like.vue"
import CommunityWorks from "./components/community-works.vue"
export default {
components: {
CommunityLike,
CommunityWorks
},
data() {
return {
isFirstLoading: true,
worksNum: 0, // 用于子组件监听触底
likeNum: 0, // 用于子组件监听触底
navStyle: {
backBg: 0.4,
backgroundBg: 0,
backColor: 'rgba(256,256,256,1)'
},
active: 0,
userId: '', //用户ID不传为当前用户
showFollowTips: false, // 取消关注
showTips: false, // 分享弹窗H5
userInfo: {
id: '', // int 用户id
sn: '', //string 用户编号
nickname: '', //string 用户昵称
avatar: '', //string 用户头像
image: '', //string 背景图
signature: '', //string 签名
follow: 0, //int 关注人数
fans: 0, //int 粉丝数
like: 0, //int 点赞数
is_self: 0, // int 是否为当前登录者 0-不是 1-是
is_follow: 0, // int 登录者是否关注获取信息目标 0-未关注 1-关注
}
}
},
computed: {
background() {
const {
image
} = this.userInfo
return image ? {
'background-image': `url(${image})`
} : {
}
},
},
onLoad() {
const options = this.$Route.query;
this.userId = options.id;
},
onShow() {
if (this.active == 0) {
this.worksNum += 1
} else if (this.active == 1) {
this.likeNum += 1
}
this.initCommunityUserCenter()
},
onUnload() {
uni.$off("changeItem")
},
onPageScroll(e) {
const top = uni.upx2px(500)
const {
scrollTop
} = e
const percent = scrollTop / top;
this.navStyle.backgroundBg = percent
this.navStyle.backBg = 0.4 * (0.5 - percent)
this.navStyle.backColor = percent < 0.5 ? 'rgba(256,256,256,' + (0.5 - percent) * 2 + ')' : 'rgba(0,0,0,' +
(
percent - 0.5) * 2 + ')'
},
// 触底加载
onReachBottom() {
console.log('触底')
if (this.active == 0) {
this.worksNum += 1
} else if (this.active == 1) {
this.likeNum += 1
}
},
methods: {
changeTabs(event) {
this.active = event
},
// 分享处理
handleShare() {
// #ifdef H5
this.showTips = true
// #endif
// #ifndef MP
this.$store.commit('setCommunity', {
user: {
nickname: this.userInfo.nickname,
},
image: this.userInfo.avatar,
content: this.userInfo.signature,
url: 'bundle_b/pages/community_user/community_user'
})
this.$store.dispatch('communityShare')
// #endif
},
// 初始化个人中心
initCommunityUserCenter() {
getCommunityUserCenter({
user_id: this.userId
}).then(res => {
this.userInfo = res.data
setTimeout(() => {
this.isFirstLoading = false;
}, 600)
})
},
// 关注 / 取消关注
handleCommunityFollow(status) {
apiCommunityFollow({
follow_id: this.userId,
status
}).then(res => {
if (res.code === 1) this.initCommunityUserCenter()
this.$toast({
title: res.msg
})
})
},
},
async onShareAppMessage() {
return {
title: `${ this.userInfo.nickname }TA的内容超级棒`,
path: '/bundle_b/pages/community_user/community_user?id=' + this.userId
};
},
}
</script>
<style lang="scss">
.user {
background-position: center;
background-size: cover;
background-repeat: no-repeat;
background-image: url('../../static/community_user_bg.png');
.header {
padding: 20rpx 24rpx;
// 用户信息
.user_info {
.user-avatar {
width: 110rpx;
height: 110rpx;
border-radius: 50%;
border: 2px solid #FFFFFF;
}
.user-name {
width: 450rpx
}
.user-id {
display: inline-block;
padding: 4rpx 16rpx;
border-radius: 20rpx;
border: 1px solid #fff;
}
.user-setting {
width: 58rpx;
height: 58rpx;
}
}
// 用户简介
.user-intro {
padding: 20rpx 14rpx 20rpx 0;
line-height: 40rpx;
height: 140rpx;
}
// 用户数据底部
.user-footer {
// 统计数据
.user-statistics {
display: flex;
&--item {
width: 160rpx;
color: #FFFFFF;
}
}
// 操作
.user-operation {
margin-left: 24rpx;
.btn-primary {
padding: 0;
width: 126rpx;
height: 52rpx;
line-height: 52rpx;
background: linear-gradient(90.00deg, #f95f2f 0%, #ff2c3c 100%);
image {
width: 28rpx;
height: 28rpx;
vertical-align: middle;
}
}
// 已关注按钮
.followed-btn {
width: 126rpx;
height: 52rpx;
line-height: 52rpx;
padding: 0;
background-color: #FFFFFF;
}
.share {
width: 36rpx;
height: 36rpx;
margin: 20rpx 0;
margin-left: 20rpx;
}
}
}
}
.main {
.nav-title {
width: 100%;
height: 92rpx;
border-radius: 20rpx 20rpx 0 0;
background-color: #FFFFFF;
}
}
.share-tips .share-arrow {
width: 140rpx;
height: 250rpx;
float: right;
margin: 15rpx 31rpx 0 0;
}
}
</style>

View File

@ -0,0 +1,99 @@
<template>
<view>
<view v-if="lists.length">
<u-waterfall ref="uWaterfall" v-model="lists" :add-time="50">
<template v-slot:left="{leftList}">
<view style="padding:0 9rpx 0 30rpx">
<community-list width="336rpx" type="waterfall" :list="leftList"></community-list>
</view>
</template>
<template v-slot:right="{rightList}">
<view style="padding:0 30rpx 0 9rpx;">
<community-list width="336rpx" type="waterfall" :list="rightList"></community-list>
</view>
</template>
</u-waterfall>
</view>
<view class="p-t-60" v-else>
<view class="flex row-center m-t-40">
<u-image width="300" height="300" :src="'/bundle_b/static/like_null.png'"></u-image>
</view>
<view class="text-center muted m-t-40">
暂未点赞过作品哦~
</view>
</view>
</view>
</template>
<script>
import {
getCommunityLikeLists
} from "@/api/community.js"
export default {
name: 'community-like',
props: {
userId: {
type: Number
},
likeNum: {
type: Number
}
},
data() {
return {
lists: [],
page: 1,
more: 1,
pageSize: 10
}
},
watch: {
likeNum: {
handler(value) {
if (this.more) {
this.getCommunityLike()
}
},
immediate: true
}
},
mounted() {
uni.$on('changeItem', (event) => {
const index = this.lists.findIndex(el => el.id == event.id)
if (index == -1) return
this.$refs.uWaterfall.remove(event.id)
})
},
methods: {
// 获取
getCommunityLike() {
getCommunityLikeLists({
user_id: this.userId,
page_no: this.page,
page_size: this.pageSize,
}).then(res => {
if (res.code == 1) {
// 如果是第一页需手动置空列表
if (this.page == 1) {
if ('uWaterfall' in this.$refs) this.$refs.uWaterfall.clear()
this.lists = []
}
if (res.data.more === 1) {
this.page += 1
}
this.more = res.data.more;
// 异步让vue能够监听到数据改变过了
setTimeout(() => {
this.lists = [...this.lists, ...res.data.list]
}, 0)
} else {
this.$toast({
title: res.msg
})
}
})
},
}
}
</script>

View File

@ -0,0 +1,97 @@
<template>
<view>
<view v-if="lists.length">
<u-waterfall ref="uWaterfall" v-model="lists" :add-time="50">
<template v-slot:left="{leftList}">
<view style="padding:0 9rpx 0 30rpx">
<community-list width="336rpx" type="works" :list="leftList"></community-list>
</view>
</template>
<template v-slot:right="{rightList}">
<view style="padding:0 30rpx 0 9rpx;">
<community-list width="336rpx" type="works" :list="rightList"></community-list>
</view>
</template>
</u-waterfall>
</view>
<view class="p-t-60" v-else>
<view class="flex row-center m-t-40">
<u-image width="300" height="300" :src="'/bundle_b/static/works_null.png'"></u-image>
</view>
<view class="text-center muted m-t-40">
暂未发布作品哦
</view>
</view>
</view>
</template>
<script>
import {
getCommunityWorksLists
} from "@/api/community.js"
export default {
name: 'community-works',
props: {
userId: {
type: Number
},
worksNum: {
type: Number
}
},
data() {
return {
lists: [],
page: 1,
more: 1,
pageSize: 10
}
},
watch: {
worksNum: {
handler(value) {
if( this.more ) {
this.getCommunityWorks()
}
},
immediate: true
}
},
mounted() {
uni.$on('changeItem', (event) => {
const index = this.lists.findIndex(el => el.id == event.id)
if (index == -1) return
this.$refs.uWaterfall.modify(event.id, 'like', event.like)
this.$refs.uWaterfall.modify(event.id, 'is_like', event.is_like)
})
},
methods: {
// 获取
getCommunityWorks() {
getCommunityWorksLists({
user_id: this.userId,
page_no: this.page,
page_size: this.pageSize,
}).then(res => {
if( res.code == 1 ) {
// 如果是第一页需手动置空列表
if (this.page == 1) {
if('uWaterfall' in this.$refs) this.$refs.uWaterfall.clear()
this.lists = []
}
if (res.data.more === 1) {
this.page += 1
}
this.more = res.data.more;
// 异步让vue能够监听到数据改变过了
setTimeout(() => {
this.lists = [...this.lists, ...res.data.list]
}, 0)
} else {
this.$toast({ title: res.msg })
}
})
},
}
}
</script>

View File

@ -0,0 +1,152 @@
<template>
<view class="user-profile">
<view class="form bg-white m-t-20">
<view class="form--item bb">
<view class="label normal nr">签名</view>
<view class="flex-1">
<input type="text" v-model="formData.signature" placeholder="请输入个性签名">
</view>
</view>
<view class="form--item ">
<view class="label normal nr">背景</view>
<view class="flex-1">
<u-upload @on-change="change" :action="action" :header="{'token': token,'version': version}"
deletable :max-count="1" @on-remove="remove" custom-btn :width="264"
:height="200" ref="upload" :file-list="fileList" :show-progress="false">
<view slot="addBtn" class="uplader-upload" hover-class="slot-btn__hover" hover-stay-time="150">
<u-icon size="48" color="#dcdee0" name="camera" />
<view class="xs m-t-10">
上传背景图
</view>
</view>
</u-upload>
</view>
</view>
</view>
<view class="footer">
<button class="bg-primary br60 white lg" @click="onSubmit">确定</button>
</view>
</view>
</template>
<script>
import store from '@/store'
import {
baseURL,
version
} from '@/config/app.js'
import {
uploadFile
} from "@/utils/tools.js"
import {
getCommunitySetting,
apiCommunitySetSetting
} from "@/api/community.js"
export default {
data() {
return {
action: '',
token: '',
version: version,
formData: {
signature: '',
image: ''
},
}
},
computed: {
fileList() {
const { image } = this.formData;
return image ? [{url: image}] : []
}
},
onLoad() {
this.action = baseURL + '/api/file/formimage';
this.token = store.getters.token
this.initCommunitySetting()
},
methods: {
// 初始化设置
initCommunitySetting() {
getCommunitySetting().then(res => {
Object.keys(this.formData).map(item => {
this.$set(this.formData, item, res.data[item])
})
})
},
// 上传,不管成不成功都返回数据|提示
change(event) {
this.$toast({
title: JSON.parse(event.data).msg
})
if (JSON.parse(event.data).code == 1) {
this.formData.image = JSON.parse(event.data).data.uri
}
},
// 删除
remove(event) {
this.formData.image = '';
},
// 提交
onSubmit() {
apiCommunitySetSetting({
...this.formData
}).then(res => {
this.$toast({
title: res.msg
})
setTimeout(() => this.$Router.back(), 500)
})
}
}
}
</script>
<style lang="scss">
.user-profile {
.bb {
border-bottom: 1px solid #f2f2f2;
}
.form {
padding-left: 24rpx;
&--item {
display: flex;
padding: 30rpx 0;
.label {
width: 100rpx;
}
}
}
// 上传图片
.uplader-upload {
position: relative;
width: 264rpx;
height: 200rpx;
padding-top: 40rpx;
text-align: center;
margin: 11rpx;
border: 2px dashed #e5e5e5;
background-color: #FFFFFF;
>view {
color: #BBB;
}
}
.footer {
padding: 50rpx 26rpx;
}
}
</style>

View File

@ -0,0 +1,83 @@
<template>
<view class="live-room">
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :up="upOption">
<view class="p-20 flex flex-wrap" style="margin: 0 -8rpx;">
<template v-for="(live, index ) in liveLists" >
<view :key="live.id" class="m-b-20 p-l-8 p-r-8" style="width: 100%;" v-if="live.live_status == 101">
<live-item :data="live"></live-item>
</view>
<view :key="live.id" style="width: 50%;" class="m-b-20 p-l-8 p-r-8" v-else>
<live-item :data="live"></live-item>
</view>
</template>
</view>
</mescroll-body>
</view>
</template>
<script>
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins";
import { getLiveLists } from '@/api/live'
export default {
mixins: [MescrollMixin],
data() {
return {
upOption: {
auto: false,
empty: {
icon: '/static/images/goods_null.png',
tip: "暂无数据~",
},
},
liveLists: []
}
},
onShareAppMessage() {
const shareInfo = this.appConfig.share
return {
title: shareInfo.mnp_share_title,
path: "/bundle_b/pages/live-room/live-room?invite_code=" + this.inviteCode
};
},
methods: {
async downCallback() {
this.mescroll.resetUpScroll();
},
onReflesh() {
this.liveLists = []
this.mescroll.resetUpScroll();
},
upCallback(page) {
let pageNum = page.num;
let pageSize = page.size;
getLiveLists({
page_no: pageNum,
page_size: pageSize,
}).then(res => {
if (res.code == 1) {
let curPageData = res.data.list;
let curPageLen = curPageData.length;
let hasNext = !!res.data.more;
if (page.num == 1) {
this.liveLists = [];
}
this.liveLists = this.liveLists.concat(curPageData);
this.mescroll.endSuccess(curPageLen, hasNext);
}
}).catch(err => {
this.mescroll.endErr()
})
},
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,217 @@
<template>
<view class="shop-street">
<mescroll-body
v-if="appConfig.shop_street_hide"
ref="mescrollRef"
@init="mescrollInit"
@down="downCallback"
@up="upCallback"
:up="upOption"
>
<view class="shop-street-content">
<view class="search bg-white flex flex-1 flex row">
<!-- 城市 -->
<router-link
v-if="appConfig.is_open_nearby"
class="m-l-16 flex black row-center"
to="/bundle_b/pages/city/city"
>
<text class="m-r-6">{{ cityInfo.name || "选择" }}</text>
<u-icon name="arrow-down" size="24" color="#000000"></u-icon>
<!-- <image class="icon-md m-l-30" src="/static/images/icon_news.png">
</image> -->
</router-link>
<view class="flex-1">
<u-search
v-model="keyword"
@search="onReflesh"
bg-color="#F4F4F4"
placeholder="搜索关键词"
></u-search>
</view>
</view>
<scroll-view
class="bg-white"
:scroll-x="true"
:scroll-with-animation="true"
>
<view class="store-category p-t-20">
<view
class="category-item flex-col col-center"
@tap="changeActive(-1)"
>
<view
class="icon-wrap"
:style="{
borderColor: active == -1 ? colorConfig.primary : '',
}"
>
<u-image
width="68rpx"
height="68rpx"
border-radius="60rpx"
src="/static/images/icon_shop_cate.png"
/>
</view>
<view class="xs m-t-10" :class="{ primary: active == -1 }"
>全部</view
>
</view>
<view
class="category-item flex-col col-center"
v-for="(item, index) in shopCategory"
:key="index"
@tap="changeActive(index)"
>
<view
class="icon-wrap"
:style="{
borderColor: active == index ? colorConfig.primary : '',
}"
>
<u-image
width="68rpx"
height="68rpx"
border-radius="60rpx"
:src="item.image"
/>
</view>
<view class="xs m-t-10" :class="{ primary: active == index }">{{
item.name
}}</view>
</view>
</view>
</scroll-view>
<view class="store-container">
<view class="m-t-20" v-for="(item, index) in shopList" :key="index">
<shop-item :item="item"></shop-item>
</view>
</view>
</view>
</mescroll-body>
<view v-else>
<view class="p-b-20" v-if="hotGoods.length">
<goods-list type="one" :list="hotGoods"></goods-list>
</view>
<!-- <view v-else class="flex-col col-center" style="margin-top: 200rpx;">
<image class="img-null" src="/static/images/goods_null.png"></image>
<view class="muted">
暂无数据~
</view>
</view> -->
</view>
</view>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins";
import { getNearbyShops, getShopCategory } from "@/api/shop";
import { getHome } from "@/api/store";
export default {
mixins: [MescrollMixin],
data() {
return {
shopList: [],
shopCategory: [],
keyword: "",
active: -1,
upOption: {
auto: false,
empty: {
icon: "/static/images/goods_null.png",
tip: "暂无数据~",
},
},
hotGoods: [],
};
},
onShow() {
this.getHomeFun();
if (this.cityInfo) this.mescroll.resetUpScroll();
},
methods: {
async downCallback() {
await this.getShopCategoryFun();
this.mescroll.resetUpScroll();
},
// 获取首页数据
async getHomeFun() {
const { code, data } = await getHome({ city_id: this.cityInfo.id });
if (code == 1) {
this.hotGoods = data.hots;
}
},
onReflesh() {
this.shopList = [];
this.mescroll.resetUpScroll();
},
changeActive(index) {
this.active = index;
this.onReflesh();
},
upCallback(page) {
let pageNum = page.num;
let pageSize = page.size;
const { keyword, active, shopCategory } = this;
const cateId = active == -1 ? "" : shopCategory[active].id;
getNearbyShops({
page_no: pageNum,
page_size: pageSize,
name: keyword,
shop_cate_id: cateId,
city_id: this.cityInfo.id,
})
.then((res) => {
if (res.code == 1) {
let curPageData = res.data.list;
let curPageLen = curPageData.length;
let hasNext = !!res.data.more;
if (page.num == 1) {
this.shopList = [];
}
this.shopList = this.shopList.concat(curPageData);
this.mescroll.endSuccess(curPageLen, hasNext);
}
})
.catch((err) => {
this.mescroll.endErr();
});
},
async getShopCategoryFun() {
const { code, data } = await getShopCategory();
if (code == 1) {
this.shopCategory = data;
}
},
},
computed: {
...mapGetters(["cityInfo"]),
},
};
</script>
<style lang="scss">
.shop-street {
.search {
}
.store-category {
white-space: nowrap;
padding-bottom: 10rpx;
.category-item {
display: inline-flex;
padding: 0 30rpx;
.icon-wrap {
border: 1px solid transparent;
border-radius: 50%;
}
}
}
.store-container {
padding: 0 30rpx;
}
}
</style>

View File

@ -0,0 +1,180 @@
<template>
<view class="goods-box">
<view class="search">
<u-search :hideRight="true" :show-action="true" action-text="取消" :animation="true" @focus="hideRight=false"
@blur="hideRight=true" @custom="handleCancel" placeholder="请输入搜索内容" height="64" @change="mescroll.resetUpScroll()"
v-model="keyword"></u-search>
</view>
<view class="tab-control">
<view v-for="(item, index) in tabsList" :key="index" :class="{'active': currentTabs == index}"
@click="changeTabs(index)">{{ item.label }}</view>
</view>
<mescroll-uni ref="mescrollRef" top="0" height="620rpx" @init="mescrollInit" @down="downCallback"
@up="upCallback" :down="downOption" :up="upOption">
<block v-for="(goodsItem, index) in lists" :key="index">
<view class="goods-item flex row-between" @click="selectCurrentGoods(goodsItem)">
<u-image :src="goodsItem.image" width="160" height="160"></u-image>
<view class="m-l-20 flex-1">
<view class="goods-name line-1 m-b-12 nr normal">{{ goodsItem.goods_name }}</view>
<view class="m-b-16 muted sm">{{ goodsItem.shop_name }}</view>
<price-format :subscript-size="32" :first-size="32" :second-size="32"
:price="goodsItem.goods_price" :color="colorConfig.primary"></price-format>
</view>
<image :src="getCurrentSelect(goodsItem)"></image>
</view>
</block>
</mescroll-uni>
</view>
</template>
<script>
import {
getCommunityGoods
} from '@/api/community.js';
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
import MescrollMoreItemMixin from "@/components/mescroll-uni/mixins/mescroll-more-item.js";
export default {
mixins: [MescrollMixin, MescrollMoreItemMixin],
props: {
value: {
type: [Object, Array]
}
},
data() {
return {
keyword: '',
hideRight: true,
height: '', // 高度
upOption: {
empty: {
icon: '/static/images/goods_null.png',
tip: '暂无商品!', // 提示
fixed: true,
top: "0",
}
},
tabsList: [{
label: '全部',
type: 'all'
}, {
label: '已购宝贝',
type: 'buy'
}],
currentTabs: 0,
lists: [],
selectData: []
}
},
computed: {
getCurrentSelect() {
return (row) => {
return this.selectData.filter(item => item.goods_id == row.goods_id || item.id == row.goods_id).length ? '/bundle_b/static/icon_select.png' :
'/bundle_b/static/icon_unselect.png'
}
}
},
watch: {
value: {
handler(val) {
console.log(val)
this.selectData = val
},
immediate: true
}
},
methods: {
handleCancel() {
this.keyword = '';
this.mescroll.resetUpScroll()
},
// 切换标签导航
changeTabs(event) {
this.currentTabs = event;
uni.showLoading({title: '加载中'})
this.mescroll.resetUpScroll()
},
// 选择当前商品
selectCurrentGoods(event) {
const index = this.selectData.findIndex(item => item.goods_id == event.goods_id || item.id == event.goods_id)
if (index === -1) this.selectData = [...this.selectData, event]
else this.selectData.splice(index, 1)
this.$emit('change', this.selectData)
},
// 获取
async upCallback(page) {
const index = this.currentTabs;
const pageNum = page.num
const pageSize = page.size
getCommunityGoods({
keyword: this.keyword,
type: this.tabsList[index].type,
page_no: pageNum,
page_size: pageSize,
}).then(res => {
uni.hideLoading()
// 如果是第一页需手动置空列表
if (pageNum == 1 || this.keyword) this.lists = []
// 重置列表数据
const hasNext = !!res.data.more;
this.lists = [...this.lists, ...res.data.list]
this.mescroll.endSuccess(res.data.list.length, hasNext);
}).catch((err) => {
this.mescroll.endErr()
uni.hideLoading()
})
},
}
}
</script>
<style lang="scss" scoped>
.goods-box {
width: 100%;
height: 700rpx;
.search {
width: 100%;
height: 90rpx;
}
.tab-control {
border-top: 1px solid $-color-body;
border-bottom: 1px solid $-color-body;
view {
width: 200rpx;
height: 90rpx;
text-align: center;
line-height: 90rpx;
display: inline-block;
color: $-color-normal;
transition: all .2s;
}
.active {
color: $-color-primary;
}
}
.goods-item {
padding: 20rpx;
border-bottom: 1px solid $-color-body;
.goods-name {
width: 460rpx;
}
image {
width: 34rpx;
height: 34rpx;
}
}
}
</style>

View File

@ -0,0 +1,130 @@
<template>
<u-popup v-model="show" mode="bottom" closeable border-radius="14" safe-area-inset-bottom :duration="100">
<!-- 内容列表 -->
<view class="content-wrapper">
<tabs :current="active" @change="changeTabs" height="100" :is-scroll="false" width="400rpx" :showBar="false" :async="isAsync">
<tab name="宝贝">
<goods @change="handleGoods" v-model="goodsData" :i="0" :index="active"></goods>
</tab>
<tab name="店铺">
<shop @change="handleShop" v-model="shopData" :i="1" :index="active"></shop>
</tab>
</tabs>
</view>
<!-- 底部 -->
<view class="recommend-footer flex">
<button class="white br60 bg-primary btn" @click="confirm">确认</button>
</view>
</u-popup>
</template>
<script>
import Goods from "./goods.vue"
import Shop from "./shop.vue"
export default {
name: "recommend",
components: {
Goods,
Shop
},
props: {
value: {
type: Boolean
},
goods: {
type: [Object, Array]
},
shop: {
type: [Object, Array]
}
},
data() {
return {
list: [{
name: '宝贝'
}, {
name: '店铺'
}],
active: 0,
goodsData: [],
shopData: [],
isAsync: true // 是否异步
}
},
computed: {
// 弹窗Popup显示状态
show: {
get: function() {
return this.value
},
set: function(value) {
this.$emit('input', value)
}
}
},
watch: {
goods(val) {
this.active = 0;
this.goodsData = val;
console.log(this.goodsData)
},
shop(val) {
this.active = 1;
this.shopData = val
}
},
methods: {
changeTabs(event) {
if(this.goodsData.length!=0 || this.shopData.length!=0) return this.$toast({title: '不能同时选择宝贝/店铺'})
this.isAsync = false
this.active = event
this.isAsync = true
},
handleGoods(event) {
this.goodsData = event;
},
handleShop(event) {
this.shopData = event;
},
confirm() {
if( this.active == 0 ) {
this.$emit('change', {
type: 0,
data: this.goodsData
})
} else {
this.$emit('change', {
type: 1,
data: this.shopData
})
}
this.$emit('input', false)
}
}
}
</script>
<style lang="scss">
.bb {
border-bottom: 1px solid $-color-body;
}
.content-wrapper {
height: 900rpx;
}
.recommend-footer {
width: 100%;
height: 100rpx;
padding: 0 30rpx;
box-shadow: 0 -4rpx 10rpx rgba(#000000, .1);
.btn {
width: 100%;
height: 82rpx;
font-size: 32rpx;
line-height: 82rpx;
}
}
</style>

View File

@ -0,0 +1,173 @@
<template>
<view class="goods-box">
<view class="search">
<u-search :hideRight="true" :show-action="true" action-text="取消" :animation="true" @focus="hideRight=false"
@blur="hideRight=true" @custom="handleCancel" placeholder="请输入搜索内容" height="64" @change="mescroll.resetUpScroll()"
v-model="keyword"></u-search>
</view>
<view class="tab-control">
<view v-for="(item, index) in tabsList" :key="index" :class="{'active': currentTabs == index}"
@click="changeTabs(index)">{{ item.label }}</view>
</view>
<mescroll-uni ref="mescrollRef" top="0" height="620rpx" @init="mescrollInit" @down="downCallback"
@up="upCallback" :down="downOption" :up="upOption">
<block v-for="(shopItem, index) in lists" :key="index">
<view class="goods-item flex row-between" @click="selectCurrentGoods(shopItem)">
<u-image :src="shopItem.logo" width="160" height="160"></u-image>
<view class="m-l-20 flex-1">
<view class="goods-name line-1 m-b-12 nr normal">{{ shopItem.name }}</view>
</view>
<image :src="getCurrentSelect(shopItem)"></image>
</view>
</block>
</mescroll-uni>
</view>
</template>
<script>
import {
getCommunityShop
} from '@/api/community.js';
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
import MescrollMoreItemMixin from "@/components/mescroll-uni/mixins/mescroll-more-item.js";
export default {
mixins: [MescrollMixin, MescrollMoreItemMixin],
props: {
value: {
type: [Object, Array]
}
},
data() {
return {
keyword: '',
hideRight: true,
height: '', // 高度
upOption: {
empty: {
icon: '/static/images/goods_null.png',
tip: '暂无店铺 ', // 提示
fixed: true,
top: "200rpx",
}
},
tabsList: [{
label: '全部',
type: 'all'
}, {
label: '已购店铺',
type: 'buy'
}],
currentTabs: 0,
lists: [],
selectData: []
}
},
computed: {
getCurrentSelect() {
return (row) => {
return this.selectData.filter(item => item.id == row.id).length ? '/bundle_b/static/icon_select.png' :
'/bundle_b/static/icon_unselect.png'
}
}
},
watch: {
value(val) {
this.selectData = val
}
},
methods: {
handleCancel() {
this.keyword = '';
this.mescroll.resetUpScroll()
},
// 切换标签导航
changeTabs(event) {
this.currentTabs = event;
uni.showLoading({title: '加载中'})
this.mescroll.resetUpScroll()
},
// 选择当前店铺
selectCurrentGoods(event) {
const index = this.selectData.findIndex(item => item.id == event.id)
if (index === -1) this.selectData = [...this.selectData, event]
else this.selectData.splice(index, 1)
this.$emit('change', this.selectData)
},
// 获取
async upCallback(page) {
const index = this.currentTabs;
const pageNum = page.num
const pageSize = page.size
getCommunityShop({
keyword: this.keyword,
type: this.tabsList[index].type,
page_no: pageNum,
page_size: pageSize,
}).then(res => {
uni.hideLoading()
// 如果是第一页需手动置空列表
if (pageNum == 1 || this.keyword) this.lists = []
// 重置列表数据
const hasNext = !!res.data.more;
this.lists = [...this.lists, ...res.data.list]
this.mescroll.endSuccess(res.data.list.length, hasNext);
}).catch((err) => {
this.mescroll.endErr()
uni.hideLoading()
})
},
}
}
</script>
<style lang="scss" scoped>
.goods-box {
width: 100%;
height: 700rpx;
.search {
width: 100%;
height: 90rpx;
}
.tab-control {
border-top: 1px solid $-color-body;
border-bottom: 1px solid $-color-body;
view {
width: 200rpx;
height: 90rpx;
text-align: center;
line-height: 90rpx;
display: inline-block;
color: $-color-normal;
transition: all .2s;
}
.active {
color: $-color-primary;
}
}
.goods-item {
padding: 20rpx;
border-bottom: 1px solid $-color-body;
.goods-name {
width: 460rpx;
}
image {
width: 34rpx;
height: 34rpx;
}
}
}
</style>

View File

@ -0,0 +1,213 @@
<template>
<u-popup v-model="show" mode="bottom" closeable border-radius="14" safe-area-inset-bottom :duration="100">
<view class="content-wrapper">
<!-- 头部 -->
<u-sticky>
<view class="sticky">
<view class="sticky-title xl normal bold">话题</view>
<u-search :hideRight="true" :show-action="true" action-text="取消" :animation="true" @focus="hideRight=false"
@blur="hideRight=true" @custom="handleCancel" placeholder="请输入搜索内容" height="64" @change="mescroll.resetUpScroll()"
v-model="keyword"></u-search>
</view>
</u-sticky>
<!-- 内容 -->
<view class="container">
<!-- 左侧菜单 -->
<scroll-view scroll-y="true" class="left-menu">
<block v-for="(menuItem, menuIndex) in topicData" :key="menuIndex">
<view class="submenu normal" :class="{'active': menuIndex === menuCurrentIndex}" @click="selectMenu(menuIndex)">{{ menuItem.name }}</view>
</block>
</scroll-view>
<!-- 右侧内容 -->
<view class="right-content">
<mescroll-uni ref="mescrollRef" top="0" height="712rpx" bgColor="#f5f5f5" @init="mescrollInit" @down="downCallback"
@up="upCallback" :down="downOption" :up="upOption">
<view class="tags">
<view class="tags-item flex" @click="unSelectTopic">
<u-image width="120" height="120" :src="'/bundle_b/static/icon_unselect_tags.png'" borderRadius="50%"></u-image>
<text class="m-l-16 nr bold normal">不添加任何话题</text>
</view>
<template v-if="topicData[menuCurrentIndex]">
<block v-for="(topicItem, topicIndex) in topicData[menuCurrentIndex].topic" :key="topicIndex">
<view class="tags-item flex" @click="selectTopic(topicItem)">
<image :src="topicItem.image" mode="aspectFill"></image>
<view class="m-l-16">
<view class="nr bold normal">{{ topicItem.name }}</view>
<view class="m-t-10 xxs muted">{{ topicItem.click }}人在看</view>
</view>
</view>
</block>
</template>
</view>
</mescroll-uni>
</view>
</view>
</view>
</u-popup>
</template>
<script>
import { getCommunityTopicLists } from "@/api/community.js"
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
import MescrollMoreItemMixin from "@/components/mescroll-uni/mixins/mescroll-more-item.js";
export default {
mixins: [MescrollMixin, MescrollMoreItemMixin],
name: "topic",
props: {
value: {
type: Boolean
},
},
data() {
return {
keyword: '',
hideRight: true,
menuCurrentIndex: 0,
topicData: []
}
},
computed: {
// 弹窗Popup显示状态
show: {
get: function() {
return this.value
},
set: function(value) {
// #ifdef H5
this.$nextTick(() => {
this.mescroll.resetUpScroll()
})
// #endif
this.$emit('input', value)
}
}
},
methods: {
handleCancel() {
this.keyword = '';
this.mescroll.resetUpScroll()
},
// 选择菜单
selectMenu(index) {
this.menuCurrentIndex = index
},
// 选择话题
selectTopic(item) {
this.$emit('input', false)
this.$emit('change', item)
},
// 不选择话题
unSelectTopic() {
this.$emit('input', false)
this.$emit('change', '')
},
// 获取
async upCallback(page) {
getCommunityTopicLists({
name: this.keyword,
}).then(res => {
this.topicData = res.data
this.mescroll.endSuccess(10, 0);
}).catch((err) => {
this.mescroll.endErr()
})
},
}
}
</script>
<style lang="scss" scoped>
.bb {
border-bottom: 1px solid $-color-body;
}
.content-wrapper {
height: 900rpx;
.sticky {
width: 100vw;
}
.sticky-title {
padding: 24rpx 0;
text-align: center;
}
.container {
height: 712rpx;
display: flex;
// 左侧菜单
.left-menu {
width: 160rpx;
.submenu {
height: 90rpx;
line-height: 90rpx;
text-align: center;
font-size: 26rpx;
}
// 菜单选中
.active {
font-weight: 500;
color: $-color-primary;
position: relative;
background-color: rgba($-color-primary, 0.1);
}
.active::before {
content: '';
width: 6rpx;
height: 30rpx;
position: absolute;
left: 10rpx;
top: 50%;
transform: translateY(-50%);
background-color: $-color-primary;
}
}
// 右侧内容
.right-content {
width: 100%;
.tags {
padding: 20rpx;
.tags-item {
margin-bottom: 30rpx;
}
image {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
position: relative;
}
image::after {
content: '';
color: #FFFFFF;
font-size: 50rpx;
font-weight: 500;
text-align: center;
line-height: 120rpx;
width: 120rpx;
height: 120rpx;
position: absolute;
border-radius: 50%;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0.1) url('/bundle_b/static/icon_tags.png') no-repeat center center;
background-size: 40rpx;
}
}
}
}
}
</style>

View File

@ -0,0 +1,377 @@
<template>
<view class="container" :style="{'padding-bottom': pageHeight+'px'}">
<view class="main">
<!-- 上传图片 -->
<view class="uploader-container flex wrap">
<u-upload @on-change="change" :action="action" :header="{'token': token,'version': version}" deletable
:max-count="9" @on-remove="remove" multiple custom-btn :width="160" :height="160" ref="upload"
:fileList="fileList" :show-progress="false">
<view slot="addBtn" class="uplader-upload" hover-class="slot-btn__hover" hover-stay-time="150">
<u-icon size="56" color="#b1b1b1" name="camera" />
</view>
</u-upload>
</view>
<!-- 评论内容 -->
<view class="content">
<textarea v-model="formData.content" id="" maxlength="999" placeholder="说说你的购物体验和心得,我们都很期待呢~"
:disable-default-padding="true" @focus="handleFocus" @blur="pageHeight = 0" :show-confirm-bar="false"></textarea>
<view class="muted text-right">
{{ formData.content.length }}/999
</view>
</view>
<!-- 宝贝推荐 店铺推荐-->
<view class="item flex row-between recommend" @click="showRecommend">
<view class="flex nr normal">
<image src="/bundle_b/static/icon_recommend.png" class="image m-r-10"></image>
<text v-if="formData.shop.length===0 && formData.goods.length===0">宝贝/店铺</text>
<text v-if="formData.shop.length!==0">店铺</text>
<text v-if="formData.goods.length!==0">宝贝</text>
</view>
<view class="nr flex">
<text class="muted m-r-10"
v-if="formData.shop.length===0 && formData.goods.length===0">选择购买过的商品</text>
<template v-if="formData.goods.length!==0">
<block v-for="(item, index) in formData.goods" :key="index">
<u-image v-if="index<=2" :src="item.image" width="58" height="58" class="m-l-6">
</u-image>
</block>
</template>
<template v-if="formData.shop.length!==0">
<block v-for="(item, index) in formData.shop" :key="index">
<u-image v-if="index<=2" :src="item.logo" width="58" height="58" class="m-l-6">
</u-image>
</block>
</template>
<u-icon name="arrow-right" size="22" color="#707070" class="m-l-10" />
</view>
</view>
<!-- 话题 -->
<view class="item flex row-between topic" @click="showTopicPopup=true">
<view class="flex nr normal">
<image src="/bundle_b/static/icon_topic.png" class="image m-r-10"></image>
<text>话题</text>
</view>
<view class="nr flex">
<view class="tags primary-tags m-r-10" v-if="formData.topic_id">
<text>#{{ formData.topic_id.name }}</text>
</view>
<u-icon name="arrow-right" size="22" color="#707070"></u-icon>
</view>
</view>
<!-- 初始化热门标签 -->
<view class="tags m-t-10 gary-tags" v-show="!formData.topic_id">
<block v-for="topicItem in recommendTopic" :key="topicItem.id">
<text @click="handleTopic(topicItem)">#{{ topicItem.name }}</text>
</block>
</view>
</view>
<!-- 底部 -->
<view class="footer">
<button class="br60 white btn lg" @click="onSubmit">{{ !formData.id ? '发布' : '编辑' }}</button>
</view>
<!-- 组件 选择宝贝推荐 店铺推荐 -->
<recommend v-model="showRecommendPopup" :shop="formData.shop" :goods="formData.goods" @change="handleRecommend">
</recommend>
<!-- 组件 话题 -->
<topic v-model="showTopicPopup" @change="handleTopic"></topic>
</view>
</template>
<script>
import store from '@/store'
import {
baseURL,
version
} from '@/config/app.js'
import {
uploadFile,
trottle
} from "@/utils/tools.js"
import {
apiCommunityAdd,
apiCommunityEdit,
getCommunityRecommendTopic,
getCommunityDetail,
} from "@/api/community.js"
import Recommend from "./components/recommend.vue"
import Topic from "./components/topic.vue"
export default {
components: {
Recommend,
Topic
},
data() {
return {
action: '',
token: '',
version: version,
showRecommendPopup: false,
showTopicPopup: false,
pageHeight: '',
fileList: [],
formData: {
id: '',
image: [],
content: '',
shop: [],
goods: [],
topic_id: ''
},
recommendTopic: []
}
},
mounted() {
this.action = baseURL + '/api/file/formimage';
this.token = store.getters.token
this.$toast = trottle(this.$toast, 3000, this)
},
onLoad() {
const options = this.$Route.query;
if (options.id) {
this.formData.id = options.id
this.initCommunityDetail()
}
this.initRecommendTopic();
},
methods: {
// 初始化文章详情
initCommunityDetail() {
try {
getCommunityDetail({
id: this.formData.id
}).then(res => {
if (res.code == 1) {
const {
id,
content,
shop_data,
goods_data,
topic,
images
} = res.data;
this.formData.id = id
this.formData.content = content
this.formData.shop = shop_data
this.formData.goods = goods_data
this.formData.topic_id = topic
this.formData.image = images.filter(item => {
item.url = item.image
return 1
})
this.fileList = JSON.parse(JSON.stringify(this.formData.image))
}
})
} catch (err) {
this.$nextTick(() => {
this.isFirstLoading = false;
});
}
},
// 初始化获取推荐话题
initRecommendTopic() {
getCommunityRecommendTopic().then(res => {
this.recommendTopic = res.data;
})
},
// 上传,不管成不成功都返回数据|提示
change(event) {
this.$toast({
title: JSON.parse(event.data).msg
})
if (JSON.parse(event.data).code == 1) {
this.formData.image.push({
url: JSON.parse(event.data).data.uri
})
}
},
// 删除一个图片
remove(event) {
this.formData.image.splice(event, 1);
},
showRecommend() {
this.showRecommendPopup = true
},
// 处理宝贝或店铺推荐
handleRecommend(event) {
const {
type,
data
} = event;
if (type == 0) {
this.formData.goods = data;
this.formData.shop = []
} else {
this.formData.goods = [];
this.formData.shop = data
}
},
// 处理选择话题
handleTopic(event) {
this.formData.topic_id = event;
},
onSubmit() {
let params = {
...this.formData
}
params.goods = params.goods.map(item => item.goods_id)
params.shop = params.shop.map(item => item.id)
if (params.topic_id != null) params.topic_id = this.formData.topic_id.id
params.image = this.formData.image.map(item => item.url)
if (params.id) {
this.handleCommunityEdit(params)
} else {
this.handleCommunityAdd(params)
}
},
// 新增
handleCommunityAdd(params) {
apiCommunityAdd({
...params
}).then(res => {
this.$toast({
title: res.msg
})
if (res.code == 1) {
setTimeout(() => this.$Router.back(), 500)
}
})
},
// 编辑
handleCommunityEdit(params) {
apiCommunityEdit({
...params
}).then(res => {
this.$toast({
title: res.msg
})
if (res.code == 1) {
setTimeout(() => this.$Router.back(), 500)
}
})
},
// 处理文字和图片多时滚动条问题
handleFocus(event) {
this.pageHeight = event.detail.height * 1.5
setTimeout(() => {
uni.pageScrollTo({
scrollTop: 160,
})
}, 50)
}
}
}
</script>
<style lang="scss">
page {
background-color: #FFFFFF;
}
.container {
.main {
padding: 20rpx;
background-color: #FFFFFF;
// 上传图片
.uploader-container {
.uplader-upload {
position: relative;
width: 160rpx;
height: 160rpx;
line-height: 160rpx;
text-align: center;
margin: 11rpx;
border: 2px solid #f0f0f0;
border-radius: 14rpx;
background-color: #f0f0f0;
>view {
color: #b1b1b1;
}
}
}
// 文章内容
.content {
padding: 20rpx;
margin: 10rpx 0;
border-radius: 10rpx;
// background: #f8f8f8;
textarea {
width: 100%;
min-height: 230rpx;
}
}
.item {
height: 88rpx;
.image {
width: 40rpx;
height: 40rpx;
}
}
// 推荐 宝贝 店铺
.recommend {
border-bottom: 1px solid #F2F2F2;
}
// 标签
.tags {
// padding-bottom: 14rpx;
// border-bottom: 1px solid #F2F2F2;
}
.tags text {
display: inline-block;
margin-bottom: 10rpx;
margin-right: 20rpx;
border-radius: 26rpx;
padding: 8rpx 24rpx;
}
// 标签主题颜色
.primary-tags text {
color: $-color-primary;
margin-bottom: 0;
background: rgba($-color-primary, .1);
}
// 标签主题颜色
.gary-tags text {
color: $-color-lighter;
background: #f4f4f4;
}
}
.footer {
left: 0%;
bottom: 50rpx;
width: 100%;
padding: 0 24rpx;
position: fixed;
.btn {
height: 84rpx;
line-height: 84rpx;
background-color: rgba($-color-primary, .8);
}
}
}
</style>

BIN
bundle_b/static/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB