| | |
| | | needToken: true |
| | | }); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 删除视频 |
| | | * |
| | | * @param params |
| | | */ |
| | | export function delVideo(id) { |
| | | return http.request({ |
| | | url: "/lmk/video/" + id, |
| | | method: Method.DELETE, |
| | | needToken: true |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 修改视频 |
| | | * |
| | | * @param params |
| | | */ |
| | | export function updateVideo(data) { |
| | | return http.request({ |
| | | url: "/lmk/video", |
| | | method: Method.PUT, |
| | | needToken: true, |
| | | data: data |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 用户下架视频 |
| | | * |
| | | * @param params |
| | | */ |
| | | export function userDownVideo(id) { |
| | | return http.request({ |
| | | url: "/lmk/video/down/" + id, |
| | | method: Method.POST, |
| | | needToken: true |
| | | }); |
| | | } |
| | |
| | | export default { |
| | | name: 'DropdownMenu', |
| | | props: { |
| | | // 业务数据,选中菜单后一同返回 |
| | | data: { |
| | | type: Object |
| | | }, |
| | | // 选项列表 |
| | | options: { |
| | | type: Array, |
| | |
| | | // 根据配置返回整个对象或value值 |
| | | const emitValue = typeof item === 'object' ? item[this.valueKey] : item |
| | | this.$emit('input', emitValue) |
| | | this.$emit('change', emitValue) |
| | | this.$emit('change', emitValue, this.data) |
| | | } |
| | | } |
| | | } |
| | |
| | | "easycom": { |
| | | "autoscan": true, |
| | | "custom": { |
| | | "^u-(.*)": "@/components/uview-components/uview-ui/components/u-$1/u-$1.vue", //uview, |
| | | "^uni-(.*)": "@/uni_modules/uni-$1/components/uni-$1/uni-$1.vue" // uniapp组件 |
| | | "^u-(.*)": "@/components/uview-components/uview-ui/components/u-$1/u-$1.vue" //uview, |
| | | // "^uni-(.*)": "@/uni_modules/uni-$1/components/uni-$1/uni-$1.vue" // uniapp组件 |
| | | } |
| | | }, |
| | | // "preloadRule": { |
| | |
| | | <view class="goods-price" style="flex: 1;">¥{{ goods.price }}</view> |
| | | <view @click.stop="() => {}" style="flex: 1;display: flex;justify-content: center;align-items: center;"> |
| | | <view style="width: 90rpx">数量:</view> |
| | | <uni-number-box v-model="goods.selectNum" :min="0"/> |
| | | <uni-number-box v-model="goods.goodsNum" :min="0"/> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | }, |
| | | // 重新上传 |
| | | reUpload() { |
| | | this.resetData() |
| | | this.videoInfo = { |
| | | url: '', |
| | | fileKey: '', |
| | | fileType: '', |
| | | fileSize: 0, |
| | | originalFileName: '', |
| | | cover: '' |
| | | }; |
| | | this.formData.videoFileKey = '' |
| | | this.formData.cover = '' |
| | | this.formData.videoFit = 'cover' |
| | | this.formData.videoDuration = 0 |
| | | this.formData.videoImgs = [] |
| | | this.formData.fileInfo = {} |
| | | this.formData.videoContentType = 'video' |
| | | this.videoPreviewImgs = [] |
| | | this.fileTypeShow = true |
| | | }, |
| | | // 选择视频图集 |
| | |
| | | |
| | | // 选择商品 |
| | | chooseGoods() { |
| | | if(this.selectedGoodsList.length > 0) { |
| | | const selectedGoodsIds = new Set(this.selectedGoodsList.map(i => i.goodsId)); |
| | | console.log(selectedGoodsIds, "mimade"); |
| | | this.goodsList?.forEach(goods => { |
| | | this.$set(goods, 'selected', selectedGoodsIds.has(goods.goodsId)); |
| | | }); |
| | | } |
| | | this.showGoodsPicker = true; |
| | | }, |
| | | |
| | | // 选择具体商品 |
| | | selectGoods(goods, index) { |
| | | if(! this.selectedGoodsList.some(item => item.id === goods.id)) { |
| | | goods["selectNum"] = 1 |
| | | goods["goodsNum"] = 1 |
| | | this.selectedGoodsList.push(goods) |
| | | this.goodsList[index].selected = true |
| | | } else { |
| | |
| | | if (valid && this.canPublish) { |
| | | this.loading = true; |
| | | this.formData.fileInfo = this.videoInfo; |
| | | this.formData["goodsList"] = this.selectedGoodsList.map(item => {return {goodsId: item.goodsId, goodsNum: item.selectNum}}); |
| | | this.formData["goodsList"] = this.selectedGoodsList.map(item => {return {goodsId: item.goodsId, goodsNum: item.goodsNum}}); |
| | | publish(this.formData).then(res => { |
| | | uni.showToast({ |
| | | title: '视频已提交审核~', |
| | |
| | | <view class="stat"> |
| | | <uni-icons type="heart" size="16" color="#fff"></uni-icons> |
| | | <text>{{item.collectNum}}</text> |
| | | <view class="more-op"> |
| | | <view class="more-op" v-if="userInfo.self"> |
| | | <dropdown-menu |
| | | :options="item.options" |
| | | :data="{id: item.id, title: item.title}" |
| | | placement="top" |
| | | theme-color="#07C160" |
| | | @change="handleChange" |
| | |
| | | <!-- <image src="/static/images/empty.png" mode="aspectFit" class="empty-image"></image> --> |
| | | <text class="empty-text">还没有点赞作品哦~</text> |
| | | </view> |
| | | |
| | | <!-- 删除视频提醒框 --> |
| | | <uni-popup ref="delDialog" type="dialog"> |
| | | <uni-popup-dialog type="error" cancelText="取消" confirmText="删除" title="提醒" :content="`您正在删除:${opVideo.title}`" @confirm="deleteVideo" |
| | | @close="dialogClose"></uni-popup-dialog> |
| | | </uni-popup> |
| | | |
| | | <!-- 下架视频提醒框 --> |
| | | <uni-popup ref="downDialog" type="dialog"> |
| | | <uni-popup-dialog type="error" cancelText="取消" confirmText="下架" title="提醒" :content="`您正在下架:${opVideo.title}`" @confirm="downVideo" |
| | | @close="dialogClose"></uni-popup-dialog> |
| | | </uni-popup> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | import DropdownMenu from '@/components/dropdown-menu.vue' |
| | | |
| | | import {getAuthorInfo, getAuthorVideoPage, getAuthorCollectVideoPage} from '@/api/user.js' |
| | | import {subscribe, unSubscribe} from '@/api/video.js' |
| | | import {subscribe, unSubscribe, delVideo, updateVideo, userDownVideo} from '@/api/video.js' |
| | | export default { |
| | | components: {DropdownMenu}, |
| | | data() { |
| | |
| | | { command: 2, label: '上海' }, |
| | | { command: 3, label: '广州' } |
| | | ], |
| | | opVideo: { // 正在操作的视频 |
| | | id: '', |
| | | title: '' |
| | | }, |
| | | currentTab: 'works', // works: 作品, likes: 喜欢 |
| | | authorId: '', |
| | | userInfo: { |
| | |
| | | this.getAuthorVideoPage(); |
| | | }, |
| | | methods: { |
| | | handleChange(value) { |
| | | dialogClose() { |
| | | this.opVideo = { |
| | | id: '', |
| | | title: '' |
| | | } |
| | | }, |
| | | // 下架视频 |
| | | downVideo() { |
| | | userDownVideo(this.opVideo.id).then(res => { |
| | | uni.showToast({ |
| | | title: '下架成功', |
| | | duration: 2000 |
| | | }); |
| | | // 刷新数据 |
| | | this.videoList = []; |
| | | this.videoQuery.pageNumber = 1; |
| | | this.getAuthorVideoPage(); |
| | | }) |
| | | }, |
| | | // 删除视频 |
| | | deleteVideo() { |
| | | delVideo(this.opVideo.id).then(res => { |
| | | uni.showToast({ |
| | | title: '删除成功', |
| | | duration: 2000 |
| | | }); |
| | | // 刷新数据 |
| | | this.videoList = []; |
| | | this.videoQuery.pageNumber = 1; |
| | | this.getAuthorVideoPage(); |
| | | }) |
| | | }, |
| | | // 触发视频操作 |
| | | handleChange(value, data) { |
| | | console.log('选中值:', value) |
| | | this.opVideo.id = data.id; |
| | | this.opVideo.title = data.title; |
| | | if (value === 'DELETE') { |
| | | this.$refs.delDialog.open() |
| | | } else if (value === 'DOWN') { |
| | | this.$refs.downDialog.open() |
| | | } else if (value === 'EDIT') { |
| | | // 跳转编辑视频页面 |
| | | uni.navigateTo({ |
| | | url: `/pages/video/video-edit?id=${this.opVideo.id}` |
| | | }); |
| | | } |
| | | }, |
| | | getPage() { |
| | | if(this.currentTab === 'works') { |
| | |
| | | <template> |
| | | <view class="publish-container"> |
| | | <u-popup v-model="fileTypeShow" mode="bottom" round="20" height="35%"> |
| | | <view style="width: 100%;height:100%;display: flex;flex-direction: column;justify-content: center;align-items: center;"> |
| | | <view>请选择要发布的类型</view> |
| | | <u-button style="width: 50%;margin-bottom: 30rpx;margin-top: 20rpx;" type="success" @click="chooseVideo">视频</u-button> |
| | | <u-button style="width: 50%;" type="success" @click="chooseImgs">图片</u-button> |
| | | </view> |
| | | </u-popup> |
| | | <!-- 视频上传区域 --> |
| | | <view class="upload-section"> |
| | | <view class="upload-btn" @click="chooseVideo" v-if="!videoInfo.url"> |
| | | <view class="upload-btn" @click="this.fileTypeShow = true" v-if="!formData.videoFileKey && formData.videoImgs.length < 1"> |
| | | <u-icon name="plus" size="40" color="#999"></u-icon> |
| | | <text class="upload-text">点击上传视频</text> |
| | | <text class="upload-tips">支持MP4格式,最长60秒</text> |
| | | <text class="upload-text">点击上传</text> |
| | | </view> |
| | | |
| | | <view class="video-preview" v-else> |
| | | <view class="video-preview" v-else-if="formData.videoContentType === 'video'"> |
| | | <video |
| | | :src="videoInfo.url" |
| | | :object-fit="formData.videoFit" |
| | | class="video-player" |
| | | :poster="videoInfo.cover || ''" |
| | | ></video> |
| | | <view class="progress-box"> |
| | | <view class="progress-box" v-if="showUploadProgress"> |
| | | <progress style="width: 100%;" :percent="videoUploadProgress" active-mode="forwards" show-info stroke-width="6" :active="true" active-color="#ff573e" /> |
| | | </view> |
| | | <view class="video-actions"> |
| | | <u-button type="error" size="mini" @click="reUpload">重新上传</u-button> |
| | | <u-button type="primary" size="mini" @click="chooseCover" v-if="videoInfo.url">{{formData.cover ? '更换封面' : '请选择封面'}}</u-button> |
| | | <u-button type="primary" size="mini" @click="chooseCover" v-if="formData.videoFileKey">{{formData.cover ? '更换封面' : '请选择封面'}}</u-button> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="image-list" v-else-if="formData.videoContentType === 'img'"> |
| | | <view |
| | | v-for="item in videoPreviewImgs" |
| | | :key="item" |
| | | class="image-item" |
| | | :style="{width: itemWidth + 'px', height: itemWidth + 'px'}" |
| | | > |
| | | <image |
| | | :src="item" |
| | | mode="aspectFill" |
| | | class="image" |
| | | /> |
| | | </view> |
| | | <view class="video-actions"> |
| | | <u-button type="error" size="mini" @click="reUpload">重新上传</u-button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | @click="chooseGoods" |
| | | ></u-icon> |
| | | </u-input> |
| | | <view class="goods-preview" v-if="selectedGoods"> |
| | | <image :src="selectedGoods.image" class="goods-image"></image> |
| | | <view class="goods-preview" @click="chooseGoods" v-for="goods in selectedGoodsList" :key="goods.id"> |
| | | <image :src="goods.thumbnail" class="goods-image"></image> |
| | | <view class="goods-info"> |
| | | <text class="goods-name">{{ selectedGoods.name }}</text> |
| | | <text class="goods-price">¥{{ selectedGoods.price }}</text> |
| | | <text class="goods-name">{{ goods.goodsName }}</text> |
| | | <view style="display: flex;"> |
| | | <view class="goods-price" style="flex: 1;">¥{{ goods.price }}</view> |
| | | <view @click.stop="() => {}" style="flex: 1;display: flex;justify-content: center;align-items: center;"> |
| | | <view style="width: 90rpx">数量:</view> |
| | | <uni-number-box v-model="goods.goodsNum" :min="0"/> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <u-icon |
| | | style="position: absolute;right: 8rpx;top: 8rpx" |
| | | name="close" |
| | | size="20" |
| | | @click="clearGoods" |
| | | size="24" |
| | | @click.stop="clearGoods(goods)" |
| | | ></u-icon> |
| | | </view> |
| | | </view> |
| | |
| | | <!-- 发布按钮 --> |
| | | <view class="publish-btn"> |
| | | <u-button |
| | | type="primary" |
| | | type="success" |
| | | shape="circle" |
| | | :loading="loading" |
| | | @click="handlePublish" |
| | |
| | | </view> |
| | | <view class="search-bar"> |
| | | <u-search |
| | | v-model="goodsSearch" |
| | | placeholder="搜索商品名称" |
| | | v-model="goodsQuery.keyword" |
| | | placeholder="搜索商品" |
| | | :showAction="false" |
| | | @change="handlerGoodsSearch" |
| | | ></u-search> |
| | | </view> |
| | | <scroll-view class="goods-list" scroll-y> |
| | | <scroll-view class="goods-list" @scrolltolower="loadMoreGoods" scroll-y :show-scrollbar="false"> |
| | | <view |
| | | class="goods-item" |
| | | v-for="goods in filteredGoods" |
| | | v-for="(goods, index) in goodsList" |
| | | :key="goods.id" |
| | | @click="selectGoods(goods)" |
| | | @click="selectGoods(goods, index)" |
| | | > |
| | | <image :src="goods.image" class="goods-image"></image> |
| | | <image :src="goods.thumbnail" class="goods-image"></image> |
| | | <view class="goods-info"> |
| | | <text class="goods-name">{{ goods.name }}</text> |
| | | <text class="goods-name">{{ goods.goodsName }}</text> |
| | | <text class="goods-price">¥{{ goods.price }}</text> |
| | | <!-- <view>{{ goods.sellingPoint }}</view> --> |
| | | </view> |
| | | <u-icon |
| | | v-if="goods.selected" |
| | | name="checkmark" |
| | | size="24" |
| | | :color="selectedGoods && selectedGoods.id === goods.id ? '#2979ff' : '#ccc'" |
| | | size="36" |
| | | :color="'#2979ff'" |
| | | ></u-icon> |
| | | </view> |
| | | </scroll-view> |
| | |
| | | import MyTag from '@/components/my-tag.vue' |
| | | |
| | | import { getSTSToken, getFilePreviewUrl } from "@/api/common.js"; |
| | | import { publish, getVideoDetail } from "@/api/video.js"; |
| | | import { updateVideo, getVideoDetail } from "@/api/video.js"; |
| | | import { getRecommendTag3 } from "@/api/video-tag.js"; |
| | | import { getFileKey } from "@/utils/file.js"; |
| | | import { getVideoGoodsList } from "@/api/goods.js"; |
| | | |
| | | export default { |
| | | components: {MyTag}, |
| | | data() { |
| | | return { |
| | | showUploadProgress: false, |
| | | fileTypeShow: false, |
| | | cosClient: null, |
| | | bucket: '', |
| | | region: '', |
| | | endpoint: '', |
| | | videoUploadProgress: 0, |
| | | loading: false, |
| | | showGoodsPicker: false, |
| | | goodsSearch: '', |
| | | tagInput: '', |
| | | videoPreviewImgs: [], // 预览图片地址 |
| | | videoInfo: { |
| | | url: '', |
| | | fileKey: '', |
| | |
| | | fileSize: 0, |
| | | originalFileName: '', |
| | | cover: '' |
| | | }, |
| | | goodsQuery: { |
| | | keyword: '', |
| | | searchFromSelfStore: false, // 是否是查询自家店铺商品 |
| | | pageNumber: 1, |
| | | pageSize: 5 |
| | | }, |
| | | formData: { |
| | | id: '', |
| | |
| | | videoDuration: 0, |
| | | videoFit: 'cover', |
| | | goodsId: '', |
| | | videoContentType: 'video', |
| | | videoImgs: [], |
| | | tags: [], |
| | | fileInfo: {} |
| | | }, |
| | | selectedGoods: null, |
| | | goodsList: [ |
| | | { |
| | | id: '1', |
| | | name: '新款无线蓝牙耳机', |
| | | price: '199.00', |
| | | image: 'https://via.placeholder.com/100' |
| | | }, |
| | | { |
| | | id: '2', |
| | | name: '智能手环运动手表', |
| | | price: '299.00', |
| | | image: 'https://via.placeholder.com/100' |
| | | } |
| | | ], |
| | | selectedGoodsList: [], |
| | | goodsList: [], |
| | | noMoreGoods: false, // 没有更多商品了 |
| | | recommendedTags: [], |
| | | rules: { |
| | | title: [ |
| | | { required: true, message: '请输入视频标题', trigger: 'blur' }, |
| | | { min: 1, max: 20, message: '标题长度在1到20个字符', trigger: 'blur' } |
| | | ] |
| | | } |
| | | }, |
| | | screenWidth: 375, |
| | | gap: 10 // 图片间距 |
| | | }; |
| | | }, |
| | | computed: { |
| | | canPublish() { |
| | | if(this.formData.videoContentType === 'video') { |
| | | return this.formData.videoFileKey && this.formData.title && this.formData.cover; |
| | | }, |
| | | filteredGoods() { |
| | | if (!this.goodsSearch) return this.goodsList; |
| | | return this.goodsList.filter(goods => |
| | | goods.name.toLowerCase().includes(this.goodsSearch.toLowerCase()) |
| | | ); |
| | | } else if(this.formData.videoContentType === 'img') { |
| | | return this.formData.videoImgs.length > 0 && this.formData.title; |
| | | } |
| | | }, |
| | | showTopicRecommendations() { |
| | | return (this.tagInput === '' || this.recommendedTags.length > 0) && this.formData.tags.length < 5; |
| | | }, |
| | | // 计算每个图片项的宽度(考虑间距) |
| | | itemWidth() { |
| | | return (this.screenWidth - (this.gap * 4) - 20) / 3 |
| | | } |
| | | }, |
| | | onLoad(option) { |
| | | this.getVideoDetail(option.id) |
| | | this.getVDetail(option.id) |
| | | // 获取屏幕宽度 |
| | | const systemInfo = uni.getSystemInfoSync() |
| | | this.screenWidth = systemInfo.windowWidth |
| | | this.goodsQuery.pageNumber = 1 |
| | | this.goodsQuery.pageSize = 10 |
| | | this.getVideoGoodsByEs() |
| | | |
| | | }, |
| | | onShow() { |
| | | this.initCOS() |
| | |
| | | this.getRecommendTags() |
| | | }, |
| | | methods: { |
| | | getVideoDetail(id) { |
| | | getVDetail(id) { |
| | | getVideoDetail(id).then(res => { |
| | | this.fileInfo.cover = res.data.data.coverUrl |
| | | this.fileInfo.url = res.data.data.videoUrl |
| | | this.videoInfo.cover = res.data.data.coverUrl |
| | | this.videoInfo.url = res.data.data.videoUrl |
| | | this.formData.videoImgs = res.data.data.imgs |
| | | this.formData.videoContentType = res.data.data.videoContentType |
| | | this.formData.cover = res.data.data.coverFileKey |
| | | this.formData.id = res.data.data.id |
| | | this.formData.title = res.data.data.title |
| | | this.formData.videoFileKey = res.data.data.videoFileKey |
| | | this.formData.videoFit = res.data.data.videoFit |
| | | this.formData.videoDuration = res.data.data.videoDuration |
| | | this.formData.goodsId = res.data.data.goodsId |
| | | this.selectedGoodsList = res.data.data.goodsList |
| | | this.formData.tags = res.data.data.tags |
| | | }).catch(() => { |
| | | uni.navigateBack({ |
| | | delta: 1 |
| | | }); |
| | | this.showUploadProgress = false |
| | | console.log("视频详情", this.formData); |
| | | }) |
| | | }, |
| | | // 加载更多商品 |
| | | loadMoreGoods() { |
| | | if(this.noMoreGoods) { |
| | | return |
| | | } |
| | | this.goodsQuery.pageNumber += 1; |
| | | this.goodsQuery.pageSize = 5; |
| | | this.getVideoGoodsByEs() |
| | | }, |
| | | // 处理商品搜索值 |
| | | handlerGoodsSearch() { |
| | | this.goodsQuery.pageNumber = 1 |
| | | this.goodsQuery.pageSize = 10 |
| | | this.getVideoGoodsByEs() |
| | | }, |
| | | // 获取商品分页 |
| | | async getVideoGoodsByEs() { |
| | | getVideoGoodsList(this.goodsQuery).then(res => { |
| | | |
| | | if(this.goodsQuery.pageNumber === 1) { |
| | | this.goodsList = res.data.data |
| | | } else { |
| | | this.goodsList = [ |
| | | ...this.goodsList, |
| | | ...res.data.data.filter( |
| | | (newItem) => !this.goodsList.some((oldItem) => oldItem.id === newItem.id) |
| | | ), |
| | | ]; |
| | | } |
| | | if(res.data.data.length < this.goodsQuery.pageSize) { |
| | | this.noMoreGoods = true; |
| | | } |
| | | }) |
| | | }, |
| | | // 获取推荐标签 |
| | |
| | | }); |
| | | this.bucket = res.data.data.bucket |
| | | this.region = res.data.data.region |
| | | this.endpoint = res.data.data.endpoint |
| | | }) |
| | | |
| | | }, |
| | | // 选择视频 |
| | | chooseVideo() { |
| | | this.fileTypeShow = false; |
| | | // 清空选择的图片 |
| | | this.videoPreviewImgs = []; |
| | | this.formData.videoImgs = []; |
| | | this.formData.videoContentType = 'video' |
| | | uni.chooseVideo({ |
| | | sourceType: ['album', 'camera'], |
| | | maxDuration: 60, |
| | | camera: 'back', |
| | | success: (res) => { |
| | | this.videoUploadProgress = 0 |
| | | |
| | | // 获取文件名 |
| | | const tempPath = res.tempFilePath; |
| | | let fileName = tempPath.substring(tempPath.lastIndexOf('/') + 1); |
| | |
| | | this.formData.videoDuration = res.duration; |
| | | // 判断视频的填充模式 |
| | | this.formData.videoFit = this.calculateVideoFit(res.width, res.height) |
| | | |
| | | this.showUploadProgress = true |
| | | this.cosClient.uploadFile({ |
| | | Bucket: this.bucket, |
| | | Region: this.region, |
| | |
| | | cover: '' |
| | | } |
| | | } else { |
| | | console.log(this.videoInfo); |
| | | |
| | | } |
| | | }); |
| | | }, |
| | |
| | | reUpload() { |
| | | this.videoInfo = { |
| | | url: '', |
| | | cover: '', |
| | | duration: 0, |
| | | size: 0 |
| | | fileKey: '', |
| | | fileType: '', |
| | | fileSize: 0, |
| | | originalFileName: '', |
| | | cover: '' |
| | | }; |
| | | this.chooseVideo(); |
| | | this.formData.videoFileKey = '' |
| | | this.formData.cover = '' |
| | | this.formData.videoFit = 'cover' |
| | | this.formData.videoDuration = 0 |
| | | this.formData.videoImgs = [] |
| | | this.formData.fileInfo = {} |
| | | this.formData.videoContentType = 'video' |
| | | this.videoPreviewImgs = [] |
| | | this.fileTypeShow = true |
| | | }, |
| | | // 选择视频图集 |
| | | chooseImgs() { |
| | | this.fileTypeShow = false |
| | | // 清空选择的视频 |
| | | this.formData.videoFileKey = ''; |
| | | this.formData.cover = ''; |
| | | this.formData.videoContentType = 'img' |
| | | uni.chooseImage({ |
| | | count: 9, |
| | | sizeType: ['compressed'], |
| | | sourceType: ['album'], |
| | | success: (res) => { |
| | | res.tempFilePaths.forEach(tmpImg => { |
| | | let fileName = tmpImg.substring(tmpImg.lastIndexOf('/') + 1); |
| | | // 处理安卓可能的URI编码 |
| | | if(fileName.indexOf('%') > -1) { |
| | | fileName = decodeURIComponent(fileName); |
| | | } |
| | | const fileKey = getFileKey(fileName); |
| | | this.cosClient.uploadFile({ |
| | | Bucket: this.bucket, |
| | | Region: this.region, |
| | | Key: fileKey, |
| | | FilePath: tmpImg, |
| | | SliceSize: 1024 * 1024 * 5 /* 触发分块上传的阈值,5M */ |
| | | }, (err, data) => { |
| | | if (err) { |
| | | console.log('上传失败', err); |
| | | } else { |
| | | // 获取封面的访问地址 |
| | | this.videoPreviewImgs.push(this.endpoint + '/' + fileKey); |
| | | this.formData.videoImgs.push(fileKey); |
| | | } |
| | | }); |
| | | }) |
| | | |
| | | } |
| | | }); |
| | | }, |
| | | // 选择封面 |
| | | chooseCover() { |
| | | uni.chooseImage({ |
| | |
| | | if (err) { |
| | | console.log('上传失败', err); |
| | | } else { |
| | | // 获取封面的访问地址 |
| | | getFilePreviewUrl(fileKey).then(res => { |
| | | this.videoInfo.cover = res.data.data |
| | | this.videoInfo.cover = this.endpoint + '/' + fileKey |
| | | this.formData.cover = fileKey |
| | | }) |
| | | } |
| | | }); |
| | | } |
| | |
| | | |
| | | // 选择商品 |
| | | chooseGoods() { |
| | | if(this.selectedGoodsList.length > 0) { |
| | | const selectedGoodsIds = new Set(this.selectedGoodsList.map(i => i.goodsId)); |
| | | console.log(selectedGoodsIds, "mimade"); |
| | | this.goodsList?.forEach(goods => { |
| | | this.$set(goods, 'selected', selectedGoodsIds.has(goods.goodsId)); |
| | | }); |
| | | } |
| | | this.showGoodsPicker = true; |
| | | }, |
| | | |
| | | // 选择具体商品 |
| | | selectGoods(goods) { |
| | | this.selectedGoods = goods; |
| | | this.formData.goodsId = goods.id; |
| | | this.showGoodsPicker = false; |
| | | selectGoods(goods, index) { |
| | | if(! this.selectedGoodsList.some(item => item.id === goods.id)) { |
| | | goods["goodsNum"] = 1 |
| | | this.selectedGoodsList.push(goods) |
| | | this.goodsList[index].selected = true |
| | | } else { |
| | | this.goodsList[index].selected = false |
| | | this.selectedGoodsList = this.selectedGoodsList.filter(item => item.id !== goods.id); |
| | | } |
| | | }, |
| | | |
| | | // 清除商品 |
| | | clearGoods() { |
| | | this.selectedGoods = null; |
| | | this.formData.goodsId = ''; |
| | | clearGoods(goods) { |
| | | this.selectedGoodsList = this.selectedGoodsList.filter(item => item.id !== goods.id); |
| | | this.goodsList.forEach(item => { |
| | | if(item.id === goods.id) { |
| | | item.selected = false |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // 搜索热门话题 |
| | |
| | | if (valid && this.canPublish) { |
| | | this.loading = true; |
| | | this.formData.fileInfo = this.videoInfo; |
| | | console.log(this.formData); |
| | | publish(this.formData).then(res => { |
| | | this.formData["goodsList"] = this.selectedGoodsList.map(item => {return {goodsId: item.goodsId, goodsNum: item.goodsNum}}); |
| | | updateVideo(this.formData).then(res => { |
| | | uni.showToast({ |
| | | title: '视频已提交审核~', |
| | | icon: 'success' |
| | | }); |
| | | this.loading = false |
| | | // 重置表单 |
| | | this.videoInfo = { |
| | | url: '', |
| | | fileKey: '', |
| | | fileType: '', |
| | | fileSize: 0, |
| | | originalFileName: '', |
| | | cover: '' |
| | | }; |
| | | this.formData = { |
| | | id: '', |
| | | title: '', |
| | | videoFileKey: '', |
| | | cover: '', |
| | | videoFit: 'cover', |
| | | videoDuration: 0, |
| | | goodsId: '', |
| | | tags: [], |
| | | fileInfo: {} |
| | | }; |
| | | this.resetData(); |
| | | this.selectedGoods = null; |
| | | this.tagInput = ''; |
| | | this.recommendedTags = []; |
| | |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | resetData() { |
| | | // 重置表单 |
| | | this.videoInfo = { |
| | | url: '', |
| | | fileKey: '', |
| | | fileType: '', |
| | | fileSize: 0, |
| | | originalFileName: '', |
| | | cover: '' |
| | | }; |
| | | this.formData = { |
| | | id: '', |
| | | title: '', |
| | | videoFileKey: '', |
| | | cover: '', |
| | | videoFit: 'cover', |
| | | videoDuration: 0, |
| | | goodsId: '', |
| | | videoContentType: 'video', |
| | | videoImgs: [], |
| | | tags: [], |
| | | fileInfo: {} |
| | | }; |
| | | this.videoPreviewImgs = [] |
| | | this.selectedGoodsList = [] |
| | | } |
| | | } |
| | | }; |
| | |
| | | |
| | | <style scoped> |
| | | .publish-container { |
| | | padding: 20rpx; |
| | | padding: 10px; |
| | | padding-bottom: 120rpx; |
| | | } |
| | | |
| | | .upload-section { |
| | | background-color: #f8f8f8; |
| | | border-radius: 16rpx; |
| | | padding: 40rpx; |
| | | margin-bottom: 30rpx; |
| | | display: flex; |
| | | justify-content: center; |
| | |
| | | } |
| | | |
| | | .video-actions { |
| | | width: 100%; |
| | | margin-top: 20rpx; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | gap: 20rpx; |
| | | } |
| | | |
| | |
| | | background-color: #f9f9f9; |
| | | border-radius: 8rpx; |
| | | margin-top: 15rpx; |
| | | position: relative; |
| | | } |
| | | |
| | | .goods-preview .goods-image { |
| | |
| | | } |
| | | |
| | | .publish-btn { |
| | | position: fixed; |
| | | /* position: fixed; |
| | | bottom: 100rpx; |
| | | left: 20rpx; |
| | | right: 20rpx; |
| | | right: 20rpx; */ |
| | | margin-top: 40rpx; |
| | | } |
| | | |
| | | .goods-picker { |
| | |
| | | height: 25px; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .image-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .image-item { |
| | | margin: 5px; |
| | | overflow: hidden; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .image { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | </style> |
| | |
| | | ## 1.9.9(2025-06-11) |
| | | - 修复 uni-popup-dialog 中 setVal 方法报错的问题 |
| | | - 修复 uni-popup-dialog 数据双向绑定问题。 |
| | | ## 1.9.8(2025-04-16) |
| | | - 修复 更新组件示例 ,解决更新数据或保存项目导致弹窗消失的问题 |
| | | ## 1.9.7(2025-04-14) |
| | |
| | | } |
| | | }, |
| | | value(val) { |
| | | setVal(val) |
| | | this.setVal(val) |
| | | }, |
| | | // #ifdef VUE3 |
| | | modelValue(val) { |
| | | setVal(val) |
| | | this.setVal(val) |
| | | }, |
| | | // #endif |
| | | val(val) { |
| | |
| | | { |
| | | "id": "uni-popup", |
| | | "displayName": "uni-popup 弹出层", |
| | | "version": "1.9.8", |
| | | "version": "1.9.9", |
| | | "description": " Popup 组件,提供常用的弹层", |
| | | "keywords": [ |
| | | "uni-ui", |
| | |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | "HBuilderX": "", |
| | | "uni-app": "^4.01", |
| | | "uni-app-x": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | "type": "component-vue", |
| | | "darkmode": "x", |
| | | "i18n": "x", |
| | | "widescreen": "x" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y", |
| | | "alipay": "n" |
| | | "tcb": "x", |
| | | "aliyun": "x", |
| | | "alipay": "x" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y", |
| | | "app-harmony": "u", |
| | | "app-uvue": "u" |
| | | "uni-app": { |
| | | "vue": { |
| | | "vue2": "√", |
| | | "vue3": "√" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | "web": { |
| | | "safari": "√", |
| | | "chrome": "√" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | "app": { |
| | | "vue": "√", |
| | | "nvue": "√", |
| | | "android": "√", |
| | | "ios": "√", |
| | | "harmony": "x" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | "mp": { |
| | | "weixin": "√", |
| | | "alipay": "√", |
| | | "toutiao": "√", |
| | | "baidu": "√", |
| | | "kuaishou": "√", |
| | | "jd": "√", |
| | | "harmony": "√", |
| | | "qq": "√", |
| | | "lark": "√" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | "quickapp": { |
| | | "huawei": "-", |
| | | "union": "-" |
| | | } |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | "uni-app-x": { |
| | | "web": { |
| | | "safari": "-", |
| | | "chrome": "-" |
| | | }, |
| | | "app": { |
| | | "android": "-", |
| | | "ios": "-", |
| | | "harmony": "-" |
| | | }, |
| | | "mp": { |
| | | "weixin": "-" |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | ## 1.3.5(2025-06-11) |
| | | - 修复 第一次执行不显示动画的问题 |
| | | ## 1.3.4(2025-04-16) |
| | | - 修复 页面数据更新到底动画复原的问题 |
| | | - 修复 示例页面打开报错的问题 |
| | |
| | | <template> |
| | | <!-- #ifndef APP-NVUE --> |
| | | <view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view> |
| | | <view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"> |
| | | <slot></slot> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view> |
| | | <view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"> |
| | | <slot></slot> |
| | | </view> |
| | | <!-- #endif --> |
| | | </template> |
| | | |
| | |
| | | return { |
| | | isShow: false, |
| | | transform: '', |
| | | opacity: 1, |
| | | opacity: 0, |
| | | animationData: {}, |
| | | durationTime: 300, |
| | | config: {} |
| | |
| | | * @param {Object} obj |
| | | */ |
| | | step(obj, config = {}) { |
| | | if (!this.animation) return |
| | | for (let i in obj) { |
| | | try { |
| | | if(typeof obj[i] === 'object'){ |
| | | this.animation[i](...obj[i]) |
| | | }else{ |
| | | this.animation[i](obj[i]) |
| | | if (!this.animation) return this |
| | | Object.keys(obj).forEach(key => { |
| | | const value = obj[key] |
| | | if (typeof this.animation[key] === 'function') { |
| | | Array.isArray(value) ? |
| | | this.animation[key](...value) : |
| | | this.animation[key](value) |
| | | } |
| | | } catch (e) { |
| | | console.error(`方法 ${i} 不存在`) |
| | | } |
| | | } |
| | | }) |
| | | this.animation.step(config) |
| | | return this |
| | | }, |
| | |
| | | // 开始过度动画 |
| | | open() { |
| | | clearTimeout(this.timer) |
| | | this.transform = '' |
| | | this.isShow = true |
| | | let { opacity, transform } = this.styleInit(false) |
| | | if (typeof opacity !== 'undefined') { |
| | | this.opacity = opacity |
| | | } |
| | | this.transform = transform |
| | | // 新增初始状态重置逻辑(关键) |
| | | this.transform = this.styleInit(false).transform || '' |
| | | this.opacity = this.styleInit(false).opacity || 0 |
| | | |
| | | // 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常 |
| | | this.$nextTick(() => { |
| | | // TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器 |
| | |
| | | this.tranfromInit(false).step() |
| | | this.animation.run(() => { |
| | | this.transform = '' |
| | | this.opacity = opacity || 1 |
| | | }) |
| | | this.opacity = this.styleInit(false).opacity || 1 |
| | | this.$emit('change', { |
| | | detail: this.isShow |
| | | }) |
| | | }, 20) |
| | | }) |
| | | }, 80) |
| | | }) |
| | | }, |
| | | // 关闭过度动画 |
| | |
| | | }, |
| | | // 处理动画开始前的默认样式 |
| | | styleInit(type) { |
| | | let styles = { |
| | | transform: '' |
| | | } |
| | | let buildStyle = (type, mode) => { |
| | | if (mode === 'fade') { |
| | | styles.opacity = this.animationType(type)[mode] |
| | | let styles = { transform: '', opacity: 1 } |
| | | const buildStyle = (type, mode) => { |
| | | const value = this.animationType(type)[mode] // 直接使用 type 控制状态 |
| | | if (mode.startsWith('fade')) { |
| | | styles.opacity = value |
| | | } else { |
| | | styles.transform += this.animationType(type)[mode] + ' ' |
| | | styles.transform += value + ' ' |
| | | } |
| | | } |
| | | |
| | | if (typeof this.modeClass === 'string') { |
| | | buildStyle(type, this.modeClass) |
| | | } else { |
| | | this.modeClass.forEach(mode => { |
| | | buildStyle(type, mode) |
| | | }) |
| | | this.modeClass.forEach(mode => buildStyle(type, mode)) |
| | | } |
| | | return styles |
| | | }, |
| | |
| | | }, |
| | | animationType(type) { |
| | | return { |
| | | fade: type ? 0 : 1, |
| | | fade: type ? 1 : 0, |
| | | 'slide-top': `translateY(${type ? '0' : '-100%'})`, |
| | | 'slide-right': `translateX(${type ? '0' : '100%'})`, |
| | | 'slide-bottom': `translateY(${type ? '0' : '100%'})`, |
| | |
| | | { |
| | | "id": "uni-transition", |
| | | "displayName": "uni-transition 过渡动画", |
| | | "version": "1.3.4", |
| | | "version": "1.3.5", |
| | | "description": "元素的简单过渡动画", |
| | | "keywords": [ |
| | | "uni-ui", |
| | |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | "HBuilderX": "", |
| | | "uni-app": "^4.01", |
| | | "uni-app-x": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | "type": "component-vue", |
| | | "darkmode": "x", |
| | | "i18n": "x", |
| | | "widescreen": "x" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-scss"], |
| | | "dependencies": [ |
| | | "uni-scss" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y", |
| | | "alipay": "n" |
| | | "tcb": "√", |
| | | "aliyun": "√", |
| | | "alipay": "x" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y", |
| | | "app-harmony": "u", |
| | | "app-uvue": "n" |
| | | "uni-app": { |
| | | "vue": { |
| | | "vue2": "√", |
| | | "vue3": "√" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | "web": { |
| | | "safari": "√", |
| | | "chrome": "√" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | "app": { |
| | | "vue": "√", |
| | | "nvue": "√", |
| | | "android": "√", |
| | | "ios": "√", |
| | | "harmony": "-" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | "mp": { |
| | | "weixin": { |
| | | "extVersion": "1.0.2", |
| | | "minVersion": "" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | "alipay": { |
| | | "extVersion": "1.0.2", |
| | | "minVersion": "" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | "toutiao": { |
| | | "extVersion": "1.0.2", |
| | | "minVersion": "" |
| | | }, |
| | | "baidu": { |
| | | "extVersion": "1.0.2", |
| | | "minVersion": "" |
| | | }, |
| | | "kuaishou": { |
| | | "extVersion": "1.1.0", |
| | | "minVersion": "" |
| | | }, |
| | | "jd": { |
| | | "extVersion": "1.0.2", |
| | | "minVersion": "" |
| | | }, |
| | | "harmony": "x", |
| | | "qq": "-", |
| | | "lark": "-" |
| | | }, |
| | | "quickapp": { |
| | | "huawei": "-", |
| | | "union": "-" |
| | | } |
| | | }, |
| | | "uni-app-x": { |
| | | "web": { |
| | | "safari": "-", |
| | | "chrome": "-" |
| | | }, |
| | | "app": { |
| | | "android": "-", |
| | | "ios": "-", |
| | | "harmony": "-" |
| | | }, |
| | | "mp": { |
| | | "weixin": "-" |
| | | } |
| | | } |
| | | } |
| | | } |