Files
huangjingfen/pro_v3.5.1/view/uniapp/pages/discover/discoverCreate/index.vue
apple 78de918c37 Initial commit: queue workspace
Made-with: Cursor
2026-03-21 02:55:24 +08:00

698 lines
26 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view :style="colorStyle">
<view>
<view class="w-full notice flex-y-center pl-30 fs-26"
v-show="(configData.community_integral_num > 0 || configData.community_exp_num > 0) && !id">
完成本次发布审核通过可得
<text class="font-num" v-show="configData.community_integral_num > 0">{{configData.community_integral_num}}积分</text>
<text class="font-num" v-show="configData.community_integral_num > 0 && configData.community_exp_num > 0">+</text>
<text class="font-num" v-show="configData.community_exp_num > 0">{{configData.community_exp_num}}经验值</text>奖励
</view>
<view class="px-30">
<view class="pt-30 grid-column-3 grid-gap-20rpx">
<view class="relative w-full h-214" v-for="(item, index) in formData.slider_image" :key="index"
@tap="proviewPic(item)">
<image :src="item" mode="aspectFill" class="w-full h-214 rd-16rpx"></image>
<view class="abs-rt w-32 h-32 del-pic flex-center" @tap.stop="DelPic(index)">
<text class="iconfont icon-ic_close text--w111-fff fs-20"></text>
</view>
<view class="abs-lt poster-badge flex-center fs-18" v-if="index == 0">封面</view>
</view>
<view
class="w-full h-214 flex-col flex-center upload bg--w111-f5f5f5 rd-16rpx text--w111-999"
@click="uploadpic"
v-if="!formData.video_url && formData.slider_image.length < 9"
>
<text class="iconfont icon-ic_camera fs-60"></text>
<text class="fs-28 lh-40rpx pt-20">添加照片</text>
</view>
<view
class="w-full h-214 flex-col flex-center upload bg--w111-f5f5f5 rd-16rpx text--w111-999"
@click="uploadVideo"
v-if="!formData.slider_image.length && !formData.video_url"
>
<text class="iconfont icon-icon_video fs-60"></text>
<text class="fs-28 lh-40rpx pt-20">添加视频</text>
</view>
<block v-if="formData.video_url">
<view class="w-full h-214 flex-col flex-center video-bg rd-16rpx relative" @tap="openVideoPopup">
<view class="abs-rt w-32 h-32" @tap.stop="delVideo()"></view>
<view class="video-player flex-center">
<text class="iconfont icon-ic_right2 fs-28 text--w111-fff relative"></text>
</view>
<text class="pt-22 fs-18 text--w111-fff">点击可预览视频</text>
</view>
<view class="w-full h-214 flex-col flex-center upload bg--w111-f5f5f5 rd-16rpx text--w111-999" @click="uploadPoster" v-if="!formData.image">
<text class="iconfont icon-ic_camera fs-60"></text>
<text class="fs-28 lh-40rpx pt-12">添加封面</text>
</view>
<view class="w-full h-214 flex-col flex-center upload bg--w111-f5f5f5 rd-16rpx relative text--w111-999" @click="uploadPoster" v-if="formData.image">
<image :src="formData.image" mode="aspectFill" class="w-full h-214 rd-16rpx"></image>
<view class="abs-lt poster-badge flex-center fs-18">封面</view>
<view class="poster-huan flex-center text--w111-fff fs-18">更换封面</view>
</view>
</block>
</view>
<view class="py-32 bb-e">
<input class="fs-30 fw-500" placeholder="填写标题会有更多赞噢~" placeholder-class="text--w111-999" v-model="formData.title" maxlength="20" />
</view>
<view class="pt-18 pb-46 bb-e textarea">
<textarea
class="w-full fs-28 h-280"
placeholder="分享你的体验和心得,你的文字我们都很喜欢~"
name="comment"
placeholder-class="text--w111-999"
v-model="formData.content"
:maxlength="600"
></textarea>
</view>
<view class="h-100 flex-between-center" @tap="openDrawer(0)">
<view class="flex-y-center">
<text class="iconfont icon-icon_talk_2-2"></text>
<view class="pl-8">添加话题</view>
</view>
<view class="fs-28 text--w111-999 flex-y-center" v-show="!topicSelectedList.length">
<text>去添加</text>
<text class="iconfont icon-ic_rightarrow"></text>
</view>
<view class="fs-28 flex-y-center" v-show="topicSelectedList.length">
<view class="w-428 line1 text-right">
<text class="pl-8" v-for="(item, index) in topicSelectedList" :key="index">#{{ item.name }}</text>
</view>
<text class="iconfont icon-ic_rightarrow text--w111-999"></text>
</view>
</view>
<view class="mt-30 flex-between-center" @tap="openDrawer(1)">
<view class="flex-y-center">
<text class="iconfont icon-icon_Link"></text>
<view class="pl-8">
关联商品
<!-- <text v-show="productNum">({{ productNum }})</text> -->
</view>
</view>
<view class="flex-y-center justify-end" v-show="productSelect.length">
<image class="w-60 h-60 rd-6rpx block mr-12" v-for="(item, index) in productSelect" :key="index" :src="item.image"></image>
<text class="iconfont icon-ic_rightarrow fs-32 text--w111-999"></text>
</view>
<view class="fs-28 text--w111-999 flex-y-center" v-show="!productSelect.length">
<text>请选择</text>
<text class="iconfont icon-ic_rightarrow"></text>
</view>
</view>
</view>
</view>
<view class="pb-safe">
<view class="h-200"></view>
</view>
<view class="fixed-lb w-full pb-safe bg--w111-fff z-100">
<view class="footer-box flex-center">
<view class="w-690 h-88 flex-center bg-color text--w111-fff fs-28 rd-44rpx" @tap="confirmSave">发布</view>
</view>
</view>
<base-drawer mode="bottom" :visible="visible" background-color="transparent" mask maskClosable @close="confirmProductDrawer">
<view class="w-full bg--w111-fff rd-t-40rpx py-32 relative">
<view class="close-btn flex-center" @tap="confirmProductDrawer">
<text class="iconfont icon-ic_close text--w111-666 fs-24"></text>
</view>
<view class="flex-center fs-30 text--w111-999">
<view :class="{ 'tab-active': tabActive == 0 }" @tap="changeTab(0, 'pay')">已购</view>
<view class="ml-70" :class="{ 'tab-active': tabActive == 1 }" @tap="changeTab(1, 'collect')">收藏</view>
<view class="ml-70" :class="{ 'tab-active': tabActive == 2 }" @tap="changeTab(2, 'visit')">浏览</view>
</view>
<view class="px-30 mt-50 flex-between-center">
<view class="flex-1 px-32 h-58 rd-30rpx bg--w111-f5f5f5 flex-y-center">
<text class="iconfont icon-ic_search text--w111-999 fs-28"></text>
<input type="text" class="flex-1 pl-20 fs-26" placeholder="输入关键字搜索" placeholder-class="text--w111-ccc" v-model="where.keyword" />
</view>
<text class="fs-28 pl-24" @tap="getSearch">搜索</text>
</view>
<view class="px-30">
<scroll-view scroll-y="true" @scrolltolower="getProductList" style="height: 780rpx">
<view class="flex-between-center" v-for="(item, index) in productList" :key="index">
<text
class="iconfont text--w111-ccc fs-36"
:class="item.isSelect ? 'icon-a-ic_CompleteSelect' : 'icon-ic_unselect'"
@tap="selectItem(item, index)"
></text>
<view class="flex-1 flex h-160 mt-30 pl-30">
<image class="w-160 h-160 rd-16rpx" :src="item.image" mode="aspectFill"></image>
<view class="h-160 flex-1 flex-col justify-between pl-30">
<view class="h-68 lh-34rpx fs-30 line2">{{ item.store_name }}</view>
<baseMoney :money="item.price" symbolSize="24" integerSize="40" decimalSize="24" weight></baseMoney>
</view>
</view>
</view>
<view v-if="!productList.length">
<emptyPage title="暂无商品,去看点别的吧~"></emptyPage>
</view>
</scroll-view>
</view>
<view class="mx-20 pb-safe bg--w111-fff">
<view class="mt-52 h-88 flex-center rd-44rpx bg-color fs-28 text--w111-fff mt-24" @tap="confirmProductDrawer">确定({{ productNum }}/5)</view>
</view>
</view>
</base-drawer>
<uni-popup ref="popup">
<view class="flex-center">
<view class="fixed-lt h-80 flex-y-center pl-20 z-999" :style="{ top: sysHeight + 'px' }" @tap="hideModal">
<text class="iconfont icon-ic_close fs-40 text--w111-fff"></text>
</view>
<!--#ifndef APP-PLUS-->
<video id="myVideo" autoplay loop muted :src="formData.video_url" :style="[videoStyle]"></video>
<!--#endif-->
<!--#ifdef APP-PLUS-->
<view v-html="videoHtml"></view>
<!--#endif-->
</view>
</uni-popup>
<canvas
canvas-id="canvas"
v-if="canvasStatus"
:style="{ width: canvasWidth + 'px', height: canvasHeight + 'px', position: 'absolute', left: '-100000px', top: '-100000px' }"
></canvas>
</view>
</template>
<script>
import colors from '@/mixins/color';
import store from '@/store';
import { mapState, mapGetters } from 'vuex';
import { toLogin } from '@/libs/login.js';
import { TOKENNAME, HTTP_REQUEST_URL } from '@/config/app.js';
import baseDrawer from '@/components/tui-drawer/tui-drawer.vue';
import emptyPage from '@/components/emptyPage.vue';
import { getProductslist } from '@/api/store.js';
import {
getTopicApi,
getProductApi,
communitySaveApi,
communityInfoApi,
communityUpdateApi,
getCommunityConfig ,
} from '@/api/community.js';
import { Debounce } from '@/utils/validate.js'
let app = getApp();
export default {
data() {
return {
sysHeight: app.globalData.sysHeight,
id: '',
canvasStatus: false,
visible: false,
tabActive: 0,
keyword: '',
topicList: [],
recommendTopic: [],
topicSelectedList: [],
productList: [],
productSelect: [],
loading: false,
where: {
page: 1,
limit: 20,
type: 'pay',
keyword: ''
},
formData: {
content_type: null, //内容类型1图文2视频
title: '',
content: '',
image: '', //视频封面
video_url: '',
slider_image: [],
topic_name: [],
product_id: []
},
upload_max: 50,
uploadUrl: `${HTTP_REQUEST_URL}/api/upload/video`,
videoContext: '',
productNum: 0,
topicNum: 0,
canvasWidth: '',
canvasHeight: '',
configData:{
community_exp_num:0, //经验
community_integral_num:0 //积分
},
};
},
components: { baseDrawer, emptyPage },
computed: {
...mapGetters(['isLogin']),
videoHtml() {
return `<video autoplay loop muted controls="controls" width="100%" height="870px"><source src="${this.formData.video_url}" type="video/mp4"></video>`;
},
videoStyle() {
return {
width: uni.getWindowInfo().windowWidth + 'px',
height: uni.getWindowInfo().windowHeight + 'px'
};
},
popupHeight(){
return uni.getWindowInfo().windowHeight * 0.8
},
},
mixins: [colors],
onShow() {
let that = this;
uni.$on("topicSave", data => {
if (data) {
that.topicSelectedList = data;
}
// 清除监听
uni.$off('topicSave');
})
},
onLoad(options) {
if (this.isLogin) {
this.getConfig();
if (options.id) {
this.id = options.id;
this.getInfo();
}
if (options.topic_name) {
// this.topicName = options.topic_name;
this.topicSelectedList.push({name:options.topic_name})
}
this.getProductList();
// #ifndef APP-PLUS
this.videoContext = uni.createVideoContext('myVideo', this);
// #endif
} else {
toLogin();
}
},
methods: {
getConfig(){
getCommunityConfig().then(res=>{
this.configData = res.data;
})
},
proviewPic(url){
uni.previewImage({
urls: this.formData.slider_image,
current: url
});
},
DelPic(index) {
this.formData.slider_image.splice(index, 1);
},
delVideo() {
this.formData.video_url = '';
},
openVideoPopup() {
this.$refs.popup.open();
this.videoContext = uni.createVideoContext('myVideo', this);
this.$nextTick(() => {
this.videoContext.play();
});
},
hideModal() {
this.$refs.popup.close();
},
uploadPoster() {
let that = this;
this.canvasStatus = true;
that.$util.uploadImageChange(
{ count: 1, url: 'upload/image' },
function (res) {
that.formData.image = res.data.url;
},
(res) => {
this.canvasStatus = false;
},
(res) => {
this.canvasWidth = res.w;
this.canvasHeight = res.h;
}
);
},
uploadpic() {
let that = this;
this.canvasStatus = true;
that.$util.uploadImageChange(
{ count: 9, url: 'upload/image' },
function (res) {
if (that.formData.slider_image.length == 9)
return that.$util.Tips({
title: '最多上传9张'
});
that.formData.slider_image.push(res.data.url);
},
(res) => {
this.canvasStatus = false;
},
(res) => {
this.canvasWidth = res.w;
this.canvasHeight = res.h;
}
);
},
// 上传视频
uploadVideo() {
let that = this;
uni.chooseVideo({
sourceType: ['camera', 'album'],
success: (res) => {
uni.showLoading({
title: '视频上传中'
});
if (Math.ceil(res.size / 1024) < that.upload_max * 1024) {
uni.uploadFile({
url: that.uploadUrl,
filePath: res.tempFilePath,
name: 'file',
header: {
// #ifdef MP
'Content-Type': 'multipart/form-data',
// #endif
[TOKENNAME]: 'Bearer ' + store.state.app.token
},
success: (uploadFileRes) => {
uni.hideLoading();
let data = JSON.parse(uploadFileRes.data);
if (data.status == 200) {
that.formData.video_url = data.data.url;
} else {
return this.$util.Tips({
title: data.msg
});
}
},
complete: function (err) {
uni.hideLoading();
}
});
} else {
uni.hideLoading();
uni.showModal({
title: '提示',
showCancel: false,
content: `视频超出限制${that.upload_max}MB`
});
}
},
fail: (err) => {
that.$util.Tips({
title: err
});
}
});
},
confirmProductDrawer() {
this.visible = false;
},
openDrawer(type) {
if (type == 1) {
if (this.id) {
this.productList.map((itemA) => {
this.formData.product_id.forEach((itemB) => {
if (itemB === itemA.product_id) {
this.$set(itemA, 'isSelect', true);
}
});
});
}
this.visible = !this.visible;
} else {
let that = this;
// uni.$emit("topicOpen", that.topicSelectedList)
let jsonStr = '';
if(this.topicSelectedList.length){
jsonStr = JSON.stringify(this.topicSelectedList)
}
uni.navigateTo({
url: '/pages/discover/discoverCreate/topic?data=' + jsonStr
})
// this.showTopic = !this.showTopic;
// if (this.showTopic) {
// this.topicKeyword = '#';
// this.focus = true;
// }
}
},
getProductList() {
if (this.loading) return;
this.loading = true;
getProductApi(this.where)
.then((res) => {
res.data.map((item) => {
this.$set(item, 'isSelect', false);
this.formData.product_id.forEach((itemB) => {
if (itemB === item.product_id) {
this.$set(item, 'isSelect', true);
}
});
});
let list = res.data;
let loading = list.length < this.where.limit;
this.productList = this.productList.concat(list);
this.where.page++;
this.loading = loading;
})
.catch((err) => {
this.loading = false;
return this.$util.Tips({
title: err
});
});
},
getSearch() {
this.loading = false;
this.where.page = 1;
this.productList = [];
this.getProductList();
},
changeTab(val, type) {
this.tabActive = val;
this.where.type = type;
this.getSearch();
},
selectItem(item) {
if (item.isSelect) {
item.isSelect = false;
if (this.formData.product_id.includes(item.product_id)) {
let i = this.productSelect.findIndex((o) => o.id == item.product_id);
this.productSelect.splice(i, 1);
this.formData.product_id.splice(i, 1);
this.productNum--;
}
} else {
if (this.productSelect.length == 5)
return this.$util.Tips({
title: '最多选择5件商品'
});
item.isSelect = true;
let arr = [];
this.productSelect.forEach((k) => {
arr.push(k.id);
});
if (!arr.includes(item.product_id)) {
this.productNum++;
this.productSelect.push(item);
this.formData.product_id.push(item.product_id);
}
}
},
confirmSave() {
// if(!this.formData.title) return this.$util.Tips({
// title: '请输入标题'
// })
if (!this.formData.content)
return this.$util.Tips({
title: '请输入内容'
});
if (!this.formData.video_url && !this.formData.slider_image.length)
return this.$util.Tips({
title: '请上传要分享的图片'
});
if (this.formData.video_url && !this.formData.image)
return this.$util.Tips({
title: '请上传海报'
});
this.formData.topic_name = this.topicSelectedList.map(item => item.name);
this.formData.content_type = this.formData.slider_image.length ? 1 : 2;
uni.showLoading({
title: '正在提交...'
});
let funApi = '';
funApi = this.id ? communityUpdateApi(this.id, this.formData) : communitySaveApi(this.formData);
funApi
.then((res) => {
uni.hideLoading();
let exp = this.configData.community_exp_num;
let integral = this.configData.community_integral_num;
return this.$util.Tips(
{
title: res.msg
},
{
tab: 4,
url: '/pages/discoverIndex/index'
}
);
})
.catch((err) => {
uni.hideLoading();
return this.$util.Tips({
title: err
});
});
},
getInfo() {
communityInfoApi(this.id)
.then((res) => {
let info = res.data;
let keys = Object.keys(this.formData);
keys.map((i) => {
this.formData[i] = info[i];
});
if (this.formData.product_id == null) {
this.formData.product_id = [];
}
if (res.data.topic.length) {
this.topicSelectedList = res.data.topic;
}
if (res.data.product && res.data.product.length) {
this.productSelect = res.data.product;
}
this.productNum = res.data.product.length;
this.topicNum = res.data.topic.length;
})
.catch((err) => {
return this.$util.Tips({
title: err
});
});
},
}
};
</script>
<style>
page {
background-color: #ffffff;
}
</style>
<style lang="scss">
.notice {
height: 84rpx;
background: var(--view-minorColorT);
}
.del-pic {
background-color: #999;
border-radius: 0 16rpx 0 16rpx;
}
.close-btn {
position: absolute;
right: 28rpx;
top: 28rpx;
width: 36rpx;
height: 36rpx;
border-radius: 50%;
background-color: #eee;
}
.bb-e {
border-bottom: 1rpx solid #eee;
}
.b-e {
border: 1rpx solid #eee;
}
.b-d {
border: 1rpx solid #ddd;
}
.filter-cell ~ .filter-cell{
border-top: 1px solid #f5f5f5;
}
.textarea {
height: 100%;
}
.label-item {
color: var(--view-theme);
background: var(--view-minorColorT);
border: 1rpx solid transparent;
}
.icon-a-ic_CompleteSelect {
color: var(--view-theme);
}
.pro-cell {
padding: 36rpx 30rpx;
}
.add-btn {
width: 690rpx;
height: 88rpx;
background: var(--view-theme);
border-radius: 50rpx;
background-color: var(--view-theme);
margin-bottom: 32rpx;
}
.tab-active {
font-weight: 500;
color: #333;
position: relative;
&:after {
content: '';
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: -14rpx;
width: 40rpx;
height: 5rpx;
background: var(--view-theme);
border-radius: 4rpx;
}
}
.poster-badge {
width: 54rpx;
height: 29rpx;
background: var(--view-theme);
color: #fff;
border-radius: 16rpx 0rpx 16rpx 0rpx;
}
.video-bg {
background-image: url('@/static/img/video_bg.png');
background-repeat: no-repeat;
background-size: cover;
}
.video-player {
width: 50rpx;
height: 50rpx;
border-radius: 50%;
background: rgba(0, 0, 0, 0.5);
.iconfont {
left: 2rpx;
}
}
.poster-huan {
position: absolute;
bottom: 0;
width: 100%;
height: 40rpx;
background: rgba(0, 0, 0, 0.5);
border-radius: 0rpx 0rpx 16rpx 16rpx;
}
.canvas-box {
position: absolute;
left: -100px;
top: -100px;
width: 750px;
height: 1624px;
}
.footer-box {
height: 126rpx;
.w-690 {
width: 690rpx;
}
}
.mt-62{
margin-top: 62rpx;
}
.close-icon{
top: 36rpx;
right: 32rpx;
}
.overflow-scroll{
overflow-y: scroll;
}
</style>