Commit f122c0c509aaf5a2972a52a5676fe49297e790cd

Authored by luowenfeng
1 parent e2cd336a

feature(管理后台): 商品添加视频

yudao-ui-admin/src/components/VideoUpload/index.vue 0 → 100644
  1 +<template>
  2 + <div class="editPage__video">
  3 + <el-upload
  4 + class="uploader"
  5 + list-type="picture-card"
  6 + :action="uploadUrl"
  7 + :on-success="handleSuccess"
  8 + :before-upload="beforeUpload"
  9 + :headers="headers"
  10 + :on-error="handleError"
  11 + :show-file-list="false"
  12 + >
  13 +
  14 + <div v-if="uploadFlag" @mouseenter="mouseover" @mouseleave="mouseout">
  15 + <i class="el-icon-success success-icon"></i>
  16 + <div :class="{'hide': activeHover, 'success': !activeHover}">
  17 + <span class="item-actions">
  18 + <span
  19 + class="item-preview"
  20 + @click.stop="handlePreview()"
  21 + >
  22 + <i class="el-icon-zoom-in"></i>
  23 + </span>
  24 + <span
  25 + class="item-delete"
  26 + @click.stop="handleRemove()"
  27 + >
  28 + <i class="el-icon-delete"></i>
  29 + </span>
  30 + </span>
  31 + </div>
  32 + </div>
  33 + <i v-else-if="uploadFlag === null" class="el-icon-plus uploader-icon"></i>
  34 + <i v-else-if="!uploadFlag" class="el-icon-circle-close uploader-icon" style="color: red"></i>
  35 + </el-upload>
  36 + <!-- 上传提示 -->
  37 + <div class="el-upload__tip" slot="tip" v-if="showTip">
  38 + 请上传
  39 + <template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
  40 + <template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
  41 + 的文件
  42 + </div>
  43 +
  44 + <el-dialog :visible.sync="dialogVisible" append-to-body width="800" title="预览">
  45 + <video width="100%" v-if="videoUrl" controls="controls" :key="menuKey">
  46 + <source :src="videoUrl" type="video/mp4" />
  47 + </video>
  48 + </el-dialog>
  49 +
  50 + </div>
  51 +</template>
  52 +
  53 +<script>
  54 +
  55 +import { getAccessToken } from "@/utils/auth";
  56 +
  57 +export default {
  58 + props: {
  59 + value: [String, Object],
  60 + // 大小限制(MB)
  61 + fileSize: {
  62 + type: Number,
  63 + default: 300,
  64 + },
  65 + // 文件类型, 例如"video/mp4"
  66 + fileType: {
  67 + type: [String, Array],
  68 + default: () =>["video/mp4"],
  69 + },
  70 + // 是否显示提示
  71 + isShowTip: {
  72 + type: Boolean,
  73 + default: true
  74 + }
  75 + },
  76 + data() {
  77 + return {
  78 + uploadFlag: null,
  79 + activeHover: true,
  80 + dialogVisible: false,
  81 + videoUrl: null,
  82 + // 视频上传
  83 + uploadUrl: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload", // 请求地址
  84 + headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
  85 + // 应付多个组件的情况 记录当前组件的key值
  86 + menuKey: 1, // 用来强制刷新,
  87 + }
  88 + },
  89 + watch: {
  90 + value: {
  91 + handler(val) {
  92 + if (val) {
  93 + this.videoUrl = val;
  94 + this.uploadFlag = true;
  95 + }
  96 + },
  97 + deep: true,
  98 + immediate: true
  99 + }
  100 + },
  101 + computed: {
  102 + // 是否显示提示
  103 + showTip() {
  104 + return this.isShowTip && (this.fileType || this.fileSize);
  105 + },
  106 + },
  107 + methods: {
  108 + // 上传成功的函数
  109 + handleSuccess(res) {
  110 + ++this.menuKey;
  111 + if(res.code === 0){
  112 + this.uploadFlag = true;
  113 + this.videoUrl = res.data;
  114 + this.$emit("input", this.videoUrl);
  115 + }else{
  116 + this.uploadFlag = false;
  117 + this.$message.error("错误!"+ res.msg);
  118 + }
  119 + },
  120 + handleError(){
  121 + this.uploadFlag = false;
  122 + },
  123 + beforeUpload(file) {
  124 + const isMp4 = this.fileType.includes(file.type);
  125 + const isLt300MB = file.size / 1024 / 1024 < 300;
  126 + if (!isMp4) {
  127 + this.$message.error("视频只能是"+ this.fileType.join("/") +"格式!");
  128 + }
  129 + if (!isLt300MB) {
  130 + this.$message.error("上传视频大小不能超过 300MB!");
  131 + }
  132 + return isMp4 && isLt300MB;
  133 + },
  134 + // 预览
  135 + handlePreview() {
  136 + this.dialogVisible = true;
  137 + },
  138 + // 删除视频
  139 + handleRemove() {
  140 + this.videoUrl = null;
  141 + this.uploadFlag = null;
  142 + this.$emit("input", null);
  143 + },
  144 + mouseover(){
  145 + this.activeHover = false;
  146 + },
  147 + mouseout(){
  148 + this.activeHover = true;
  149 + }
  150 + }
  151 +}
  152 +</script>
  153 +
  154 +
  155 +<style lang="scss">
  156 +
  157 + .editPage__video {
  158 + .hide{
  159 + visibility:hidden;
  160 + }
  161 + .success{
  162 + position: relative;
  163 + width: 78px;
  164 + height: 78px;
  165 + line-height: 78px;
  166 + background-color: rgba(0,0,0,.5);
  167 + transition: opacity .3s;
  168 + cursor: default;
  169 +
  170 + .item-preview .el-icon-zoom-in{
  171 + width: 30px;
  172 + font-size: 20px;
  173 + color: #f2f2f2;
  174 + cursor: pointer;
  175 + }
  176 + .item-delete .el-icon-delete{
  177 + width: 30px;
  178 + font-size: 20px;
  179 + color: #f2f2f2;
  180 + cursor: pointer;
  181 + }
  182 + }
  183 +
  184 + .uploader-icon {
  185 + font-size: 28px;
  186 + color: #8c939d;
  187 + width: 80px;
  188 + height: 80px;
  189 + line-height: 80px;
  190 + text-align: center;
  191 + position: absolute;
  192 + left: 0;
  193 + }
  194 + .success-icon {
  195 + font-size: 28px;
  196 + color: green;
  197 + width: 80px;
  198 + height: 80px;
  199 + line-height: 80px;
  200 + text-align: center;
  201 + position: absolute;
  202 + left: 0;
  203 + }
  204 +
  205 + .el-upload{
  206 + width: 80px;
  207 + height: 80px;
  208 + }
  209 +
  210 + }
  211 +</style>
yudao-ui-admin/src/views/mall/product/spu/save.vue
@@ -10,11 +10,13 @@ @@ -10,11 +10,13 @@
10 <el-form-item label="商品卖点"> 10 <el-form-item label="商品卖点">
11 <el-input type="textarea" v-model="baseForm.sellPoint" placeholder="请输入商品卖点"/> 11 <el-input type="textarea" v-model="baseForm.sellPoint" placeholder="请输入商品卖点"/>
12 </el-form-item> 12 </el-form-item>
13 - <!-- TODO @Luowenfeng:商品主图,80 x 80 即可 -->  
14 <el-form-item label="商品主图" prop="picUrls"> 13 <el-form-item label="商品主图" prop="picUrls">
15 - <ImageUpload v-model="baseForm.picUrls" :value="baseForm.picUrls" :limit="10" /> 14 + <ImageUpload v-model="baseForm.picUrls" :value="baseForm.picUrls" :limit="10" class="mall-image" />
16 </el-form-item> 15 </el-form-item>
17 - <!-- TODO @Luowenfeng:商品视频 --> 16 + <el-form-item label="商品视频" prop="videoUrl">
  17 + <VideoUpload v-model="baseForm.videoUrl" :value="baseForm.videoUrl" />
  18 + </el-form-item>
  19 +
18 <el-form-item label="商品品牌" prop="brandId"> 20 <el-form-item label="商品品牌" prop="brandId">
19 <el-select v-model="baseForm.brandId" placeholder="请选择商品品牌"> 21 <el-select v-model="baseForm.brandId" placeholder="请选择商品品牌">
20 <el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id" /> 22 <el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id" />
@@ -37,7 +39,6 @@ @@ -37,7 +39,6 @@
37 <el-tab-pane label="价格库存" name="rates" class="rates"> 39 <el-tab-pane label="价格库存" name="rates" class="rates">
38 <el-form ref="rates" :model="ratesForm" :rules="rules"> 40 <el-form ref="rates" :model="ratesForm" :rules="rules">
39 <el-form-item label="启用多规格"> 41 <el-form-item label="启用多规格">
40 - <!-- TODO @Luowenfeng:改成开关的按钮;关闭,单规格;开启,多规格 -->  
41 <el-switch v-model="activeSwitch" @change="changeRadio"></el-switch> 42 <el-switch v-model="activeSwitch" @change="changeRadio"></el-switch>
42 </el-form-item> 43 </el-form-item>
43 44
@@ -149,7 +150,7 @@ @@ -149,7 +150,7 @@
149 </template> 150 </template>
150 </el-table> 151 </el-table>
151 </el-form-item> 152 </el-form-item>
152 - <el-form-item label="虚拟销量" prop="virtualSalesCount"> 153 + <el-form-item label="虚拟销量" prop="virtualSalesCount" >
153 <el-input v-model="baseForm.virtualSalesCount" placeholder="请输入虚拟销量" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/> 154 <el-input v-model="baseForm.virtualSalesCount" placeholder="请输入虚拟销量" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/>
154 </el-form-item> 155 </el-form-item>
155 </el-form> 156 </el-form>
@@ -196,11 +197,13 @@ import {createSpu, updateSpu, getSpuDetail} from &quot;@/api/mall/product/spu&quot;; @@ -196,11 +197,13 @@ import {createSpu, updateSpu, getSpuDetail} from &quot;@/api/mall/product/spu&quot;;
196 import {getPropertyListAndValue,} from "@/api/mall/product/property"; 197 import {getPropertyListAndValue,} from "@/api/mall/product/property";
197 import Editor from "@/components/Editor"; 198 import Editor from "@/components/Editor";
198 import ImageUpload from "@/components/ImageUpload"; 199 import ImageUpload from "@/components/ImageUpload";
  200 +import VideoUpload from "@/components/VideoUpload";
199 201
200 export default { 202 export default {
201 components: { 203 components: {
202 Editor, 204 Editor,
203 - ImageUpload 205 + ImageUpload,
  206 + VideoUpload
204 }, 207 },
205 props:{ 208 props:{
206 obj: Object 209 obj: Object
@@ -223,11 +226,13 @@ export default { @@ -223,11 +226,13 @@ export default {
223 sort: null, 226 sort: null,
224 description: null, 227 description: null,
225 picUrls: null, 228 picUrls: null,
  229 + videoUrl: null,
226 status: 0, 230 status: 0,
227 virtualSalesCount: 0, 231 virtualSalesCount: 0,
228 showStock: true, 232 showStock: true,
229 - brandId: null 233 + brandId: null,
230 }, 234 },
  235 +
231 categoryList: [], 236 categoryList: [],
232 // 价格库存 237 // 价格库存
233 ratesForm: { 238 ratesForm: {
@@ -255,8 +260,8 @@ export default { @@ -255,8 +260,8 @@ export default {
255 description: [{required: true, message: "描述不能为空", trigger: "blur"},], 260 description: [{required: true, message: "描述不能为空", trigger: "blur"},],
256 categoryIds: [{required: true, message: "分类id不能为空", trigger: "blur"},], 261 categoryIds: [{required: true, message: "分类id不能为空", trigger: "blur"},],
257 status: [{required: true, message: "商品状态不能为空", trigger: "blur"}], 262 status: [{required: true, message: "商品状态不能为空", trigger: "blur"}],
  263 + brandId: [{required: true, message: "商品品牌不能为空", trigger: "blur"}],
258 picUrls: [{required: true, message: "商品轮播图地址不能为空", trigger: "blur"}], 264 picUrls: [{required: true, message: "商品轮播图地址不能为空", trigger: "blur"}],
259 - virtualSalesCount: [{required: true, message: "虚拟销量不能为空", trigger: "blur"}],  
260 }, 265 },
261 }; 266 };
262 }, 267 },
@@ -379,7 +384,6 @@ export default { @@ -379,7 +384,6 @@ export default {
379 rates[0].status = this.baseForm.status; 384 rates[0].status = this.baseForm.status;
380 } 385 }
381 let form = this.baseForm 386 let form = this.baseForm
382 -  
383 if(form.picUrls instanceof Array){ 387 if(form.picUrls instanceof Array){
384 form.picUrls = form.picUrls.flatMap(m=>m.split(',')) 388 form.picUrls = form.picUrls.flatMap(m=>m.split(','))
385 }else if(form.picUrls.split(',') instanceof Array){ 389 }else if(form.picUrls.split(',') instanceof Array){
@@ -428,6 +432,7 @@ export default { @@ -428,6 +432,7 @@ export default {
428 this.baseForm.name=data.name; 432 this.baseForm.name=data.name;
429 this.baseForm.sellPoint=data.sellPoint; 433 this.baseForm.sellPoint=data.sellPoint;
430 this.baseForm.categoryIds=data.categoryIds; 434 this.baseForm.categoryIds=data.categoryIds;
  435 + this.baseForm.videoUrl = data.videoUrl;
431 this.baseForm.sort=data.sort; 436 this.baseForm.sort=data.sort;
432 this.baseForm.description=data.description; 437 this.baseForm.description=data.description;
433 this.baseForm.picUrls=data.picUrls; 438 this.baseForm.picUrls=data.picUrls;
@@ -460,7 +465,8 @@ export default { @@ -460,7 +465,8 @@ export default {
460 } 465 }
461 this.ratesForm.rates=data.skus 466 this.ratesForm.rates=data.skus
462 }) 467 })
463 - } 468 + },
  469 +
464 }, 470 },
465 }; 471 };
466 </script> 472 </script>
@@ -550,4 +556,17 @@ export default { @@ -550,4 +556,17 @@ export default {
550 margin-left: 15px; 556 margin-left: 15px;
551 } 557 }
552 } 558 }
  559 +
  560 +.mall-image{
  561 + .el-upload--picture-card{
  562 + width: 80px;
  563 + height: 80px;
  564 + line-height: 90px;
  565 + }
  566 + .el-upload-list__item{
  567 + width: 80px;
  568 + height: 80px;
  569 + }
  570 +}
  571 +
553 </style> 572 </style>