15个文件已修改
10个文件已添加
3657 ■■■■■ 已修改文件
manager/package.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/api/customer.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/api/file.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/api/health-video.js 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/api/kitchen-video.js 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/api/kitchen.js 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/utils/file.js 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/activity/index.vue 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/customer/index.vue 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/goods/goods-info/goods.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/goods/goods-info/goodsApply.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/goods/goods-info/goodsDetail.vue 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/health/HealthVideoList.vue 929 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/kitchen/kitchenType.vue 370 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/kitchen/kitchenVideo.vue 975 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/video/GoodsExpandRow.vue 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/video/VideoList.vue 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
seller/package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
seller/src/api/file.js 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
seller/src/libs/axios.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
seller/src/utils/file.js 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
seller/src/views/goods/goods-seller/goods.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
seller/src/views/goods/goods-seller/goodsOperation.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
seller/src/views/goods/goods-seller/goodsOperationFirst.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
seller/src/views/goods/goods-seller/goodsOperationSec.vue 618 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/package.json
@@ -14,9 +14,11 @@
    "@antv/g2": "^4.1.12",
    "axios": "^0.21.1",
    "core-js": "^3.6.5",
    "cos-js-sdk-v5": "^1.10.1",
    "dplayer": "^1.26.0",
    "js-cookie": "^2.2.1",
    "node-sass": "^4.14.1",
    "price-color": "1.0.2",
    "sass-loader": "^8.0.2",
    "sockjs-client": "^1.4.0",
    "swiper": "^6.3.5",
@@ -31,8 +33,7 @@
    "vue-router": "^3.1.3",
    "vuedraggable": "^2.23.2",
    "vuex": "^3.4.0",
    "xss": "^1.0.7",
    "price-color": "1.0.2"
    "xss": "^1.0.7"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^4.4.4",
manager/src/api/customer.js
@@ -78,3 +78,9 @@
  })
}
export const getCustomerInfo= (params) =>{
  return service({
    url: '/customerManager/'+ params,
    method: 'GET'
  })
}
manager/src/api/file.js
@@ -12,3 +12,12 @@
    })
}
// 获取STS访问令牌
export const getSts = () => {
    return service({
        baseURL: commonUrl,
        url: "/common/lmk/file/sts",
        method: "GET"
    })
}
manager/src/api/health-video.js
New file
@@ -0,0 +1,33 @@
import service from "@/libs/axios";
// 视频发布
export const healthVideo = (data) => {
  return service({
    url: "/lkm/health/healthVideo",
    method: "POST",
    data: data
  })
}
// 视频修改
export const updateHealthVideo = (data) => {
  return service({
    url: "/lkm/health/updateHealthVideo",
    method: "POST",
    data: data
  })
}
// 视频查询
export const getHealthVideos = (data) => {
  return service({
    url: "/lkm/health/page",
    method: "POST",
    data: data
  })
}
// 视频查询
export const delHealth = (id) => {
  return service({
    url: "/lkm/health/del/" + id,
    method: "POST"
  })
}
manager/src/api/kitchen-video.js
New file
@@ -0,0 +1,33 @@
import service from "@/libs/axios";
// 厨神视频发布
export const addKitchenVideo = (data) => {
  return service({
    url: "/lmk/kitchen-video/addKitchenVideo",
    method: "POST",
    data: data
  })
}
// 视频修改
export const updateKitchenVideo = (data) => {
  return service({
    url: "/lmk/kitchen-video/updateKitchenVideo",
    method: "POST",
    data: data
  })
}
// 视频查询
export const getKitchenVideos = (data) => {
  return service({
    url: "/lmk/kitchen-video/page",
    method: "POST",
    data: data
  })
}
// 删除视频
export const delKitchen = (id) => {
  return service({
    url: "/lmk/kitchen-video/del/" + id,
    method: "POST"
  })
}
manager/src/api/kitchen.js
New file
@@ -0,0 +1,48 @@
import service from "@/libs/axios";
// 神厨分页
export const getKitchenType = (data) => {
  return service({
    url: "/lmk/kitchen-type/page",
    method: "GET",
    params: data
  })
}
// 神厨分类不分页
export const getKitchenTypeAllList = () => {
  return service({
    url: "/lmk/kitchen-type/list",
    method: "GET",
  })
}
// 添加神厨标签
export const addKitchenType = (data) => {
  return service({
    url: "/lmk/kitchen-type",
    method: "POST",
    data: data
  })
}
// 添加神厨标签
export const editKitchenType = (data) => {
  return service({
    url: "/lmk/kitchen-type",
    method: "PUT",
    data: data
  })
}
// 删除神厨标签
export const delKitchenType = (id) => {
  return service({
    url: "/lmk/kitchen-type/"+id,
    method: "DELETE",
  })
}
// 批量删除神厨标签
export const deleteBatch = (data) => {
  return service({
    url: "/lmk/kitchen-type/batch",
    method: "DELETE",
    data: data
  })
}
manager/src/utils/file.js
New file
@@ -0,0 +1,40 @@
/**
 * 生成fileKey
 *
 * @param fileName 文件名
 * @returns {*}
 */
export function getFileKey (fileName) {
    const extension = fileName.split('.').pop().toLowerCase();
    const fileTypes = {
        image: { name: '图片', exts: ["jpg", "png", "jpeg", "gif", "bmp", "webp", "tiff", "svg", "ico", "psd", "raw"] },
        video: { name: '视频', exts: ["mp4", "avi", "rmvb", "mov", "wmv", "flv", "mkv", "mpeg", "mpg", "m4v", "3gp", "webm", "vob", "swf"] },
        radio: { name: '音频', exts: ["mp3", "wma", "wav", "mpeg-4", "cd", "m4a", "aac", "flac", "ogg", "aiff", "ape", "midi", "amr", "ra"] },
        text: { name: '文本', exts: ["txt", "xls", "xlsx", "doc", "docx", "pdf", "ppt", "pptx", "csv", "rtf", "odt", "ods", "odp", "epub", "mobi", "pages", "numbers", "key"] },
        zip: { name: '压缩文件', exts: ["zip", "rar", "7z", "tar", "gz", "bz2", "xz", "iso", "dmg", "pkg", "cab", "z", "lz", "lzma", "lzo"] }
    };
    // 获取当前时间并格式化为 yyyyMMddHHmmss
    const now = new Date();
    const yyyyMMddHHmmss = [
        now.getFullYear(),
        String(now.getMonth() + 1).padStart(2, '0'),
        String(now.getDate()).padStart(2, '0'),
        String(now.getHours()).padStart(2, '0'),
        String(now.getMinutes()).padStart(2, '0'),
        String(now.getSeconds()).padStart(2, '0')
    ].join('');
    // 生成5位随机数字
    const random5Digits = Math.floor(10000 + Math.random() * 90000);
    // 查找匹配的文件类型
    for (const [type, data] of Object.entries(fileTypes)) {
        if (data.exts.includes(extension)) {
            return `${type}/${yyyyMMddHHmmss}${random5Digits}` + '.' + extension;
        }
    }
    return '';
}
manager/src/views/activity/index.vue
@@ -136,6 +136,11 @@
            <Button
              type="info"
              size="small"
              @click="detail(row)"
            >详情</Button>
            <Button
              type="info"
              size="small"
              @click="openEdit(row)"
            >编辑</Button>
            <Button
@@ -336,6 +341,82 @@
        </Row>
      </Modal>
      <Modal
        v-model="infoModelShow"
        :title="modelTitle"
        @on-cancel="infoModelClose"
        width="800"
        :mask-closable="false"
      >
        <div class="detail-container">
          <Row :gutter="16">
            <Col span="12">
              <div class="detail-item">
                <label>活动名称:</label>
                <span>{{ activityInfo.activityName || '-' }}</span>
              </div>
            </Col>
            <Col span="12">
              <div class="detail-item">
                <label>活动类型:</label>
                <span>{{activityInfo.activityType === 'online' ? '线上':'线下'}}</span>
              </div>
            </Col>
            <Col span="12">
              <div class="detail-item">
                <label>报名时间段:</label>
                <span>{{ activityInfo.reportStartTime }} - {{ activityInfo.reportEndTime }}</span>
              </div>
            </Col>
            <Col span="12">
              <div class="detail-item">
                <label>活动时间段:</label>
                <span>{{ activityInfo.startTime }} - {{ activityInfo.endTime }}</span>
              </div>
            </Col>
            <Col span="24" v-if="coverType === '输入文字封面'">
              <div class="detail-item">
                <label>封面文字:</label>
                <span>{{ activityInfo.cover || '-' }}</span>
              </div>
            </Col>
            <Col span="24" v-if="coverType === '选择文件封面'">
              <div class="detail-item">
                <label>上传封面:</label>
                <span>{{ activityInfo.cover }}</span>
              </div>
            </Col>
            <Col span="12">
              <div class="detail-item">
                <label>人数限制:</label>
                <span>{{ activityInfo.limitUserNum || '无限制' }}</span>
              </div>
            </Col>
            <Col span="12">
              <div class="detail-item">
                <label>活动地点:</label>
                <span>{{ activityInfo.activityLocation || '-' }}</span>
              </div>
            </Col>
            <Col span="24">
              <div class="detail-item">
                <label>活动内容:</label>
                <div
                  class="activity-content"
                  v-html="activityInfo.activityContent || '无内容'"
                ></div>
              </div>
            </Col>
          </Row>
        </div>
        <div slot="footer">
          <Button @click="infoModelClose">关闭</Button>
        </div>
      </Modal>
      <!-- 图片预览模态框 -->
      <Modal v-model="previewVisible" title="图片预览" footer-hide>
        <img :src="previewImageUrl" style="width: 100%">
@@ -361,6 +442,8 @@
  components: {Editor},
  data() {
    return {
      infoModelShow:false,
      loading: false,
      membersLoading: false,
      submitLoading: false,
@@ -397,6 +480,24 @@
      // 活动表单
      activityFrom: {
        id: '',
        activityName: '',
        activityType: '',
        reportTime: [],
        time: [],
        activityContent: '',
        cover: '',
        coverType: '',
        status: '',
        reportStartTime: '',
        reportEndTime: '',
        startTime: '',
        endTime: '',
        recommend: false,
        limitUserNum: 0,
        activityLocation: '',
      },
      activityInfo: {
        id: '',
        activityName: '',
        activityType: '',
@@ -645,6 +746,11 @@
    this.init()
  },
  methods: {
    detail(row){
      this.modelTitle = '活动详情'
      this.infoModelShow = true
      this.activityInfo = row
    },
    // 获取富文本编辑器的内容
    getReason(content) {
      this.activityFrom.activityContent = content
@@ -757,7 +863,9 @@
        this.coverType = row.coverType === 'text' ? '输入文字封面' : '选择文件封面'
      })
    },
    infoModelClose(){
      this.infoModelShow = false
    },
    // 关闭模态框
    modelClose() {
      this.modelShow = false
@@ -1183,4 +1291,33 @@
    margin-top: 4px;
  }
}
.detail-container {
  padding: 16px;
}
.detail-item {
  margin-bottom: 18px;
  line-height: 1.5;
  label {
    display: inline-block;
    width: 100px;
    color: #666;
    font-weight: bold;
    vertical-align: top;
  }
  span {
    display: inline-block;
    width: calc(100% - 110px);
  }
}
.activity-content {
  border: 1px solid #dcdee2;
  border-radius: 4px;
  padding: 12px;
  min-height: 100px;
  margin-top: 8px;
}
</style>
manager/src/views/customer/index.vue
@@ -68,6 +68,7 @@
        @on-selection-change="showSelect"
      >
        <template slot-scope="{ row, index }" slot="action">
          <Button type="info" size="small" style="margin-right: 5px" @click="openInfo(row)">查看详情</Button>
          <Button type="info" size="small" style="margin-right: 5px" @click="openEdit(row)">编辑标签</Button>
          <Button type="error" size="small" style="margin-right: 5px" @click="joinBlack(row)">加入黑名单</Button>
        </template>
@@ -87,6 +88,82 @@
        ></Page>
      </Row>
      <Modal
        v-model="showCustomerInfo"
        :title="modelTitle"
        width="700"
        :mask-closable="false"
      >
        <div class="customer-detail">
          <div class="avatar-section">
            <Avatar :src="customerInfo.face" size="large" />
            <div class="basic-info">
              <h3>{{ customerInfo.nickName || '微信用户' }}</h3>
              <p>ID: {{ customerInfo.id }}</p>
              <p>用户名: {{ customerInfo.username }}</p>
            </div>
          </div>
          <Divider />
          <div class="detail-grid">
            <div class="detail-row">
              <span class="detail-label">性别:</span>
              <span class="detail-value">{{ customerInfo.sex === 0 ? "女" : "男"}}</span>
            </div>
            <div class="detail-row">
              <span class="detail-label">地区:</span>
              <span class="detail-value">{{ customerInfo.region || '未设置' }}</span>
            </div>
            <div class="detail-row">
              <span class="detail-label">手机号:</span>
              <span class="detail-value">{{ customerInfo.mobile || '未绑定' }}</span>
            </div>
            <div class="detail-row">
              <span class="detail-label">当前积分:</span>
              <span class="detail-value">{{ customerInfo.point }}</span>
            </div>
            <div class="detail-row">
              <span class="detail-label">总积分:</span>
              <span class="detail-value">{{ customerInfo.totalPoint }}</span>
            </div>
            <div class="detail-row">
              <span class="detail-label">账号状态:</span>
              <span class="detail-value">
          <Tag :color="customerInfo.disabled ? 'error' : 'success'">
            {{ customerInfo.disabled ? '已禁用' : '正常' }}
          </Tag>
        </span>
            </div>
            <div class="detail-row">
              <span class="detail-label">是否关联店铺:</span>
              <span class="detail-value">{{ customerInfo.haveStore ? '是' : '否' }}</span>
            </div>
            <div class="detail-row">
              <span class="detail-label">注册时间:</span>
              <span class="detail-value">{{ customerInfo.createTime }}</span>
            </div>
            <div class="detail-row">
              <span class="detail-label">最后登录时间:</span>
              <span class="detail-value">{{ customerInfo.lastLoginDate }}</span>
            </div>
          </div>
          <div v-if="customerInfo.customerTagList && customerInfo.customerTagList.length > 0" class="tags-section">
            <h4>用户标签</h4>
            <div>
              <Tag v-for="tag in customerInfo.customerTagList" :key="tag" color="default" style="margin-right: 8px;">
                {{ tag }}
              </Tag>
            </div>
          </div>
        </div>
        <div slot="footer">
          <Button type="primary" @click="showCustomerInfo = false">关闭</Button>
        </div>
      </Modal>
<!--      标签弹窗-->
      <Modal
        v-model="showCustomerTag"
        :title="modelTitle"
@@ -125,7 +202,7 @@
<script>
import JsonExcel from "vue-json-excel";
import {getCustomerList,addCustomerTag,saveCustomerTagById,getTagList,getStoreSelectOptions} from "@/api/customer";
import {getCustomerList,addCustomerTag,saveCustomerTagById,getTagList,getStoreSelectOptions,getCustomerInfo} from "@/api/customer";
import {addCustomerBlackByPC} from "@/api/customer-black.js"
export default {
@@ -135,6 +212,30 @@
  },
  data(){
    return{
      customerInfo: {
        birthday: null,
        blackId: null,
        clientEnum: null,
        createTime: "",
        customerTagList: [],
        disabled: true,
        experience: null,
        face: "",
        gradeId: null,
        haveStore: false,
        id: "",
        lastLoginDate: "",
        mobile: null,
        nickName: "",
        openId: null,
        point: 10,
        region: "",
        regionId: "",
        sex: 0,
        storeId: null,
        totalPoint: 0,
        username: ""
      },
      loading: false, // 表单加载状态
      //查询客户列表请求参数
      searchForm:{
@@ -225,8 +326,9 @@
      selectCount: 0, // 已选数量
      selectList: [], // 已选数据列表
      //客户详情对话框---
      showCustomerInfo:false,
      //客户标签对话框---
      showCustomerTag:false,
      submitLoading:false,
      selectLoading:false,
@@ -301,6 +403,39 @@
      this.selectList = e.map(d => d.id);
      this.selectCount = e.length;
    },
    //查看详情
    openInfo(row){
      this.showCustomerInfo = true;
      this.modelTitle = "用户详情"
      getCustomerInfo(row.id).then(res =>{
        if(res.code === 200){
          this.customerInfo = {
            birthday: res.data.birthday || null,
            blackId: res.data.blackId || null,
            clientEnum: res.data.clientEnum || null,
            createTime: res.data.createTime || '',
            customerTagList: res.data.customerTagList || [],
            disabled: res.data.disabled || false,
            experience: res.data.experience || null,
            face: res.data.face || '默认头像URL',
            gradeId: res.data.gradeId || null,
            haveStore: res.data.haveStore || false,
            id: res.data.id || '',
            lastLoginDate: res.data.lastLoginDate || '',
            mobile: res.data.mobile || null,
            nickName: res.data.nickName || '微信用户',
            openId: res.data.openId || null,
            point: res.data.point || 0,
            region: res.data.region || '',
            regionId: res.data.regionId || '',
            sex: res.data.sex || 0,
            storeId: res.data.storeId || null,
            totalPoint: res.data.totalPoint || 0,
            username: res.data.username || ''
          };
        }
      })
    },
    // 编辑标签
    openEdit(row){
      this.showCustomerTag = true
@@ -360,6 +495,40 @@
</script>
<style lang="scss" scoped>
.customer-detail {
  padding: 16px;
}
.avatar-section {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
}
.basic-info {
  margin-left: 16px;
}
.basic-info h3 {
  margin: 0 0 8px 0;
  font-size: 18px;
}
.basic-info p {
  margin: 6px 0;
  color: #808695;
  font-size: 14px;
}
.tags-section {
  margin-top: 16px;
  padding-top: 16px;
}
.tags-section h4 {
  margin-bottom: 12px;
  color: #17233d;
}
.export {
  margin: 10px 20px 10px 0;
}
manager/src/views/goods/goods-info/goods.vue
@@ -89,9 +89,12 @@
        <template slot="goodsSlot" slot-scope="{ row }">
          <div style="margin: 5px 0px; height: 80px; display: flex">
            <div style="">
              <img
              <img v-if="row.original"
                :src="row.original"
                style="height: 60px; margin-top: 1px; width: 60px"
              />
              <video v-else
                     :src="row.goodsVideo" style="height: 60px; margin-top: 1px; width: 60px"
              />
            </div>
@@ -159,6 +162,7 @@
<script>
import { getGoodsListData, upGoods, lowGoods } from "@/api/goods";
import {getSts} from '@/api/file'
import vueQr from "vue-qr";
export default {
  components: {
@@ -376,6 +380,18 @@
        this.loading = false;
        if (res.success) {
          this.data = res.result.records;
          getSts().then(res => {
         const endpoint = res.data.endpoint
            this.data.forEach(item => {
              if (item.goodsVideo != null && item.goodsVideo.indexOf('http')===-1) {
                item.goodsVideo = endpoint + '/' + item.goodsVideo;
              }
              if (item.original !=null && item.original.indexOf('http') ===-1) {
                item.original = endpoint + '/' + item.original;
              }
            })
          })
          this.total = res.result.total;
        }
      });
manager/src/views/goods/goods-info/goodsApply.vue
@@ -42,11 +42,13 @@
        <!-- 商品栏目格式化 -->
        <template slot="goodsSlot" slot-scope="scope">
          <div style="margin-top: 5px; height: 80px; display: flex">
            <div style="">
              <img
            <div style="" >
              <img v-if="scope.row.original"
                :src="scope.row.original"
                style="height: 60px; margin-top: 3px; width: 60px"
              />
              <video v-else :src="scope.row.goodsVideo"      style="height: 60px; margin-top: 3px; width: 60px"
              />
            </div>
            <div style="margin-left: 13px">
manager/src/views/goods/goods-info/goodsDetail.vue
@@ -143,6 +143,7 @@
</template>
<script>
import { getGoodsDetail } from "@/api/goods";
import {getSts} from '@/api/file'
export default {
  name: "goodsDetail",
  data() {
@@ -193,6 +194,26 @@
    initGoods(id) {
      getGoodsDetail(id).then((res) => {
        this.goods = res.result;
        if (this.goods.goodsVideo != null && this.goods.goodsVideo !== '') {
          getSts().then(res => {
            console.log('--------------->',this.goods.goodsVideo.indexOf('http')!== -1)
            if (this.goods.goodsVideo !== null && this.goods.goodsVideo.indexOf('http')=== -1) {
              this.goods.goodsVideo = res.data.endpoint+'/'+this.goods.goodsVideo;
            }
          })
        }
        if (this.goods.goodsGalleryList != null && this.goods.goodsGalleryList.length > 0) {
          getSts().then(res => {
            this.goods.goodsGalleryList =  this.goods.goodsGalleryList.map((item) => {
              if (item !== null&&item.indexOf('http')===-1) {
                return  res.data.endpoint+'/'+item;
              }else {
                return item;
              }
            })
          })
        }
        let that = this;
        res.result.skuList.forEach(function (sku, index, array) {
          that.skuData.push({
manager/src/views/health/HealthVideoList.vue
New file
@@ -0,0 +1,929 @@
<template>
  <div>
    <Card>
      <Form
        ref="searchForm"
        @keydown.enter.native="handleSearch"
        :model="searchForm"
        inline
        :label-width="70"
        class="search-form"
      >
        <Form-item label="标题" prop="title">
          <Input
            type="text"
            v-model="searchForm.title"
            clearable
            @on-clear="handleSearch"
            @on-change="handleSearch"
            style="width: 160px"
          />
        </Form-item>
<!--      todo  暂时隐藏大健康一期没定义标签-->
        <Form-item v-if="false" label="标签" prop="tagList">
          <Select
            v-model="searchForm.tagList"
            clearable
            filterable
            multiple
            @on-clear="handleSearch"
            @on-change="handleSearch"
            style="width: 160px"
          >
            <Option v-for="tag in tagList" :key="tag.id" :value="tag.id">{{ tag.tagName }}</Option>
          </Select>
        </Form-item>
        <Form-item label="视频状态" prop="status">
          <Select
            v-model="searchForm.status"
            clearable
            @on-clear="handleSearch"
            @on-change="handleSearch"
            style="width: 160px"
          >
            <Option value="99">待审核</Option>
            <Option value="1">已发布</Option>
            <Option value="0">已下架</Option>
            <Option value="-1">审核未通过</Option>
          </Select>
        </Form-item>
        <Button
          @click="handleSearch"
          type="primary"
          icon="ios-search"
          class="search-btn"
        >搜索
        </Button
        >
        <Button
          @click="handleVideoUpLoad"
          type="primary"
          icon="md-arrow-up"
          class="search-btn"
        >上传视频
        </Button
        >
      </Form>
      <!--      大健康视频上传-->
      <Modal
        v-model="upLoadVideoShow"
        :title="uploadVideoForm.id?'编辑大健康视频':'上传大健康视频'"
        width="800"
        :mask-closable="false"
      >
        <Form
          :model="uploadVideoForm"
          ref="uploadVideoForm"
          :rules="uploadVideoFileRule"
        >
          <Row :gutter="24">
            <Col span="24">
              <Spin size="large" fix v-if="upLoadVideoLoading"> 文件上传中...</Spin>
            </Col>
            <Col span="12">
              <FormItem label="标题" prop="title">
                <Input placeholder="请输入标题" v-model="uploadVideoForm.title"
                       style="width: 200px"
                />
              </FormItem>
            </Col>
            <Col span="24">
              <FormItem label="刷新dom使用" prop="title" v-show="false">
                <Input placeholder="请输入标题" v-model="uploadVideoForm.temp"
                       style="width: 200px"
                />
              </FormItem>
            </Col>
            <Col span="12" v-if="!uploadVideoForm.videoFileKey">
              <FormItem label="上传视频" prop="videoFileKey">
                <Upload
                  :multiple="true"
                  :before-upload="upLoadVideo"
                  accept="video/*"
                  action=""
                >
                  <Button icon="ios-cloud-upload-outline">选择视频</Button>
                </Upload>
              </FormItem>
            </Col>
            <Col span="24" v-else>
              <FormItem label="视频">
                <video style="width: 150px;height: 150px"
                       controls
                       @loadedmetadata="getVideoDuration"
                       :poster="uploadVideoForm.showCoverUrl"
                       :autoplay="false"
                       id="remoteVideo" :src="uploadVideoForm.showVideoUrl"
                       ref="healthVideoInfo"
                ></video>
              </FormItem>
            </Col>
            <Col span="12" v-show="uploadVideoForm.videoFileKey && !uploadVideoForm.coverUrl">
              <FormItem label="上传封面" prop="coverUrl">
                <Upload
                  :multiple="true"
                  :before-upload="upLoadImg"
                  accept="image/*"
                  action=""
                >
                  <Button icon="ios-cloud-upload-outline">选择封面</Button>
                </Upload>
              </FormItem>
            </Col>
            <Col span="24">
              <div>
                <Button type="primary" @click="clearCoverImage" v-show="uploadVideoForm.coverUrl">重新上传封面</Button>
                <Button type="primary" @click="clearVideo" style="margin-left: 20px"
                        v-show="uploadVideoForm.videoFileKey">重新上传视频
                </Button>
              </div>
            </Col>
            <!--            <Col span="24" v-else>-->
            <!--              <FormItem label="封面">-->
            <!--                <div class="showCoverImg" style="width: 150px;height: 150px;">-->
            <!--                  <img :src="uploadVideoForm.showCoverUrl" style="width: 150px;height: 150px" class="coverImg"/>-->
            <!--                  <Icon type="ios-close" size="24" class="coverImgRemove" color="red"-->
            <!--                        style="border-radius: 50%;background: #fff;cursor: pointer"-->
            <!--                        @click="removeCover"-->
            <!--                  />-->
            <!--                </div>-->
            <!--              </FormItem>-->
            <!--            </Col>-->
          </Row>
        </Form>
        <div slot="footer">
          <Button type="text" @click="closeHealthVideo">关闭</Button>
          <Button type="primary" @click="submitHealthVideo">确认</Button>
        </div>
      </Modal>
      <Modal
        v-model="playVideoShow"
        :title="playVideoTitle"
        width="800"
        :mask-closable="false"
      >
        <div class="video-warp">
          <video :src="playVideoUrl" autoplay controls style="width: 768px;height: 432px"/>
        </div>
        <div slot="footer">
          <Button type="text" @click="playVideoClose">关闭</Button>
        </div>
      </Modal>
      <Modal
        v-model="auditingShow"
        title="视频审核"
        width="800"
        :loading="auditingLoading"
        :mask-closable="false"
      >
        <Form
          ref="auditingForm"
          :model="auditingForm"
          :label-width="70"
          :rules="auditingRule"
        >
          <Form-item label="标题:">
            <div>{{ detail.title }}</div>
          </Form-item>
          <Form-item label="标签:">
            <div style="display: flex;flex-wrap: wrap">
              <div v-for="(tag, index) in detail.tagList" :key="'tag' + index" style="margin-right: 5px">
                <Tag color="red">{{ tag.tagName }}</Tag>
              </div>
            </div>
          </Form-item>
          <Form-item label="视频时长:" :label-width="72">
            <div>{{ formatSeconds(detail.videoDuration) }}</div>
          </Form-item>
          <Form-item class="video-warp" :label-width="0">
            <video :src="detail.videoUrl" autoplay controls style="width: 768px;height: 432px"/>
          </Form-item>
          <Form-item label="审核结果:" :label-width="100" prop="result">
            <RadioGroup v-model="auditingForm.result">
              <Radio :label="1">通过</Radio>
              <Radio :label="0">不通过</Radio>
            </RadioGroup>
          </Form-item>
          <Form-item v-show="auditingForm.result === 0" label="不通过原因:" :label-width="100" prop="reason">
            <Input
              type="textarea"
              v-model="auditingForm.reason"
              clearable
              style="width: 100%"
            />
          </Form-item>
        </Form>
        <div slot="footer">
          <Button type="text" @click="closeAuditing">关闭</Button>
          <Button type="primary" @click="submitAuditing">确认</Button>
        </div>
      </Modal>
      <Modal
        v-model="videoDownShow"
        title="视频下架"
        width="800"
        :mask-closable="false"
      >
        <Form :model="videoDownForm" :rules="videoDownRule" ref="videoDownForm">
          <FormItem label="下架原因:" :labelWidth="100" prop="reason">
            <editor ref="editor" @input="getReason"/>
          </FormItem>
        </Form>
        <div slot="footer">
          <Button type="text" @click="closeVideoDown">关闭</Button>
          <Button type="primary" @click="videoDown">确认</Button>
        </div>
      </Modal>
      <Table
        :loading="loading"
        border
        :columns="columns"
        :data="data"
        ref="table"
        sortable="custom"
        @on-sort-change="changeSort"
        @on-selection-change="showSelect"
      >
        <template slot-scope="{ row, index }" slot="tagList">
          <div v-for="(tag, index) in row.tagList" :key="'tag' + index" style="margin-top: 5px">
            <Tag color="red">{{ tag.tagName }}</Tag>
          </div>
        </template>
        <template slot-scope="{ row, index }" slot="videoFileKey">
          <div class="play-text" @click="playVideo(row.videoFileKey, row.title)">点击播放</div>
        </template>
        <template slot-scope="{ row, index }" slot="videoDuration">
          <div>{{ formatSeconds(row.videoDuration) }}</div>
        </template>
        <template slot-scope="{ row, index }" slot="recommend">
          <i-switch v-model="row.recommend" :before-change="() => handleBeforeChange(row)" true-color="#13ce66"/>
        </template>
        <template slot-scope="{ row, index }" slot="status">
          {{ transStatus(row.status) }}
        </template>
        <template slot-scope="{ row, index }" slot="action">
          <Button type="primary" size="small" style="margin-right: 5px" v-if="row.status === '99'"
                  @click="openAuditing(row)">审核
          </Button>
          <Button type="error" size="small" style="margin-right: 5px" v-if="row.status === '1'"
                  @click="openVideoDown(row)">下架
          </Button>
          <Button type="success" size="small" style="margin-right: 5px" v-else-if="row.status === '0'"
                  @click="videoUp(row)">上架
          </Button>
          <Button type="success" size="small" style="margin-right: 5px"
                  @click="handleVideoUpLoad(row)">编辑
          </Button>
          <Button type="success" size="small" style="margin-right: 5px"
                  @click="deleteHealthVideo(row)">删除
          </Button>
        </template>
      </Table>
      <Row type="flex" justify="end" class="mt_10">
        <Page
          :current="searchForm.pageNumber"
          :total="total"
          :page-size="searchForm.pageSize"
          @on-change="changePage"
          @on-page-size-change="changePageSize"
          :page-size-opts="[10, 20, 50]"
          size="small"
          show-total
          show-elevator
          show-sizer
        ></Page>
      </Row>
    </Card>
  </div>
</template>
<script>
import {recommendSet, getVideoById, auditingVideo, up, down} from "@/api/video";
import {healthVideo, getHealthVideos, updateHealthVideo,delHealth} from "@/api/health-video";
import {getVideoTagList} from "@/api/videoTag";
import {getFilePreview, getSts} from "@/api/file";
import Editor from '@/components/editor/index.vue'
import COS from 'cos-js-sdk-v5';
import {getFileKey} from "@/utils/file.js";
export default {
  name: "VideoList",
  components: {Editor},
  data() {
    return {
      videoDownForm: {
        id: '',
        reason: ''
      },
      videoDownRule: {
        reason: [
          {
            require: true,
            message: '请输入下架原因',
            trigger: 'blur',
            validator: (rule, value, callback) => {
              if (value === null || value === '') {
                callback(new Error('请输入下架原因'));
              } else {
                callback();
              }
            }
          }
        ]
      },
      videoDownShow: false, // 视频下架
      videoDownMsg: '', // 下架提示信息
      auditingForm: { // 审核表单
        id: null,
        result: null,
        reason: ''
      },
      auditingRule: {
        result: [
          {
            required: true,
            message: '请选择视频审核结果',
            trigger: 'change',
            validator: (rule, value, callback) => {
              if (value === null || value === undefined) {
                callback(new Error('请选择视频审核结果'));
              } else {
                callback();
              }
            }
          }
        ],
      },
      uploadVideoFileRule: {
        coverUrl: [
          {
            required: true,
            message: '请上传封面',
            trigger: 'blur',
            validator: (rule, value, callback) => {
              if (value === null || value === undefined) {
                callback(new Error('请上传封面'));
              } else {
                callback();
              }
            }
          }
        ],
        title: [
          {
            required: true,
            message: '请输入标题',
            trigger: 'blur',
            validator: (rule, value, callback) => {
              console.log('验证值:-------------->', value, '类型:', typeof value);
              if (value === null || value === undefined) {
                callback(new Error('请输入标题'));
              } else {
                callback();
              }
            }
          }
        ],
      },
      detail: {}, // 视频详情信息
      auditingShow: false, // 审核弹窗
      upLoadVideoShow: false,//文件上传弹窗
      auditingLoading: false, // 审核弹窗
      upLoadVideoLoading: false,//上传视频
      playVideoShow: false, // 视频播放弹窗
      playVideoTitle: '', // 视频播放标题
      playVideoUrl: '', // 当前正在播放的视频地址
      modelShow: false, // 弹窗显隐
      modelTitle: '', // 弹窗title
      loading: false, // 表单加载状态
      searchForm: {
        // 搜索框初始化对象
        pageNumber: 1, // 当前页数
        pageSize: 10, // 页面大小
        title: '', // 标题
        tagList: [], // 标签
        status: '99'
      },
      tagList: [], // 标签列表
      columns: [
        {
          type: 'selection',
          width: 60,
          align: 'center'
        },
        {
          title: "标题",
          key: "title",
          minWidth: 240,
          tooltip: true,
        },
        // {
        //   title: "作者",
        //   key: "authorName",
        //   width: 130,
        //   tooltip: true,
        // },
        // {
        //   title: "视频标签",
        //   key: "tagList",
        //   width: 180,
        //   slot: "tagList",
        // },
        {
          title: "视频内容",
          key: "videoFileKey",
          width: 170,
          slot: "videoFileKey"
        },
        {
          title: "时长",
          key: "videoDuration",
          width: 80,
          align: 'center',
          slot: "videoDuration",
        },
        {
          title: "播放量",
          key: "playNum",
          width: 80,
          align: 'center'
        },
        {
          title: "收藏数",
          key: "collectNum",
          width: 80,
          align: 'center'
        },
        {
          title: "评论数",
          key: "commentNum",
          width: 80,
          align: 'center'
        },
        {
          title: "首页推荐",
          key: "recommend",
          slot: "recommend",
          width: 100,
          align: 'center'
        },
        {
          title: "权重",
          key: "weight",
          width: 170,
        },
        {
          title: "状态",
          key: "status",
          slot: "status",
          width: 120,
          align: 'center'
        },
        {
          title: "操作",
          key: "action",
          slot: "action",
          align: "center",
          width: 200,
        },
      ],
      data: [], // 表单数据
      total: 0, // 表单数据总数
      selectCount: 0, // 已选数量
      selectList: [], // 已选数据列表
      uploadVideoForm: {
        id: null,
        coverUrl: null,
        videoFileKey: null,
        videoFit: null,
        videoDuration: null,
        title: null,
        videoContentType: null,
        videoType: null,
        showCoverUrl: null,
        showVideoUrl: null,
        temp: null
      },
    }
  },
  created() {
    this.getDataList();
    this.getTags('')
  },
  methods: {
    // 秒转x分x秒
    formatSeconds(seconds) {
      if (isNaN(seconds) || seconds < 0) return '0秒';
      const mins = Math.floor(seconds / 60);
      const secs = seconds % 60;
      if (mins === 0) return `${secs}秒`;
      if (secs === 0) return `${mins}分`;
      return `${mins}分${secs}秒`;
    },
    // 获取标签列表
    getTags(tagName) {
      let params = {
        'tagName': tagName
      }
      getVideoTagList(params).then(res => {
        this.tagList = res.data
      })
    },
    // 获取富文本编辑器的内容
    getReason(content) {
      this.videoDownForm.reason = content
    },
    //重新上传封面
    clearCoverImage() {
      this.$set(this.uploadVideoForm, 'coverUrl', '');
      this.$set(this.uploadVideoForm, 'showCoverUrl', '');
      this.$set(this.uploadVideoForm, 'temp', new Date().getTime());
    },
    //重新上传视频
    clearVideo() {
      this.clearCoverImage();
      this.$set(this.uploadVideoForm, 'videoFileKey', '');
      this.$set(this.uploadVideoForm, 'showVideoUrl', '');
      this.$set(this.uploadVideoForm, 'temp', new Date().getTime());
      console.log(this.uploadVideoForm)
    },
    // 视频上架
    videoUp(row) {
      this.$Modal.confirm({
        title: "操作确认",
        content: "您确认要上架视频【 " + row.title + "】吗?",
        loading: true,
        onOk: () => {
          up(row.id).then(res => {
            this.$Modal.remove();
            if (res.code == 200) {
              this.$Message.success("视频上架成功");
              this.getDataList();
            }
          });
        }
      });
    },
    // 视频下架
    videoDown() {
      this.$refs.videoDownForm.validate((valid) => {
        if (valid) {
          down(this.videoDownForm).then(res => {
            this.$Message.success("下架成功")
            this.closeVideoDown()
            this.getDataList()
          })
        }
      })
    },
    // 关闭视频下架
    closeVideoDown() {
      this.videoDownShow = false
      this.videoDownForm = {
        id: '',
        reason: ''
      }
      this.$refs.editor.setContent('')
    },
    // 视频下架
    openVideoDown(row) {
      this.videoDownForm.id = row.id
      this.videoDownShow = true
    },
    // 视频发布/修改
    submitHealthVideo() {
      try {
        this.upLoadVideoLoading = true
        this.$refs.uploadVideoForm.validate((valid) => {
          if (valid) {
            // 修改
            if (this.uploadVideoForm.id) {
              updateHealthVideo(this.uploadVideoForm).then(res => {
                this.$Message.success("修改完成")
                this.closeHealthVideo()
                this.getDataList()
              })
            } else {
              healthVideo(this.uploadVideoForm).then(res => {
                this.$Message.success("添加完成")
                this.closeHealthVideo()
                this.getDataList()
              })
            }
          }
        })
      } finally {
        this.upLoadVideoLoading = false
      }
    },
    // 关闭窗口
    closeHealthVideo() {
      //
      this.uploadVideoForm = {};
      this.upLoadVideoShow = false;
    },
    // 提交审核结果
    submitAuditing() {
      console.log(this.auditingForm, "sb")
      this.$refs.auditingForm.validate((valid) => {
        if (valid) {
          auditingVideo(this.auditingForm).then(res => {
            this.$Message.success("审核完成")
            this.closeAuditing()
            this.getDataList()
          })
        }
      })
    },
    // 审核结果变化
    resultChange(selected) {
      this.auditingForm.result = selected === '通过' ? 1 : 0
      console.log(this.auditingForm.result)
    },
    closeAuditing() {
      this.auditingForm = {
        id: null,
        result: null,
        reason: ''
      }
      this.detail = {}
      this.auditingShow = false
    },
    // 打开审核弹窗
    openAuditing(row) {
      this.auditingShow = true
      this.auditingLoading = true
      this.auditingForm.id = row.id
      getVideoById(row.id).then(res => {
        this.detail = res.data
        this.auditingLoading = false
      })
    },
    getVideoDuration(e){
      const duration = this.$refs.healthVideoInfo.duration;
      this.uploadVideoForm.videoDuration = Math.floor(duration);
    },
    // 打开编辑弹窗
    deleteHealthVideo(row) {
      console.log('删除测试', row)
      this.$Modal.confirm({
        title: "操作确认",
        content: "您确认要删除视频【 " + row.title + "】吗?",
        loading: true,
        onOk: () => {
          delHealth(row.id).then(res => {
            this.$Modal.remove();
            if (res.code === 200) {
              this.$Message.success("视频删除成功");
              this.getDataList();
            }
          });
        }
      });
    },
    // 翻译状态
    transStatus(status) {
      switch (status) {
        case '99':
          return '待审核'
        case '1':
          return '已发布'
        case '0':
          return '已下架'
        case '-1':
          return '审核未通过'
        default:
          return '未知'
      }
    },
    // 开启或关闭推荐的方法
    handleBeforeChange(row) {
      let content = ""
      if (row.recommend) {
        content = '确认要关闭首页推荐吗?'
      } else {
        content = '确认要开启首页推荐吗?'
      }
      return new Promise((resolve) => {
        this.$Modal.confirm({
          title: '操作提醒',
          content: content,
          onOk: () => {
            recommendSet({id: row.id, recommend: !row.recommend}).then(res => {
              this.$Message.success(res.msg);
              resolve();
            })
          }
        });
      });
    },
    // 关闭视频播放
    playVideoClose() {
      this.playVideoTitle = '';
      this.playVideoUrl = '';
      this.playVideoShow = false
    },
    // 点击播放视频
    playVideo(fileKey, title) {
      this.playVideoTitle = title;
      getFilePreview(fileKey).then(res => {
        this.playVideoUrl = res.data
        this.playVideoShow = true
      })
    },
    // 搜索
    handleSearch() {
      this.searchForm.pageNumber = 1;
      this.searchForm.pageSize = 10;
      this.getDataList();
    },
    // 获取列表数据
    getDataList() {
      this.loading = true;
      getHealthVideos(this.searchForm).then((res) => {
        console.log(res)
        this.loading = false;
        if (res.code === 200) {
          this.data = res.data;
          this.total = res.total;
        }
      });
      this.total = this.data.length;
      this.loading = false;
    },
    showSelect(e) {
      this.selectList = e.map(d => d.id);
      this.selectCount = e.length;
    },
    // 排序
    changeSort(e) {
      this.searchForm.sort = e.key;
      this.searchForm.order = e.order;
      if (e.order == "normal") {
        this.searchForm.order = "";
      }
      this.getDataList();
    },
    // 分页 改变页码
    changePage(v) {
      this.searchForm.pageNumber = v;
      this.getDataList();
    },
    // 分页 改变页数
    changePageSize(v) {
      this.searchForm.pageNumber = 1;
      this.searchForm.pageSize = v;
      this.getDataList();
    },
    handleVideoUpLoad(row) {
      this.uploadVideoForm = {};
      this.upLoadVideoShow = true;
      if (row.id) {
        this.uploadVideoForm.id = row.id;
        this.uploadVideoForm.showVideoUrl = row.videoUrl
        this.uploadVideoForm.videoFileKey = row.videoFileKey;
        this.uploadVideoForm.coverUrl = row.coverUrl;
        this.uploadVideoForm.showCoverUrl = row.coverShowUrl;
        this.uploadVideoForm.title = row.title;
      }
    },
    async upLoadVideo(file) {
      try {
        this.$nextTick(() => {
          this.upLoadVideoLoading = true;
        })
        // 获取文件上传临时密钥
        const sts = await getSts();
        const cos = new COS({
          getAuthorization: async function (options, callback) {
            callback({
              TmpSecretId: sts.data.tmpSecretId,
              TmpSecretKey: sts.data.tmpSecretKey,
              SecurityToken: sts.data.sessionToken,
              // 建议返回服务器时间作为签名的开始时间,避免客户端本地时间偏差过大导致签名错误
              StartTime: sts.data.stsStartTime, // 时间戳,单位秒,如:1580000000
              ExpiredTime: sts.data.stsEndTime,// 时间戳,单位秒,如:1580000000
              ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用
            });
          }
        })
        const fileKey = getFileKey(file.name)
        const upData = await cos.uploadFile({
          Bucket: sts.data.bucket,
          Region: sts.data.region,
          Key: fileKey,
          Body: file, // 要上传的文件对象。
          SliceSize: 1024 * 1024 * 5,
          onProgress: function (progressData) {
            console.log('上传进度:', progressData);
          },
        });
        console.log(this.uploadVideoForm)
        this.$nextTick(() => {
          this.uploadVideoForm.videoFileKey = fileKey;
          this.uploadVideoForm.showVideoUrl = sts.data.endpoint + "/" + fileKey;
        })
        console.log("上传成功", upData)
        const duration = this.$refs.healthVideoInfo.duration;
        console.log('-测试获取时间信息---------------->', duration);
      } catch (e) {
        console.log("上传失败", upData)
      } finally {
        this.$nextTick(() => {
          this.upLoadVideoLoading = false;
        })
      }
      return false;
    },
    async upLoadImg(file) {
      try {
        this.upLoadVideoLoading = true;
        // 获取文件上传临时密钥
        const sts = await getSts();
        const cos = new COS({
          getAuthorization: async function (options, callback) {
            callback({
              TmpSecretId: sts.data.tmpSecretId,
              TmpSecretKey: sts.data.tmpSecretKey,
              SecurityToken: sts.data.sessionToken,
              // 建议返回服务器时间作为签名的开始时间,避免客户端本地时间偏差过大导致签名错误
              StartTime: sts.data.stsStartTime, // 时间戳,单位秒,如:1580000000
              ExpiredTime: sts.data.stsEndTime,// 时间戳,单位秒,如:1580000000
              ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用
            });
          }
        })
        const fileKey = getFileKey(file.name)
        const upData = await cos.uploadFile({
          Bucket: sts.data.bucket,
          Region: sts.data.region,
          Key: fileKey,
          Body: file, // 要上传的文件对象。
          SliceSize: 1024 * 1024 * 5,
          onProgress: function (progressData) {
            console.log('上传进度:', progressData);
          },
        });
        console.log("上传成功", upData)
        this.$nextTick(() => {
          this.uploadVideoForm.coverUrl = fileKey;
          this.uploadVideoForm.showCoverUrl = sts.data.endpoint + "/" + fileKey;
        })
      } catch (e) {
        console.log("上传失败", upData)
      } finally {
        this.upLoadVideoLoading = false;
      }
      return false;
    }
  }
}
</script>
<style scoped>
.play-text {
  width: 100%;
  text-align: center;
  color: #2d8cf0;
}
.play-text:hover {
  cursor: pointer;
}
.video-warp {
  width: 786px;
  height: 432px;
}
.data-item {
  display: flex;
  align-items: center;
}
.showCoverImg {
  position: relative;
}
.coverImgRemove {
  position: absolute;
  top: 15px;
  right: -15px;
}
</style>
manager/src/views/kitchen/kitchenType.vue
New file
@@ -0,0 +1,370 @@
<template>
  <div>
    <Card>
      <Form
        ref="searchForm"
        @keydown.enter.native="handleSearch"
        :model="searchForm"
        inline
        :label-width="70"
        class="search-form"
      >
        <Form-item label="标签名称" prop="typeName">
          <Input
            type="text"
            v-model="searchForm.typeName"
            clearable
            @on-clear="handleSearch"
            @on-change="handleSearch"
            style="width: 160px"
          />
        </Form-item>
        <Button
          @click="handleSearch"
          type="primary"
          icon="ios-search"
          class="search-btn"
        >搜索</Button
        >
      </Form>
      <Row class="operation padding-row">
        <Button @click="openAdd" type="info">添加</Button>
        <Button @click="delBatch" type="error">批量删除</Button>
      </Row>
      <Modal
        v-model="modelShow"
        :title="modelTitle"
        >
        <Form ref="form" :model="form" :label-width="70" :rules="rules">
          <Row :gutter="24">
            <Col span="12">
              <FormItem label="标签名称" prop="typeName">
                <Input v-model="form.typeName" autocomplete="off"/>
              </FormItem>
            </Col>
            <Col span="12">
              <FormItem label="排序" prop="sortNum">
                <Input v-model="form.sortNum" autocomplete="off"/>
              </FormItem>
            </Col>
          </Row>
        </Form>
        <div slot="footer">
          <Button type="text" @click="modelClose">取消</Button>
          <Button type="primary" :loading="submitLoading" @click="saveOrUpdate">提交</Button>
        </div>
      </Modal>
      <Table
        :loading="loading"
        border
        :columns="columns"
        :data="data"
        ref="table"
        sortable="custom"
        @on-sort-change="changeSort"
        @on-selection-change="showSelect"
      >
        <template slot-scope="{ row, index }" slot="action">
          <Button type="info" size="small" style="margin-right: 5px" @click="openEdit(row)">编辑</Button>
          <Button type="error" size="small" @click="delById(row)">删除</Button>
        </template>
      </Table>
      <Row type="flex" justify="end" class="mt_10">
        <Page
          :current="searchForm.pageNumber"
          :total="total"
          :page-size="searchForm.pageSize"
          @on-change="changePage"
          @on-page-size-change="changePageSize"
          :page-size-opts="[10, 20, 50]"
          size="small"
          show-total
          show-elevator
          show-sizer
        ></Page>
      </Row>
    </Card>
  </div>
</template>
<script>
import JsonExcel from "vue-json-excel";
import {deleteVideoTagById, getVideoTags, editVideoTag, addVideoTag, deleteVideoTagByIds} from "@/api/videoTag";
import {addKitchenType, deleteBatch, delKitchenType, editKitchenType, getKitchenType} from "@/api/kitchen";
export default {
  name: "VideoTagList",
  components: {
    "download-excel": JsonExcel,
  },
  data() {
    return {
      // 保存加载
      submitLoading: false,
      // 新增修改表单
      form: {
        id: '',
        typeName: '',
        sortNum: '',
      },
      rules: {
        typeName: [
          {required: true, message: "标签名称不能为空", trigger: "blur",
            validator: (rule, value, callback) => {
              if (value === null || value === '') {
                callback(new Error('标签名称不能为空'));
              } else {
                callback();
              }
            }
          }
        ],
        sortNum : [
          {required: true, message: "序号不能为空", trigger: "blur",
            validator: (rule, value, callback) => {
              if (value === null || value === '') {
                callback(new Error('序号不能为空'));
              } else {
                callback();
              }
            }}
        ],
      },
      modelShow: false, // 弹窗显隐
      modelTitle: '', // 弹窗title
      // 表格的表头以及内容
      fields: {
        类型: "tagName",
        引用次数: "useNum",
        排序: "sort_num",
        操作: "action"
      },
      loading: true, // 表单加载状态
      searchForm: {
        // 搜索框初始化对象
        pageNumber: 1, // 当前页数
        pageSize: 10, // 页面大小
        typeName: '', // 标签名称
      },
      selectDate: null,
      columns: [
        {
          type: 'selection',
          width: 60,
          align: 'center'
        },
        {
          title: "标签",
          key: "typeName",
          minWidth: 170,
          tooltip: true,
        },
        // {
        //   title: "引用次数",
        //   key: "useNum",
        //   width: 170,
        //   sortable: true
        // },
        {
          title: "排序",
          key: "sortNum",
          width: 400,
        },
        {
          title: "操作",
          key: "action",
          slot: "action",
          align: "center",
          width: 400,
        },
      ],
      data: [], // 表单数据
      total: 0, // 表单数据总数
      selectCount: 0, // 已选数量
      selectList: [], // 已选数据列表
    };
  },
  methods: {
    showSelect(e) {
      this.selectList = e.map(d => d.id);
      this.selectCount = e.length;
    },
    // 排序
    changeSort(e) {
      this.searchForm.sort = e.key;
      this.searchForm.order = e.order;
      if (e.order == "normal") {
        this.searchForm.order = "";
      }
      this.getDataList();
    },
    // 批量删除
    delBatch() {
      if (this.selectCount <= 0) {
        this.$Message.warning("您还未选择要删除的数据");
        return;
      }
      this.$Modal.confirm({
        title: "确认删除",
        content: "您确认要删除所选的 " + this.selectCount + " 条数据?",
        loading: true,
        onOk: () => {
          deleteBatch(this.selectList).then(res => {
            this.$Modal.remove();
            if (res.code == 200) {
              this.$Message.success("删除成功");
              this.selectList = [];
              this.selectCount = 0;
              this.getDataList();
            }
          });
        }
      });
    },
    // id删除
    delById(row) {
      this.$Modal.confirm({
        title: "确认删除",
        content: "您确认要删除标签: " + row.typeName + " ?",
        loading: true,
        onOk: () => {
          delKitchenType(row.id).then(res => {
            this.$Modal.remove();
            if (res.code == 200) {
              this.$Message.success("删除成功");
              this.getDataList();
            }
          });
        }
      });
    },
    // 打开新增
    openAdd() {
      this.modelTitle = "新增标签"
      this.modelShow = true
      this.form ={}
    },
    // 打开修改
    openEdit(row) {
      this.modelTitle = "修改标签"
      this.form = {};
      this.form.id = row.id
      this.form.typeName = row.typeName
      this.form.sortNum = row.sortNum
      this.modelShow = true
      console.log(this.form)
    },
    // 新增或修改
    saveOrUpdate() {
      this.$refs.form.validate(valid => {
        try {
          if (valid) {
            this.submitLoading = true
            if (this.form.id) {
              // 修改
              editKitchenType(this.form).then(res => {
                if (res.code == 200) {
                  this.$Message.success("修改成功");
                  this.modelClose()
                  this.getDataList()
                }
              })
            } else {
              // 新增
              addKitchenType(this.form).then(res => {
                if (res.code == 200) {
                  this.$Message.success("添加成功");
                  this.modelClose()
                  this.getDataList()
                }
              })
            }
          }
        }finally {
          this.submitLoading = false
        }
      });
    },
    // 关闭弹窗
    modelClose() {
      this.submitLoading = false
      this.modelShow = false
    },
    // 初始化数据
    init() {
      this.getDataList();
    },
    // 分页 改变页码
    changePage(v) {
      this.searchForm.pageNumber = v;
      this.getDataList();
    },
    // 分页 改变页数
    changePageSize(v) {
      this.searchForm.pageNumber = 1;
      this.searchForm.pageSize = v;
      this.getDataList();
    },
    // 搜索
    handleSearch() {
      this.searchForm.pageNumber = 1;
      this.searchForm.pageSize = 10;
      this.getDataList();
    },
    // 起止时间从新赋值
    selectDateRange(v) {
      if (v) {
        this.searchForm.startDate = v[0];
        this.searchForm.endDate = v[1];
      }
    },
    // 获取列表数据
    getDataList() {
      this.loading = true;
      getKitchenType(this.searchForm).then((res) => {
        console.log(res)
        this.loading = false;
        if (res.code == 200) {
          this.data = res.data;
          this.total = res.total;
        }
      });
      this.total = this.data.length;
      this.loading = false;
    },
  },
  mounted() {
    this.init();
  },
};
</script>
<style lang="scss" scoped>
.export {
  margin: 10px 20px 10px 0;
}
.export-excel-wrapper {
  display: inline;
}
.order-tab {
  width: 950px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background-color: #f0f0f0;
  padding: 0 10px;
  margin-bottom: 10px;
  div {
    text-align: center;
    padding: 4px 12px;
    border-radius: 4px;
    cursor: pointer;
  }
  .current {
    background-color: #ffffff;
  }
}
</style>
manager/src/views/kitchen/kitchenVideo.vue
New file
@@ -0,0 +1,975 @@
<template>
  <div>
    <Card>
      <Form
        ref="searchForm"
        @keydown.enter.native="handleSearch"
        :model="searchForm"
        inline
        :label-width="70"
        class="search-form"
      >
        <Form-item label="标题" prop="title">
          <Input
            type="text"
            v-model="searchForm.title"
            clearable
            @on-clear="handleSearch"
            @on-change="handleSearch"
            style="width: 160px"
          />
        </Form-item>
        <Form-item label="分类" prop="typeList">
          <Select
            v-model="searchForm.typeList"
            clearable
            filterable
            multiple
            @on-clear="handleSearch"
            @on-change="handleSearch"
            style="width: 160px"
          >
            <Option v-for="type in typeList" :key="type.id" :value="type.id">{{ type.typeName }}</Option>
          </Select>
        </Form-item>
        <Form-item label="视频状态" prop="status">
          <Select
            v-model="searchForm.status"
            clearable
            @on-clear="handleSearch"
            @on-change="handleSearch"
            style="width: 160px"
          >
            <Option value="99">待审核</Option>
            <Option value="1">已发布</Option>
            <Option value="0">已下架</Option>
            <Option value="-1">审核未通过</Option>
          </Select>
        </Form-item>
        <Button
          @click="handleSearch"
          type="primary"
          icon="ios-search"
          class="search-btn"
        >搜索
        </Button
        >
        <Button
          @click="handleVideoUpLoad"
          type="primary"
          icon="md-arrow-up"
          class="search-btn"
        >上传视频
        </Button
        >
      </Form>
      <!--      厨神视频上传-->
      <Modal
        v-model="upLoadVideoShow"
        :title="uploadVideoForm.id?'编辑厨神视频':'上传厨神视频'"
        width="800"
        :mask-closable="false"
      >
        <Form
          :model="uploadVideoForm"
          ref="uploadVideoForm"
          :rules="uploadVideoFileRule"
        >
          <Row :gutter="24">
            <Col span="24">
              <Spin size="large" fix v-if="upLoadVideoLoading"> 文件上传中...</Spin>
            </Col>
            <Col span="12">
              <FormItem label="标题" prop="title">
                <Input placeholder="请输入标题" v-model="uploadVideoForm.title"
                       style="width: 200px"
                />
              </FormItem>
            </Col>
<!--            刷新dom使用-->
            <Col span="24">
              <FormItem label="刷新dom使用" prop="title" v-show="false">
                <Input placeholder="请输入标题" v-model="uploadVideoForm.temp"
                       style="width: 200px"
                />
              </FormItem>
            </Col>
            <Col span="12" v-if="!uploadVideoForm.videoFileKey">
              <FormItem label="上传视频" prop="videoFileKey">
                <Upload
                  :multiple="true"
                  :before-upload="upLoadVideo"
                  accept="video/*"
                  action=""
                >
                  <Button icon="ios-cloud-upload-outline">选择视频</Button>
                </Upload>
              </FormItem>
            </Col>
            <Col span="24" v-else>
              <FormItem label="视频">
                <video style="width: 150px;height: 150px"
                       controls
                       @loadedmetadata="getVideoDuration"
                       :poster="uploadVideoForm.showCoverUrl"
                       :autoplay="false"
                       id="remoteVideo" :src="uploadVideoForm.showVideoUrl"
                       ref="healthVideoInfo"
                ></video>
              </FormItem>
            </Col>
            <Col span="12" v-show="uploadVideoForm.videoFileKey && !uploadVideoForm.coverUrl">
              <FormItem label="上传封面" prop="coverUrl">
                <Upload
                  :multiple="true"
                  :before-upload="upLoadImg"
                  accept="image/*"
                  action=""
                >
                  <Button icon="ios-cloud-upload-outline">选择封面</Button>
                </Upload>
              </FormItem>
            </Col>
            <Col span="24" style="padding-right:10px">
              <FormItem  label="厨神标签" prop="checkKitchenType">
                <Select  filterable multiple
                         style="width: 600px"
                         v-model="uploadVideoForm.checkKitchenType"
                         >
                  <Option v-for="item in kitchenType" :value="item.id" :key="item.id">{{ item.typeName }}</Option>
                </Select>
              </FormItem>
            </Col>
            <Col span="24">
              <div>
                <Button type="primary" @click="clearCoverImage" v-show="uploadVideoForm.coverUrl">重新上传封面</Button>
                <Button type="primary" @click="clearVideo" style="margin-left: 20px"
                        v-show="uploadVideoForm.videoFileKey">重新上传视频
                </Button>
              </div>
            </Col>
          </Row>
        </Form>
        <div slot="footer">
          <Button type="text" @click="closeHealthVideo">关闭</Button>
          <Button type="primary" @click="submitHealthVideo">确认</Button>
        </div>
      </Modal>
      <Modal
        v-model="playVideoShow"
        :title="playVideoTitle"
        width="800"
        :mask-closable="false"
      >
        <div class="video-warp">
          <video :src="playVideoUrl" autoplay controls style="width: 768px;height: 432px"/>
        </div>
        <div slot="footer">
          <Button type="text" @click="playVideoClose">关闭</Button>
        </div>
      </Modal>
      <Modal
        v-model="auditingShow"
        title="视频审核"
        width="800"
        :loading="auditingLoading"
        :mask-closable="false"
      >
        <Form
          ref="auditingForm"
          :model="auditingForm"
          :label-width="70"
          :rules="auditingRule"
        >
          <Form-item label="标题:">
            <div>{{ detail.title }}</div>
          </Form-item>
          <Form-item label="标签:">
            <div style="display: flex;flex-wrap: wrap">
              <div v-for="(tag, index) in detail.typeList" :key="'tag' + index" style="margin-right: 5px">
                <Tag color="red">{{ tag.tagName }}</Tag>
              </div>
            </div>
          </Form-item>
          <Form-item label="视频时长:" :label-width="72">
            <div>{{ formatSeconds(detail.videoDuration) }}</div>
          </Form-item>
          <Form-item class="video-warp" :label-width="0">
            <video :src="detail.videoUrl" autoplay controls style="width: 768px;height: 432px"/>
          </Form-item>
          <Form-item label="审核结果:" :label-width="100" prop="result">
            <RadioGroup v-model="auditingForm.result">
              <Radio :label="1">通过</Radio>
              <Radio :label="0">不通过</Radio>
            </RadioGroup>
          </Form-item>
          <Form-item v-show="auditingForm.result === 0" label="不通过原因:" :label-width="100" prop="reason">
            <Input
              type="textarea"
              v-model="auditingForm.reason"
              clearable
              style="width: 100%"
            />
          </Form-item>
        </Form>
        <div slot="footer">
          <Button type="text" @click="closeAuditing">关闭</Button>
          <Button type="primary" @click="submitAuditing">确认</Button>
        </div>
      </Modal>
      <Modal
        v-model="videoDownShow"
        title="视频下架"
        width="800"
        :mask-closable="false"
      >
        <Form :model="videoDownForm" :rules="videoDownRule" ref="videoDownForm">
          <FormItem label="下架原因:" :labelWidth="100" prop="reason">
            <editor ref="editor" @input="getReason"/>
          </FormItem>
        </Form>
        <div slot="footer">
          <Button type="text" @click="closeVideoDown">关闭</Button>
          <Button type="primary" @click="videoDown">确认</Button>
        </div>
      </Modal>
      <Table
        :loading="loading"
        border
        :columns="columns"
        :data="data"
        ref="table"
        sortable="custom"
        @on-sort-change="changeSort"
        @on-selection-change="showSelect"
      >
        <template slot-scope="{ row, index }" slot="typeList">
          <div v-for="(tag, index) in row.typeList" :key="'tag' + index" style="margin-top: 5px">
            <Tag color="red">{{ tag.typeName }}</Tag>
          </div>
        </template>
        <template slot-scope="{ row, index }" slot="videoFileKey">
          <div class="play-text" @click="playVideo(row.videoFileKey, row.title)">点击播放</div>
        </template>
        <template slot-scope="{ row, index }" slot="videoDuration">
          <div>{{ formatSeconds(row.videoDuration) }}</div>
        </template>
        <template slot-scope="{ row, index }" slot="recommend">
          <i-switch v-model="row.recommend" :before-change="() => handleBeforeChange(row)" true-color="#13ce66"/>
        </template>
        <template slot-scope="{ row, index }" slot="status">
          {{ transStatus(row.status) }}
        </template>
        <template slot-scope="{ row, index }" slot="action">
          <Button type="primary" size="small" style="margin-right: 5px" v-if="row.status === '99'"
                  @click="openAuditing(row)">审核
          </Button>
          <Button type="error" size="small" style="margin-right: 5px" v-if="row.status === '1'"
                  @click="openVideoDown(row)">下架
          </Button>
          <Button type="success" size="small" style="margin-right: 5px" v-else-if="row.status === '0'"
                  @click="videoUp(row)">上架
          </Button>
          <Button type="success" size="small" style="margin-right: 5px"
                  @click="handleVideoUpLoad(row)">编辑
          </Button>
          <Button type="success" size="small" style="margin-right: 5px"
                  @click="deleteHealthVideo(row)">删除
          </Button>
        </template>
      </Table>
      <Row type="flex" justify="end" class="mt_10">
        <Page
          :current="searchForm.pageNumber"
          :total="total"
          :page-size="searchForm.pageSize"
          @on-change="changePage"
          @on-page-size-change="changePageSize"
          :page-size-opts="[10, 20, 50]"
          size="small"
          show-total
          show-elevator
          show-sizer
        ></Page>
      </Row>
    </Card>
  </div>
</template>
<script>
import {recommendSet, getVideoById, auditingVideo, up, down} from "@/api/video";
import {addKitchenVideo, delKitchen, getKitchenVideos, updateKitchenVideo} from "@/api/kitchen-video";
import {getFilePreview, getSts} from "@/api/file";
import Editor from '@/components/editor/index.vue'
import COS from 'cos-js-sdk-v5';
import {getFileKey} from "@/utils/file.js";
import {getKitchenTypeAllList} from "@/api/kitchen";
export default {
  name: "VideoList",
  components: {Editor},
  data() {
    return {
      videoDownForm: {
        id: '',
        reason: ''
      },
      videoDownRule: {
        reason: [
          {
            require: true,
            message: '请输入下架原因',
            trigger: 'blur',
            validator: (rule, value, callback) => {
              if (value === null || value === '') {
                callback(new Error('请输入下架原因'));
              } else {
                callback();
              }
            }
          }
        ]
      },
      videoDownShow: false, // 视频下架
      videoDownMsg: '', // 下架提示信息
      auditingForm: { // 审核表单
        id: null,
        result: null,
        reason: ''
      },
      auditingRule: {
        result: [
          {
            required: true,
            message: '请选择视频审核结果',
            trigger: 'change',
            validator: (rule, value, callback) => {
              if (value === null || value === undefined) {
                callback(new Error('请选择视频审核结果'));
              } else {
                callback();
              }
            }
          }
        ],
      },
      uploadVideoFileRule: {
        coverUrl: [
          {
            required: true,
            message: '请上传封面',
            trigger: 'blur',
            validator: (rule, value, callback) => {
              if (value === null || value === undefined || value === '') {
                callback(new Error('请上传封面'));
              } else {
                callback();
              }
            }
          }
        ],
        videoFileKey: [
          {
            required: true,
            message: '请上传封面',
            trigger: 'blur',
            validator: (rule, value, callback) => {
              if (value === null || value === undefined || value === '') {
                callback(new Error('请上传封面'));
              } else {
                callback();
              }
            }
          }
        ],
        checkKitchenType: [
          {
            required: true,
            message: '请选择厨神标签',
            trigger: 'blur',
            validator: (rule, value, callback) => {
              console.log('验证值:-------------->', value, '类型:', typeof value);
              if (value === null || value === undefined||value.length >5) {
                callback(new Error('请选择厨神标签且不能超过5个'));
              } else {
                callback();
              }
            }
          }
        ],
        title: [
          {
            required: true,
            message: '请输入标题',
            trigger: 'blur',
            validator: (rule, value, callback) => {
              if (value === null || value === undefined) {
                callback(new Error('请输入标题'));
              } else {
                callback();
              }
            }
          }
        ],
      },
      detail: {}, // 视频详情信息
      auditingShow: false, // 审核弹窗
      upLoadVideoShow: false,//文件上传弹窗
      auditingLoading: false, // 审核弹窗
      upLoadVideoLoading: false,//上传视频
      playVideoShow: false, // 视频播放弹窗
      playVideoTitle: '', // 视频播放标题
      playVideoUrl: '', // 当前正在播放的视频地址
      modelShow: false, // 弹窗显隐
      modelTitle: '', // 弹窗title
      loading: false, // 表单加载状态
      searchForm: {
        // 搜索框初始化对象
        pageNumber: 1, // 当前页数
        pageSize: 10, // 页面大小
        title: '', // 标题
        typeList: [], // 标签
        status: '99'
      },
      typeList: [], // 标签列表
      columns: [
        {
          type: 'selection',
          width: 60,
          align: 'center'
        },
        {
          title: "标题",
          key: "title",
          minWidth: 240,
          tooltip: true,
        },
        {
          title: "作者",
          key: "authorName",
          width: 130,
          tooltip: true,
        },
        {
          title: "视频分类",
          key: "typeList",
          width: 180,
          slot: "typeList",
        },
        {
          title: "视频内容",
          key: "videoFileKey",
          width: 170,
          slot: "videoFileKey"
        },
        {
          title: "时长",
          key: "videoDuration",
          width: 80,
          align: 'center',
          slot: "videoDuration",
        },
        {
          title: "播放量",
          key: "playNum",
          width: 80,
          align: 'center'
        },
        {
          title: "收藏数",
          key: "collectNum",
          width: 80,
          align: 'center'
        },
        {
          title: "评论数",
          key: "commentNum",
          width: 80,
          align: 'center'
        },
        {
          title: "首页推荐",
          key: "recommend",
          slot: "recommend",
          width: 100,
          align: 'center'
        },
        {
          title: "权重",
          key: "weight",
          width: 170,
        },
        {
          title: "状态",
          key: "status",
          slot: "status",
          width: 120,
          align: 'center'
        },
        {
          title: "操作",
          key: "action",
          slot: "action",
          align: "center",
          width: 200,
        },
      ],
      data: [], // 表单数据
      kitchenType: [], // 厨神分类
      total: 0, // 表单数据总数
      selectCount: 0, // 已选数量
      selectList: [], // 已选数据列表
      uploadVideoForm: {
        id: null,
        checkKitchenType:[],
        coverUrl: null,
        videoFileKey: null,
        videoFit: null,
        videoDuration: null,
        title: null,
        videoContentType: null,
        videoType: null,
        showCoverUrl: null,
        showVideoUrl: null,
        temp: null
      },
    }
  },
  created() {
    this.getDataList();
    this.getTypes('')
  },
  methods: {
    // 秒转x分x秒
    formatSeconds(seconds) {
      if (isNaN(seconds) || seconds < 0) return '0秒';
      const mins = Math.floor(seconds / 60);
      const secs = seconds % 60;
      if (mins === 0) return `${secs}秒`;
      if (secs === 0) return `${mins}分`;
      return `${mins}分${secs}秒`;
    },
    // 获取分类列表
    getTypes(typeName) {
      let params = {
        'typeName': typeName
      }
      getKitchenTypeAllList(params).then(res => {
        this.typeList = res.data
      })
    },
    // 获取富文本编辑器的内容
    getReason(content) {
      this.videoDownForm.reason = content
    },
    //重新上传封面
    clearCoverImage() {
      this.$set(this.uploadVideoForm, 'coverUrl', '');
      this.$set(this.uploadVideoForm, 'showCoverUrl', '');
      this.$set(this.uploadVideoForm, 'temp', new Date().getTime());
    },
    //重新上传视频
    clearVideo() {
      this.clearCoverImage();
      this.$set(this.uploadVideoForm, 'videoFileKey', '');
      this.$set(this.uploadVideoForm, 'showVideoUrl', '');
      this.$set(this.uploadVideoForm, 'temp', new Date().getTime());
      console.log(this.uploadVideoForm)
    },
    getVideoDuration(e){
      const duration = this.$refs.healthVideoInfo.duration;
      this.uploadVideoForm.videoDuration = Math.floor(duration);
    },
    // 视频上架
    videoUp(row) {
      this.$Modal.confirm({
        title: "操作确认",
        content: "您确认要上架视频【 " + row.title + "】吗?",
        loading: true,
        onOk: () => {
          up(row.id).then(res => {
            this.$Modal.remove();
            if (res.code == 200) {
              this.$Message.success("视频上架成功");
              this.getDataList();
            }
          });
        }
      });
    },
    // 视频下架
    videoDown() {
      this.$refs.videoDownForm.validate((valid) => {
        if (valid) {
          down(this.videoDownForm).then(res => {
            this.$Message.success("下架成功")
            this.closeVideoDown()
            this.getDataList()
          })
        }
      })
    },
    // 关闭视频下架
    closeVideoDown() {
      this.videoDownShow = false
      this.videoDownForm = {
        id: '',
        reason: ''
      }
      this.$refs.editor.setContent('')
    },
    // 视频下架
    openVideoDown(row) {
      this.videoDownForm.id = row.id
      this.videoDownShow = true
    },
    // 视频发布/修改
    //todo 1
    submitHealthVideo() {
      console.log(this.uploadVideoForm)
      try {
        this.upLoadVideoLoading = true
        this.$refs.uploadVideoForm.validate((valid) => {
          if (valid) {
            // 修改
            if (this.uploadVideoForm.id) {
              console.log(this.uploadVideoForm)
              updateKitchenVideo(this.uploadVideoForm).then(res => {
                this.$Message.success("修改完成")
                this.closeHealthVideo()
                this.getDataList()
              })
            } else {
              addKitchenVideo(this.uploadVideoForm).then(res => {
                this.$Message.success("添加完成")
                this.closeHealthVideo()
                this.getDataList()
              })
            }
          }
        })
      } finally {
        this.upLoadVideoLoading = false
      }
    },
    //获取标签
    getKitchenTypeList(e){
    const typeParam =   {typeName:e}
      getKitchenTypeAllList(typeParam).then(res => {
       this.kitchenType = res.data
     })
    },
    // 关闭窗口
    closeHealthVideo() {
      //
      this.uploadVideoForm = {};
      this.upLoadVideoShow = false;
    },
    // 提交审核结果
    submitAuditing() {
      console.log(this.auditingForm, "sb")
      this.$refs.auditingForm.validate((valid) => {
        if (valid) {
          auditingVideo(this.auditingForm).then(res => {
            this.$Message.success("审核完成")
            this.closeAuditing()
            this.getDataList()
          })
        }
      })
    },
    // 审核结果变化
    resultChange(selected) {
      this.auditingForm.result = selected === '通过' ? 1 : 0
      console.log(this.auditingForm.result)
    },
    closeAuditing() {
      this.auditingForm = {
        id: null,
        result: null,
        reason: ''
      }
      this.detail = {}
      this.auditingShow = false
    },
    // 打开审核弹窗
    openAuditing(row) {
      this.auditingShow = true
      this.auditingLoading = true
      this.auditingForm.id = row.id
      getVideoById(row.id).then(res => {
        this.detail = res.data
        this.auditingLoading = false
      })
    },
    // 打开编辑弹窗
    deleteHealthVideo(row) {
      console.log('删除测试', row)
      this.$Modal.confirm({
        title: "操作确认",
        content: "您确认要删除视频【 " + row.title + "】吗?",
        loading: true,
        onOk: () => {
          delKitchen(row.id).then(res => {
            this.$Modal.remove();
            if (res.code === 200) {
              this.$Message.success("视频删除成功");
              this.getDataList();
            }
          });
        }
      });
    },
    // 翻译状态
    transStatus(status) {
      switch (status) {
        case '99':
          return '待审核'
        case '1':
          return '已发布'
        case '0':
          return '已下架'
        case '-1':
          return '审核未通过'
        default:
          return '未知'
      }
    },
    // 开启或关闭推荐的方法
    handleBeforeChange(row) {
      let content = ""
      if (row.recommend) {
        content = '确认要关闭首页推荐吗?'
      } else {
        content = '确认要开启首页推荐吗?'
      }
      return new Promise((resolve) => {
        this.$Modal.confirm({
          title: '操作提醒',
          content: content,
          onOk: () => {
            recommendSet({id: row.id, recommend: !row.recommend}).then(res => {
              this.$Message.success(res.msg);
              resolve();
            })
          }
        });
      });
    },
    // 关闭视频播放
    playVideoClose() {
      this.playVideoTitle = '';
      this.playVideoUrl = '';
      this.playVideoShow = false
    },
    // 点击播放视频
    playVideo(fileKey, title) {
      this.playVideoTitle = title;
      getFilePreview(fileKey).then(res => {
        this.playVideoUrl = res.data
        this.playVideoShow = true
      })
    },
    // 搜索
    handleSearch() {
      this.searchForm.pageNumber = 1;
      this.searchForm.pageSize = 10;
      this.getDataList();
    },
    // 获取列表数据
    getDataList() {
      this.loading = true;
      getKitchenVideos(this.searchForm).then((res) => {
        console.log(res)
        this.loading = false;
        if (res.code === 200) {
          this.data = res.data;
          this.total = res.total;
        }
      });
      this.total = this.data.length;
      this.loading = false;
    },
    showSelect(e) {
      this.selectList = e.map(d => d.id);
      this.selectCount = e.length;
    },
    // 排序
    changeSort(e) {
      this.searchForm.sort = e.key;
      this.searchForm.order = e.order;
      if (e.order == "normal") {
        this.searchForm.order = "";
      }
      this.getDataList();
    },
    // 分页 改变页码
    changePage(v) {
      this.searchForm.pageNumber = v;
      this.getDataList();
    },
    // 分页 改变页数
    changePageSize(v) {
      this.searchForm.pageNumber = 1;
      this.searchForm.pageSize = v;
      this.getDataList();
    },
    handleVideoUpLoad(row) {
      this.uploadVideoForm = {};
      this.upLoadVideoShow = true;
      console.log(row)
      if (row.id) {
        this.uploadVideoForm.id = row.id;
        this.uploadVideoForm.showVideoUrl = row.videoUrl
        this.uploadVideoForm.videoFileKey = row.videoFileKey;
        this.uploadVideoForm.coverUrl = row.coverUrl;
        this.uploadVideoForm.showCoverUrl = row.coverShowUrl;
        this.uploadVideoForm.title = row.title;
        this.uploadVideoForm.checkKitchenType = row.typeList.map(item => {return item.id});
      }
      this.getKitchenTypeList();
    },
    async upLoadVideo(file) {
      try {
        this.$nextTick(() => {
          this.upLoadVideoLoading = true;
        })
        // 获取文件上传临时密钥
        const sts = await getSts();
        const cos = new COS({
          getAuthorization: async function (options, callback) {
            callback({
              TmpSecretId: sts.data.tmpSecretId,
              TmpSecretKey: sts.data.tmpSecretKey,
              SecurityToken: sts.data.sessionToken,
              // 建议返回服务器时间作为签名的开始时间,避免客户端本地时间偏差过大导致签名错误
              StartTime: sts.data.stsStartTime, // 时间戳,单位秒,如:1580000000
              ExpiredTime: sts.data.stsEndTime,// 时间戳,单位秒,如:1580000000
              ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用
            });
          }
        })
        const fileKey = getFileKey(file.name)
        const upData = await cos.uploadFile({
          Bucket: sts.data.bucket,
          Region: sts.data.region,
          Key: fileKey,
          Body: file, // 要上传的文件对象。
          SliceSize: 1024 * 1024 * 5,
          onProgress: function (progressData) {
            console.log('上传进度:', progressData);
          },
        });
        console.log(this.uploadVideoForm)
        this.$nextTick(() => {
          this.uploadVideoForm.videoFileKey = fileKey;
          this.uploadVideoForm.showVideoUrl = sts.data.endpoint + "/" + fileKey;
        })
        console.log("上传成功", upData)
        const duration = this.$refs.healthVideoInfo.duration;
        console.log('-测试获取时间信息---------------->', duration);
      } catch (e) {
        console.log("上传失败", upData)
      } finally {
        this.$nextTick(() => {
          this.upLoadVideoLoading = false;
        })
      }
      return false;
    },
    async upLoadImg(file) {
      try {
        this.upLoadVideoLoading = true;
        // 获取文件上传临时密钥
        const sts = await getSts();
        const cos = new COS({
          getAuthorization: async function (options, callback) {
            callback({
              TmpSecretId: sts.data.tmpSecretId,
              TmpSecretKey: sts.data.tmpSecretKey,
              SecurityToken: sts.data.sessionToken,
              // 建议返回服务器时间作为签名的开始时间,避免客户端本地时间偏差过大导致签名错误
              StartTime: sts.data.stsStartTime, // 时间戳,单位秒,如:1580000000
              ExpiredTime: sts.data.stsEndTime,// 时间戳,单位秒,如:1580000000
              ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用
            });
          }
        })
        const fileKey = getFileKey(file.name)
        const upData = await cos.uploadFile({
          Bucket: sts.data.bucket,
          Region: sts.data.region,
          Key: fileKey,
          Body: file, // 要上传的文件对象。
          SliceSize: 1024 * 1024 * 5,
          onProgress: function (progressData) {
            console.log('上传进度:', progressData);
          },
        });
        console.log("上传成功", upData)
        this.$nextTick(() => {
          this.uploadVideoForm.coverUrl = fileKey;
          this.uploadVideoForm.showCoverUrl = sts.data.endpoint + "/" + fileKey;
        })
      } catch (e) {
        console.log("上传失败", upData)
      } finally {
        this.upLoadVideoLoading = false;
      }
      return false;
    }
  }
}
</script>
<style scoped>
.play-text {
  width: 100%;
  text-align: center;
  color: #2d8cf0;
}
.play-text:hover {
  cursor: pointer;
}
.video-warp {
  width: 786px;
  height: 432px;
}
.data-item {
  display: flex;
  align-items: center;
}
.showCoverImg {
  position: relative;
}
.coverImgRemove {
  position: absolute;
  top: 15px;
  right: -15px;
}
</style>
manager/src/views/video/GoodsExpandRow.vue
New file
@@ -0,0 +1,35 @@
<template>
  <div>
    <Row v-for="goods in goodsList" :key="goods.goodsId">
      <Row style="width:100%;align-items: center">
        <Col span="6">
          <img :src="goods.thumbnail" style="width: 150px;height: 150px"/>
        </Col>
        <Col span="6">
          <p>商品名称: {{ goods.goodsName }}</p>
        </Col>
        <Col span="6">
          <p>商品单价: ¥{{ goods.price }}</p>
        </Col>
        <Col span="6">
          <p>商品数量: {{ goods.goodsNum }}</p>
        </Col>
      </Row>
    </Row>
  </div>
</template>
<script>
export default {
  name: "GoodsExpandRow",
  props: {
    goodsList: {
      type: Array
    }
  }
}
</script>
<style scoped>
</style>
manager/src/views/video/VideoList.vue
@@ -72,7 +72,7 @@
      <Modal
        v-model="auditingShow"
        title="视频审核"
        width="800"
        width="1200"
        :loading="auditingLoading"
        :mask-closable="false"
      >
@@ -92,11 +92,45 @@
              </div>
            </div>
          </Form-item>
          <Form-item label="视频时长:" :label-width="72">
          <Form-item v-if="detail.videoContentType === 'video'" label="视频时长:" :label-width="72">
            <div>{{formatSeconds(detail.videoDuration)}}</div>
          </Form-item>
          <Form-item class="video-warp" :label-width="0">
            <video :src="detail.videoUrl" autoplay controls style="width: 768px;height: 432px"/>
          <Form-item v-if="detail.videoContentType === 'img'" label="图片张数:" :label-width="72">
            <div>{{detail.imgs.length}}</div>
          </Form-item>
          <Form-item v-if="detail.videoContentType === 'video'" class="video-warp" :label-width="0">
            <Row>
              <Col span="11">
                <video :src="detail.videoUrl" autoplay controls style="width: 500px;height: 300px"/>
              </Col>
              <Col span="13" style="max-height: 300px;overflow-y: scroll">
                <Row v-for="goods in detail.goodsList" :key="goods.goodsId" style="width: 100%">
                  <Row style="width:100%;align-items: center">
                    <Col span="6">
                      <img :src="goods.thumbnail" style="width: 100px;height: 100px"/>
                    </Col>
                    <Col span="10">
                      <p>商品名称: {{ goods.goodsName }}</p>
                    </Col>
                    <Col span="4">
                      <p>商品单价: ¥{{ goods.price }}</p>
                    </Col>
                    <Col span="4">
                      <p>商品数量: {{ goods.goodsNum }}</p>
                    </Col>
                  </Row>
                </Row>
              </Col>
            </Row>
          </Form-item>
          <Form-item v-if="detail.videoContentType === 'img'" :label-width="0">
            <div style="display: flex;flex-direction: row;flex-wrap: wrap">
              <div v-for="img in detail.imgs" :key="img" class="img-warp">
                <img :src="img" class="image">
              </div>
            </div>
          </Form-item>
          <Form-item label="审核结果:" :label-width="100" prop="result">
            <RadioGroup v-model="auditingForm.result">
@@ -193,9 +227,10 @@
import {getVideoTagList} from "@/api/videoTag";
import {getFilePreview} from "@/api/file";
import Editor from '@/components/editor/index.vue'
import GoodsExpandRow from '@/views/video/GoodsExpandRow'
export default {
  name: "VideoList",
  components: {Editor},
  components: {Editor,GoodsExpandRow},
  data() {
    return {
      videoDownForm: {
@@ -264,6 +299,17 @@
          type: 'selection',
          width: 60,
          align: 'center'
        },
        {
          width: 60,
          type: 'expand',
          render: (h, params) => {
            return h(GoodsExpandRow, {
              props: {
                goodsList: params.row.goodsList
              }
            })
          }
        },
        {
          title: "标题",
@@ -566,11 +612,18 @@
  cursor: pointer;
}
.video-warp {
  width: 786px;
  height: 432px;
  width: 100%;
  height: 350px;
}
.data-item {
  display: flex;
  align-items: center;
}
.img-warp {
  padding: 10px;
}
.image {
  width: 150px;
  height: 200px;
}
</style>
seller/package.json
@@ -13,6 +13,7 @@
    "@amap/amap-jsapi-loader": "0.0.7",
    "@antv/g2": "^4.1.14",
    "axios": "^0.21.1",
    "cos-js-sdk-v5": "^1.10.1",
    "dplayer": "^1.27.1",
    "js-cookie": "^2.2.1",
    "node-sass": "^4.14.1",
seller/src/api/file.js
New file
@@ -0,0 +1,31 @@
import service, {
  getRequest,
  postRequest,
  putRequest,
  deleteRequest,
  importRequest,
  getRequestWithNoToken,
  commonUrl,
  postRequestWithNoForm
} from "@/libs/axios";
// 获取文件临时访问url
export const getFilePreview = (fileKey) => {
    return service({
        baseURL: commonUrl,
        url: "/common/lmk/file/preview",
        method: "POST",
        data: {"fileKey": fileKey}
    })
}
// 获取STS访问令牌
export const getSts = () => {
    return service({
        baseURL: commonUrl,
        url: "/common/lmk/file/sts",
        method: "GET"
    })
}
seller/src/libs/axios.js
@@ -353,7 +353,8 @@
    }
  });
};
// 原始的axios暴露出去用即可
export default service
export const uploadFileRequest = (url, params) => {
  let accessToken = getStore("accessToken");
  return service({
seller/src/utils/file.js
New file
@@ -0,0 +1,40 @@
/**
 * 生成fileKey
 *
 * @param fileName 文件名
 * @returns {*}
 */
export function getFileKey (fileName) {
    const extension = fileName.split('.').pop().toLowerCase();
    const fileTypes = {
        image: { name: '图片', exts: ["jpg", "png", "jpeg", "gif", "bmp", "webp", "tiff", "svg", "ico", "psd", "raw"] },
        video: { name: '视频', exts: ["mp4", "avi", "rmvb", "mov", "wmv", "flv", "mkv", "mpeg", "mpg", "m4v", "3gp", "webm", "vob", "swf"] },
        radio: { name: '音频', exts: ["mp3", "wma", "wav", "mpeg-4", "cd", "m4a", "aac", "flac", "ogg", "aiff", "ape", "midi", "amr", "ra"] },
        text: { name: '文本', exts: ["txt", "xls", "xlsx", "doc", "docx", "pdf", "ppt", "pptx", "csv", "rtf", "odt", "ods", "odp", "epub", "mobi", "pages", "numbers", "key"] },
        zip: { name: '压缩文件', exts: ["zip", "rar", "7z", "tar", "gz", "bz2", "xz", "iso", "dmg", "pkg", "cab", "z", "lz", "lzma", "lzo"] }
    };
    // 获取当前时间并格式化为 yyyyMMddHHmmss
    const now = new Date();
    const yyyyMMddHHmmss = [
        now.getFullYear(),
        String(now.getMonth() + 1).padStart(2, '0'),
        String(now.getDate()).padStart(2, '0'),
        String(now.getHours()).padStart(2, '0'),
        String(now.getMinutes()).padStart(2, '0'),
        String(now.getSeconds()).padStart(2, '0')
    ].join('');
    // 生成5位随机数字
    const random5Digits = Math.floor(10000 + Math.random() * 90000);
    // 查找匹配的文件类型
    for (const [type, data] of Object.entries(fileTypes)) {
        if (data.exts.includes(extension)) {
            return `${type}/${yyyyMMddHHmmss}${random5Digits}` + '.' + extension;
        }
    }
    return '';
}
seller/src/views/goods/goods-seller/goods.vue
@@ -95,10 +95,11 @@
        <template slot="goodsSlot" slot-scope="{ row }">
          <div style="margin-top: 5px; height: 90px; display: flex">
            <div style="">
              <img
              <img v-if="row.original"
                :src="row.original"
                style="height: 80px; margin-top: 3px; width: 70px"
              />
              <video :src="row.goodsVideo" v-else style="height: 80px; margin-top: 3px; width: 70px"></video>
            </div>
            <div style="margin-left: 13px">
seller/src/views/goods/goods-seller/goodsOperation.vue
@@ -13,8 +13,8 @@
    <second-step ref='second' :firstData="firstData" v-if="activestep === 1"></second-step>
    <!-- 第三步 发布完成 -->
    <third-step ref='third' v-if="activestep === 2"></third-step>
  </div>
</template>
<script>
@@ -51,7 +51,7 @@
      this.activestep = 0
      this.$refs.first.selectGoodsType = true;
    }
  }
};
</script>
seller/src/views/goods/goods-seller/goodsOperationFirst.vue
@@ -109,19 +109,19 @@
          type: "PHYSICAL_GOODS",
          check: false,
        },
        {
          title: "虚拟商品",
          img: require("@/assets/goodsType2.png"),
          desc: "虚拟核验,无需物流",
          type: "VIRTUAL_GOODS",
          check: false,
        },
        {
          title: "商品模板导入",
          img: require("@/assets/goodsTypeTpl.png"),
          desc: "商品模板,一键导入",
          check: false,
        },
        // {
        //   title: "虚拟商品",
        //   img: require("@/assets/goodsType2.png"),
        //   desc: "虚拟核验,无需物流",
        //   type: "VIRTUAL_GOODS",
        //   check: false,
        // },
        // {
        //   title: "商品模板导入",
        //   img: require("@/assets/goodsTypeTpl.png"),
        //   desc: "商品模板,一键导入",
        //   check: false,
        // },
      ],
      // 商品分类选择数组
      category: [
@@ -130,7 +130,8 @@
        { name: "", id: "" },
      ],
      // 商品类型
      goodsType: "",
      // goodsType: "",
      goodsType: "PHYSICAL_GOODS",
      /** 1级分类列表*/
      categoryListLevel1: [],
      /** 2级分类列表*/
@@ -230,10 +231,13 @@
      if (!this.category[0].name) {
        this.$Message.error("请选择商品分类");
        return;
      } else if (!this.category[2].name) {
        this.$Message.error("必须选择到三级分类");
        return;
      } else if (this.category[2].name) {
      }
      //关闭必须选择3个流程的选项
      // else if (!this.category[2].name) {
      //   this.$Message.error("必须选择到三级分类");
      //   return;
      // }
      else if (this.category[2].name) {
        let params = {
          category: this.category,
          goodsType: this.goodsType,
@@ -245,6 +249,14 @@
          this.$emit("change", params);
        }
      }
      //流程调整
      else {
        let params = {
          category: this.category,
          goodsType: this.goodsType,
        };
        this.$emit("change", params);
      }
    },
  },
  mounted() {
seller/src/views/goods/goods-seller/goodsOperationSec.vue
@@ -26,13 +26,13 @@
            <FormItem label="商品卖点" prop="sellingPoint">
              <Input v-model="baseInfoForm.sellingPoint" :rows="4" style="width: 260px" type="textarea"/>
            </FormItem>
            <FormItem label="商品品牌" prop="brandId">
              <Select v-model="baseInfoForm.brandId" filterable style="width: 200px">
                <Option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id"></Option>
              </Select>
              <Button class="refresh-icon" icon="md-refresh" shape="circle" type="text"
                      @click="refresh('brand')"></Button>
            </FormItem>
<!--            <FormItem label="商品品牌" prop="brandId">-->
<!--              <Select v-model="baseInfoForm.brandId" filterable style="width: 200px">-->
<!--                <Option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id"></Option>-->
<!--              </Select>-->
<!--              <Button class="refresh-icon" icon="md-refresh" shape="circle" type="text"-->
<!--                      @click="refresh('brand')"></Button>-->
<!--            </FormItem>-->
          </div>
          <h4>商品交易信息</h4>
          <div class="form-item-view">
@@ -101,10 +101,10 @@
          </div>
          <h4>商品规格及图片</h4>
          <div class="form-item-view">
            <FormItem class="form-item-view-el required" label="主图" prop="goodsGalleryFiles">
            <FormItem class="form-item-view-el required" label="主图" prop="goodsGalleryFiles"  v-if=" baseInfoForm.goodsVideo ==null || baseInfoForm.goodsVideo ===''">
              <div style="display: flex; flex-wrap: wrap;">
                <vuedraggable :animation="200" :list="baseInfoForm.goodsGalleryFiles">
                  <div v-for="(item, __index) in baseInfoForm.goodsGalleryFiles" :key="__index"
                <vuedraggable :animation="200" :list="showListImages">
                  <div v-for="(item, __index) in showListImages" :key="__index"
                       class="demo-upload-list">
                    <template>
                      <img :src="item"/>
@@ -118,17 +118,17 @@
                  </div>
                </vuedraggable>
                <!--<Upload ref="upload"-->
                        <!--:action="uploadFileUrl" :before-upload="handleBeforeUploadGoodsPicture"-->
                        <!--:format="['jpg', 'jpeg', 'png', 'webp']"-->
                        <!--:headers="{ ...accessToken }"-->
                        <!--:max-size="2048" :on-error="() => { $Spin.hide(); }" :on-exceeded-size="handleMaxSize"-->
                        <!--:on-format-error="handleFormatError" :on-progress="() => { $Spin.show(); }"-->
                        <!--:on-success="handleSuccessGoodsPicture" :show-upload-list="false" multiple-->
                        <!--style="margin-left: 10px"-->
                        <!--type="drag">-->
                  <!--<div style="width: 148px; height: 148px; line-height: 148px">-->
                    <!--<Icon size="20" type="md-add"></Icon>-->
                  <!--</div>-->
                <!--:action="uploadFileUrl" :before-upload="handleBeforeUploadGoodsPicture"-->
                <!--:format="['jpg', 'jpeg', 'png', 'webp']"-->
                <!--:headers="{ ...accessToken }"-->
                <!--:max-size="2048" :on-error="() => { $Spin.hide(); }" :on-exceeded-size="handleMaxSize"-->
                <!--:on-format-error="handleFormatError" :on-progress="() => { $Spin.show(); }"-->
                <!--:on-success="handleSuccessGoodsPicture" :show-upload-list="false" multiple-->
                <!--style="margin-left: 10px"-->
                <!--type="drag">-->
                <!--<div style="width: 148px; height: 148px; line-height: 148px">-->
                <!--<Icon size="20" type="md-add"></Icon>-->
                <!--</div>-->
                <!--</Upload>-->
              </div>
              <div style="width: 100%;display: flex;justify-content: start;margin-top: 10px;">
@@ -139,22 +139,33 @@
              </Modal>
            </FormItem>
            <FormItem>
              <div style="color: grey">主图仅支持png,jpg,jpeg格式,宽高至少600*600px,大小2M内,可拖拽调整主图顺序</div>
<!--              <div class="demo-upload-list" v-for="(item,index) in showListImages">-->
<!--                <template style="display: flex">-->
<!--                  <img :src="item">-->
<!--                  <div class="demo-upload-list-cover">-->
<!--                    <Icon type="ios-eye-outline" @click.native="handleView(item)"></Icon>-->
<!--                    <Icon type="ios-trash-outline" @click.native="handleRemove(index)"></Icon>-->
<!--                  </div>-->
<!--                </template>-->
<!--              </div>-->
              <div style="color: grey"  v-if="!baseInfoForm.goodsVideo">主图仅支持png,jpg,jpeg格式,宽高至少600*600px,大小2M内</div>
            </FormItem>
            <FormItem class="form-item-view-el" label="主图视频" prop="goodsVideo">
            <FormItem class="form-item-view-el" label="视频" prop="goodsVideo" v-if="!(baseInfoForm.goodsGalleryFiles !=null &&baseInfoForm.goodsGalleryFiles.length>0)">
              <div class="goods-video">
                <div v-if="baseInfoForm.goodsVideo">
                  <div>
                    <video :src="baseInfoForm.goodsVideo" class="video" controls style="max-width: 300px;"/>
                    <video :src="baseInfoForm.showGoodsVideo" class="video" controls style="max-width: 300px;"/>
                  </div>
                </div>
                <Upload ref="upload" :action="uploadFileUrl" :format="['avi', 'wmv', 'mpeg', 'mp4', 'mov']"
                <Upload ref="upload" action="-" :format="['avi', 'wmv', 'mpeg', 'mp4', 'mov']"
                        :headers="{ ...accessToken }"
                        :max-size="10240" :on-error="() => { loadingVideo = false }"
                        :on-exceeded-size="handleVideoMaxSize"
                        :on-format-error="handleFormatError" :on-progress="() => { loadingVideo = true }"
                        :on-success="handleSuccessGoodsVideo" :show-upload-list="false"
                        multiple
                        :before-upload="upLoadVideo"
                        accept="video/*"
                        style="margin-left: 10px" type="drag">
                  <Button :loading="loadingVideo" icon="ios-cloud-upload-outline" type="text">
                    <span v-if="!loadingVideo">
@@ -165,6 +176,9 @@
                    </span>
                  </Button>
                </Upload>
                <Button :loading="loadingVideo" v-if="baseInfoForm.goodsVideo"  icon="delete" @click="removeVideo" type="text">
                  <span>删除视频</span>
                </Button>
              </div>
            </FormItem>
            <div class="layout" style="width: 100%">
@@ -176,7 +190,7 @@
                      <div v-for="(item, $index) in skuInfo" :key="$index" class="sku-item-content">
                        <Card :bordered="true" class="ivu-card-body">
                          <a slot="extra" style="margin-left: 6px">
                            <Icon size="20" type="md-trash"  @click="handleCloseSkuItem($index, item)"></Icon>
                            <Icon size="20" type="md-trash" @click="handleCloseSkuItem($index, item)"></Icon>
                          </a>
                          <div>
                            <div style="display: flex;margin-bottom: 10px;font-weight: bold">规格项</div>
@@ -185,10 +199,13 @@
                              <div>
                                <AutoComplete v-model="item.name" :filter-method="filterMethod"
                                              :maxlength="30" placeholder="请输入规格项名称" style="width: 150px"
                                              @on-focus="changeSkuItem(item.name)" @on-change="editSkuItem(item.name, $index, item)">
                                              @on-focus="changeSkuItem(item.name)"
                                              @on-change="editSkuItem(item.name, $index, item)">
                                </AutoComplete>
                                <iSwitch v-if="$index === 0" style="margin-left: 10px" size="small" @on-change="changeSkuOpenImage" v-model="openImage" /><span v-if="$index === 0" style="margin-left: 5px">添加规格图片</span>
<!--                                <iSwitch v-if="$index === 0" style="margin-left: 10px" size="small"-->
<!--                                         @on-change="changeSkuOpenImage" v-model="openImage"/>-->
<!--                                <span v-if="$index === 0" style="margin-left: 5px">添加规格图片</span>-->
                              </div>
                            </FormItem>
@@ -210,7 +227,8 @@
                                                @on-change="skuValueChange(val, index, item)">
                                  </AutoComplete>
                                  <a style="margin-left: 6px" v-if="val.value && val.value !== ''">
                                    <Icon size="15" type="md-trash" @click="handleCloseSkuValue(val, index, item)"></Icon>
                                    <Icon size="15" type="md-trash"
                                          @click="handleCloseSkuValue(val, index, item)"></Icon>
                                  </a>
                                </div>
                                <div v-if="$index === 0 && openImage" style="margin-top: 10px">
@@ -225,19 +243,20 @@
                                          style="width: 180px;height: 140px"
                                        />
                                        <div class="sku-upload-list-cover">
                                          <div style="margin-top: 50px" >
                                          <div style="margin-top: 50px">
                                            <Icon size="25" type="md-search" @click="handleView(img)"></Icon>
                                            <Icon size="25" type="md-trash" @click="handleRemove(val.images, __index)"></Icon>
                                            <Icon size="25" type="md-trash"
                                                  @click="handleRemove(val.images, __index)"></Icon>
                                          </div>
                                        </div>
                                      </template>
                                    </div>
                                  </vuedraggable>
                                  <Upload ref="uploadSku" :action="uploadFileUrl"
<!--                                  todo 3-->
                                  <Upload ref="uploadSku" action="-"
                                          v-if="val.images < 1"
                                          :before-upload="handleBeforeUpload"
                                          :format="['jpg', 'jpeg', 'png', 'webp']"
                                          :headers="{ ...accessToken }"
                                          :max-size="2048"
                                          :on-error="() => { $Spin.hide(); }"
                                          :on-exceeded-size="handleMaxSize"
@@ -255,7 +274,9 @@
                                </div>
                              </FormItem>
                              <FormItem v-if="item.spec_values.length < 10 && item.spec_values.length >= 1 && item.spec_values[0].value !== ''" class="sku-item-content-val flex" label="" style="line-height: 32px;">
                              <FormItem
                                v-if="item.spec_values.length < 10 && item.spec_values.length >= 1 && item.spec_values[0].value !== ''"
                                class="sku-item-content-val flex" label="" style="line-height: 32px;">
                                <AutoComplete ref="input" v-model="newSkuValues[$index]"
                                              :filter-method="filterMethod"
                                              :maxlength="30" placeholder="自定义规格值" style="width: 180px"
@@ -473,8 +494,32 @@
    <!--<Modal width="1200px" v-model="picModelFlag">-->
    <!--<ossManage @callback="callbackSelected" ref="ossManage" />-->
    <!--</Modal>-->
    <Modal v-model="picModelFlag" width="1200px" @on-ok="confirmUrls">
      <ossManage ref="ossManage" :isComponent="true" :initialize="picModelFlag" @callback="callbackSelected" @selected="(list)=>{ selectedImage = list}"/>
    <!--    todo web-->
    <Modal v-model="picModelFlag" width="1200px">
      <!--      <ossManage ref="ossManRage" :isComponent="true" :initialize="picModelFlag" @callback="callbackSelected" @selected="(list)=>{ selectedImage = list}"/>-->
      <div class="demo-upload-list" v-for="(item,index) in showListImages">
        <template>
          <img :src="item">
          <div class="demo-upload-list-cover">
            <Icon type="ios-eye-outline" @click.native="handleView(item)"></Icon>
            <Icon type="ios-trash-outline" @click.native="handleRemove(null,index)"></Icon>
          </div>
        </template>
      </div>
      <div class="demo-upload-list">
        <Upload
          :before-upload="upLoadImg"
          accept="image/*"
          action="-"
          type="drag"
          style=" display: inline-block;width: 58px"
        >
          <div style="width: 58px;height:58px;line-height: 58px;">
            <Icon type="ios-camera" size="20"></Icon>
          </div>
        </Upload>
      </div>
    </Modal>
  </div>
@@ -491,6 +536,9 @@
import DPlayer from 'dplayer';
// import ossManage from "@/views/sys/oss-manage/ossManage";
import ossManage from "@/views/shop/ossManages";
import COS from 'cos-js-sdk-v5';
import {getFileKey} from "@/utils/file.js";
import {getFilePreview, getSts} from "@/api/file";
export default {
@@ -523,6 +571,7 @@
      }, 1000);
    };
    return {
      showListImages: [],
      regular,
      openImage: false,
      needToloadSku: false,
@@ -625,6 +674,7 @@
        /** 商品分类中文名 */
        categoryName: [],
        goodsVideo: "",
        showGoodsVideo: "",
      },
      /** 表格头 */
      skuTableColumn: [],
@@ -698,12 +748,19 @@
      if (val) {
        this.initVideo();
      }
    },
    showListImages(newValue){
      this.baseInfoForm.goodsGalleryFiles = newValue.map(item => {
        return   item.split('/').slice(-2).join('/')
      })
    }
  },
  methods: {
    // 选择图片modal
    handleCLickImg(val, index) {
      this.$refs.ossManage.selectImage = true;
      console.log('测试输入的值----------------》', val)
      // 废弃原有的图片上传
      // this.$refs.ossManage.selectImage = true;
      this.picModelFlag = true;
      this.selectedFormBtnName = val;
      // this.picIndex = index;
@@ -865,7 +922,15 @@
    },
    // 移除已选图片
    handleRemove(item, index) {
      item.splice(index, 1)
      if (!item) {
        this.listImages.splice(index, 1);
        this.showListImages.splice(index, 1);
        this.baseInfoForm.goodsGalleryFiles.splice(index, 1);
        this.baseInfoForm.goodsGalleryList.splice(index, 1);
      } else {
        console.log('移除测试',item, index);
        item.splice(index, 1)
      }
      this.previewPicture = "";
    },
    // 查看商品大图
@@ -999,14 +1064,57 @@
      }
    },
    // sku图片上传前钩子
    handleBeforeUpload(file) {
  async handleBeforeUpload(file) {
      const check =
        this.selectedSku.images !== undefined &&
        this.selectedSku.images.length > 5;
      if (check) {
        this.$Notice.warning({title: "图片数量不能大于五张"});
        return false;
      }
      try {
        // this.upLoadVideoLoading = true;
        // 获取文件上传临时密钥
        const sts = await getSts();
        const cos = new COS({
          getAuthorization: async function (options, callback) {
            callback({
              TmpSecretId: sts.data.tmpSecretId,
              TmpSecretKey: sts.data.tmpSecretKey,
              SecurityToken: sts.data.sessionToken,
              // 建议返回服务器时间作为签名的开始时间,避免客户端本地时间偏差过大导致签名错误
              StartTime: sts.data.stsStartTime, // 时间戳,单位秒,如:1580000000
              ExpiredTime: sts.data.stsEndTime,// 时间戳,单位秒,如:1580000000
              ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用
            });
          }
        })
        const fileKey = getFileKey(file.name)
        const upData = await cos.uploadFile({
          Bucket: sts.data.bucket,
          Region: sts.data.region,
          Key: fileKey,
          Body: file, // 要上传的文件对象。
          SliceSize: 1024 * 1024 * 5,
          onProgress: function (progressData) {
            console.log('上传进度:', progressData);
          },
        });
        console.log("上传成功", upData)
        this.$nextTick(() => {
          this.listImages.push(fileKey);
          this.showListImages.push(sts.data.endpoint + "/" + fileKey);
          this.baseInfoForm.goodsGalleryFiles.push(fileKey);
        })
      } catch (e) {
        console.log("上传失败", upData)
        return false;
      } finally {
        // this.upLoadVideoLoading = false;
      }
      return false;
    },
    /** 查询商品品牌列表 */
@@ -1086,8 +1194,34 @@
        this.baseInfoForm.goodsGalleryFiles =
          response.result.goodsGalleryList.map((i) => {
            return i;
          });
          })
        try {
          const stsInfo = await getSts();
          const endpoint = stsInfo.data.endpoint;
          this.showListImages = response.result.goodsGalleryList.map((i) => {
            if (i!=null&&i.indexOf('http')===-1)
            return endpoint+'/'+i;
            else return i;
          })
          if (response.result.goodsVideo){
          }
        } catch (e) {
          console.log('组装路径出问题',e);
        }
      }
     if (response.result.goodsVideo){
       try {
         const stsInfo = await getSts();
         const endpoint = stsInfo.data.endpoint;
         this.baseInfoForm.goodsVideo = response.result.goodsVideo;
         this.baseInfoForm.showGoodsVideo = endpoint + '/' + response.result.goodsVideo;
         console.log('显示商品视频------------------->', this.baseInfoForm.showGoodsVideo);
       } catch (e) {
         console.log('组装视频地址出错了')
       }
     }
      if (
        response.result.wholesaleList &&
@@ -1105,7 +1239,8 @@
      this.renderGoodsDetailSku(response.result.skuList);
      /** 查询品牌列表 */
      this.getGoodsBrandList();
      //todo 移除品牌概念
      // this.getGoodsBrandList();
      /** 查询店铺商品分类 */
      this.GET_ShopGoodsLabel();
@@ -1125,7 +1260,7 @@
      this.firstData.goodsType &&
      (this.baseInfoForm.goodsType = this.firstData.goodsType);
      /** 查询商品参数 */
      this.GET_GoodsParams();
      // this.GET_GoodsParams();
    },
    // 渲染sku数据
    renderGoodsDetailSku(skuList) {
@@ -1780,124 +1915,129 @@
    },
    /**  添加商品 **/
    save() {
      this.submitLoading = true;
      this.$refs["baseInfoForm"].validate((valid) => {
        if (valid) {
          if (this.baseInfoForm.salesModel === "WHOLESALE") {
            for (let i = 0; i < this.wholesaleData.length; i++) {
              this.checkWholesaleNum(i);
              this.checkWholesalePrice(i);
              this.wholesaleData[i].goodsId = this.goodsId;
            }
            this.baseInfoForm.wholesaleList = this.wholesaleData;
          }
          this.baseInfoForm.goodsId = this.goodsId;
          let submit = JSON.parse(JSON.stringify(this.baseInfoForm));
          if (
            submit.goodsGalleryFiles &&
            submit.goodsGalleryFiles.length <= 0
          ) {
            this.submitLoading = false;
            this.$Message.error("请上传商品图片");
            return;
          }
          if (submit.templateId === "") submit.templateId = 0;
          let flag = false;
          let paramValue = "";
          if (flag) {
            this.$Message.error(paramValue + " 参数值不能为空");
            this.submitLoading = false;
            return;
          }
          if (this.goodsUnitList && !this.goodsUnitList.find(i => i === this.baseInfoForm.goodsUnit)) {
            submit.goodsUnit = ""
            this.$Message.error("商品单位不存在");
            this.submitLoading = false;
            return;
          }
          let skuInfoNames = this.skuInfo.map((n) => n.name);
          submit.skuList = [];
          let containEmptyImage = false;
          this.skuTableData.map((sku) => {
            let skuCopy = {
              cost: 1,
              price: sku.price,
              quantity: sku.quantity,
              // alertQuantity: sku.alertQuantity,
              sn: sku.sn,
              images: [],
            };
            if (this.openImage) {
              this.skuInfo[0].spec_values.forEach(item => {
                if (!item.images || item.images.length === 0) {
                  containEmptyImage = true;
                  return;
                }
                if (item.value === sku[this.skuInfo[0].name]) {
                  skuCopy.images = item.images
                }
              })
            }
            if (sku.weight) {
              skuCopy.weight = sku.weight;
            }
            if (this.baseInfoForm.weight) {
              skuCopy.weight = this.baseInfoForm.weight;
            }
            if (sku.id) {
              skuCopy.id = sku.id;
            }
            for (let skuInfoName of skuInfoNames) {
              skuCopy[skuInfoName] = sku[skuInfoName];
            }
            submit.skuList.push(skuCopy);
          });
          if (containEmptyImage) {
            this.$Message.error("开启规格图片,所有规格图片不能为空!");
            this.submitLoading = false;
            return;
          }
          if (submit.goodsGalleryFiles.length > 0) {
            submit.goodsGalleryList = submit.goodsGalleryFiles.map(
              (i) => i
            );
          }
          /** 参数校验 **/
          /* Object.keys(submit.goodsParamsList).forEach((item) => {
          });*/
          submit.release ? (submit.release = true) : (submit.release = false);
          submit.recommend
            ? (submit.recommend = true)
            : (submit.recommend = false);
          if (this.goodsId) {
            API_GOODS.editGoods(this.goodsId, submit).then((res) => {
              if (res.success) {
                this.submitLoading = false;
                this.$router.go(-1);
              } else {
                this.submitLoading = false;
      try {
        this.submitLoading = true;
        this.$refs["baseInfoForm"].validate((valid) => {
          if (valid) {
            if (this.baseInfoForm.salesModel === "WHOLESALE") {
              for (let i = 0; i < this.wholesaleData.length; i++) {
                this.checkWholesaleNum(i);
                this.checkWholesalePrice(i);
                this.wholesaleData[i].goodsId = this.goodsId;
              }
              this.baseInfoForm.wholesaleList = this.wholesaleData;
            }
            this.baseInfoForm.goodsId = this.goodsId;
            let submit = JSON.parse(JSON.stringify(this.baseInfoForm));
            console.log('----------------->提交',submit);
            // if (
            //   (submit.goodsGalleryFiles &&
            //     submit.goodsGalleryFiles.length <= 0) && !submit.goodsVideo
            // ) {
            //   this.submitLoading = false;
            //   this.$Message.error("请上传商品图片或者视频");
            //   return;
            // }
            if (submit.templateId === "") submit.templateId = 0;
            let flag = false;
            let paramValue = "";
            if (flag) {
              this.$Message.error(paramValue + " 参数值不能为空");
              this.submitLoading = false;
              return;
            }
            if (this.goodsUnitList && !this.goodsUnitList.find(i => i === this.baseInfoForm.goodsUnit)) {
              submit.goodsUnit = ""
              this.$Message.error("商品单位不存在");
              this.submitLoading = false;
              return;
            }
            let skuInfoNames = this.skuInfo.map((n) => n.name);
            submit.skuList = [];
            let containEmptyImage = false;
            this.skuTableData.map((sku) => {
              let skuCopy = {
                cost: 1,
                price: sku.price,
                quantity: sku.quantity,
                // alertQuantity: sku.alertQuantity,
                sn: sku.sn,
                images: [],
              };
              if (this.openImage) {
                this.skuInfo[0].spec_values.forEach(item => {
                  if (!item.images || item.images.length === 0) {
                    containEmptyImage = true;
                    return;
                  }
                  if (item.value === sku[this.skuInfo[0].name]) {
                    skuCopy.images = item.images
                  }
                })
              }
              if (sku.weight) {
                skuCopy.weight = sku.weight;
              }
              if (this.baseInfoForm.weight) {
                skuCopy.weight = this.baseInfoForm.weight;
              }
              if (sku.id) {
                skuCopy.id = sku.id;
              }
              for (let skuInfoName of skuInfoNames) {
                skuCopy[skuInfoName] = sku[skuInfoName];
              }
              submit.skuList.push(skuCopy);
            });
            if (containEmptyImage) {
              this.$Message.error("开启规格图片,所有规格图片不能为空!");
              this.submitLoading = false;
              return;
            }
            if (submit.goodsGalleryFiles.length > 0) {
              submit.goodsGalleryList = submit.goodsGalleryFiles.map(
                (i) => i
              );
            }
            /** 参数校验 **/
            /* Object.keys(submit.goodsParamsList).forEach((item) => {
            });*/
            submit.release ? (submit.release = true) : (submit.release = false);
            submit.recommend
              ? (submit.recommend = true)
              : (submit.recommend = false);
            if (this.goodsId) {
              API_GOODS.editGoods(this.goodsId, submit).then((res) => {
                if (res.success) {
                  this.submitLoading = false;
                  this.$router.go(-1);
                } else {
                  this.submitLoading = false;
                }
              });
            } else {
              API_GOODS.createGoods(submit).then((res) => {
                if (res.success) {
                  this.submitLoading = false;
                  this.$parent.activestep = 2;
                  window.scrollTo(0, 0);
                } else {
                  this.submitLoading = false;
                }
              });
            }
          } else {
            API_GOODS.createGoods(submit).then((res) => {
              if (res.success) {
                this.submitLoading = false;
                this.$parent.activestep = 2;
                window.scrollTo(0, 0);
              } else {
                this.submitLoading = false;
              }
            });
          }
        } else {
          this.submitLoading = false;
            this.submitLoading = false;
          this.$Message.error("还有必填项未做处理,请检查表单");
        }
      });
            this.$Message.error("还有必填项未做处理,请检查表单");
          }
        });
      } finally {
        this.submitLoading = false;
      }
    },
    /** 保存为模板 */
    saveToDraft() {
@@ -1961,8 +2101,110 @@
        } else if (type === 'localRefresh') {
          this.$Message.error("刷新失败,请重试");
        }
      }).catch(reason => {
        console.log("获取模板失败",reason)
      });
    }
    },
    removeVideo(){
      this.baseInfoForm.showGoodsVideo = null;
      this.baseInfoForm.goodsVideo = null;
    },
    // todo 文件上传
    async upLoadImg(file) {
      console.log(file,this.count++);
      if (this.listImages.length >= 5) {
        this.$Message.error("图片上传不能超过5个");
        return;
      }
      try {
        // this.upLoadVideoLoading = true;
        // 获取文件上传临时密钥
        const sts = await getSts();
        const cos = new COS({
          getAuthorization: async function (options, callback) {
            callback({
              TmpSecretId: sts.data.tmpSecretId,
              TmpSecretKey: sts.data.tmpSecretKey,
              SecurityToken: sts.data.sessionToken,
              // 建议返回服务器时间作为签名的开始时间,避免客户端本地时间偏差过大导致签名错误
              StartTime: sts.data.stsStartTime, // 时间戳,单位秒,如:1580000000
              ExpiredTime: sts.data.stsEndTime,// 时间戳,单位秒,如:1580000000
              ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用
            });
          }
        })
        const fileKey = getFileKey(file.name)
        const upData = await cos.uploadFile({
          Bucket: sts.data.bucket,
          Region: sts.data.region,
          Key: fileKey,
          Body: file, // 要上传的文件对象。
          SliceSize: 1024 * 1024 * 5,
          onProgress: function (progressData) {
            console.log('上传进度:', progressData);
          },
        });
        console.log("上传成功", upData)
        this.$nextTick(() => {
          this.listImages.push(fileKey);
          this.showListImages.push(sts.data.endpoint + "/" + fileKey);
          this.baseInfoForm.goodsGalleryFiles.push(fileKey);
        })
      } catch (e) {
        console.log("上传失败", upData)
        return false;
      } finally {
        // this.upLoadVideoLoading = false;
      }
      return false;
    },
    async upLoadVideo(file) {
      try {
        // this.upLoadVideoLoading = true;
        // 获取文件上传临时密钥
        const sts = await getSts();
        const cos = new COS({
          getAuthorization: async function (options, callback) {
            callback({
              TmpSecretId: sts.data.tmpSecretId,
              TmpSecretKey: sts.data.tmpSecretKey,
              SecurityToken: sts.data.sessionToken,
              // 建议返回服务器时间作为签名的开始时间,避免客户端本地时间偏差过大导致签名错误
              StartTime: sts.data.stsStartTime, // 时间戳,单位秒,如:1580000000
              ExpiredTime: sts.data.stsEndTime,// 时间戳,单位秒,如:1580000000
              ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用
            });
          }
        })
        const fileKey = getFileKey(file.name)
        const upData = await cos.uploadFile({
          Bucket: sts.data.bucket,
          Region: sts.data.region,
          Key: fileKey,
          Body: file, // 要上传的文件对象。
          SliceSize: 1024 * 1024 * 5,
          onProgress: function (progressData) {
            console.log('上传进度:', progressData);
          },
        });
        console.log("上传成功", upData)
        this.$nextTick(() => {
          this.baseInfoForm.goodsVideo = fileKey;
          this.baseInfoForm.showGoodsVideo = sts.data.endpoint + "/" + fileKey;
          this.baseInfoForm.goodsVideo = fileKey;
        })
      } catch (e) {
        console.log("上传失败", upData)
        return false;
      } finally {
        // this.upLoadVideoLoading = false;
      }
      return false;
    },
  },
  mounted() {
    this.accessToken = {
@@ -1988,12 +2230,17 @@
        this.baseInfoForm.goodsType = this.firstData.goodsType;
        /** 获取该商城分类下 商品参数信息 */
        this.GET_GoodsParams();
        /** 查询品牌列表 */
        this.getGoodsBrandList();
        /** 查询分类绑定的规格信息 */
        this.Get_SkuInfoByCategory(this.categoryId);
        if (this.categoryId!=null && this.categoryId!=='') {
          /** 获取该商城分类下 商品参数信息 */
          this.GET_GoodsParams();
          console.log('分类id------------------>',this.categoryId);
          /** 查询分类绑定的规格信息 */
          this.Get_SkuInfoByCategory(this.categoryId);
          /** 查询品牌列表 */
          this.getGoodsBrandList();
        }
        // 获取商品单位
        this.GET_GoodsUnit();
        // 获取当前店铺分类
@@ -2044,4 +2291,45 @@
.refresh-icon {
  margin-left: 10px;
}
.demo-upload-list {
  display: inline-block;
  width: 60px;
  height: 60px;
  text-align: center;
  line-height: 60px;
  border: 1px solid transparent;
  border-radius: 4px;
  overflow: hidden;
  background: #fff;
  position: relative;
  box-shadow: 0 1px 1px rgba(0, 0, 0, .2);
  margin-right: 4px;
}
.demo-upload-list img {
  width: 100%;
  height: 100%;
}
.demo-upload-list-cover {
  display: none;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0, 0, 0, .6);
}
.demo-upload-list:hover .demo-upload-list-cover {
  display: block;
}
.demo-upload-list-cover i {
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  margin: 0 2px;
}
</style>