Prechádzať zdrojové kódy

mall 模块添加商品管理页面, 利用 this.$emit 实现子组件和父页面通信

reghao 1 rok pred
rodič
commit
d7cdef304a

+ 8 - 1
src/router/mall.js

@@ -10,6 +10,7 @@ const ConfirmOrder = () => import('views/mall/ConfirmOrder')
 const Visit = () => import('views/mall/Visit')
 const Pay = () => import('views/mall/Pay')
 const Product = () => import('views/mall/Product')
+const MyProduct = () => import('views/mall/MyProduct')
 
 export default {
   path: '/mall',
@@ -31,7 +32,7 @@ export default {
     },
     {
       path: '/mall/fav',
-      name: '播放列表',
+      name: '收藏夹',
       component: Fav,
       meta: { needAuth: false }
     },
@@ -64,6 +65,12 @@ export default {
       name: 'Product',
       component: Product,
       meta: { needAuth: false }
+    },
+    {
+      path: '/mall/myproduct',
+      name: 'MyProduct',
+      component: MyProduct,
+      meta: { needAuth: false }
     }
   ]
 }

+ 4 - 1
src/views/mall/Mall.vue

@@ -26,6 +26,9 @@
               <el-menu-item index="/mall/visit">
                 <template slot="title" class="el-icon-shopping-cart-full">浏览记录</template>
               </el-menu-item>
+              <el-menu-item index="/mall/myproduct">
+                <template slot="title" class="el-icon-shopping-cart-full">商品管理</template>
+              </el-menu-item>
             </el-submenu>
             <el-menu-item index="/mall/cart">
               <template slot="title">
@@ -34,7 +37,7 @@
               </template>
             </el-menu-item>
             <el-menu-item index="/mall/fav">
-              <template slot="title" class="el-icon-collection">播放列表</template>
+              <template slot="title" class="el-icon-collection">收藏夹</template>
             </el-menu-item>
           </el-menu>
         </el-col>

+ 319 - 0
src/views/mall/MyProduct.vue

@@ -0,0 +1,319 @@
+<template>
+  <el-container>
+    <el-header height="220">
+      <el-row style="margin-top: 10px">
+        <el-select
+          v-model="queryInfo.scope"
+          clearable
+          placeholder="查询条件"
+          style="margin-left: 5px"
+          @change="onSelectChange"
+        />
+        <el-button type="plain" icon="el-icon-plus" style="margin-left: 5px" @click="publishVideoDiaglog = true">发布商品</el-button>
+      </el-row>
+    </el-header>
+    <el-main>
+      <el-table
+        :data="dataList"
+        border
+        style="width: 100%"
+      >
+        <el-table-column
+          fixed="left"
+          label="No"
+          type="index"
+        />
+        <el-table-column
+          prop="createTime"
+          label="发布时间"
+          width="150"
+        />
+        <el-table-column
+          prop="picUrl"
+          label="封面"
+          width="90"
+        >
+          <template slot-scope="scope">
+            <el-image :src="scope.row.picUrl" min-width="30" height="20" />
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="itemId"
+          label="商品 ID"
+        >
+          <template slot-scope="scope">
+            <router-link style="text-decoration-line: none" target="_blank" :to="`/mall/item?id=${scope.row.itemId}`">
+              <span>{{ scope.row.itemId }}</span>
+            </router-link>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="title"
+          label="商品名"
+          :show-overflow-tooltip="true"
+        >
+          <template slot-scope="scope">
+            <el-tooltip
+              v-if="scope.row.title"
+              :content="scope.row.title"
+              raw-content
+              placement="top-start"
+            >
+              <span v-if="scope.row.title.length <= 15">
+                {{ scope.row.title }}
+              </span>
+              <span v-else>
+                {{ scope.row.title.substr(0, 15) + "..." }}
+              </span>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="price"
+          label="价格"
+        />
+        <el-table-column
+          prop="stock"
+          label="库存"
+        />
+        <el-table-column
+          fixed="right"
+          label="操作"
+          width="280"
+        >
+          <template slot-scope="scope">
+            <el-button
+              size="mini"
+              @click="handlePreview(scope.$index, scope.row)"
+            >预览</el-button>
+            <el-button
+              size="mini"
+              @click="handleEdit(scope.$index, scope.row)"
+            >编辑</el-button>
+            <el-button
+              size="mini"
+              type="danger"
+              @click="handleDelete(scope.$index, scope.row)"
+            >删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-pagination
+        background
+        :small="screenWidth <= 768"
+        layout="prev, pager, next"
+        :page-size="pageSize"
+        :current-page="currentPage"
+        :total="totalSize"
+        @current-change="handleCurrentChange"
+        @prev-click="handleCurrentChange"
+        @next-click="handleCurrentChange"
+      />
+    </el-main>
+
+    <!-- 发布视频稿件对话框 -->
+    <el-dialog
+      :visible.sync="publishVideoDiaglog"
+      :before-close="handleClose"
+      width="70%"
+      center
+    >
+      <my-product-publish @post-publish="onPostPublish"/>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="publishVideoDiaglog = false">取 消</el-button>
+        <el-button type="primary" @click="publishVideoDiaglog = false">关 闭</el-button>
+      </span>
+    </el-dialog>
+    <!-- 修改视频可见范围对话框 -->
+    <el-dialog
+      append-to-body
+      :visible.sync="showEditScopeDialog"
+      width="30%"
+      center
+    >
+      <el-card class="box-card">
+        <div slot="header" class="clearfix">
+          <span>修改视频可见范围</span>
+          <el-button style="float: right; padding: 3px 0" type="text" @click="onUpdateScope">更新</el-button>
+        </div>
+        <div class="text item">
+          <el-select v-model="form.scope" placeholder="选择可见范围">
+            <el-option label="本人可见" value="1" />
+            <el-option label="所有人可见" value="2" />
+            <el-option label="VIP 可见" value="3" />
+            <el-option label="验证码可见" value="4" />
+          </el-select>
+        </div>
+      </el-card>
+    </el-dialog>
+    <!-- 视频预览对话框 -->
+    <el-dialog
+      title="预览视频"
+      append-to-body
+      :visible.sync="showPreviewDialog"
+      :before-close="handleDialogClose"
+      width="70%"
+      center
+    >
+      <template>
+        <video-preview-player :video-prop.sync="videoProp" />
+      </template>
+    </el-dialog>
+  </el-container>
+</template>
+
+<script>
+import VideoPreviewPlayer from 'components/VideoPreviewPlayer'
+import MyProductPublish from '@/views/mall/MyProductPublish'
+import { updateVideoScope, videoInfo, deleteVideoPost, getVideoPosts } from '@/api/video'
+import { getProducts } from '@/api/mall'
+
+export default {
+  name: 'MyProduct',
+  components: { VideoPreviewPlayer, MyProductPublish },
+  data() {
+    return {
+      queryInfo: {
+        scope: null,
+        pn: 1
+      },
+      // 屏幕宽度, 为了控制分页条的大小
+      screenWidth: document.body.clientWidth,
+      currentPage: 1,
+      pageSize: 12,
+      totalSize: 0,
+      dataList: [],
+      // **********************************************************************
+      videoProp: null,
+      showVideoResourceDialog: false,
+      showEditScopeDialog: false,
+      showPreviewDialog: false,
+      form: {
+        title: null,
+        price: 9.99,
+        coverFileId: null
+      },
+      videoResources: [],
+      publishVideoDiaglog: false
+    }
+  },
+  created() {
+    document.title = '商品管理'
+    this.getData()
+  },
+  methods: {
+    handleCurrentChange(pageNumber) {
+      this.currentPage = pageNumber
+      this.getData()
+      // 回到顶部
+      scrollTo(0, 0)
+    },
+    getData() {
+      this.dataList = []
+      getProducts(this.currentPage).then(resp => {
+        if (resp.code === 0) {
+          this.dataList = resp.data.list
+          this.totalSize = resp.data.totalSize
+        } else {
+          this.$notify({
+            title: '提示',
+            message: resp.msg,
+            type: 'warning',
+            duration: 3000
+          })
+        }
+      }).catch(error => {
+        this.$notify({
+          title: '提示',
+          message: error.message,
+          type: 'error',
+          duration: 3000
+        })
+      })
+    },
+    handleScope(index, row) {
+      this.form.videoId = row.videoId
+      this.form.scope = '' + row.scope
+      this.showEditScopeDialog = true
+    },
+    handleDialogClose(done) {
+      this.showPreviewDialog = false
+      this.videoProp = {
+        videoId: null,
+        play: false
+      }
+      done()
+    },
+    handlePreview(index, row) {
+      videoInfo(row.videoId).then(res => {
+        if (res.code === 0) {
+          this.showPreviewDialog = true
+          this.videoProp = {
+            videoId: res.data.videoId,
+            play: true
+          }
+        }
+      })
+    },
+    handleEdit(index, row) {
+      const path = '/post/video/edit/' + row.videoId
+      this.$router.push(path)
+    },
+    handleDelete(index, row) {
+      this.$confirm('确定要删除 ' + row.title + '?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        deleteVideoPost(row.videoId).then(res => {
+          if (res.code === 0) {
+            this.$notify({
+              title: '提示',
+              message: '稿件已删除',
+              type: 'warning',
+              duration: 3000
+            })
+            this.$router.go(0)
+          }
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消'
+        })
+      })
+    },
+    onUpdateScope() {
+      this.showEditScopeDialog = false
+      updateVideoScope(this.form).then(res => {
+        if (res.code === 0) {
+          this.$notify({
+            title: '提示',
+            message: '视频可见范围已更新',
+            type: 'warning',
+            duration: 3000
+          })
+        }
+      }).catch(error => {
+        this.$notify({
+          title: '提示',
+          message: error.message,
+          type: 'warning',
+          duration: 3000
+        })
+      })
+    },
+    onSelectChange() {
+      this.$message.info(this.queryInfo)
+    },
+    handleClose() {
+    },
+    onPostPublish() {
+      this.publishVideoDiaglog = false
+    }
+  }
+}
+</script>
+
+<style>
+</style>

+ 226 - 0
src/views/mall/MyProductPublish.vue

@@ -0,0 +1,226 @@
+<template>
+  <el-row class="movie-list">
+    <el-col :md="12" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
+      <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
+        <el-card class="box-card">
+          <div slot="header" class="clearfix">
+            <span>上传商品封面</span>
+          </div>
+          <div class="text item">
+            <el-tooltip class="item" effect="dark" content="点击上传图片" placement="top-end">
+              <el-upload
+                class="avatar-uploader"
+                :action="imgOssUrl"
+                :headers="imgHeaders"
+                :data="imgData"
+                :with-credentials="false"
+                :show-file-list="false"
+                :before-upload="beforeAvatarUpload"
+                :on-success="handleAvatarSuccess"
+                :on-error="handleAvatarError"
+                :on-change="handleOnChange"
+              >
+                <img v-if="coverUrl" :src="coverUrl" class="avatar">
+                <i v-else class="el-icon-plus avatar-uploader-icon" />
+              </el-upload>
+            </el-tooltip>
+          </div>
+        </el-card>
+      </el-row>
+    </el-col>
+    <el-col :md="12" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
+      <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
+        <el-card class="box-card">
+          <div slot="header" class="clearfix">
+            <span>商品信息</span>
+            <el-button style="float: right; padding: 3px 0" type="text" @click="onSubmit">发布</el-button>
+          </div>
+          <div class="text item">
+            <el-form ref="form" :model="form" label-width="80px">
+              <el-form-item label="商品名">
+                <el-input v-model="form.title" style="padding-right: 1px" placeholder="商品名不能超过 50 个字符" />
+              </el-form-item>
+              <el-form-item label="价格">
+                <el-input-number v-model="form.price" :min="1" :max="1000" style="margin-left: 5px" />
+              </el-form-item>
+              <el-form-item label="库存数量">
+                <el-input-number v-model="form.amount" :min="1" :max="10000" style="margin-left: 5px" />
+              </el-form-item>
+            </el-form>
+          </div>
+        </el-card>
+      </el-row>
+    </el-col>
+  </el-row>
+</template>
+
+<script>
+import { getVideoCoverChannelInfo } from '@/api/file'
+import { addProduct } from '@/api/mall'
+
+export default {
+  name: 'MyProductPublish',
+  data() {
+    return {
+      // ****************************************************************************************************************
+      imgOssUrl: '',
+      imgHeaders: {
+        Authorization: ''
+      },
+      imgData: {
+        channelCode: null
+      },
+      coverUrl: null,
+      // ****************************************************************************************************************
+      form: {
+        coverFileId: null,
+        coverChannelId: 0,
+        title: null,
+        price: null,
+        amount: null
+      }
+    }
+  },
+  created() {
+    getVideoCoverChannelInfo().then(res => {
+      if (res.code === 0) {
+        const resData = res.data
+        this.form.coverChannelId = resData.channelCode
+        this.imgData.channelCode = resData.channelCode
+        this.imgOssUrl = resData.ossUrl
+        this.imgHeaders.Authorization = 'Bearer ' + resData.token
+      } else {
+        this.$notify({
+          title: '提示',
+          message: '获取 OSS 服务器地址失败, 暂时无法上传视频封面',
+          type: 'error',
+          duration: 3000
+        })
+      }
+    }).catch(error => {
+      this.$notify({
+        title: '提示',
+        message: '获取 OSS 服务器地址失败, 暂时无法上传视频封面',
+        type: 'warning',
+        duration: 3000
+      })
+    })
+  },
+  mounted() {
+  },
+  methods: {
+    // ****************************************************************************************************************
+    beforeAvatarUpload(file) {
+      const isJPG = file.type === 'image/jpeg'
+      const isLt2M = file.size / 1024 / 1024 < 10
+      if (!isJPG) {
+        this.$message.error('封面图片只能是 JPG 格式!')
+      }
+      if (!isLt2M) {
+        this.$message.error('封面图片大小不能超过 10MB!')
+      }
+      return isJPG && isLt2M
+    },
+    handleAvatarSuccess(res, file) {
+      if (res.code === 0) {
+        const resData = res.data
+        this.coverUrl = URL.createObjectURL(file.raw)
+        this.form.coverFileId = resData.uploadId
+      } else {
+        this.$notify({
+          title: '提示',
+          message: '视频封面上传失败,请重试!' + res.msg,
+          type: 'warning',
+          duration: 3000
+        })
+      }
+    },
+    handleAvatarError(error, file) {
+      this.$notify({
+        title: '提示',
+        message: '视频封面上传失败,请重试!' + error,
+        type: 'warning',
+        duration: 3000
+      })
+    },
+    handleOnChange(file, fileList) {
+    },
+    // ****************************************************************************************************************
+    onSubmit() {
+      if (!this.form.coverFileId) {
+        this.$notify({
+          title: '提示',
+          message: '你还没有上传视频封面',
+          type: 'warning',
+          duration: 3000
+        }
+        )
+        return
+      }
+
+      addProduct(this.form).then(res => {
+        if (res.code === 0) {
+          this.$notify({
+            title: '提示',
+            message: '商品已发布',
+            type: 'warning',
+            duration: 3000
+          })
+        } else {
+          this.$notify({
+            title: '提示',
+            message: res.msg,
+            type: 'warning',
+            duration: 3000
+          })
+        }
+      }).catch(error => {
+        this.$notify({
+          title: '提示',
+          message: error.message,
+          type: 'warning',
+          duration: 3000
+        })
+      })
+
+      this.$emit('post-publish', this.form.coverFileId)
+    }
+  }
+}
+</script>
+
+<style>
+.uploader-example .uploader-btn {
+  margin-right: 4px;
+}
+.uploader-example .uploader-list {
+  max-height: 440px;
+  overflow: auto;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409EFF;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 320px;
+  height: 240px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 320px;
+  height: 240px;
+  display: block;
+}
+</style>

+ 1 - 1
src/views/mall/Overview.vue

@@ -73,7 +73,7 @@ export default {
     getData() {
       getProducts(1).then(resp => {
         if (resp.code === 0) {
-          this.dataList = resp.data
+          this.dataList = resp.data.list
         }
       })
     }