diff --git a/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/SysDishesController.java b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/SysDishesController.java
index af5b66e53..74c1a5e42 100644
--- a/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/SysDishesController.java
+++ b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/SysDishesController.java
@@ -71,6 +71,7 @@ public class SysDishesController extends BaseController {
     public AjaxResult getMenuTypes(@PathVariable("id") Long id) {
         JSONObject object = new JSONObject();
         object.put("type", sysDishesService.getDishesMenuTypeById(id));
+        object.put("className", sysDishesService.getDishClassNameById(id));
         return AjaxResult.success(object);
     }
 
diff --git a/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/SysNutritionalVideoController.java b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/SysNutritionalVideoController.java
index 404dc8bb3..5a55b905c 100644
--- a/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/SysNutritionalVideoController.java
+++ b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/SysNutritionalVideoController.java
@@ -8,6 +8,7 @@ import com.stdiet.common.core.page.TableDataInfo;
 import com.stdiet.common.utils.AliyunVideoUtils;
 import com.stdiet.common.utils.StringUtils;
 import com.stdiet.common.utils.oss.AliyunOSSUtils;
+import org.aspectj.weaver.loadtime.Aj;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -54,6 +55,8 @@ public class SysNutritionalVideoController extends BaseController
         SysNutritionalVideo sysNutritionalVideos = sysNutritionalVideoService.selectSysNutritionalVideoById(id);
         if(sysNutritionalVideos != null && StringUtils.isNotEmpty(sysNutritionalVideos.getCoverUrl())){
             sysNutritionalVideos.setPreviewUrl(AliyunOSSUtils.generatePresignedUrl(sysNutritionalVideos.getCoverUrl()));
+        }else{
+            sysNutritionalVideos.setPreviewUrl(AliyunVideoUtils.getVideoCoverUrl(sysNutritionalVideos.getVideoId()));
         }
         return AjaxResult.success(sysNutritionalVideos);
     }
@@ -149,4 +152,47 @@ public class SysNutritionalVideoController extends BaseController
         return AjaxResult.success(sysNutritionalVideos);
     }
 
+    /**
+     * 根据视频videoId提交视频截图请求
+     */
+    @PreAuthorize("@ss.hasPermi('custom:nutritionalVideo:add')")
+    @GetMapping("/submitVideoSnapshot")
+    public AjaxResult submitVideoSnapshot(@RequestParam("videoId")String videoId)
+    {
+        if(StringUtils.isEmpty(videoId)){
+            return AjaxResult.error("视频资源不存在");
+        }
+        AjaxResult result = AjaxResult.error("截图请求失败");
+        try {
+            if(AliyunVideoUtils.submitVideoSnapshot(videoId)){
+                result = AjaxResult.success();
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    /**
+     * 根据视频videoId获取视频截图
+     */
+    @PreAuthorize("@ss.hasPermi('custom:nutritionalVideo:add')")
+    @GetMapping("/getVideoSnapshot")
+    public AjaxResult getVideoSnapshot(@RequestParam("videoId")String videoId)
+    {
+        if(StringUtils.isEmpty(videoId)){
+            return AjaxResult.error("视频资源不存在");
+        }
+        AjaxResult result = AjaxResult.error("截图不存在");
+        try {
+            List<String> videoSnapshotList = AliyunVideoUtils.getVideoSnapshot(videoId);
+            if(videoSnapshotList != null){
+                result = AjaxResult.success(videoSnapshotList);
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return result;
+    }
+
 }
\ No newline at end of file
diff --git a/stdiet-common/src/main/java/com/stdiet/common/utils/AliyunVideoUtils.java b/stdiet-common/src/main/java/com/stdiet/common/utils/AliyunVideoUtils.java
index 8a4151073..37e174d34 100644
--- a/stdiet-common/src/main/java/com/stdiet/common/utils/AliyunVideoUtils.java
+++ b/stdiet-common/src/main/java/com/stdiet/common/utils/AliyunVideoUtils.java
@@ -29,6 +29,9 @@ public class AliyunVideoUtils {
 
     public static final String search_field = "VideoId,Title,CoverURL,CateName,Tags,Status,Description,CreationTime";
 
+    //默认截图模板
+    public static final String defaultSnapshotTemplateId = "f8ccc3b5113ca8ea356ef9adf8c573b2";
+
     /**
      * 初始化视频点播Client
      * @return
@@ -176,7 +179,7 @@ public class AliyunVideoUtils {
      * @return
      * @throws Exception
      */
-    public static String updateVideo(String videoId, String title, String tags, String description, Long cateId) throws Exception{
+    public static String updateVideo(String videoId, String title, String tags, String description, Long cateId, String coverUrl) throws Exception{
         com.aliyun.vod20170321.Client client = AliyunVideoUtils.createClient();
         if(StringUtils.isEmpty(videoId)){
             return null;
@@ -194,6 +197,30 @@ public class AliyunVideoUtils {
         if(cateId != null && cateId.longValue() > 0){
             updateVideoInfoRequest.setCateId(cateId);
         }
+        if(StringUtils.isNotEmpty(coverUrl)){
+            updateVideoInfoRequest.setCoverURL(coverUrl);
+        }
+        UpdateVideoInfoResponse updateVideoInfoResponse = client.updateVideoInfo(updateVideoInfoRequest);
+        if(updateVideoInfoResponse != null){
+            return updateVideoInfoResponse.body.requestId;
+        }
+        return null;
+    }
+
+    /**
+     * 更新视频封面
+     * @param videoId 视频ID必须
+     * @param coverUrl 封面
+     * @return
+     * @throws Exception
+     */
+    public static String updateVideoCoverUrl(String videoId, String coverUrl) throws Exception{
+        com.aliyun.vod20170321.Client client = AliyunVideoUtils.createClient();
+        if(StringUtils.isEmpty(videoId) || StringUtils.isEmpty(coverUrl)){
+            return null;
+        }
+        UpdateVideoInfoRequest updateVideoInfoRequest = new UpdateVideoInfoRequest().setVideoId(videoId);
+        updateVideoInfoRequest.setCoverURL(coverUrl);
         UpdateVideoInfoResponse updateVideoInfoResponse = client.updateVideoInfo(updateVideoInfoRequest);
         if(updateVideoInfoResponse != null){
             return updateVideoInfoResponse.body.requestId;
@@ -208,7 +235,7 @@ public class AliyunVideoUtils {
      * @throws Exception
      */
     public static String delVideo(String videoId) throws Exception{
-        return updateVideo(videoId, null,null,null, default_delete_cateId);
+        return updateVideo(videoId, null,null,null, default_delete_cateId, null);
     }
 
     /**
@@ -261,10 +288,52 @@ public class AliyunVideoUtils {
         return coverUrl;
     }
 
+    /**
+     * 根据VideoId提交截图请求
+     * @param videoId
+     * @return
+     */
+    public static boolean submitVideoSnapshot(String videoId){
+        try{
+            com.aliyun.vod20170321.Client client = AliyunVideoUtils.createClient();
+            SubmitSnapshotJobRequest submitSnapshotJobRequest = new SubmitSnapshotJobRequest()
+                    .setSnapshotTemplateId(defaultSnapshotTemplateId)
+                    .setVideoId(videoId);
+            SubmitSnapshotJobResponse response = client.submitSnapshotJob(submitSnapshotJobRequest);
 
+            return response != null && response.body != null && response.body.snapshotJob != null;
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return false;
+    }
 
-
-
+    /**
+     * 根据VideoId获取普通截图信息
+     * @param videoId
+     * @return
+     */
+    public static List<String> getVideoSnapshot(String videoId){
+        List<String> snapshotList = new ArrayList<>();
+        try{
+            com.aliyun.vod20170321.Client client = AliyunVideoUtils.createClient();
+            ListSnapshotsRequest listSnapshotsRequest = new ListSnapshotsRequest()
+                    .setVideoId(videoId)
+                    .setSnapshotType("NormalSnapshot");
+            ListSnapshotsResponse response = client.listSnapshots(listSnapshotsRequest);
+            if(response != null && response.body != null){
+                List<ListSnapshotsResponseBody.ListSnapshotsResponseBodyMediaSnapshotSnapshotsSnapshot>  listSnapshots = response.body.mediaSnapshot.snapshots.snapshot;
+                if(listSnapshots != null && listSnapshots.size() > 0){
+                    for (ListSnapshotsResponseBody.ListSnapshotsResponseBodyMediaSnapshotSnapshotsSnapshot snapshot : listSnapshots) {
+                        snapshotList.add(snapshot.url);
+                    }
+                }
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return snapshotList;
+    }
 
 
 }
diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/mapper/SysDishesMapper.java b/stdiet-custom/src/main/java/com/stdiet/custom/mapper/SysDishesMapper.java
index 58cea23e5..6df35fb08 100644
--- a/stdiet-custom/src/main/java/com/stdiet/custom/mapper/SysDishesMapper.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/mapper/SysDishesMapper.java
@@ -3,9 +3,11 @@ package com.stdiet.custom.mapper;
 import com.stdiet.custom.domain.SysDishes;
 import com.stdiet.custom.domain.SysDishesIngredient;
 import com.stdiet.custom.domain.SysPhysicalSignsObj;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 菜品Mapper接口
@@ -80,4 +82,12 @@ public interface SysDishesMapper {
 
     int deleteDishesNotRecByDishesId(Long dishesId);
 
+
+    /**
+     * 根据菜品ID查询大类小类名称
+     * @param dishId
+     * @return
+     */
+    String getDishClassNameById(@Param("dishId")Long dishId);
+
 }
\ No newline at end of file
diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/service/ISysDishesService.java b/stdiet-custom/src/main/java/com/stdiet/custom/service/ISysDishesService.java
index 03f058e11..765823661 100644
--- a/stdiet-custom/src/main/java/com/stdiet/custom/service/ISysDishesService.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/service/ISysDishesService.java
@@ -1,6 +1,8 @@
 package com.stdiet.custom.service;
 
 import java.util.List;
+import java.util.Map;
+
 import com.stdiet.custom.domain.SysDishes;
 import com.stdiet.custom.domain.SysDishesIngredient;
 import com.stdiet.custom.domain.SysIngredient;
@@ -66,4 +68,11 @@ public interface ISysDishesService
 
     public String getDishesMenuTypeById(Long id);
 
+    /**
+     * 根据菜品ID查询大类小类名称
+     * @param dishId
+     * @return
+     */
+    String getDishClassNameById(Long dishId);
+
 }
\ No newline at end of file
diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysDishesServiceImpl.java b/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysDishesServiceImpl.java
index 906493262..c3e01cfa5 100644
--- a/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysDishesServiceImpl.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysDishesServiceImpl.java
@@ -10,6 +10,7 @@ import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 菜品Service业务层处理
@@ -183,4 +184,15 @@ public class SysDishesServiceImpl implements ISysDishesService {
         return sysDishesMapper.getDishesMenuTypeById(id);
     }
 
+
+    /**
+     * 根据菜品ID查询大类小类名称
+     * @param dishId
+     * @return
+     */
+    @Override
+    public String getDishClassNameById(Long dishId){
+        return sysDishesMapper.getDishClassNameById(dishId);
+    }
+
 }
\ No newline at end of file
diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysNutritionalVideoServiceImpl.java b/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysNutritionalVideoServiceImpl.java
index 3fa709ac8..237f05cde 100644
--- a/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysNutritionalVideoServiceImpl.java
+++ b/stdiet-custom/src/main/java/com/stdiet/custom/service/impl/SysNutritionalVideoServiceImpl.java
@@ -106,6 +106,17 @@ public class SysNutritionalVideoServiceImpl implements ISysNutritionalVideoServi
     public int insertSysNutritionalVideo(SysNutritionalVideo sysNutritionalVideo)
     {
         sysNutritionalVideo.setCreateTime(DateUtils.getNowDate());
+        //判断封面是上传的还是阿里云视频截图封面
+        if(isSnapshot(sysNutritionalVideo.getCoverUrl())){
+            //更新阿里云视频封面
+            try{
+                AliyunVideoUtils.updateVideoCoverUrl(sysNutritionalVideo.getVideoId(), sysNutritionalVideo.getCoverUrl());
+            }catch (Exception e){
+                e.printStackTrace();
+                return 0;
+            }
+            sysNutritionalVideo.setCoverUrl("");
+        }
         return sysNutritionalVideoMapper.insertSysNutritionalVideo(sysNutritionalVideo);
     }
 
@@ -119,19 +130,23 @@ public class SysNutritionalVideoServiceImpl implements ISysNutritionalVideoServi
     public int updateSysNutritionalVideo(SysNutritionalVideo sysNutritionalVideo)
     {
         sysNutritionalVideo.setUpdateTime(DateUtils.getNowDate());
+        sysNutritionalVideo.setCoverUrl(sysNutritionalVideo.getCoverUrl() == null ? "" : sysNutritionalVideo.getCoverUrl());
+        String coverUrl = sysNutritionalVideo.getCoverUrl();
+        //判断封面是上传的还是阿里云视频截图封面
+        sysNutritionalVideo.setCoverUrl(isSnapshot(coverUrl) ? "" : coverUrl);
         int row = sysNutritionalVideoMapper.updateSysNutritionalVideo(sysNutritionalVideo);
         if(row > 0){
-            updateAliyunVideo(sysNutritionalVideo.getId());
+            sysNutritionalVideo.setCoverUrl(isSnapshot(coverUrl) ? coverUrl : null);
+            updateAliyunVideo(sysNutritionalVideo);
         }
         return row;
     }
 
     @Async
-    public void updateAliyunVideo(Long id){
+    public void updateAliyunVideo(SysNutritionalVideo sysNutritionalVideo){
         try{
-            SysNutritionalVideo sysNutritionalVideo = selectSysNutritionalVideoById(id);
             if(sysNutritionalVideo != null && sysNutritionalVideo.getVideoId() != null){
-                AliyunVideoUtils.updateVideo(sysNutritionalVideo.getVideoId(), sysNutritionalVideo.getTitle(), sysNutritionalVideo.getTags(), sysNutritionalVideo.getDescription(), null);
+                AliyunVideoUtils.updateVideo(sysNutritionalVideo.getVideoId(), sysNutritionalVideo.getTitle(), sysNutritionalVideo.getTags(), sysNutritionalVideo.getDescription(), null, sysNutritionalVideo.getCoverUrl());
             }
         }catch (Exception e){
             e.printStackTrace();
@@ -270,4 +285,15 @@ public class SysNutritionalVideoServiceImpl implements ISysNutritionalVideoServi
         return sysNutritionalVideoMapper.updateVideoPlayNum(videoId);
     }
 
+    /**
+     * 判断是否为阿里点播的截图
+     * @param url
+     * @return
+     */
+    private boolean isSnapshot(String url){
+       return StringUtils.isNotEmpty(url) && url.startsWith("http://outin");
+    }
+
+
+
 }
\ No newline at end of file
diff --git a/stdiet-custom/src/main/resources/mapper/custom/SysDishesMapper.xml b/stdiet-custom/src/main/resources/mapper/custom/SysDishesMapper.xml
index 2ef345d3d..da6d88115 100644
--- a/stdiet-custom/src/main/resources/mapper/custom/SysDishesMapper.xml
+++ b/stdiet-custom/src/main/resources/mapper/custom/SysDishesMapper.xml
@@ -207,4 +207,12 @@
         delete from sys_dishes_not_rec where dishes_id=#{dishesId}
     </delete>
 
+    <!-- 根据菜品ID查询菜品对应大类小类名称 -->
+    <select id="getDishClassNameById" parameterType="Long" resultType="String">
+        select concat(IFNULL(big.dict_label,''),'/',IFNULL(small.dict_label,'')) as className from sys_dishes dish
+        LEFT JOIN (SELECT dict_label, dict_value FROM sys_dict_data WHERE dict_type = 'dish_class_big') AS big ON big.dict_value = dish.big_class
+        LEFT JOIN (SELECT dict_label, dict_value FROM sys_dict_data WHERE dict_type = 'dish_class_small') AS small ON small.dict_value = dish.small_class
+        where dish.id = #{dishId} and dish.del_flag = 0
+    </select>
+
 </mapper>
\ No newline at end of file
diff --git a/stdiet-ui/src/api/custom/nutritionalVideo.js b/stdiet-ui/src/api/custom/nutritionalVideo.js
index 649406caa..d4c88087f 100644
--- a/stdiet-ui/src/api/custom/nutritionalVideo.js
+++ b/stdiet-ui/src/api/custom/nutritionalVideo.js
@@ -78,3 +78,23 @@ export function getVideoPlayUrlById(id) {
   })
 }
 
+// 根据视频videoId提交视频截图请求
+export function submitVideoSnapshot(id) {
+  return request({
+    url: '/custom/nutritionalVideo/submitVideoSnapshot',
+    method: 'get',
+    params: {'videoId': id}
+  })
+}
+
+//根据视频videoId获取视频截图
+export function getVideoSnapshot(id) {
+  return request({
+    url: '/custom/nutritionalVideo/getVideoSnapshot',
+    method: 'get',
+    params: {'videoId': id}
+  })
+}
+
+
+
diff --git a/stdiet-ui/src/api/custom/recipes.js b/stdiet-ui/src/api/custom/recipes.js
index 3842cc8d1..4fe01a607 100644
--- a/stdiet-ui/src/api/custom/recipes.js
+++ b/stdiet-ui/src/api/custom/recipes.js
@@ -45,3 +45,17 @@ export function replaceMenuApi(data) {
     data
   });
 }
+
+/**
+ * 根据菜品ID查询大类小类名称
+ * @param dishId
+ */
+export function getDishClassNameById(dishId) {
+  return request({
+    url: "/custom/recipes/getDishClassNameById",
+    method: "get",
+    params: {'dishId':dishId}
+  });
+}
+
+
diff --git a/stdiet-ui/src/components/FileUpload/UploadFile.vue b/stdiet-ui/src/components/FileUpload/UploadFile.vue
index cd1cb78e1..62b4e31b7 100644
--- a/stdiet-ui/src/components/FileUpload/UploadFile.vue
+++ b/stdiet-ui/src/components/FileUpload/UploadFile.vue
@@ -8,7 +8,7 @@
   :accept="'.png,.jpg'"
   :before-upload="beforeAvatarUpload">
   <img v-if="imageUrl || coverUrl" :src="imageUrl || coverUrl" class="avatar">
-  <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+  <i v-else class="el-icon-plus avatar-uploader-icon" title="手动上传图片"></i>
   
   <div class="el-upload__tip" slot="tip" style="color:#1890ff">
     <el-button v-if="imageUrl || coverUrl" size="small" type="danger" @click="removeFile">移除</el-button>
diff --git a/stdiet-ui/src/components/UploadVideo/index.vue b/stdiet-ui/src/components/UploadVideo/index.vue
index af0031d96..c8ed8cbfb 100644
--- a/stdiet-ui/src/components/UploadVideo/index.vue
+++ b/stdiet-ui/src/components/UploadVideo/index.vue
@@ -17,15 +17,11 @@
                 placeholder="请输入视频描述"
                 v-model.trim="videoFrom.description"
                 maxlength="1000"
-                rows="3"
+                rows="2"
                 show-word-limit
             />
         </el-form-item>
-        
-         <el-form-item label="视频封面" prop="coverUrl">
-              <UploadFile ref="uploadFile" :prefix="'videoCover'" @callbackMethod="handleCoverUrl" :tips="'视频未传封面图片时,会主动截取封面,但会存在延迟,请勿直接发布到小程序'"></UploadFile>
-          </el-form-item>  
-          <div style="display:flex">
+         <div style="display:flex">
             <el-form-item label="视频类别" prop="cateId" style="width:300px">
             <treeselect
                 v-model="videoFrom.cateId"
@@ -46,40 +42,47 @@
             </el-select>
           </el-form-item>   
           </div>
-          
         <el-form-item label="视频文件" prop="file">
             <div>
             <input type="file" accept=".mp4" ref="videoFile" id="videoFile" @change="fileChange($event)">
             <div > <span>上传状态:{{statusText}}</span><span style="margin-left:100px">进度:{{authProgress}}%</span></div>
             <div style="color:#1890ff"> 
-                1、只能上传mp4文件,上传大文件时请使用客户端上传,防止上传超时 
+                <el-button type="primary" @click="authUpload" :disabled="uploadDisabled" size="small" icon="el-icon-upload2">上传视频</el-button><span style="margin-left:20px">1、只能上传mp4格式视频</span>
             </div>
+            
             </div>
         </el-form-item>
+         <el-form-item label="视频封面" prop="coverUrl">
+              <UploadFile ref="uploadFile" :prefix="'videoCover'" :coverUrl="videoFrom.previewUrl" @callbackMethod="handleCoverUrl" :tips="''"></UploadFile>
+              <el-button type="primary" size="small" icon="el-icon-film" @click="selectVideoCover" :disabled="!uploadVideoFlag" title="上传视频之后选择视频截图作为封面">选择封面</el-button>
+          </el-form-item>  
         <el-form-item label="展示状态" prop="wxShow">
               <el-switch
                 v-model="videoFrom.wxShow"
-                active-text="小程序展示"
-                inactive-text="小程序不展示">
+                active-text="展示"
+                inactive-text="不展示">
               </el-switch>
-              <div style="color:red">提示:请保证内容正确再展示到小程序</div>
+              <div style="color:red">提示:开启展示之后客户可看到该视频,请保证内容正确再展示</div>
           </el-form-item>  
       </el-form>
       <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="authUpload" :disabled="uploadDisabled">开始上传</el-button>
+        <el-button type="primary" @click="submitVideoForm">保存</el-button>
         <el-button @click="cancel">取 消</el-button>
       </div>
+      <!-- 手动选择封面 -->
+      <VideoSelectCover ref="videoSelectCoverRef"></VideoSelectCover>
 </el-dialog>
   
 </template>
 <script>
   import axios from 'axios'
-  import {getUploadVideoAuth,addNutritionalVideo } from "@/api/custom/nutritionalVideo";
+  import {getUploadVideoAuth,addNutritionalVideo,getVideoSnapshot,submitVideoSnapshot } from "@/api/custom/nutritionalVideo";
   import {getAllClassify } from "@/api/custom/videoClassify";
   import UploadFile from "@/components/FileUpload/UploadFile";
    import Treeselect from "@riophae/vue-treeselect";
   import "@riophae/vue-treeselect/dist/vue-treeselect.css";
   import IconSelect from "@/components/IconSelect";
+  import VideoSelectCover from "@/components/VideoSelectCover";
   export default {
       name: "UploadVideo",
     data () {
@@ -122,6 +125,8 @@
         statusText: '',
         fileType:['mp4','MP4'],
         uploading: false,
+        //视频是否上传成功标识
+        uploadVideoFlag: false
       }
     },
     created(){
@@ -142,7 +147,7 @@
         
     },
     components: {
-      UploadFile,Treeselect, IconSelect
+      UploadFile,Treeselect, IconSelect,VideoSelectCover
     },
     methods: {
         showDialog(classifyList, callback){
@@ -167,7 +172,8 @@
                 tags: null,
                 payLevel: this.defaultPayLevel ? parseInt(this.defaultPayLevel) : null,
                 videoId: null,
-                wxShow: false
+                wxShow: false,
+                previewUrl: null 
             };
             if(this.$refs.uploadFile){
                 this.$refs.uploadFile.resetUpload();
@@ -180,11 +186,53 @@
             this.uploader = null;
             this.statusText = '';
             this.uploading = false;
+            this.uploadVideoFlag = false;
             this.resetForm("videoFrom"); 
         },
         submitVideoForm(){
+          this.$refs["videoFrom"].validate((valid) => {
+              if (valid) {
+                    //视频分类不能选择主分类
+                    if(this.videoFrom.cateId == 0){
+                        this.$message({
+                            message: "视频分类不能选择主分类",
+                            type: "warning",
+                        });
+                        return;
+                    }
+                    if(this.uploading){
+                        this.$message({
+                            message: "视频正在上传,请勿取消",
+                            type: "warning",
+                        });
+                        return;
+                    }
+                    if(!this.uploadVideoFlag){
+                        this.$message({
+                            message: "请先上传视频再保存",
+                            type: "warning",
+                        });
+                        return;
+                    }
+                    this.videoFrom.showFlag = this.videoFrom.wxShow ? 1 : 0;
+                    addNutritionalVideo(this.videoFrom).then(response => {
+                        if (response.code === 200) {
+                          this.msgSuccess("视频保存成功");
+                          this.open = false;
+                          this.callback && this.callback();
+                        }
+                    })
+              }
+          });
             
         },
+        selectVideoCover(){
+            this.$refs.videoSelectCoverRef.showDialog(this.videoFrom,(url)=>{
+               //console.log(url);
+               this.videoFrom.previewUrl = url;
+               this.videoFrom.coverUrl = url;
+            });
+        },
         /** 转换菜单数据结构 */
         normalizer(node) {
           if (node.children && !node.children.length) {
@@ -205,7 +253,7 @@
         cancel(){
             if(this.uploading){
                 this.$message({
-                    message: "文件正在上传,请勿取消",
+                    message: "视频正在上传,请勿取消",
                     type: "warning",
                 });
                 return;
@@ -219,7 +267,7 @@
               if (valid) {
                  if(this.uploading){
                     this.$message({
-                      message: "文件正在上传,请勿取消",
+                      message: "视频正在上传,请勿取消",
                       type: "warning",
                     });
                     return;
@@ -242,7 +290,7 @@
                   this.videoFrom.fileName = this.file.name;
                   if(this.videoFrom.fileName == null || this.videoFrom.fileName.length == 0 || this.videoFrom.fileName.lastIndexOf(".") == -1){
                       this.$message({
-                          message: "当前文件名称错误",
+                          message: "当前视频名称错误",
                           type: "warning",
                       });
                       return;
@@ -250,7 +298,7 @@
                   let fileType = this.videoFrom.fileName.substring(this.videoFrom.fileName.lastIndexOf(".")+1);
                   if(this.fileType.indexOf(fileType) == -1){
                       this.$message({
-                          message: "当前文件格式错误",
+                          message: "当前视频格式错误",
                           type: "warning",
                       });
                       return;
@@ -268,8 +316,8 @@
                           this.resumeDisabled = true
                       }
                   })
-                    }
-                  }); 
+              }
+            }); 
       },
       authUpload () {
         // 然后调用 startUpload 方法, 开始上传
@@ -309,7 +357,7 @@
           addFileSuccess: function (uploadInfo) {
             self.uploadDisabled = false
             self.resumeDisabled = false
-            self.statusText = '添加文件成功, 等待上传...'
+            self.statusText = '添加视频成功, 等待上传...'
             console.log("addFileSuccess: " + uploadInfo.file.name)
           },
           // 开始上传
@@ -322,32 +370,32 @@
             // 如果 uploadInfo.videoId 存在, 调用 刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)
             // 如果 uploadInfo.videoId 不存在,调用 获取视频上传地址和凭证接口(https://help.aliyun.com/document_detail/55407.html)
             uploader.setUploadAuthAndAddress(uploadInfo, self.uploadAuth.uploadAuth, self.uploadAuth.uploadAddress, self.uploadAuth.videoId) 
-            self.statusText = '文件开始上传...'
+            self.statusText = '视频开始上传...'
             console.log("onUploadStarted:" + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
           },
           // 文件上传成功
           onUploadSucceed: function (uploadInfo) {
             console.log("onUploadSucceed: " + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
-            self.statusText = '文件上传成功!'
+            self.statusText = '视频上传成功!'
             
           },
           // 文件上传失败
           onUploadFailed: function (uploadInfo, code, message) {
             console.log("onUploadFailed: file:" + uploadInfo.file.name + ",code:" + code + ", message:" + message);
             self.uploading = false;
-            self.statusText = '文件上传失败!'
+            self.statusText = '视频上传失败!'
           },
           // 取消文件上传
           onUploadCanceled: function (uploadInfo, code, message) {
             console.log("Canceled file: " + uploadInfo.file.name + ", code: " + code + ", message:" + message)
-            self.statusText = '文件已暂停上传'
+            self.statusText = '视频已暂停上传'
           },
           // 文件上传进度,单位:字节, 可以在这个函数中拿到上传进度并显示在页面上
           onUploadProgress: function (uploadInfo, totalSize, progress) {
             console.log("onUploadProgress:file:" + uploadInfo.file.name + ", fileSize:" + totalSize + ", percent:" + Math.ceil(progress * 100) + "%")
             let progressPercent = Math.ceil(progress * 100)
             self.authProgress = progressPercent
-            self.statusText = '文件上传中...'
+            self.statusText = '视频上传中...'
           },
           // 上传凭证超时
           onUploadTokenExpired: function (uploadInfo) {
@@ -360,23 +408,20 @@
               uploader.resumeUploadWithAuth(uploadAuth)
               console.log('upload expired and resume upload with uploadauth ' + uploadAuth)
             })*/
-            self.statusText = '文件上传超时...';
+            self.statusText = '视频上传超时...';
             self.uploading = false;
             
           },
           // 全部文件上传结束
           onUploadEnd: function (uploadInfo) {
-            self.statusText = '文件上传完毕'
+            self.statusText = '视频上传完毕'
             self.uploading = false;
-            //self.msgSuccess("上传成功");
-            self.videoFrom.showFlag = self.videoFrom.wxShow ? 1 : 0;
-            addNutritionalVideo(self.videoFrom).then(response => {
-                if (response.code === 200) {
-                  self.msgSuccess("视频上传成功");
-                  self.open = false;
-                  self.callback && self.callback();
+            self.uploadVideoFlag = true;
+            submitVideoSnapshot(self.videoFrom.videoId).then(response => {
+                if(response.code == 200){
+                    console.log("-- 截图成功 --");
                 }
-            })         
+            })
           }
         })
         return uploader
diff --git a/stdiet-ui/src/components/VideoSelectCover/index.vue b/stdiet-ui/src/components/VideoSelectCover/index.vue
new file mode 100644
index 000000000..bb2bcbbe1
--- /dev/null
+++ b/stdiet-ui/src/components/VideoSelectCover/index.vue
@@ -0,0 +1,69 @@
+<template>
+<el-dialog title="选择视频封面(每隔两秒一张截图,共十张)" :visible.sync="open" width="650px" style="height: 80%; overflow: auto;" :close-on-click-modal="true" :show-close="true" append-to-body>
+    <div v-if="!showFlag"><el-divider>{{showStatusText}}</el-divider></div>
+    <div v-else class="demo-image__lazy" style="margin-left:20px;">
+        <div v-for="url in videoSnapshotList" :key="url">
+            <el-image :src="url" lazy style="width:550px;height:300px;margin-top:10px;cursor:pointer" title="点击选择" @click="selectVideoSnapshot(url)"></el-image>
+            <el-divider></el-divider>
+        </div>
+    </div>
+    <div slot="footer" class="dialog-footer">
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    
+</el-dialog>
+  
+</template>
+<script>
+  import axios from 'axios'
+  import {getVideoSnapshot } from "@/api/custom/nutritionalVideo";
+  export default {
+    name: "VideoSelectCover",
+    data () {
+      return {
+        open: false,
+        videoData: null,
+        callback: undefined,
+        showFlag: false,
+        showStatusText:"加载中...",
+        videoSnapshotList:[]
+      }
+    },
+    created(){
+        
+    },
+    components: {
+      
+    },
+    methods: {
+        showDialog(data,callback){
+            if(data && data.videoId != null){
+                this.videoSnapshotList = [];
+                this.showFlag = false;
+                this.showStatusText = "加载中...";
+                this.open = true;
+                this.videoData = data;
+                this.callback = callback;
+                this.getVideoSnapshot();
+            } 
+        },
+        getVideoSnapshot(){
+            getVideoSnapshot(this.videoData.videoId).then(response => {
+                if(response.code == 200 && response.data.length > 0){
+                    this.videoSnapshotList = response.data;
+                    this.showFlag = true;
+                }else{
+                    this.showStatusText = "暂无截图数据!"
+                }
+            })
+        },
+        selectVideoSnapshot(url){
+            this.callback && this.callback(url);
+            this.open = false;
+        },
+        cancel(){
+            this.open = false;
+        }
+    }
+  }
+</script>
\ No newline at end of file
diff --git a/stdiet-ui/src/store/modules/recipes.js b/stdiet-ui/src/store/modules/recipes.js
index 1fbe644f8..23e4e2a08 100644
--- a/stdiet-ui/src/store/modules/recipes.js
+++ b/stdiet-ui/src/store/modules/recipes.js
@@ -555,6 +555,7 @@ const actions = {
             id: new Date().getTime(),
             name: tarDishes.name,
             type: response.data.type.split(",").sort(),
+            className: response.data.className,  //大类小类名称
             data: tarDishes
           }).then(() => {
             window.postMessage(
diff --git a/stdiet-ui/src/utils/shortCutUtils.js b/stdiet-ui/src/utils/shortCutUtils.js
index 53fe07159..df49202b9 100644
--- a/stdiet-ui/src/utils/shortCutUtils.js
+++ b/stdiet-ui/src/utils/shortCutUtils.js
@@ -1,8 +1,19 @@
-export function getShortCut() {
+export function getShortCut(key) {
   return new Promise((res, rej) => {
     try {
       const data = JSON.parse(localStorage.getItem("shortCut") || "[]");
-      res(data);
+      //关键字检索
+      if(key != undefined && key != null && key != ""){
+          const resultData = [];
+          data.forEach((item,index) => {
+              if(item.name.indexOf(key) != -1 || (item.className != undefined && item.className != null && item.className.indexOf(key) != -1)){
+                resultData.push(item);
+              }
+          });
+          res(resultData);
+      }else{
+        res(data);
+      }
     } catch (error) {
       rej(error);
     }
@@ -35,6 +46,19 @@ export async function removeShortCut(id) {
   });
 }
 
+export async function removeMuchShortCut(ids) {
+  const shortCutList = await getShortCut();
+  return new Promise((res, rej) => {
+    try {
+      const newShortCutList = shortCutList.filter(obj => ids.indexOf(obj.id) == -1);
+      localStorage.setItem("shortCut", JSON.stringify(newShortCutList));
+      res();
+    } catch (error) {
+      rej(error);
+    }
+  });
+}
+
 export async function editShortCut(data) {
   const shortCutList = await getShortCut();
   return new Promise((res, rej) => {
diff --git a/stdiet-ui/src/views/custom/nutritionalVideo/index.vue b/stdiet-ui/src/views/custom/nutritionalVideo/index.vue
index dea7f8b97..fa8e6b55c 100644
--- a/stdiet-ui/src/views/custom/nutritionalVideo/index.vue
+++ b/stdiet-ui/src/views/custom/nutritionalVideo/index.vue
@@ -9,7 +9,7 @@
           size="small"
         />
       </el-form-item>
-      <el-form-item label="小程序展示状态" prop="showFlag" label-width="200">
+      <el-form-item label="展示状态" prop="showFlag" label-width="200">
         <el-select
           v-model="queryParams.showFlag"
           placeholder="请选示状态"
@@ -131,7 +131,7 @@
       <!--<el-table-column label="标签" align="center" prop="tags" width="100"/>-->
        <el-table-column label="分类" align="center" prop="cateName" width="100"/>
        <el-table-column label="权限等级" align="center" prop="payLevelName" width="100"/>
-      <el-table-column label="小程序展示状态" align="center" prop="showFlag" width="200">
+      <el-table-column label="展示状态" align="center" prop="showFlag" width="200">
         <template slot-scope="scope" >
           <el-switch
                 v-model="scope.row.wxShow"
@@ -208,20 +208,26 @@
                 show-word-limit
             />
         </el-form-item>
-         <el-form-item label="视频封面" prop="coverUrl">
-              <UploadFile ref="uploadFile" v-if="open" :prefix="'videoCover'" :coverUrl="form.previewUrl" @callbackMethod="handleCoverUrl" :tips="'视频未传封面图片时,会主动截取封面,但会存在延迟,请勿直接发布到小程序'"></UploadFile>
-          </el-form-item>
-        <el-form-item label="视频类别" prop="cateId">
-            <el-select v-model="form.cateId" clearable filterable placeholder="请选择类别">
+         <div style="display:flex">
+        <el-form-item label="视频类别" prop="cateId" style="width:300px">
+            <!--<el-select v-model="form.cateId" clearable filterable placeholder="请选择类别">
               <el-option
                 v-for="classify in classifyList"
                 :key="classify.id"
                 :label="classify.cateName"
                 :value="classify.id"
               />
-            </el-select>
+            </el-select>-->
+            <treeselect
+                v-model="form.cateId"
+                :options="classifyList"
+                :normalizer="normalizer"
+                :show-count="true"
+                placeholder="选择分类"
+                style="width:200px"
+              />
           </el-form-item>
-          <el-form-item label="视频权限" prop="payLevel">
+          <el-form-item label="视频权限" prop="payLevel" style="margin-left:40px">
             <el-select v-model="form.payLevel" clearable filterable placeholder="请选择权限">
               <el-option
                 v-for="dict in payVideoLevelList"
@@ -231,13 +237,18 @@
               />
             </el-select>
           </el-form-item>
+         </div>
+         <el-form-item label="视频封面" prop="coverUrl">
+              <UploadFile ref="uploadFile" v-if="open" :prefix="'videoCover'" :coverUrl="form.previewUrl" @callbackMethod="handleCoverUrl" :tips="''"></UploadFile>
+              <el-button type="primary" size="small" icon="el-icon-film" @click="selectVideoCover" title="上传视频之后选择视频截图作为封面">选择封面</el-button>
+          </el-form-item>
            <el-form-item label="展示状态" prop="wxShow">
               <el-switch
                 v-model="form.wxShow"
-                active-text="小程序展示"
-                inactive-text="小程序不展示">
+                active-text="展示"
+                inactive-text="不展示">
               </el-switch>
-              <div style="color:red">提示:请保证内容正确再展示到小程序</div>
+              <div style="color:red">提示:开启展示之后客户可看到该视频,请保证内容正确再展示</div>
           </el-form-item>    
       </el-form>
      
@@ -255,6 +266,9 @@
            <VideoClassify ref="videoClassifyRef"></VideoClassify>
       </div>
     </el-dialog>
+
+    <!-- 手动选择封面 -->
+      <VideoSelectCover ref="videoSelectCoverRef"></VideoSelectCover>
   </div>
 </template>
 
@@ -268,6 +282,7 @@
     import Treeselect from "@riophae/vue-treeselect";
   import "@riophae/vue-treeselect/dist/vue-treeselect.css";
   import IconSelect from "@/components/IconSelect";
+  import VideoSelectCover from "@/components/VideoSelectCover";
   export default {
     name: "NutritionalVideo",
     data() {
@@ -316,6 +331,8 @@
         coverImageList:[],
         //分类列表
         classifyList:[],
+        //所有分类
+        allClassifyList:[],
         //权限等级列表
         payVideoLevelList:[],
         //视频分类弹窗显示标识
@@ -331,7 +348,7 @@
       });
     },
     components: {
-      UploadVideo,UploadFile,VideoClassify,AutoHideMessage,Treeselect, IconSelect
+      UploadVideo,UploadFile,VideoClassify,AutoHideMessage,Treeselect, IconSelect,VideoSelectCover
     },
     methods: {
       /** 查询营养视频列表 */
@@ -350,6 +367,7 @@
       getAllVideoClassify(){
         getAllClassify().then(response => {
           if(response.code == 200){
+            this.allClassifyList = response.data;
             this.classifyList = [];
             const classify = { id: 0, cateName: '主分类', children: [] };
             classify.children = this.handleTree(response.data, "id");
@@ -366,6 +384,7 @@
       reset() {
         this.form = {
           id: null,
+          videoId:null,
           cateId: null,
           coverUrl: null,
           title: null,
@@ -373,10 +392,18 @@
           tags: null,
           payLevel:null,
           showFlag: null,
-          wxShow: false
+          wxShow: false,
+          previewUrl: null
         };
         this.resetForm("form");
       },
+      selectVideoCover(){
+            this.$refs.videoSelectCoverRef.showDialog(this.form,(url)=>{
+               //console.log(url);
+               this.form.previewUrl = url;
+               this.form.coverUrl = url;
+            });
+      },
       /** 搜索按钮操作 */
       handleQuery() {
         this.queryParams.pageNum = 1;
@@ -394,7 +421,7 @@
         this.multiple = !selection.length
       },
       clickUploadVideo(){
-        this.$refs.uploadVideoRef.showDialog(this.classifyList, ()=>{
+        this.$refs.uploadVideoRef.showDialog(this.allClassifyList, ()=>{
          this.getList();
         });
       },
@@ -443,6 +470,14 @@
         this.$refs["form"].validate(valid => {
           if (valid) {
             this.form.showFlag = this.form.wxShow ? 1 : 0;
+            //视频分类不能选择主分类
+            if(this.form.cateId == 0){
+                this.$message({
+                    message: "视频分类不能选择主分类",
+                    type: "warning",
+                });
+                return;
+            }
             if (this.form.id != null) {
               updateNutritionalVideo(this.form).then(response => {
                 if (response.code === 200) {
diff --git a/stdiet-ui/src/views/custom/recipesBuild/InfoView/ShortCutCom/index.vue b/stdiet-ui/src/views/custom/recipesBuild/InfoView/ShortCutCom/index.vue
index 3693aa495..1499eff0b 100644
--- a/stdiet-ui/src/views/custom/recipesBuild/InfoView/ShortCutCom/index.vue
+++ b/stdiet-ui/src/views/custom/recipesBuild/InfoView/ShortCutCom/index.vue
@@ -1,14 +1,32 @@
 <template>
   <div class="short_cut_com_wrapper">
-    <div class="header">
-      <el-button icon="el-icon-refresh" size="mini" @click="getList" circle />
-    </div>
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="关键词" prop="key" >
+        <el-input
+          v-model.trim="queryParams.key"
+          placeholder="请输入名称/种类"
+          clearable
+          @clear="getList"
+          @keyup.native="getList"
+          size="small"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="getList">搜索</el-button>
+      </el-form-item>
+    </el-form>
+    
+      <div class="header">
+        <el-button icon="el-icon-search" size="mini" @click="showSearch = !showSearch" circle :title="showSearch ? '隐藏搜索' : '显示搜索'"/>
+        <el-button icon="el-icon-refresh" size="mini" @click="getList" circle title="刷新"/>
+        <el-button icon="el-icon-delete" size="mini" circle :title="'清空快捷列表'" @click="handleOnMuchDelete"/>
+      </div>
     <el-table
       :data="dataList"
       ref="shortCutTable"
       highlight-current-row
       @current-change="handleOnCurrentChange"
-      height="800"
+      height="700"
     >
       <el-table-column prop="name" label="对象" align="center">
         <template slot-scope="scope">
@@ -95,6 +113,7 @@ import {
   getShortCut,
   removeShortCut,
   editShortCut,
+  removeMuchShortCut
 } from "@/utils/shortCutUtils";
 import AutoHideInfo from "@/components/AutoHideInfo";
 import { createNamespacedHelpers } from "vuex";
@@ -113,6 +132,10 @@ export default {
     return {
       dataList: [],
       modifingId: 0,
+      showSearch: false,
+      queryParams:{
+        key: null
+      }
     };
   },
   created() {
@@ -147,8 +170,12 @@ export default {
       }
     },
     getList(setCurrent) {
-      getShortCut().then((data) => {
+      getShortCut(this.queryParams.key).then((data) => {
         this.dataList = data;
+        //超过10个就显示搜索按钮
+        if(this.dataList && this.dataList.length > 5 && !this.showSearch){
+           this.showSearch = true;
+        }
         // console.log(this.dataList);
         if (setCurrent) {
           this.$refs.shortCutTable.setCurrentRow(data[0]);
@@ -163,6 +190,27 @@ export default {
         }
       });
     },
+    handleOnMuchDelete() {
+      if(this.dataList && this.dataList.length > 0){
+         let ids = [];
+         this.dataList.forEach((item,index) => {
+            ids.push(item.id);
+         });
+         this.$confirm("是否确定清除当前 "+ids.length+" 条快捷数据?", "警告", {
+            confirmButtonText: "确定",
+            cancelButtonText: "取消",
+            type: "warning",
+          }).then(function () {
+            return removeMuchShortCut(ids);
+          }).then((response) => {
+            this.getList();
+            if (ids.indexOf(this.curShortCutObj.id) != -1) {
+              this.setCurShortCutObj({});
+            }
+          })
+          .catch(function () {});     
+      }
+    },
     handleOnCurrentChange(data) {
       this.setCurShortCutObj({ data });
     },
diff --git a/stdiet-ui/src/views/custom/recipesBuild/RecipesView/RecipesCom/index.vue b/stdiet-ui/src/views/custom/recipesBuild/RecipesView/RecipesCom/index.vue
index 2eb697373..b5dab009f 100644
--- a/stdiet-ui/src/views/custom/recipesBuild/RecipesView/RecipesCom/index.vue
+++ b/stdiet-ui/src/views/custom/recipesBuild/RecipesView/RecipesCom/index.vue
@@ -342,6 +342,9 @@
   </div>
 </template>
 <script>
+import {
+  getDishClassNameById 
+} from "@/api/custom/recipes";
 import { createNamespacedHelpers } from "vuex";
 const {
   mapActions,