소스 검색

Merge remote-tracking branch 'origin/developer' into developerB

zhengjinyi 2 년 전
부모
커밋
78d409a05b
36개의 변경된 파일1615개의 추가작업 그리고 452개의 파일을 삭제
  1. 163 0
      backup.sql
  2. 4 0
      src/main/java/com/caimei/www/controller/BaseController.java
  3. 5 6
      src/main/java/com/caimei/www/mapper/BaikeDao.java
  4. 24 1
      src/main/java/com/caimei/www/pojo/baike/BaikeProduct.java
  5. 19 2
      src/main/java/com/caimei/www/service/page/impl/ProductServiceImpl.java
  6. 266 0
      src/main/java/com/caimei/www/utils/OssUtil.java
  7. 5 0
      src/main/resources/config/beta/application-beta.yml
  8. 16 10
      src/main/resources/config/dev/application-dev.yml
  9. 5 0
      src/main/resources/config/prod/application-prod.yml
  10. 39 1
      src/main/resources/mapper/BaikeMapper.xml
  11. 1 1
      src/main/resources/static/css/base/floor.css
  12. 1 1
      src/main/resources/static/css/encyclopedia/common.css
  13. 12 1
      src/main/resources/static/css/encyclopedia/detail.css
  14. 2 0
      src/main/resources/static/css/supplier-center/encyclopedia/recommend-edit.css
  15. 84 0
      src/main/resources/static/js/common/serviceapi/supplier.service.js
  16. 9 0
      src/main/resources/static/js/common/serviceapi/utils.service.js
  17. 16 2
      src/main/resources/static/js/encyclopedia/common.js
  18. 10 0
      src/main/resources/static/js/encyclopedia/detail.js
  19. 1 1
      src/main/resources/static/js/index.js
  20. 80 0
      src/main/resources/static/js/oss-upload.js
  21. 75 4
      src/main/resources/static/js/supplier-center/article/article-edit.js
  22. 118 24
      src/main/resources/static/js/supplier-center/encyclopedia/components.js
  23. 135 25
      src/main/resources/static/js/supplier-center/encyclopedia/instrument-edit.js
  24. 2 2
      src/main/resources/static/js/supplier-center/encyclopedia/instrument-list.js
  25. 138 25
      src/main/resources/static/js/supplier-center/encyclopedia/product-edit.js
  26. 2 2
      src/main/resources/static/js/supplier-center/encyclopedia/product-list.js
  27. 194 5
      src/main/resources/static/js/supplier-center/encyclopedia/recommend-edit.js
  28. 3 0
      src/main/resources/static/lib/aliyun-oss-sdk-6.17.1.min.js
  29. 1 0
      src/main/resources/static/lib/uuidv4.min.js
  30. 2 4
      src/main/resources/templates/activity/attestation.html
  31. 15 113
      src/main/resources/templates/encyclopedia/instrument-detail.html
  32. 16 114
      src/main/resources/templates/encyclopedia/product-detail.html
  33. 8 7
      src/main/resources/templates/supplier-center/article/article-edit.html
  34. 29 27
      src/main/resources/templates/supplier-center/encyclopedia/instrument-edit.html
  35. 50 48
      src/main/resources/templates/supplier-center/encyclopedia/product-edit.html
  36. 65 26
      src/main/resources/templates/supplier-center/encyclopedia/recommend-edit.html

+ 163 - 0
backup.sql

@@ -675,6 +675,25 @@ ALTER TABLE `cm_hehe_image`
 ALTER TABLE `cm_hehe_discount_product`
     ADD COLUMN `discountPrice` DECIMAL(11,2) NULL COMMENT '折扣价' AFTER `productId`;
 
+ALTER TABLE `new_page_floor_content`
+    ADD COLUMN `productId1` INT NULL COMMENT '跳转商品id1(颜选)' AFTER `displayDate3`,
+    ADD COLUMN `productId2` INT NULL COMMENT '跳转商品id2(颜选)' AFTER `productId1`,
+    ADD COLUMN `productId3` INT NULL COMMENT '跳转商品id3(颜选)' AFTER `productId2`,
+    ADD COLUMN `productId4` INT NULL COMMENT '跳转商品id4(颜选)' AFTER `productId3`,
+    ADD COLUMN `productId5` INT NULL COMMENT '跳转商品id5(颜选)' AFTER `productId4`,
+    ADD COLUMN `jumpImage1` VARCHAR(255) NULL COMMENT '跳转图片1(颜选)' AFTER `productId5`,
+    ADD COLUMN `jumpImage2` VARCHAR(255) NULL COMMENT '跳转图片2(颜选)' AFTER `jumpImage1`,
+    ADD COLUMN `jumpImage3` VARCHAR(255) NULL COMMENT '跳转图片3(颜选)' AFTER `jumpImage2`,
+    ADD COLUMN `jumpImage4` VARCHAR(255) NULL COMMENT '跳转图片4(颜选)' AFTER `jumpImage3`,
+    ADD COLUMN `jumpImage5` VARCHAR(255) NULL COMMENT '跳转图片5(颜选)' AFTER `jumpImage4`,
+    ADD COLUMN `jumpType1` INT NULL DEFAULT 1 COMMENT '跳转类型1(颜选):1链接,2商品,3图片' AFTER `jumpImage5`,
+    ADD COLUMN `jumpType2` INT NULL DEFAULT 1 COMMENT '跳转类型2(颜选):1链接,2商品,3图片' AFTER `jumpType1`,
+    ADD COLUMN `jumpType3` INT NULL DEFAULT 1 COMMENT '跳转类型3(颜选):1链接,2商品,3图片' AFTER `jumpType2`,
+    ADD COLUMN `jumpType4` INT NULL DEFAULT 1 COMMENT '跳转类型4(颜选):1链接,2商品,3图片' AFTER `jumpType3`,
+    ADD COLUMN `jumpType5` INT NULL DEFAULT 1 COMMENT '跳转类型5(颜选):1链接,2商品,3图片' AFTER `jumpType4`;
+
+
+
 -- =================================== 2022年6月 信息中心小版本 end =========================================
 -- =================================== 2022年6月 认证通ross版本 start =========================================
 ALTER TABLE `cm_brand_auth`
@@ -717,6 +736,11 @@ ALTER TABLE `logistics_information`
 ALTER TABLE `cm_brand_club_feedback`
     ADD COLUMN `authUserId` INT NULL COMMENT '供应商用户id' AFTER `clubUserId`;
 
+ALTER TABLE `logistics_information`
+    ADD COLUMN `mobile` VARCHAR(30) NULL COMMENT '认证通机构收货人手机号' AFTER `logisticsBatchID`;
+
+ALTER TABLE `cm_brand_product_type`
+    ADD COLUMN `createSource` INT NULL DEFAULT 1 COMMENT '创建来源:1供应商添加,2机构用户添加' AFTER `createBy`;
 
 -- =================================== 2022年6月 认证通ross版本 end =========================================
 -- =================================== 2022年7月 认证通日常sql start =========================================
@@ -732,3 +756,142 @@ ALTER TABLE `cm_brand_product_type`
     ADD COLUMN `brandId` INT NULL COMMENT '品牌id' AFTER `authUserId`;
 
 -- =================================== 2022年6月 认证通日常sql end =========================================
+-- =================================== 2022年6月 认证通1.7.1版本 start =========================================
+ALTER TABLE `cm_brand_auth`
+    ADD COLUMN `checkFlag` INT NULL DEFAULT 1 COMMENT '是否查看过:1是,0否' AFTER `shopAuditTime`;
+
+ALTER TABLE `cm_brand_auth_product`
+    ADD COLUMN `checkFlag` INT NULL DEFAULT 1 COMMENT '是否查看过:1是,0否' AFTER `invalidReason`;
+
+ALTER TABLE `cm_brand_doctor`
+    ADD COLUMN `checkFlag` INT NULL DEFAULT 1 COMMENT '是否查看过:1是,0否' AFTER `auditTime`;
+
+ALTER TABLE `cm_brand_product_type`
+    ADD COLUMN `checkFlag` INT NULL DEFAULT 1 COMMENT '是否查看过:1是,0否' AFTER `auditTime`;
+
+CREATE TABLE `cm_brand_shop_user` (
+    `id` INT NOT NULL AUTO_INCREMENT,
+    `authUserId` INT NULL COMMENT '供应商用户id',
+    `loginAccount` VARCHAR(45) NULL COMMENT '登录账号',
+    `mobile` VARCHAR(20) NULL COMMENT '手机号',
+    `password` VARCHAR(100) NULL COMMENT '密码',
+    `linkMan` VARCHAR(30) NULL COMMENT '联系人',
+    `status` INT NULL COMMENT '状态:0停用,1启用',
+    `createTime` VARCHAR(45) NULL COMMENT '创建时间',
+    `createBy` VARCHAR(45) NULL COMMENT '创建人',
+    PRIMARY KEY (`id`))
+COMMENT = '认证通供应商子用户';
+
+ALTER TABLE `cm_brand_auth_user`
+    ADD COLUMN `parentId` INT NULL COMMENT '子用户关联的供应商用户id' AFTER `authUserId`,
+    CHANGE COLUMN `userIdentity` `userIdentity` INT NULL DEFAULT NULL COMMENT '用户身份:1管理员,2供应商,3供应商子用户' ;
+
+ALTER TABLE `auth_role`
+    ADD COLUMN `authUserId` INT NULL COMMENT '子用户角色关联的供应商用户id' AFTER `id`,
+    CHANGE COLUMN `role_type` `role_type` CHAR(1) CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_0900_ai_ci' NOT NULL DEFAULT '1' COMMENT '角色类型:1管理员角色,2供应商角色,3供应商子用户角色' ;
+
+
+ALTER TABLE `cm_brand_auth`
+    ADD COLUMN `linkMan` VARCHAR(20) NULL COMMENT '运营联系人' AFTER `userMobile`,
+    ADD COLUMN `linkMobile` VARCHAR(20) NULL COMMENT '运营联系人手机号' AFTER `linkMan`;
+
+ALTER TABLE `cm_brand_auth`
+    ADD COLUMN `starFlag` INT NULL DEFAULT 0 COMMENT '明星机构标识:0不是,1是' AFTER `checkFlag`;
+
+CREATE TABLE `cm_brand_product_relation` (
+    `id` INT NOT NULL AUTO_INCREMENT,
+    `authId` INT NULL COMMENT '机构id',
+    `productId` INT NULL COMMENT '设备id',
+    `authType` INT NULL COMMENT '认证方式:1新设备认证,2关联已认证设备',
+    PRIMARY KEY (`id`))
+COMMENT = '机构设备关联表';
+
+ALTER TABLE `cm_brand_auth_user`
+    ADD COLUMN `cmShopId` INT NULL COMMENT '关联采美供应商id' AFTER `authUserId`,
+    CHANGE COLUMN `logo` `logo` TEXT NULL DEFAULT NULL COMMENT '供应商logo' ;
+
+ALTER TABLE `cm_brand_auth_shop_info`
+    ADD COLUMN `brandName` VARCHAR(45) NULL COMMENT '品牌名称' AFTER `brandId`,
+    ADD COLUMN `brandLogo` VARCHAR(255) NULL COMMENT '品牌logo' AFTER `brandName`,
+    ADD COLUMN `producePlace` VARCHAR(100) NULL COMMENT '产地' AFTER `countryId`,
+    CHANGE COLUMN `brandId` `brandId` INT NULL DEFAULT NULL COMMENT '所属品牌Id(废弃)' ,
+    CHANGE COLUMN `securityLink` `securityLink` TEXT NULL DEFAULT NULL COMMENT '官网认证链接(废弃)' ,
+    CHANGE COLUMN `statementType` `statementType` INT NULL DEFAULT NULL COMMENT '代理声明类型:1弹窗,2链接,3图片,4文件(废弃)' ,
+    CHANGE COLUMN `statementContent` `statementContent` TEXT NULL DEFAULT NULL COMMENT '声明弹窗内容(废弃)' ,
+    CHANGE COLUMN `statementLink` `statementLink` VARCHAR(255) NULL DEFAULT NULL COMMENT '声明链接(废弃)' ,
+    CHANGE COLUMN `statementImage` `statementImage` TEXT NULL DEFAULT NULL COMMENT '声明图片(废弃)' ,
+    CHANGE COLUMN `countryId` `countryId` INT NULL DEFAULT NULL COMMENT '产地国家id(废弃)' ;
+
+
+ALTER TABLE `cm_brand_auth_product`
+    ADD COLUMN `certificateImageType` INT NULL COMMENT '授权牌照类型:1模板库生成,2自定义上传' AFTER `originalCertificateImage`,
+    CHANGE COLUMN `originalCertificateImage` `originalCertificateImage` VARCHAR(255) NULL DEFAULT NULL COMMENT '原授权牌照(废弃)' ,
+    CHANGE COLUMN `addQrCodeFlag` `addQrCodeFlag` INT NULL DEFAULT '0' COMMENT '是否生成二维码授权牌:0否,1是(废弃)' ,
+    CHANGE COLUMN `addTemplateType` `addTemplateType` INT NULL DEFAULT NULL COMMENT '生成二维码授权牌模板:1左下,2右边,3左边(废弃)' ;
+
+update cm_brand_auth_product set certificateImageType = if(certificateImage is null,1,2);
+
+ALTER TABLE `cm_brand_auth_product`
+    CHANGE COLUMN `authId` `authId` INT NULL DEFAULT NULL COMMENT '授权Id(废弃)' ;
+
+ALTER TABLE `cm_brand_auth_product`
+    CHANGE COLUMN `brandId` `infoId` INT NULL DEFAULT NULL COMMENT '品牌信息id' ;
+
+ALTER TABLE `cm_brand_product_type`
+    CHANGE COLUMN `brandId` `infoId` INT NULL DEFAULT NULL COMMENT '品牌信息id' ;
+
+ALTER TABLE `cm_brand_auth_file`
+    COMMENT = '品牌授权代理声明文件(废弃)' ;
+
+insert into cm_brand_product_relation(productId,authId,authType)
+select id,authId,1 from cm_brand_auth_product;
+
+ALTER TABLE `cm_brand_auth`
+    ADD COLUMN `scanCount` INT NULL DEFAULT 0 COMMENT '扫码次数' AFTER `sendStatus`;
+
+ALTER TABLE `cm_brand_auth_product`
+    ADD COLUMN `scanCount` INT NULL COMMENT '扫码次数' AFTER `invoiceImage`;
+
+ALTER TABLE `auth_role`
+    DROP INDEX `role_name` ;
+
+UPDATE `auth_role` SET `authUserId` = '1' WHERE (`id` = '1');
+UPDATE `auth_role` SET `authUserId` = '1' WHERE (`id` = '4');
+
+ALTER TABLE `cm_brand_auth_user`
+    ADD COLUMN `prefix` VARCHAR(45) NULL COMMENT '前缀' AFTER `loginAccount`;
+
+-- ===============================================================================================================
+CREATE TABLE `cm_brand_auth_template` (
+    `id` INT NOT NULL AUTO_INCREMENT,
+    `authUserId` INT NULL COMMENT '供应商用户id',
+    `templateImage` VARCHAR(255) NULL COMMENT '模板图片',
+    `templateSize` VARCHAR(45) NULL COMMENT '模板尺寸',
+    `authFlag` INT NULL DEFAULT 0 COMMENT '作为机构认证模板:1是,其余否',
+    `productFlag` INT NULL DEFAULT 0 COMMENT '作为设备认证模板:1是,其余否',
+    `qrPosition` VARCHAR(45) NULL COMMENT '二维码位置:二维码左上角的坐标值(x,y)',
+    `qrSize` INT NULL COMMENT '二维码尺寸',
+    `status` INT NULL DEFAULT 1 COMMENT '状态:0停用,1启用',
+    `addTime` DATETIME NULL COMMENT '添加时间',
+    PRIMARY KEY (`id`))
+COMMENT = '授权牌模板';
+
+
+ALTER TABLE `cm_brand_auth`
+    ADD COLUMN `authCode` VARCHAR(45) NULL COMMENT '认证编号' AFTER `userMobile`,
+    ADD COLUMN `authDate` DATETIME NULL COMMENT '认证日期' AFTER `authCode`,
+    ADD COLUMN `authImageLogo` VARCHAR(255) NULL COMMENT '授权牌logo(logo与机构名称组合)' AFTER `authDate`,
+    ADD COLUMN `authImage` VARCHAR(255) NULL COMMENT '授权牌' AFTER `authImageLogo`,
+    ADD COLUMN `pcAuthImage` VARCHAR(255) NULL COMMENT 'pc添加水印授权牌' AFTER `authImage`,
+    ADD COLUMN `appletsAuthImage` VARCHAR(255) NULL COMMENT '小程序添加水印授权牌' AFTER `pcAuthImage`,
+    ADD COLUMN `authImageType` INT NULL DEFAULT 1 COMMENT '授权牌类型:1模板库生成,自定义上传' AFTER `appletsAuthImage`;
+
+ALTER TABLE `cm_brand_auth_template`
+    ADD COLUMN `logoSize` VARCHAR(45) NULL COMMENT 'logo尺寸' AFTER `qrSize`;
+
+ALTER TABLE `cm_brand_video`
+    ADD COLUMN `image` VARCHAR(300) NULL COMMENT '视频封面' AFTER `title`;
+
+ALTER TABLE `cm_brand_auth_shop_info`
+    ADD COLUMN `manufacturer` VARCHAR(45) NULL COMMENT '制造商' AFTER `securityLink`;
+-- =================================== 2022年6月 认证通1.7.1版本 end =========================================

+ 4 - 0
src/main/java/com/caimei/www/controller/BaseController.java

@@ -27,6 +27,8 @@ public class BaseController {
 	private String zplmDomain;
 	@Value("${caimei.zplmServer}")
 	private String zplmServer;
+	@Value("${aliyunOss.ossBucket}")
+	private String ossBucket;
     /** 打包时间 */
     @Value("${caimei.siteEnv}")
     private String siteEnv;
@@ -64,6 +66,8 @@ public class BaseController {
 		model.addAttribute("zplmDomain", zplmDomain);
 		// zplm接口地址
 		model.addAttribute("zplmServer", zplmServer);
+		// 阿里云oss对象存储文件上传目录
+		model.addAttribute("ossBucket", ossBucket);
 		// 搜索热门关键字
 		List<String> searchHotWord = baseService.getSearchHotWord();
 		model.addAttribute("searchHotWord", searchHotWord);

+ 5 - 6
src/main/java/com/caimei/www/mapper/BaikeDao.java

@@ -1,12 +1,6 @@
 package com.caimei.www.mapper;
 
 import com.caimei.www.pojo.baike.*;
-import com.caimei.www.pojo.classify.Bigtype;
-import com.caimei.www.pojo.classify.SmallType;
-import com.caimei.www.pojo.order.CartItem;
-import com.caimei.www.pojo.page.Parameter;
-import com.caimei.www.pojo.page.ProductDetail;
-import com.caimei.www.pojo.page.ProductList;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -43,4 +37,9 @@ public interface BaikeDao {
     BaikeType getBaikeType(Integer typeId);
 
     List<BaikeProductFile> findVideoList(Integer productId);
+
+    List<BaikeProduct> getManualRecommendList(Integer productId);
+
+    List<BaikeProduct> getAutoRecommendList(Integer publishSource, @Param("shopId") Integer shopId, @Param("typeId") Integer typeId, @Param("productId") Integer productId);
+
 }

+ 24 - 1
src/main/java/com/caimei/www/pojo/baike/BaikeProduct.java

@@ -20,6 +20,11 @@ public class BaikeProduct implements Serializable {
 
     private Integer productId;
 
+    /**
+     * 商品类型:1产品,2仪器
+     */
+    private Integer commodityType;
+
     /**
      * 产品/仪器图片
      */
@@ -30,6 +35,11 @@ public class BaikeProduct implements Serializable {
      */
     private String name;
 
+    /**
+     * 发布来源:1采美,2供应商
+     */
+    private Integer publishSource;
+
     /**
      * 供应商id
      */
@@ -117,12 +127,16 @@ public class BaikeProduct implements Serializable {
     /**
      * 发布时间
      */
-    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone = "GMT+8")
     private Date publishTime;
     /**
      * 是否置顶标识:0否,1是
      */
     private Integer topFlag;
+    /**
+     * 分类id
+     */
+    private Integer typeId;
     /**
      * 分类名称
      */
@@ -133,6 +147,11 @@ public class BaikeProduct implements Serializable {
      */
     private Integer pv;
 
+    /**
+     * 推荐类型:1手动推荐,2自动推荐
+     */
+    private Integer recommendType;
+
     /**
      * 参数列表
      */
@@ -153,4 +172,8 @@ public class BaikeProduct implements Serializable {
      * 视频列表
      */
     private List<BaikeProductFile> videoList;
+    /**
+     * 相关推荐商品列表
+     */
+    private List<BaikeProduct> recommendList;
 }

+ 19 - 2
src/main/java/com/caimei/www/service/page/impl/ProductServiceImpl.java

@@ -3,6 +3,7 @@ package com.caimei.www.service.page.impl;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.aliyun.oss.common.utils.StringUtils;
+import com.aliyun.oss.internal.OSSUtils;
 import com.caimei.www.mapper.BaikeDao;
 import com.caimei.www.mapper.ProductDao;
 import com.caimei.www.pojo.JsonModel;
@@ -12,6 +13,7 @@ import com.caimei.www.pojo.page.ProductDetail;
 import com.caimei.www.pojo.page.ProductList;
 import com.caimei.www.service.page.ProductService;
 import com.caimei.www.utils.ImageUtil;
+import com.caimei.www.utils.OssUtil;
 import com.caimei.www.utils.PriceUtil;
 import com.caimei.www.utils.RequestUtil;
 import io.netty.util.internal.StringUtil;
@@ -279,8 +281,9 @@ public class ProductServiceImpl implements ProductService {
             List<BaikeProductQuestion> questionList = baikeDao.findQuestionList(productId);
             baikeProduct.setQuestionList(questionList);
             // 视频列表
-            /*List<BaikeProductFile> videoList = baikeDao.findVideoList(productId);
-            baikeProduct.setVideoList(videoList);*/
+            List<BaikeProductFile> videoList = baikeDao.findVideoList(productId);
+            videoList.forEach(video -> video.setFileUrl(OssUtil.getOssUrl(video.getOssName())));
+            baikeProduct.setVideoList(videoList);
             // 格式化时间
             String marketTime = baikeProduct.getMarketTime();
             String nmpaTime = baikeProduct.getNmpaTime();
@@ -289,6 +292,20 @@ public class ProductServiceImpl implements ProductService {
             if (StringUtils.isNullOrEmpty(baikeProduct.getShopLogo())) {
                 baikeProduct.setShopLogo("/img/default/suppliver.jpg");
             }
+            // 相关推荐数据
+            List<BaikeProduct> recommendList;
+            if (1 == baikeProduct.getRecommendType()) {
+                // 手动推荐
+                recommendList = baikeDao.getManualRecommendList(productId);
+            } else {
+                recommendList = baikeDao.getAutoRecommendList(baikeProduct.getPublishSource(), baikeProduct.getShopId(), baikeProduct.getTypeId(), baikeProduct.getProductId());
+            }
+            recommendList.forEach(recommendProduct->{
+                // 问题列表
+                List<BaikeProductQuestion> queList = baikeDao.findQuestionList(recommendProduct.getProductId());
+                recommendProduct.setQuestionList(queList);
+            });
+            baikeProduct.setRecommendList(recommendList);
         }
         return baikeProduct;
     }

+ 266 - 0
src/main/java/com/caimei/www/utils/OssUtil.java

@@ -0,0 +1,266 @@
+package com.caimei.www.utils;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.GetObjectRequest;
+import com.aliyun.oss.model.ObjectMetadata;
+import com.aliyun.oss.model.UploadFileRequest;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.auth.sts.AssumeRoleRequest;
+import com.aliyuncs.auth.sts.AssumeRoleResponse;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.profile.DefaultProfile;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.UUID;
+
+
+/**
+ * 文件的上传下载
+ *
+ * @author Administrator
+ */
+@Component
+public class OssUtil {
+    private static final String endpoint = "https://oss-cn-shenzhen.aliyuncs.com";
+    private static final String accessKeyId = "LTAI4GBL3o4YkWnbKYgf2Xia";
+    private static final String accessKeySecret = "dBjAXqbYiEPP6Ukuk2ZsXQeET7FVkK";
+    private static final String privateBucket = "caimei-oss";
+
+    private static String active;
+
+    @Value("${spring.profiles.active}")
+    public void setActive(String actives) {
+        active = actives;
+    }
+
+
+    public static HashMap<String, String> getToken() {
+        //构建一个阿里云客户端,用于发起请求。
+        //设置调用者(RAM用户或RAM角色)的AccessKey ID和AccessKey Secret。
+//        DefaultProfile.addEndpoint("oss-cn-shenzhen", "Sts", endpoint.replace("https://",""));
+        DefaultProfile profile = DefaultProfile.getProfile("oss-cn-shenzhen", accessKeyId, accessKeySecret);
+        IAcsClient client = new DefaultAcsClient(profile);
+
+        //构造请求,设置参数。
+        AssumeRoleRequest request = new AssumeRoleRequest();
+        request.setRegionId(endpoint);
+        request.setRoleArn("acs:ram::1565565840178476:role/caimeitst");
+        request.setRoleSessionName("TSTtest");
+        AssumeRoleResponse response = null;
+        try {
+            response = client.getAcsResponse(request);
+        } catch (ClientException e) {
+            e.printStackTrace();
+        }
+        String requestId = response.getRequestId();
+        String securityToken = response.getCredentials().getSecurityToken();
+        String accessKeySecret = response.getCredentials().getAccessKeySecret();
+        String accessKeyId = response.getCredentials().getAccessKeyId();
+        HashMap<String, String> tokenMap = new HashMap<>(5);
+        tokenMap.put("requestId", requestId);
+        tokenMap.put("accessKeyId", accessKeyId);
+        tokenMap.put("accessKeySecret", accessKeySecret);
+        tokenMap.put("securityToken", securityToken);
+        tokenMap.put("bucket", privateBucket);
+        return tokenMap;
+    }
+
+
+    /*public static HashMap<String, String> getToken() throws ClientException {
+        // regionId表示RAM的地域ID。以华东1(杭州)地域为例,regionID填写为cn-hangzhou。也可以保留默认值,默认值为空字符串("")。
+        String regionId = "";
+        // 添加endpoint。适用于Java SDK 3.12.0及以上版本。
+        DefaultProfile.addEndpoint(regionId, "Sts", endpoint.replace("https://",""));
+        // 添加endpoint。适用于Java SDK 3.12.0以下版本。
+        // DefaultProfile.addEndpoint("",regionId, "Sts", endpoint);
+        // 构造default profile。
+        IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
+        // 构造client。
+        DefaultAcsClient client = new DefaultAcsClient(profile);
+        final AssumeRoleRequest request = new AssumeRoleRequest();
+        // 适用于Java SDK 3.12.0及以上版本。
+        request.setSysMethod(MethodType.POST);
+        // 适用于Java SDK 3.12.0以下版本。
+        //request.setMethod(MethodType.POST);
+        request.setRoleArn("acs:ram::1565565840178476:role/caimeitst");
+        request.setRoleSessionName("TSTtest");
+        request.setPolicy();
+//        request.setPolicy(null); // 如果policy为空,则用户将获得该角色下所有权限。
+        request.setDurationSeconds(3600L); // 设置临时访问凭证的有效时间为3600秒。
+        final AssumeRoleResponse response = client.getAcsResponse(request);
+        System.out.println("Expiration: " + response.getCredentials().getExpiration());
+        System.out.println("Access Key Id: " + response.getCredentials().getAccessKeyId());
+        System.out.println("Access Key Secret: " + response.getCredentials().getAccessKeySecret());
+        System.out.println("Security Token: " + response.getCredentials().getSecurityToken());
+        System.out.println("RequestId: " + response.getRequestId());
+
+        String requestId = response.getRequestId();
+        String securityToken = response.getCredentials().getSecurityToken();
+        String accessKeySecret = response.getCredentials().getAccessKeySecret();
+        String accessKeyId = response.getCredentials().getAccessKeyId();
+        HashMap<String, String> tokenMap = new HashMap<>(5);
+        tokenMap.put("requestId", requestId);
+        tokenMap.put("accessKeyId", accessKeyId);
+        tokenMap.put("accessKeySecret", accessKeySecret);
+        tokenMap.put("securityToken", securityToken);
+        tokenMap.put("bucket", privateBucket);
+        return tokenMap;
+    }*/
+
+    public static String ossUpload(String fileName, File file, String contentType) {
+        String url = null;
+        try {
+            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+            ObjectMetadata meta = new ObjectMetadata();
+            meta.setContentType(contentType);
+            fileName = active + "/" + fileName;
+            UploadFileRequest uploadFileRequest = new UploadFileRequest(privateBucket, fileName);
+            // 指定上传的本地文件。
+            uploadFileRequest.setUploadFile(file.toString());
+            // 指定上传并发线程数,默认为1。
+            uploadFileRequest.setTaskNum(10);
+            // 指定上传的分片大小,范围为100KB~5GB,默认为文件大小/10000。
+            uploadFileRequest.setPartSize(1024 * 1024);
+            // 开启断点续传,默认关闭。
+            uploadFileRequest.setEnableCheckpoint(true);
+            uploadFileRequest.setCheckpointFile(file.getAbsolutePath() + "uploadFile.ucp");
+            // 文件的元数据。
+            uploadFileRequest.setObjectMetadata(meta);
+            // 设置上传成功回调,参数为Callback类型。
+            //uploadFileRequest.setCallback("<yourCallbackEvent>");
+            //断点续传上传。
+            ossClient.uploadFile(uploadFileRequest);
+            Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000);
+            url = ossClient.generatePresignedUrl(privateBucket, fileName, expiration).toString();
+            // 关闭OSSClient。
+            ossClient.shutdown();
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+        return url;
+    }
+
+
+    /**
+     * 通过文件名判断并获取OSS服务文件上传时文件的contentType
+     */
+    public static String getContentType(String fileName) {
+        String fileExtension = fileName.substring(fileName.lastIndexOf("."));
+        if (".bmp".equalsIgnoreCase(fileExtension)) {
+            return "image/bmp";
+        }
+        if (".gif".equalsIgnoreCase(fileExtension)) {
+            return "image/gif";
+        }
+        if (".jpeg".equalsIgnoreCase(fileExtension)) {
+            return "image/jpeg";
+        }
+        if (".jpg".equalsIgnoreCase(fileExtension)) {
+            return "image/jpg";
+        }
+        if (".png".equalsIgnoreCase(fileExtension)) {
+            return "image/png";
+        }
+        if (".html".equalsIgnoreCase(fileExtension)) {
+            return "text/html";
+        }
+        if (".txt".equalsIgnoreCase(fileExtension)) {
+            return "text/plain";
+        }
+        if (".vsd".equalsIgnoreCase(fileExtension)) {
+            return "application/vnd.visio";
+        }
+        if (".ppt".equalsIgnoreCase(fileExtension) || "pptx".equalsIgnoreCase(fileExtension)) {
+            return "application/vnd.ms-powerpoint";
+        }
+        if (".doc".equalsIgnoreCase(fileExtension) || "docx".equalsIgnoreCase(fileExtension)) {
+            return "application/msword";
+        }
+        if (".xml".equalsIgnoreCase(fileExtension)) {
+            return "text/xml";
+        }
+        if (".mp4".equalsIgnoreCase(fileExtension)) {
+            return "video/mp4";
+        }
+        if (".mp3".equalsIgnoreCase(fileExtension)) {
+            return "audio/mp3";
+        }
+        if (".pdf".equalsIgnoreCase(fileExtension)) {
+            return "application/pdf";
+        }
+        return "text/html";
+    }
+
+    public static void deleteFile(File... files) {
+        for (File file : files) {
+            //logger.info("File:[{}]",file.getAbsolutePath());
+            if (file.exists()) {
+                file.delete();
+            }
+        }
+    }
+
+    public static File ossUpload(MultipartFile file) throws IOException {
+        // 获取文件名
+        String fileName = file.getOriginalFilename();
+        // 获取文件后缀
+        String prefix = fileName.substring(fileName.lastIndexOf("."));
+        // 用uuid作为文件名,防止生成的临时文件重复
+        File excelFile = File.createTempFile(UUID.randomUUID().toString(), prefix);
+        // MultipartFile to File
+        file.transferTo(excelFile);
+        //程序结束时,删除临时文件
+        return excelFile;
+    }
+
+    /**
+     * 授权生成签名URL临时访问
+     *
+     * @param fileName 文件名称
+     */
+    public static String getOssUrl(String fileName) {
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        // 设置URL过期时间为1个小时
+        Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000);
+        String url = ossClient.generatePresignedUrl(privateBucket, active + "/" + fileName, expiration).toString();
+        // 关闭OSSClient。
+        ossClient.shutdown();
+        return url;
+    }
+
+    /**
+     * oss单个文件删除
+     *
+     * @param fileName 文件名称或文件夹名称
+     */
+    public static void deleteSingleFile(String fileName) {
+        // 创建OSSClient实例。
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        // 删除文件。如需删除文件夹,请将ObjectName设置为对应的文件夹名称。
+        // 如果文件夹非空,则需要将文件夹下的所有object删除后才能删除该文件夹。
+        fileName = active + "/" + fileName;
+        ossClient.deleteObject(privateBucket, fileName);
+        // 关闭OSSClient。
+        ossClient.shutdown();
+    }
+
+    /**
+     * oss单个文件下载
+     */
+    public static void downFile(String ossName, String fileName) {
+        // 创建OSSClient实例。
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        // 下载OSS文件到本地文件。如果指定的本地文件存在会覆盖,不存在则新建。
+        ossClient.getObject(new GetObjectRequest(privateBucket, ossName), new File("./" + fileName));
+        // 关闭OSSClient。
+        ossClient.shutdown();
+    }
+}

+ 5 - 0
src/main/resources/config/beta/application-beta.yml

@@ -58,6 +58,11 @@ caimei:
   zplmDomain: https://zp-b.caimei365.com
   zplmServer: https://zplma-b.caimei365.com
 
+# 阿里云oss配置
+aliyunOss:
+  # 文件上传目录
+  ossBucket: beta
+
 #DFS配置
 fdfs:
   so-timeout: 5000 #上传的超时时间

+ 16 - 10
src/main/resources/config/dev/application-dev.yml

@@ -4,14 +4,14 @@ spring:
   #数据源连接--start
   datasource:
     #本地连接数据库
-#    url: jdbc:mysql://192.168.2.100:3306/caimei?characterEncoding=UTF8&serverTimezone=Asia/Shanghai
-#    username: developer
-#    password: 05bZ/OxTB:X+yd%1
-    #测试连接数据库
-    url: jdbc:mysql://120.79.25.27:3306/caimei?characterEncoding=UTF8&serverTimezone=Asia/Shanghai
+    url: jdbc:mysql://192.168.2.100:3306/caimei?characterEncoding=UTF8&serverTimezone=Asia/Shanghai
     username: developer
-    password: J5p3tgOVazNl4ydf
-    type: com.zaxxer.hikari.HikariDataSource
+    password: 05bZ/OxTB:X+yd%1
+    #测试连接数据库
+#    url: jdbc:mysql://120.79.25.27:3306/caimei?characterEncoding=UTF8&serverTimezone=Asia/Shanghai
+#    username: developer
+#    password: J5p3tgOVazNl4ydf
+    #type: com.zaxxer.hikari.HikariDataSource
     hikari:
       minimum-idle: 5
       maximum-pool-size: 15
@@ -54,9 +54,10 @@ logging:
 caimei:
   siteEnv: 0 #网站环境,(2:正式环境,1:测试环境,0:开发环境)
   #spiServer: http://192.168.2.68:8008
-  coreServer: https://core-b.caimei365.com
-#  coreServer: http://192.168.2.67:18002
-#  coreServer: http://192.168.2.75:18002
+  #coreServer: https://core-b.caimei365.com
+  #coreServer: http://192.168.2.67:18002
+  #coreServer: http://192.168.2.75:18002
+  coreServer: http://192.168.2.100:18002
   imageDomain: https://img-b.caimei365.com
   wwwDomain: http://localhost:8009
   destPath: classpath:/
@@ -64,6 +65,11 @@ caimei:
   zplmDomain: https://zp-b.caimei365.com
   zplmServer: https://zplma-b.caimei365.com
 
+# 阿里云oss配置
+aliyunOss:
+  # 文件上传目录
+  ossBucket: dev
+
 #DFS配置
 fdfs:
   so-timeout: 5000 #上传的超时时间

+ 5 - 0
src/main/resources/config/prod/application-prod.yml

@@ -58,6 +58,11 @@ caimei:
   zplmDomain: https://zp.caimei365.com
   zplmServer: https://zplma.caimei365.com
 
+# 阿里云oss配置
+aliyunOss:
+  # 阿里云 oss 文件上传目录
+  ossBucket: prod
+
 #DFS配置
 fdfs:
   so-timeout: 5000 #上传的超时时间

+ 39 - 1
src/main/resources/mapper/BaikeMapper.xml

@@ -5,9 +5,10 @@
 		update cm_baike_product set actualPv = actualPv + 1 where id = #{id}
 	</update>
 	<select id="getBaikeProductDetail" resultType="com.caimei.www.pojo.baike.BaikeProduct">
-		select a.id              AS "id",
+		select a.id              AS "productId",
 			   a.commodityType   AS "commodityType",
 			   a.name            AS "name",
+		       a.publishSource,
 		       a.shopId			 AS "shopId",
 		       s.name			 AS "shopName",
 		       s.logo			 AS "shopLogo",
@@ -35,6 +36,7 @@
 			   a.topPosition     AS "topPosition",
 			   a.status          AS "status",
 			   a.addTime         AS "addTime",
+			   a.recommendType,
 			   cbt.name          as "typeName"
 		from cm_baike_product a
 				 left join cm_baike_type cbt on a.typeId = cbt.id
@@ -62,4 +64,40 @@
 		from cm_baike_product_file
 		where productId = #{productId}
 	</select>
+	<select id="getManualRecommendList" resultType="com.caimei.www.pojo.baike.BaikeProduct">
+        select p.id                    as productId,
+               p.commodityType,
+               p.image,
+               p.name,
+               p.discription,
+               p.publishTime,
+               (p.basePv + p.actualPv) as pv
+        from cm_baike_product_recommend cbpr
+                 left join cm_baike_product p on cbpr.recommendProductId = p.id
+        where productId = #{productId}
+          and p.delFlag = 0
+          and p.onlineStatus = 2
+          and p.status = 1
+        order by -cbpr.sort desc;
+	</select>
+	<select id="getAutoRecommendList" resultType="com.caimei.www.pojo.baike.BaikeProduct">
+		select p.id as productId, p.commodityType, p.image, p.name, p.discription, p.publishTime, (p.basePv + p.actualPv) as pv
+		from cm_baike_product p
+		where typeId = #{typeId}
+		<if test="1 == publishSource">
+			and publishSource = 1
+		</if>
+		<if test="2 == publishSource">
+			and publishSource = 2
+			and shopId = #{shopId}
+		</if>
+		<if test="productId != null">
+			and id != #{productId}
+		</if>
+		and p.delFlag = 0
+		and p.status = 1
+		and p.onlineStatus = 2
+		order by p.addTime desc
+		limit 15
+	</select>
 </mapper>

+ 1 - 1
src/main/resources/static/css/base/floor.css

@@ -141,7 +141,7 @@
 .page_main_li.ad_01 img{width:100%;height:100%}
 .section_right{padding:3.2vw}
 .box{white-space:nowrap;overflow-x:auto}
-.section_right .section_right_item{display:inline-block;width:42.2vw;height:61.6vw;background:#fff;margin-right:2.4vw;flex-shrink:0}
+.section_right .section_right_item{display:inline-block;width:42.2vw;height:61.6vw;background:#fff;margin-right:2.4vw;flex-shrink:0;vertical-align: top;}
 .section_right .right_item_title{overflow:hidden;padding:2.4vw}
 .section_right .right_item_title p{float:left;font-size:3.4vw;font-weight:bold;color:#4a4f58;text-align:left}
 .section_right .right_item_title a{display:inline-block;float:right;font-size:3.2vw;color:#909090;text-align:right}

+ 1 - 1
src/main/resources/static/css/encyclopedia/common.css

@@ -36,7 +36,7 @@ body{padding-top:80px;}
 .copyright p .icp{display:inline-block;vertical-align:middle}
 .copyright p a{color:#f1f1f1}
 .copyright p a:hover{color:#fff}
-.navigate{position:fixed;right:20px;top:50%;z-index:9;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);width:88px;background:#fff;-webkit-box-shadow:0px 4px 8px rgba(74,79,88,0.16);box-shadow:0px 4px 8px rgba(74,79,88,0.16)}
+.navigate{position:fixed;right:20px;top:50%;z-index:19;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);width:88px;background:#fff;-webkit-box-shadow:0px 4px 8px rgba(74,79,88,0.16);box-shadow:0px 4px 8px rgba(74,79,88,0.16)}
 .navigate li{line-height:40px;height:40px;text-align:center}
 .navigate li.active a{color:#e15616;background:#fef6f3}
 .navigate li a{display:block;font-size:12px;color:#4a4f58}

+ 12 - 1
src/main/resources/static/css/encyclopedia/detail.css

@@ -30,6 +30,7 @@ img{cursor: pointer;}
 /*.article .section.params .tr .th{background:#f4f5f8;width: 30%;}*/
 /*.article .section.params .tr .td{background:#ffffff;width: 70%; overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}*/
 .article .section.params .content{width:100%;font-size:14px;border-collapse:unset;border-spacing:unset}
+.article .section.params .content.mobile{display: none;}
 .article .section.params .content tr td{padding:8px;line-height:1.6;border-top:1px solid;border-left:1px solid;border-color:#e3e6eb !important}
 .article .section.params .content tr td:last-child{border-right:1px solid}
 .article .section.params .content tr:last-child td{border-bottom:1px solid}
@@ -57,6 +58,7 @@ img{cursor: pointer;}
 
 .article .section.related-video .content::after{content: "";display: block;clear:both;}
 .article .section.related-video .content .video-control{width: 338px;height: 230px;position: relative;background: #f1f1f1;float: left;margin: 24px 24px 0 0;}
+.article .section.related-video .content .video-control .video-title { position: absolute; left: 0; bottom: 0; z-index: 10; width: 100%; height: 40px; background: rgba(0,0,0,0.39); text-align: center; line-height: 40px ; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #fff; font-size: 16px; box-sizing: border-box;padding: 0 16px; }
 .article .section.related-video .content .video-control:nth-child(1),
 .article .section.related-video .content .video-control:nth-child(2),
 .article .section.related-video .content .video-control:nth-child(3){margin-top: 0;}
@@ -81,7 +83,7 @@ img{cursor: pointer;}
 .article .section .recommend-section .content .tag-list{height:24px}
 .article .section .recommend-section .content .tag-list .tag{display:inline-block;height:24px;line-height:24px;background:#fef6f3;border-radius:2px;font-size:12px;color:#e15616;padding:0 8px;margin-right:8px}
 .article .section .recommend-section .content .dashed-line{margin-top:12px;height:0px;border-bottom:1px dashed #b8bfca}
-.article .section .recommend-section .content .footer{height:50px;line-height:50px;font-size:14px;color:#93979f}
+.article .section .recommend-section .content .footer{height:50px;line-height:50px;font-size:14px;color:#93979f;display: flex;justify-content: space-between;}
 
 .contact-popup{display: none; position:fixed;top: 50%;left: 50%;z-index: 99999; transform: translate(-50%,-50%); width:314px;height:418px;overflow:hidden;background:url(/img/encyclopedia/contact.png) no-repeat center}
 .contact-popup .close{position:absolute;display:block;width:30px;height:30px;text-align:center;line-height:30px;top:10px;right:20px;font-size:28px;color:#2c3038;cursor:pointer}
@@ -123,6 +125,7 @@ body{padding-top:40.3vw}
 .article .section.description .names .alias{color:#93979f}
 .article .section.description .desc{text-align:justify;font-size:3.2vw;line-height:5.6vw;margin:3.2vw 0}
 .article .section.description .cover img{display:block;width:23.6vw;height:23.6vw;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px dashed #e3e6eb}
+.article .section.params .content.pc{display: none;}
 .article .section.params .tr{line-height:5.6vw}
 .article .section.params .tr .group{font-size:3.2vw;margin:1.6vw 0}
 .article .section.params .tr .group .th::after{content:":"}
@@ -155,6 +158,7 @@ body{padding-top:40.3vw}
 .article .section.related-video .content .video-control:last-child{margin-right: 3.2vw;}
 .article .section.related-video .content .video-control video{width: 100%;height: 100%;position: relative;z-index: 6}
 .article .section.related-video .content .video-control .play{display: block;position: absolute;left: 50%;top: 50%; border-radius: 50%; width: 10.8vw;height: 10.8vw; transform: translate(-50%,-50%);background: url(/img/activity/pc-palyer.png);cursor: pointer;z-index: 9;background-size: 10.8vw 10.8vw;}
+    .article .section.related-video .content .video-control .video-title { position: absolute; left: 0; bottom: 0; z-index: 10; width: 100%; height: 8.8vw; background: rgba(0,0,0,0.39); text-align: center; line-height: 8.8vw ; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #fff; font-size: 3.4vw; box-sizing: border-box;padding: 0 3.4vw; }
 
 .article .section.recommend {margin-top: 3.2vw;}
 .article .section .recommend-section {-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;display: flex;padding: 2.4vw 0;border-top: 1px solid #B8BFCA;}
@@ -172,4 +176,11 @@ body{padding-top:40.3vw}
 .article .section .recommend-section .content .tag-list .tag:last-child{display:none}
 .article .section .recommend-section .content .dashed-line{display:none}
 .article .section .recommend-section .content .footer{height:8vw;font-size:2.6vw;color:#93979f;line-height:8vw}
+
+.video-popup{position: fixed;width: 100vw;height: 100vh;top: 0; left: 0;background: #333;z-index: 99999}
+/*.video-popup .mask{width: 100vw;height: 100vh;background: #333333;}*/
+.video-popup .content{width: 100%;height: 100%;position: absolute;left: 0; top: 0;}
+.video-popup .content video{width: 100%;height: 100%;display: block;}
+.video-popup .close {position: absolute;right: 3.6vw;top: 3.6vw; z-index: 1; width: 7.2vw;height: 7.2vw;background: url(/img/activity/pc_close.png);background-size: 7.2vw;cursor: pointer;}
+
 }

+ 2 - 0
src/main/resources/static/css/supplier-center/encyclopedia/recommend-edit.css

@@ -1,6 +1,8 @@
 table{border-collapse:initial}
 table,td,th{border:initial}
 #recommendEdit .navLayout{min-height:auto}
+.el-input.center .el-input__inner{text-align: center}
+
 @media screen and (min-width:768px){
 .recommend-edit .right{background:#fff;padding:32px 40px;-webkit-box-sizing:border-box;box-sizing:border-box}
 .recommend-edit .recommend-title{font-size:16px;font-weight:bold;color:#4A4F58;text-align:center}

+ 84 - 0
src/main/resources/static/js/common/serviceapi/supplier.service.js

@@ -4,6 +4,78 @@
  * auther ZHJY
  */
 var SupplierApi = {
+        ShopBaikeRecommendTypeChange: function(params, callback){ // 百科商品/相关推荐 保存
+            Http.AjaxService({
+                url:'/user/shop/baike/recommend/type/save',
+                type:'post',
+                data:params,
+                json:false,
+            })
+                .then(function(res){
+                    callback(res);
+                });
+        },
+
+        ShopBaikeRecommendSort: function(params, callback){ // 百科商品/相关推荐 保存
+            Http.AjaxService({
+                url:'/user/shop/baike/recommend/sort/save',
+                type:'post',
+                data:params,
+                json:false,
+            })
+                .then(function(res){
+                    callback(res);
+                });
+        },
+
+        ShopBaikeRecommendRemove: function(params, callback){ // 百科商品/相关推荐 保存
+            Http.AjaxService({
+                url:'/user/shop/baike/recommend/delete',
+                type:'post',
+                data:params,
+                json:false,
+            })
+                .then(function(res){
+                    callback(res);
+                });
+        },
+
+        ShopBaikeRecommendAdd: function(params, callback){ // 百科商品/相关推荐 保存
+            Http.AjaxService({
+                url:'/user/shop/baike/recommend/add',
+                type:'post',
+                data:params,
+                json:false,
+            })
+                .then(function(res){
+                    callback(res);
+                });
+        },
+
+        ShopBaikeRecommendAddList: function(params, callback){ // 百科商品/相关推荐选择列表
+            Http.AjaxService({
+                url:'/user/shop/baike/recommend/add/list',
+                type:'get',
+                data:params,
+                json:false,
+            })
+                .then(function(res){
+                    callback(res);
+                });
+        },
+
+        ShopBaikeRecommendForm: function(params, callback){ // 百科商品/相关推荐回显
+            Http.AjaxService({
+                url:'/user/shop/baike/recommend/form',
+                type:'get',
+                data:params,
+                json:false,
+            })
+                .then(function(res){
+                    callback(res);
+                });
+        },
+
         ShopBaikeProductDelete: function(params, callback){ // 百科商品/仪器 删除
             Http.AjaxService({
                 url:'/user/shop/baike/product/delete',
@@ -41,6 +113,18 @@ var SupplierApi = {
             });
         },
 
+        ShopBaikeProductWordsValidate: function (params, callback) { // 供应商百科商品/仪器信息敏感词校验
+            Http.AjaxService({
+                url: '/user/shop/sensitive/words',
+                type: 'get',
+                data: params,
+                json: false,
+            })
+            .then(function (res) {
+                callback(res);
+            });
+        },
+
         ShopBaikeProductSave: function(params, callback){ // 供应商百科商品/仪器信息保存
             Http.AjaxService({
                 url:'/user/shop/baike/product/save',

+ 9 - 0
src/main/resources/static/js/common/serviceapi/utils.service.js

@@ -4,6 +4,15 @@
  * auther ZHJY
  */
 var PublicApi = {
+        // oss 初始化 (临时凭证获取)
+        fetchOssInitData: function(params){
+            return Http.AjaxService({
+                url:'/user/oss/token',
+                type:'get',
+                data:params,
+                json:false,
+            })
+        },
         uploadimg: function (params, callback) {//供应商添加物流上传图片
             Http.uploadImage({ url:'/tools/image/upload/multi',data:params},callback)
         },

+ 16 - 2
src/main/resources/static/js/encyclopedia/common.js

@@ -204,11 +204,25 @@ function pageScrollObserve(selector) {
         if (scrollFlag) return;
         var scrollTop = $(this).scrollTop();
 
-        if (scrollTop < $(selector).eq(0).offset().top - offset) {
+        var selectorEl = $(selector);
+
+        if(selectorEl.length === 0){
+            return;
+        }
+
+        if (scrollTop < selectorEl.eq(0).offset().top - offset) {
+            callback(null, -1);
+            return
+        }
+
+        // console.log(scrollTop, selectorEl.last().offset().top - offset)
+
+        if(scrollTop > selectorEl.last().offset().top - offset){
             callback(null, -1);
+            return
         }
 
-        $(selector).each(function (index, el) {
+        selectorEl.each(function (index, el) {
             var offsetTop = $(el).offset().top - offset;
             var height = $(el).height();
 

+ 10 - 0
src/main/resources/static/js/encyclopedia/detail.js

@@ -78,4 +78,14 @@ $(function () {
     }
     initPreviewImage()
     // toggleNavigate('.navigate', 1000, middleScreenWidth, 80);
+
+    // 视频播放
+    $('.related-video .video-control .play').on('click', function(){
+        var videoUrl = $(this).siblings('video').attr('src');
+        $('#video-popup video').attr('src', videoUrl);
+        $('#video-popup').show();
+    })
+    $('#video-popup .close').on('click', function(){
+        $(this).parents('#video-popup').hide();
+    });
 });

+ 1 - 1
src/main/resources/static/js/index.js

@@ -226,7 +226,7 @@ var homeData = new Vue({
                         }
                     });
                 } else {
-                    var swiper = new Swiper('#recommendBox', {
+                    var swiper = new Swiper('.swiper-container-floor', {
                         slidesPerView: 2,
                         slidesPerColumn: 2,
                         spaceBetween:0,

+ 80 - 0
src/main/resources/static/js/oss-upload.js

@@ -0,0 +1,80 @@
+"use strict";
+
+var client = null;
+var baseFileUrl = '';
+
+function initOssClient(callback) {
+    if (client) {
+        callback && callback();
+        return;
+    }
+
+    PublicApi.fetchOssInitData().then(function (res) {
+        if (res.code) return;
+        var _res$data = res.data,
+            accessKeyId = _res$data.accessKeyId,
+            securityToken = _res$data.securityToken,
+            bucket = _res$data.bucket,
+            accessKeySecret = _res$data.accessKeySecret;
+        var config = {
+            // 以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
+            region: 'oss-cn-shenzhen',
+            accessKeyId: accessKeyId,
+            accessKeySecret: accessKeySecret,
+            stsToken: securityToken,
+            bucket: bucket
+        };
+        client = new OSS(config);
+        baseFileUrl = "https://".concat(bucket, ".").concat(config.region, ".aliyuncs.com/");
+        setInterval(function () {
+            initOssClient();
+        }, 1000 * 60 * 60);
+        if (client) callback && callback();
+    }).catch(function (error) {
+        console.log(error);
+    });
+}
+
+function multipartUpload(options, callback) {
+    initOssClient(function () {
+        if (options.status > -1) return; // 指定上传到examplebucket的Object名称,例如exampleobject.txt。
+
+        var name = $("#ossBucket").val() + '/' + options.fileName.replace(/([^\\/]+)\.([^\\/]+)/i, function (match, $1, $2) {
+            options.uuid = options.uuid + '.' + $2;
+            return options.uuid;
+        }); // 分片上传
+
+        client.multipartUpload(name, options.file, {
+            // 获取分片上传进度、断点和返回值。
+            progress: function progress(p, cpt, res) {
+                callback.progress && callback.progress(options, {
+                    p: p,
+                    cpt: cpt,
+                    res: res
+                });
+            },
+            // 设置并发上传的分片数量。
+            parallel: 10,
+            // 设置分片大小。默认值为1 MB,最小值为100 KB。
+            partSize: 1024 * 1024,
+            // 文件类型
+            mime: options.type,
+            headers: {
+                // 指定初始化分片上传时是否覆盖同名Object。此处设置为true,表示禁止覆盖同名Object。
+                'x-oss-forbid-overwrite': 'true'
+            }
+        }).then(function (res) {
+            callback.success && callback.success(options, res, baseFileUrl + res.name);
+        }).catch(function (err) {
+            console.log(err);
+            callback.success && callback.faild(options, err);
+        });
+    });
+}
+
+// 上传
+function upload(fileList, callback) {
+    for (var i = 0; i < fileList.length; i++) {
+        multipartUpload(fileList[i], callback);
+    }
+}

+ 75 - 4
src/main/resources/static/js/supplier-center/article/article-edit.js

@@ -83,7 +83,7 @@ var articleEdit = new Vue({
                 {required: true, message: '请选择文章所属分类', trigger: 'change'}
             ]
         },
-
+        formValidate: {}
     },
     watch: {
         chooseLabels: function chooseLabels(newVal) {
@@ -114,6 +114,7 @@ var articleEdit = new Vue({
             this.formData.articleId = articleId;
             this.fetchArticleCatagory(); // 文章id就是修改文章
             this.fetchFormList();
+            this.watchFormData()
         },
         // 获取标签列表和分类列表
         fetchFormList: function fetchFormList() {
@@ -191,11 +192,72 @@ var articleEdit = new Vue({
             var _this = this;
             this.$refs.ruleForm.validate(valide=>{
                 if(!valide) return;
-                _this.save();
+                // _this.save();
+                _this.sensitiveWordsValidate(_this.formData)
             });
         },
+
+        // 监听formData
+        watchFormData: function(){
+            const self = this
+            for (const key in this.formData) {
+                self.$set(self.formValidate, key, '')
+                this.$watch('formData.' + key, function () {
+                    self.formValidate[key] = ''
+                })
+            }
+        },
+
+        // 匹配敏感词
+        matchWords: function(word, validate){
+            var list = []
+            const matcher = word.matchAll(new RegExp(validate, 'g'))
+            var done = false
+            while (!done){
+                const item = matcher.next()
+                done = item.done
+                if(item.value){
+                    list.push(item.value[0])
+                }
+            }
+            return list.join(',')
+        },
+
+        // 敏感词校验
+        sensitiveWordsValidate: function(params){
+            const whiteList = [];
+            const self = this;
+            SupplierApi.ShopBaikeProductWordsValidate({ checkPoint: 1 }, function(res){
+                let flag = true
+                for (const key in params) {
+                    if (!whiteList.includes(key)) {
+                        const target = JSON.stringify(params[key])
+                        const bool = new RegExp(res.data, 'g').test(target)
+                        if (bool) {
+                            console.log(key, true)
+                            const value = self.matchWords(target, res.data)
+                            self.formValidate[key] = value
+                            flag = false
+                        } else {
+                            console.log(key, false)
+                            self.formValidate[key] = ''
+                        }
+                    }
+                }
+                console.log(self.formValidate)
+                if(flag) return self.onReallySave(params)
+                const tip = '当前发布内容存在敏感词,已为您标记在输入框下方,请修改后,再进行保存发布,强行保存发布将会导致审核不通过!'
+                self.$confirm(tip, '提示', { confirmButtonText: '保存', cancelButtonText: '取消' }).then(function(){
+                    self.onReallySave(params)
+                }).catch(function(){
+                    console.log('修改敏感词')
+                    self.socrllToErrorWord() // 滚动到提示处
+                })
+            })
+        },
+
         // 保存接口
-        save: function save() {
+        onReallySave: function onReallySave() {
             var _this = this;
             this.formData.shopId = this.shopId;
             SupplierApi.ArticleSubmitSave(this.formData, function (res) {
@@ -222,7 +284,16 @@ var articleEdit = new Vue({
             localStorage.removeItem('articleId');
             window.open('/supplier/article/list.html', 'supplier-article-list');
             window.close();
-        }
+        },
+
+        socrllToErrorWord(){
+            this.$nextTick(() => {
+                const scrollTop = $('.mint-message').eq(0).parents('.mint-filter').offset().top
+                $('body,html').animate({
+                    scrollTop: scrollTop - $('#globalHead').height() -  60
+                }, 800)
+            })
+        },
     }
 });
 

+ 118 - 24
src/main/resources/static/js/supplier-center/encyclopedia/components.js

@@ -6,18 +6,6 @@ function createFormVideoGroup() {
     let groupId = 0
     return {
         template: fetchCompoentTemplate('form-video-group'),
-        props: {
-            action: {
-                type: String,
-                default: ''
-            },
-            data: {
-                type: Object,
-                default: function () {
-                    return {}
-                }
-            }
-        },
         data() {
             return {
                 fileList: [],
@@ -33,36 +21,142 @@ function createFormVideoGroup() {
                 return this.formList.length < 6
             }
         },
-        watch: {
+        watch:{
             formList: {
-                handler: function (nVal) {
+                deep: true,
+                handler: function(){
                     this.$emit('change', this.formList)
-                },
-                deep: true
+                }
             }
         },
         methods: {
-            onUploadSuccess(response, file, fileList) {
-                this.fileList = []
-                this.formData.videoUrl = response.data
-                console.log(this.formData.videoUrl)
+            validate() {
+                const self = this
+                const validateList = self.formList.map(function (form) {
+                    return self.$refs[form.ref][0].validate()
+                })
+                return {
+                    validator: Promise.all(validateList),
+                    formList: self.formList
+                }
             },
-            onUploadError(err, file, fileList) {
-                this.fileList = []
-                this.formData.videoUrl = ''
+
+            init(data = []){
+                const self = this
+                data.forEach(function(item, index){
+                    self.formList.push({
+                        ref: 'videoGroup' + index,
+                        groupId: index,
+                        videoTitle: item.fileTitle,
+                        videoUrl: item.fileUrl,
+                        percentage: 1,
+                        status: 1,
+                        fileName: item.fileName,
+                        ossName: item.ossName
+                    })
+                })
+            },
+
+            // 选择文件
+            onChooseFile(index){
+                const self = this
+                this.$nextTick(function () {
+                    const input = self.$refs.ossFile
+                    input[index - 1].click()
+                })
             },
+            // 文件变化
+            onFileChange(formData, $event) {
+                const self = this
+                let msg = null
+                let flag = true
+                const fileList = Array.from($event.target.files).map(function (file) {
+                    if(file.size > 1024 * 1024 * 50){
+                        CAIMEI.dialog('请上传50MB以内的视频', false);
+                        flag = false
+                    }
+                    return self.generageRowfile(file, formData.groupId)
+                })
+                if(!flag) return
+                this.onUploadFile(fileList)
+            },
+            // 文件上传成功
+            onUploadSuccess(file, response, url) {
+                file.response = response
+                file.status = 1
+                file.url = url
+                const formData = this.formList.find(function (item) {
+                    return item.groupId === file.groupId
+                })
+                formData.videoUrl = file.url
+                formData.status = file.status
+                formData.ossName = file.uuid
+                formData.fileName = file.fileName
+            },
+            // 文件上传失败
+            onUploadError(file, error) {
+                file.response = error
+                file.status = 0
+                const formData = this.formList.find(function (item) {
+                    return item.groupId === file.groupId
+                })
+                formData.status = file.status
+            },
+            // 文件上传进度条
+            onProgress(file, response){
+                const { p, cpt, res } = response
+                file.percentage = p * 100
+                file.checkpoint = cpt
+                file.response = res
+                file.status = 2
+                const formData = this.formList.find(function (item) {
+                    return item.groupId === file.groupId
+                })
+                formData.status = 2
+                formData.percentage = file.percentage
+            },
+            // 插入视频
             insertOne() {
                 groupId++
                 this.formList.push({
                     ref: 'videoGroup' + groupId,
                     groupId: groupId,
                     videoTitle: '',
-                    videoUrl: ''
+                    videoUrl: '',
+                    percentage: 0,
+                    status: -1,
+                    fileName: '',
+                    ossName: ''
                 })
             },
+            // 删除视频
             removeOne(row) {
                 const index = this.formList.findIndex(item => item.groupId === row.groupId)
                 this.formList.splice(index, 1)
+            },
+            // 上传文件对象
+            generageRowfile(file, groupId) {
+                return {
+                    groupId: groupId,
+                    uuid: uuidv4(),
+                    fileName: file.name,
+                    size: file.size,
+                    type: file.type,
+                    file: file,
+                    percentage: 0,
+                    status: -1, // 0 上次失败 1 上传成功 2 上传中 -1 待上传
+                    response: null,
+                    url: '',
+                    checkpoint: null
+                }
+            },
+            // 上传文件
+            onUploadFile(fileList){
+                upload(fileList, {
+                    success: this.onUploadSuccess,
+                    faild: this.onUploadError,
+                    progress: this.onProgress
+                })
             }
         }
     }

+ 135 - 25
src/main/resources/static/js/supplier-center/encyclopedia/instrument-edit.js

@@ -28,6 +28,14 @@ const app = new Vue({
             }
         }
 
+        var linkValidate = (rule, value, callback) => {
+            if(/[a-zA-z]+:\/\/[^\s]*/.test(value)){
+                callback()
+            }else{
+                callback('url地址不正确')
+            }
+        }
+
         return {
             productId: 0,
             commodityType: 2, // 商品类型 1: 产品  2: 仪器
@@ -84,6 +92,8 @@ const app = new Vue({
                 seoKeyword: '',
                 // 仪器链接
                 productLink: '',
+                // 视频列表
+                videoList: []
             },
             rules: {
                 // 仪器名称
@@ -95,7 +105,9 @@ const app = new Vue({
                 // 仪器图片
                 image: [{required: true, message: '请上传仪器图片', trigger: 'change'}],
                 // 产品链接
-                productLink: [{required: true, message: '产品链接不能为空', trigger: ['change', 'blur']}],
+                productLink: [
+                    {required: true, message: '产品链接不能为空', trigger: ['change', 'blur']},
+                    {validator: linkValidate, trigger: ['blur']}],
                 // 产仪器参数
                 paramList: [{required: true, validator: validateParamList, trigger: 'change'}],
                 // // 认证链接
@@ -131,7 +143,7 @@ const app = new Vue({
                 // // 常见问题
                 // questionList: [{required: true, validator: validateQuestionList, trigger: 'change'}],
                 // // 仪器类别
-                // typeId: [{required: true, message: '请选择仪器类别', trigger: 'change'}],
+                typeId: [{required: true, message: '请选择仪器类别', trigger: 'change'}],
                 // // 仪器状态
                 // status: [{required: true, message: '请选择仪器状态', trigger: 'change'}],
             },
@@ -154,7 +166,10 @@ const app = new Vue({
             marketTimeType: 'year',
             nmpaTimeType: 'year',
             // 空缺数量
-            emptyNum: ''
+            emptyNum: '',
+            formValidate: {},
+
+            videoGroupList: [] // 视频列表
         }
     },
     computed: {
@@ -181,10 +196,13 @@ const app = new Vue({
         this.getTypeList();
         this.watchArrayStatus(['paramList', 'questionList', 'authImageList']);
         this.getProductFormData();
+        this.watchFormData()
     },
 
     methods: {
-
+        onVideoGroupChange: function(data){
+            this.formValidate.videoList = ''
+        },
         // 返回文章列表页面
         handleBack: function handleBack() {
             localStorage.removeItem('target-name');
@@ -193,23 +211,27 @@ const app = new Vue({
         },
         // 保存事件
         handleSave() {
-            this.$refs.ruleForm.validate(valide => {
-                console.log(valide);
-                if (!valide) {
-                    this.socrllToErrorInput()
-                    return;
-                }
-                this.emptyNum = this.validateEmptyParams(this.formData)
-                if( this.emptyNum> 0 && this.formData.status === 1){
-                    this.$confirm('您还剩余' + this.emptyNum + '项未完善,将会导致用户对您产品/仪器的认识度不够,确认是否提交?', '提示', {
-                        confirmButtonText: '是',
-                        cancelButtonText: '否'
-                    }).then(() => {
-                        this.saveFormData()
-                    })
-                } else{
-                    this.saveFormData()
-                }
+            const self = this
+            this.$nextTick(function () {
+                const formVideoGroup = self.$refs.formVideoGroup.validate()
+                self.videoGroupList = formVideoGroup.formList;
+                self.formData.videoList = formVideoGroup.formList;
+                console.log(self.videoGroupList)
+                Promise.all([formVideoGroup.validator, self.$refs.ruleForm.validate()]).then(function () {
+                    self.emptyNum = self.validateEmptyParams(self.formData)
+                    if (self.emptyNum > 0 && self.formData.status === 1) {
+                        self.$confirm('您还剩余' + self.emptyNum + '项未完善,将会导致用户对您产品/仪器的认识度不够,确认是否提交?', '提示', {
+                            confirmButtonText: '是',
+                            cancelButtonText: '否'
+                        }).then(() => {
+                            self.saveFormData()
+                        })
+                    } else {
+                        self.saveFormData()
+                    }
+                }).catch(function () {
+                    self.socrllToErrorInput()
+                })
             })
         },
 
@@ -263,10 +285,7 @@ const app = new Vue({
                 commodityType: this.commodityType,
                 paramList: JSON.stringify(this.paramList),
                 questionList: JSON.stringify(this.questionList),
-                // marketTime: dateFormat(this.formData.marketTime, 'yyyy-MM-dd'),
-                // nmpaTime: dateFormat(this.formData.nmpaTime, 'yyyy-MM-dd'),
                 authImageList: this.authImageList.map(image => (image.response ? image.response.data : image.url)),
-                // displayImageList: this.displayImageList.map(image => (image.response ? image.response.data : image.url))
             };
 
             /* 处理时间 */
@@ -283,6 +302,84 @@ const app = new Vue({
 
             params.emptyNum = this.emptyNum;
 
+            const videoList = this.videoGroupList.map(function (video) {
+                return {fileTitle: video.videoTitle, fileName: video.fileName, ossName: video.ossName}
+            })
+
+            params.videoList = JSON.stringify(videoList)
+
+            this.sensitiveWordsValidate(params)
+
+        },
+
+        // 监听formData
+        watchFormData: function(){
+            const self = this
+            for (const key in this.formData) {
+                self.$set(self.formValidate, key, '')
+                this.$watch('formData.' + key, function () {
+                    self.formValidate[key] = ''
+                })
+            }
+        },
+
+        // 匹配敏感词
+        matchWords: function(word, validate){
+            var list = []
+            const matcher = word.matchAll(new RegExp(validate, 'g'))
+            var done = false
+            while (!done){
+                const item = matcher.next()
+                done = item.done
+                if(item.value){
+                    list.push(item.value[0])
+                }
+            }
+            return list.join(',')
+        },
+
+        // 敏感词校验
+        sensitiveWordsValidate: function(params){
+            console.log(params);
+            const whiteList = [];
+            const self = this;
+            SupplierApi.ShopBaikeProductWordsValidate({ checkPoint: 2 }, function(res){
+                let flag = true
+                if(res.data.startsWith('|')){
+                    res.data = res.data.substring(1)
+                }
+                if(res.data.endsWith('|')){
+                    res.data = res.data.substring(0, res.data.length - 1)
+                }
+                for (const key in params) {
+                    if (!whiteList.includes(key)) {
+                        const target = JSON.stringify(params[key])
+                        const bool = new RegExp(res.data, 'g').test(target)
+                        if (bool) {
+                            console.log(key, true)
+                            const value = self.matchWords(target, res.data)
+                            self.formValidate[key] = value
+                            flag = false
+                        } else {
+                            console.log(key, false)
+                            self.formValidate[key] = ''
+                        }
+                    }
+                }
+                console.log(self.formValidate)
+                if(flag) return self.onReallySave(params)
+                const tip = '当前发布内容存在敏感词,已为您标记在输入框下方,请修改后,再进行保存发布,强行保存发布将会导致审核不通过!'
+                self.$confirm(tip, '提示', { confirmButtonText: '保存', cancelButtonText: '取消' }).then(function(){
+                    self.onReallySave(params)
+                }).catch(function(){
+                    console.log('修改敏感词')
+                    self.socrllToErrorWord() // 滚动到提示处
+                })
+            })
+        },
+
+        onReallySave(params){
+            const that = this
             console.log(params);
             SupplierApi.ShopBaikeProductSave(params, function (res) {
                 if (res.code === 0) {
@@ -309,6 +406,7 @@ const app = new Vue({
         },
 
 		initFormData: function(formData){
+            const self = this
 			console.log(formData);
 			// 初始化this.formData
             for (var key in this.formData) {
@@ -335,6 +433,9 @@ const app = new Vue({
             // 处理图片
             this.authImageList = formData.authImageList.map(image => ({ url: image, name: 'authImage'}))
             // this.displayImageList = formData.displayImageList.map(image => ({ url: image, name: 'authImage'}))
+            this.$nextTick(function(){
+                self.$refs.formVideoGroup.init(formData.videoList)
+            })
 		},
 
 
@@ -528,10 +629,19 @@ const app = new Vue({
             })
         },
 
+        socrllToErrorWord(){
+            this.$nextTick(() => {
+                const scrollTop = $('.mint-message').eq(0).parents('.mint-filter').offset().top
+                $('body,html').animate({
+                    scrollTop: scrollTop - $('#globalHead').height() -  60
+                }, 800)
+            })
+        },
+
         // 滚动到必填位置
         socrllToErrorInput(){
             this.$nextTick(() => {
-                const scrollTop = $('.el-form-item__error').eq(0).parent().siblings('.el-form-item__label').offset().top
+                const scrollTop = $('.el-form-item__error').eq(0).parents('.el-form-item__content').offset().top
                 $('body,html').animate({
                     scrollTop: scrollTop - $('#globalHead').height() -  40
                 }, 800)

+ 2 - 2
src/main/resources/static/js/supplier-center/encyclopedia/instrument-list.js

@@ -119,8 +119,8 @@ var productList = new Vue({
 
         // 配置推荐信息
         handleEditRecommend(product){
-            localStorage.setItem('productId', product.productId);
-            window.open('/supplier/encyclopedia/recommend-edit.html');
+            localStorage.setItem('target-name', this.name);
+            window.open('/supplier/encyclopedia/recommend-edit.html?type=2&id=' + product.productId + '&name=' + encodeURIComponent(product.name));
         },
 
         //跳转编辑页面

+ 138 - 25
src/main/resources/static/js/supplier-center/encyclopedia/product-edit.js

@@ -1,6 +1,7 @@
 const Editor = window.createEditorComponent()
 const FormVideoGroup = window.createFormVideoGroup()
 
+Vue.config.devtools = true
 
 const app = new Vue({
     components: {
@@ -29,6 +30,14 @@ const app = new Vue({
             }
         }
 
+        var linkValidate = (rule, value, callback) => {
+            if(/[a-zA-z]+:\/\/[^\s]*/.test(value)){
+                callback()
+            }else{
+                callback('url地址不正确')
+            }
+        }
+
         return {
 
             productId: 0,
@@ -82,6 +91,8 @@ const app = new Vue({
                 seoKeyword: '',
                 // 产品链接
                 productLink: '',
+
+                videoList: []
             },
             rules: {
                 // 产品名称
@@ -95,7 +106,9 @@ const app = new Vue({
                 // 产品参数
                 paramList: [{required: true, validator: validateParamList, trigger: 'change'}],
                 // 产品链接
-                productLink: [{required: true, message: '产品链接不能为空', trigger: ['change', 'blur']}],
+                productLink: [
+                    {required: true, message: '产品链接不能为空', trigger: ['change', 'blur']},
+                    {validator: linkValidate, trigger: ['blur']}],
                 // // 产品优点
                 // advantage: [{required: true, message: '产品优点不能为空', trigger: 'change'}],
                 // // 产品缺点
@@ -150,7 +163,10 @@ const app = new Vue({
             marketTimeType: 'year',
             nmpaTimeType: 'year',
             // 空缺数量
-            emptyNum: ''
+            emptyNum: '',
+            formValidate: {},
+
+            videoGroupList: []
         }
     },
     computed: {
@@ -177,11 +193,13 @@ const app = new Vue({
         this.getTypeList();
         this.watchArrayStatus(['paramList', 'questionList', 'authImageList']);
         this.getProductFormData();
-
+        this.watchFormData()
     },
 
     methods: {
-
+        onVideoGroupChange: function(data){
+            this.formValidate.videoList = ''
+        },
         // 返回文章列表页面
         handleBack: function handleBack() {
             localStorage.removeItem('target-name');
@@ -190,23 +208,27 @@ const app = new Vue({
         },
         // 保存事件
         handleSave() {
-            this.$refs.ruleForm.validate(valide => {
-                console.log(valide);
-                if (!valide) {
-                    this.socrllToErrorInput()
-                    return
-                }
-                this.emptyNum = this.validateEmptyParams(this.formData)
-                if( this.emptyNum> 0 && this.formData.status === 1){
-                    this.$confirm('您还剩余' + this.emptyNum + '项未完善,将会导致用户对您产品/仪器的认识度不够,确认是否提交?', '提示', {
-                        confirmButtonText: '是',
-                        cancelButtonText: '否'
-                    }).then(() => {
-                        this.saveFormData()
-                    })
-                } else{
-                    this.saveFormData()
-                }
+            const self = this
+            this.$nextTick(function () {
+                const formVideoGroup = self.$refs.formVideoGroup.validate()
+                self.videoGroupList = formVideoGroup.formList
+                self.formData.videoList = formVideoGroup.formList;
+                console.log(self.videoGroupList)
+                Promise.all([formVideoGroup.validator, self.$refs.ruleForm.validate()]).then(function () {
+                    self.emptyNum = self.validateEmptyParams(self.formData)
+                    if (self.emptyNum > 0 && self.formData.status === 1) {
+                        self.$confirm('您还剩余' + self.emptyNum + '项未完善,将会导致用户对您产品/仪器的认识度不够,确认是否提交?', '提示', {
+                            confirmButtonText: '是',
+                            cancelButtonText: '否'
+                        }).then(() => {
+                            self.saveFormData()
+                        })
+                    } else {
+                        self.saveFormData()
+                    }
+                }).catch(function () {
+                    self.socrllToErrorInput()
+                })
             })
         },
         // 校验空参数个数
@@ -257,7 +279,6 @@ const app = new Vue({
         // 保存表单数据
         saveFormData() {
             const that = this;
-
             const params = {
                 ...this.formData,
                 shopId: this.shopId,
@@ -273,19 +294,97 @@ const app = new Vue({
             console.log(params);
 
             /* 处理时间 */
-            if(this.formData.marketTime){
+            if (this.formData.marketTime) {
                 params.marketTime = this.sliceDateStr(this.formData.marketTime, this.marketTimeType)
             }
 
             /* 处理时间 */
-            if(this.formData.nmpaTime){
+            if (this.formData.nmpaTime) {
                 params.nmpaTime = this.sliceDateStr(this.formData.nmpaTime, this.nmpaTimeType)
             }
 
             if (this.productId) params.productId = this.productId;
             params.emptyNum = this.emptyNum;
 
+            const videoList = this.videoGroupList.map(function (video) {
+                return {fileTitle: video.videoTitle, fileName: video.fileName, ossName: video.ossName}
+            })
+
+            params.videoList = JSON.stringify(videoList)
+
+            // this.onReallySave(params)
+            this.sensitiveWordsValidate(params)
+        },
+
+        // 监听formData
+        watchFormData: function(){
+            const self = this
+            for (const key in this.formData) {
+                self.$set(self.formValidate, key, '')
+                this.$watch('formData.' + key, function () {
+                    self.formValidate[key] = ''
+                })
+            }
+        },
+
+        // 匹配敏感词
+        matchWords: function(word, validate){
+            var list = []
+            const matcher = word.matchAll(new RegExp(validate, 'g'))
+            var done = false
+            while (!done){
+                const item = matcher.next()
+                done = item.done
+                if(item.value){
+                    list.push(item.value[0])
+                }
+            }
+            return list.join(',')
+        },
+
+        // 敏感词校验
+        sensitiveWordsValidate: function(params){
             console.log(params);
+            const whiteList = [];
+            const self = this;
+            SupplierApi.ShopBaikeProductWordsValidate({ checkPoint: 2 }, function(res){
+                let flag = true
+                if(res.data.startsWith('|')){
+                    res.data = res.data.substring(1)
+                }
+                if(res.data.endsWith('|')){
+                    res.data = res.data.substring(0, res.data.length - 1)
+                }
+                for (const key in params) {
+                    if (!whiteList.includes(key)) {
+                        const target = JSON.stringify(params[key])
+                        const bool = new RegExp(res.data, 'g').test(target)
+                        if (bool) {
+                            console.log(key, true)
+                            const value = self.matchWords(target, res.data)
+                            self.formValidate[key] = value
+                            flag = false
+                        } else {
+                            console.log(key, false)
+                            self.formValidate[key] = ''
+                        }
+                    }
+                }
+                console.log(self.formValidate)
+                if(flag) return self.onReallySave(params)
+                const tip = '当前发布内容存在敏感词,已为您标记在输入框下方,请修改后,再进行保存发布,强行保存发布将会导致审核不通过!'
+                self.$confirm(tip, '提示', { confirmButtonText: '保存', cancelButtonText: '取消' }).then(function(){
+                    self.onReallySave(params)
+                }).catch(function(){
+                    console.log('修改敏感词')
+                    self.socrllToErrorWord() // 滚动到提示处
+                })
+            })
+        },
+
+        // 保存表单到数据库
+        onReallySave(params){
+            const that = this;
             SupplierApi.ShopBaikeProductSave(params, function (res) {
                 if (res.code === 0) {
                     CAIMEI.dialog('保存成功', false);
@@ -311,6 +410,7 @@ const app = new Vue({
         },
 
 		initFormData: function(formData){
+            const self = this
 			console.log(formData);
 			// 初始化this.formData
             for (var key in this.formData) {
@@ -337,6 +437,10 @@ const app = new Vue({
             // 处理图片
             this.authImageList = formData.authImageList.map(image => ({ url: image, name: 'authImage'}))
             this.displayImageList = formData.displayImageList.map(image => ({ url: image, name: 'authImage'}))
+
+            this.$nextTick(function(){
+                self.$refs.formVideoGroup.init(formData.videoList)
+            })
 		},
 
 
@@ -525,10 +629,19 @@ const app = new Vue({
             })
         },
 
+        socrllToErrorWord(){
+            this.$nextTick(() => {
+                const scrollTop = $('.mint-message').eq(0).parents('.mint-filter').offset().top
+                $('body,html').animate({
+                    scrollTop: scrollTop - $('#globalHead').height() -  60
+                }, 800)
+            })
+        },
+
         // 滚动到必填位置
         socrllToErrorInput(){
             this.$nextTick(() => {
-                const scrollTop = $('.el-form-item__error').eq(0).parent().siblings('.el-form-item__label').offset().top
+                const scrollTop = $('.el-form-item__error').eq(0).parents('.el-form-item__content').offset().top
                 $('body,html').animate({
                     scrollTop: scrollTop - $('#globalHead').height() -  40
                 }, 800)

+ 2 - 2
src/main/resources/static/js/supplier-center/encyclopedia/product-list.js

@@ -117,8 +117,8 @@ var productList = new Vue({
 
         // 配置推荐信息
         handleEditRecommend(product){
-            localStorage.setItem('productId', product.productId);
-            window.open('/supplier/encyclopedia/recommend-edit.html');
+            localStorage.setItem('target-name', this.name);
+            window.open('/supplier/encyclopedia/recommend-edit.html?type=1&id=' + product.productId + '&name=' + encodeURIComponent(product.name));
         },
 
         //跳转编辑页面

+ 194 - 5
src/main/resources/static/js/supplier-center/encyclopedia/recommend-edit.js

@@ -1,17 +1,206 @@
+function getQueryObject(url) {
+    url = url == null ? window.location.href : url
+    var search = url.substring(url.lastIndexOf('?') + 1)
+    var obj = {}
+    var reg = /([^?&=]+)=([^?&=]*)/g
+    search.replace(reg, (rs, $1, $2) => {
+        var name = decodeURIComponent($1)
+        var val = decodeURIComponent($2)
+        val = String(val)
+        obj[name] = val
+        return rs
+    })
+    return obj
+}
+
 var RecommendEdit = new Vue({
     el: '#recommendEdit',
     data: {
+        query:{},
+        listQuery: {
+            productId: '',
+            typeId: '',
+            name: '',
+            pageNum: '',
+            pageSize: ''
+        },
+        pageInput: '',
+        list: [],
+        listRecord: 0,
+        pageTotal: 0,
         recommendList: [],
-        typeList: [{
-            typeName: '胚胎层',
-            typeId: 1
-        }],
-        recommendType: 0,
+        typeList: [],
+        recommendType: '',
+        selectedList: [],
+
+        searchFlag: false
+    },
+    created(){
+        this.query = getQueryObject()
+        this.fetchProductTypeList()
+        this.fetchRecommendForm()
+        // this.fetchRecommendAddList()
+    },
+    computed: {
+        pageName(){
+            return this.query.type === 1 ? '产品' : '仪器'
+        },
+        pageTotal: function pageTotal() {
+            var total = Math.ceil(this.listRecord / this.listQuery.pageSize);
+            return total > 0 ? total : 1;
+        },
+        showPageBtn: function showPageBtn() {
+            var total = Math.ceil(this.listRecord / this.listQuery.pageSize);
+            total = total > 0 ? total : 1;
+            var index = this.listQuery.pageNum,
+                arr = [];
+
+            if (total <= 6) {
+                for (var i = 1; i <= total; i++) {
+                    arr.push(i);
+                }
+                return arr;
+            }
+
+            if (index <= 3) return [1, 2, 3, 4, 5, 0, total];
+            if (index >= total - 2) return [1, 0, total - 4, total - 3, total - 2, total - 1, total];
+            return [1, 0, index - 2, index - 1, index, index + 1, index + 2, 0, total];
+        }
     },
     methods: {
+        // 选择推荐产品
+        onCheckboxChange(row){
+            this.selectedList =  this.list.filter(function(item){ return item.checked })
+            if(this.selectedList.length > 15){
+                row.ischecked = false;
+            }
+        },
+        // 获取列表
+        fetchList(){
+            if(!this.listQuery.typeId && !this.listQuery.name) {
+                CAIMEI.dialog('请选择产品分类', false);
+                return;
+            }
+            this.searchFlag = true
+            this.listQuery.pageNum = 1;
+            this.list = [];
+            this.fetchRecommendAddList();
+        },
+        // 获取相关推荐可选择列表
+        fetchRecommendAddList(){
+            var self = this;
+            this.listQuery.productId = this.query.id;
+            SupplierApi.ShopBaikeRecommendAddList(this.listQuery, function(res){
+                self.listRecord = res.data.total;
+                self.list = self.formatRecommendAddList(res.data.list);
+                if(self.searchFlag && res.data.total === 0){
+                    CAIMEI.dialog('暂无相关产品', false);
+                }
+                self.searchFlag = false
+            })
+        },
+        // 格式化相关推荐可选择列表
+        formatRecommendAddList(list = []){
+            list = list ? list : [];
+            return list.map(function(item){
+                item.checked = false;
+                item.sort = 0;
+                return item;
+            })
+        },
+        // 获取已推荐列表 回显数据
+        fetchRecommendForm(){
+            var self = this;
+            SupplierApi.ShopBaikeRecommendForm({ productId: this.query.id }, function(res){
+                if (self.recommendType === '') {
+                    self.recommendType = res.data.recommendType;
+                }
+                if (self.recommendType === 1) {
+                    self.recommendList = res.data.manualRecommendList;
+                } else {
+                    self.recommendList = res.data.autoRecommendList;
+                }
+            })
+        },
+        // 保存勾选数据
+        saveSelectRecommend() {
+            var self = this;
+            var list = this.selectedList.map(function (item) {
+                return {
+                    recommendProductId: item.productId,
+                    sort: item.sort
+                };
+            });
+            SupplierApi.ShopBaikeRecommendAdd({baikeRecommendList: JSON.stringify(list), productId: this.query.id}, function (res) {
+                CAIMEI.dialog('保存成功', false);
+                self.fetchRecommendForm();
+                self.fetchRecommendAddList();
+                self.selectedList = []
+            })
+        },
+        // 获取产品分类
+        fetchProductTypeList(){
+            var self = this;
+            SupplierApi.ShopBaikeProductTypeList({ commodityType: this.query.type }, function(res){
+                self.typeList = res.data;
+            })
+        },
+
+        // 类型切换
+        recommendTypeChange(){
+            var self = this;
+            SupplierApi.ShopBaikeRecommendTypeChange({productId: this.query.id, recommendType: this.recommendType}, function (res) {
+                CAIMEI.dialog('保存成功', false);
+                self.fetchRecommendForm();
+            })
+        },
+
+        onRemove(row){
+            var self = this;
+            self.$confirm('确认移除该条推荐?', '提示', {
+                confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
+            }).then(function () {
+                SupplierApi.ShopBaikeRecommendRemove({recommendId: row.recommendId}, function (res) {
+                    CAIMEI.dialog('移除成功', false);
+                    self.fetchRecommendForm();
+                    self.fetchRecommendAddList();
+                })
+            }).catch(function () {
+                CAIMEI.dialog('已取消操作', false);
+            })
+        },
+
+        onSortChange(row){
+            var self = this;
+            SupplierApi.ShopBaikeRecommendSort({recommendId: row.recommendId, sort: row.sort}, function (res) {
+                self.fetchRecommendForm();
+            })
+        },
+
+        onRecommendTypeChange(){
+            this.fetchRecommendForm()
+            this.listQuery.name = '';
+            this.listQuery.typeId = '';
+            this.list = []
+        },
+
+        // 页码跳转
+        toPagination(){
+
+        },
+        // 页码切换
+        checkNum(){},
+        // 取消
         handleBack() {
+            var targetName = localStorage.getItem('target-name')
+            localStorage.removeItem('target-name');
+            window.open('/supplier/encyclopedia/product-list.html', targetName);
+            window.close();
         },
+        // 保存
         handleSave() {
+            if(this.recommendType === 1) this.saveSelectRecommend()
+            this.recommendTypeChange()
         }
     }
 })

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 3 - 0
src/main/resources/static/lib/aliyun-oss-sdk-6.17.1.min.js


+ 1 - 0
src/main/resources/static/lib/uuidv4.min.js

@@ -0,0 +1 @@
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).uuidv4=e()}(this,(function(){"use strict";var t,e=new Uint8Array(16);function o(){if(!t&&!(t="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return t(e)}var n=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;function r(t){return"string"==typeof t&&n.test(t)}for(var i=[],u=0;u<256;++u)i.push((u+256).toString(16).substr(1));return function(t,e,n){var u=(t=t||{}).random||(t.rng||o)();if(u[6]=15&u[6]|64,u[8]=63&u[8]|128,e){n=n||0;for(var f=0;f<16;++f)e[n+f]=u[f];return e}return function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,o=(i[t[e+0]]+i[t[e+1]]+i[t[e+2]]+i[t[e+3]]+"-"+i[t[e+4]]+i[t[e+5]]+"-"+i[t[e+6]]+i[t[e+7]]+"-"+i[t[e+8]]+i[t[e+9]]+"-"+i[t[e+10]]+i[t[e+11]]+i[t[e+12]]+i[t[e+13]]+i[t[e+14]]+i[t[e+15]]).toLowerCase();if(!r(o))throw TypeError("Stringified UUID is invalid");return o}(u)}}));

+ 2 - 4
src/main/resources/templates/activity/attestation.html

@@ -5,9 +5,7 @@
     <title>采美365网-认证通-一款专业的正品认证SaaS软件系统</title>
     <template th:replace="components/head-link"></template>
     <link th:href="@{/css/activity/attestation.css(v=${version})}" rel="stylesheet" type="text/css">
-    <link type="text/css" rel="stylesheet" href="/lib/swiper.min.css">
     <template th:replace="components/analysis"></template>
-    <script charset="utf-8" type="text/javascript" src="/lib/swiper.min.js"></script>
 </head>
 <body>
 <input type="hidden" th:value="${zplmDomain}" id="zplmDomain">
@@ -15,8 +13,8 @@
 <template th:replace="components/header"></template>
 
 <!-- 商品列表 -->
-<div id="attestation">
-    <!-- banner   -->
+<div id="attestation" >
+<!-- banner   -->
     <div class="section_banner">
         <h1>认证通</h1>
         <p>一款专业的正品认证SaaS软件系统</p>

+ 15 - 113
src/main/resources/templates/encyclopedia/instrument-detail.html

@@ -41,35 +41,16 @@
     </section>
 
     <!-- 相关视频 -->
-    <section class="section related-video">
+    <section class="section related-video" th:if="*{videoList.size() > 0}">
         <div class="title">
             <h2>相关视频</h2>
             <div class="line"></div>
         </div>
         <div class="content">
-            <div class="video-control">
+            <div class="video-control" th:each="video: *{videoList}">
                 <span class="play"></span>
-                <video></video>
-            </div>
-            <div class="video-control">
-                <span class="play"></span>
-                <video></video>
-            </div>
-            <div class="video-control">
-                <span class="play"></span>
-                <video></video>
-            </div>
-            <div class="video-control">
-                <span class="play"></span>
-                <video></video>
-            </div>
-            <div class="video-control">
-                <span class="play"></span>
-                <video></video>
-            </div>
-            <div class="video-control">
-                <span class="play"></span>
-                <video></video>
+                <div class="video-title" th:text="${video.fileTitle}"></div>
+                <video th:src="${video.fileUrl}"></video>
             </div>
         </div>
     </section>
@@ -133,7 +114,7 @@
             <h2>仪器档案</h2>
             <div class="line"></div>
         </div>
-        <table class="content">
+        <table class="content pc">
             <tr>
                 <td class="th">品牌</td>
                 <td class="td" th:text="*{brand}"></td>
@@ -155,8 +136,7 @@
                 </td>
             </tr>
         </table>
-        <!--
-        <div class="content">
+        <div class="content mobile">
             <div class="tr">
                 <div class="group">
                     <div class="th">品牌</div>
@@ -188,7 +168,6 @@
                 </div>
             </div>
         </div>
-        -->
     </section>
     <!-- 仪器认证 -->
     <section class="section approve" th:unless="*{#arrays.isEmpty(authImageList)}">
@@ -263,7 +242,7 @@
 
     <!-- 日期 && 浏览量 -->
     <section class="section publish-info">
-        <div class="publish-date"><span>日   期:</span><span>2021-10-27 17:23</span></div>
+        <div class="publish-date"><span>日   期:</span><span th:text="*{#dates.format(publishTime, 'yyyy-MM-dd HH:mm')}"></span></div>
         <div class="review"><span>浏览量:</span><span th:text="*{pv}"></span></div>
     </section>
 
@@ -286,98 +265,21 @@
     </section>
 
     <!--  猜你喜欢 -->
-    <section class="section recommend">
+    <section class="section recommend" th:if="*{(null != recommendList) && (recommendList.size() > 0)}">
         <div class="title">
             <h2>猜你喜欢</h2>
             <!-- <div class="line"></div> -->
         </div>
         <div class="content">
-            <a class="recommend-section">
-                <div class="cover">
-                    <img src="https://picsum.photos/400/400"/>
-                </div>
-                <div class="content">
-                    <h3>MD果酸</h3>
-                    <div class="title">MD果酸(ceuticals™ )源自英国,由多位科学家及皮肤科医生共同研创;通过使用不同比</div>
-                    <div class="question">
-                        <p>MD果酸为什么不会刺激皮肤?</p>
-                        <p>MD果酸对于暗疮性皮肤能达到什么效果?</p>
-                        <p>MD果酸是否具有腐蚀性?</p>
-                    </div>
-                    <div class="tag-list">
-                        <span class="tag">常见问题</span>
-                        <span class="tag">效果展示</span>
-                        <span class="tag">技术原理</span>
-                        <span class="tag">术前术后</span>
-                    </div>
-                    <div class="dashed-line"></div>
-                    <footer class="footer">
-                        <time>日期:<span>2022-06-01   16:52</span></time>
-                        <span>浏览量:<span>999</span></span>
-                    </footer>
-                </div>
-            </a>
-            <a class="recommend-section">
-                <div class="cover">
-                    <img src="https://picsum.photos/400/400"/>
-                </div>
-                <div class="content">
-                    <h3>MD果酸</h3>
-                    <div class="title">MD果酸(ceuticals™ )源自英国,由多位科学家及皮肤科医生共同研创;通过使用不同比</div>
-                    <div class="question">
-                        <p>MD果酸为什么不会刺激皮肤?</p>
-                        <p>MD果酸对于暗疮性皮肤能达到什么效果?</p>
-                        <p>MD果酸是否具有腐蚀性?</p>
-                    </div>
-                    <div class="tag-list">
-                        <span class="tag">常见问题</span>
-                        <span class="tag">效果展示</span>
-                        <span class="tag">技术原理</span>
-                        <span class="tag">术前术后</span>
-                    </div>
-                    <div class="dashed-line"></div>
-                    <footer class="footer">
-                        <time>日期:<span>2022-06-01   16:52</span></time>
-                        <span>浏览量:<span>999</span></span>
-                    </footer>
-                </div>
-            </a>
-            <a class="recommend-section">
-                <div class="cover">
-                    <img src="https://picsum.photos/400/400"/>
-                </div>
-                <div class="content">
-                    <h3>MD果酸</h3>
-                    <div class="title">MD果酸(ceuticals™ )源自英国,由多位科学家及皮肤科医生共同研创;通过使用不同比</div>
-                    <div class="question">
-                        <p>MD果酸为什么不会刺激皮肤?</p>
-                        <p>MD果酸对于暗疮性皮肤能达到什么效果?</p>
-                        <p>MD果酸是否具有腐蚀性?</p>
-                    </div>
-                    <div class="tag-list">
-                        <span class="tag">常见问题</span>
-                        <span class="tag">效果展示</span>
-                        <span class="tag">技术原理</span>
-                        <span class="tag">术前术后</span>
-                    </div>
-                    <div class="dashed-line"></div>
-                    <footer class="footer">
-                        <time>日期:<span>2022-06-01   16:52</span></time>
-                        <span>浏览量:<span>999</span></span>
-                    </footer>
-                </div>
-            </a>
-            <a class="recommend-section">
+            <a class="recommend-section" th:href="${ 'product-' +  recommend.productId + '.html'} " th:each="recommend,stat : *{recommendList}">
                 <div class="cover">
-                    <img src="https://picsum.photos/400/400"/>
+                    <img th:src="${recommend.image}" th:alt="${recommend.name}"/>
                 </div>
                 <div class="content">
-                    <h3>MD果酸</h3>
-                    <div class="title">MD果酸(ceuticals™ )源自英国,由多位科学家及皮肤科医生共同研创;通过使用不同比</div>
+                    <h3 th:text="${recommend.name}"></h3>
+                    <div class="title" th:text="${recommend.discription}"></div>
                     <div class="question">
-                        <p>MD果酸为什么不会刺激皮肤?</p>
-                        <p>MD果酸对于暗疮性皮肤能达到什么效果?</p>
-                        <p>MD果酸是否具有腐蚀性?</p>
+                        <p th:each="question : ${recommend.questionList}" th:text="${question}"></p>
                     </div>
                     <div class="tag-list">
                         <span class="tag">常见问题</span>
@@ -387,8 +289,8 @@
                     </div>
                     <div class="dashed-line"></div>
                     <footer class="footer">
-                        <time>日期:<span>2022-06-01   16:52</span></time>
-                        <span>浏览量:<span>999</span></span>
+                        <time>日期:<span th:text="${#dates.format(recommend.publishTime, 'yyyy-MM-dd HH:mm:ss') }"></span></time>
+                        <span>浏览量:<span th:text="${recommend.pv lt 10000 ? recommend.pv : '9999+'}"></span></span>
                     </footer>
                 </div>
             </a>

+ 16 - 114
src/main/resources/templates/encyclopedia/product-detail.html

@@ -40,35 +40,16 @@
     </section>
 
     <!-- 相关视频 -->
-    <section class="section related-video">
+    <section class="section related-video" th:if="*{videoList.size() > 0}">
         <div class="title">
             <h2>相关视频</h2>
             <div class="line"></div>
         </div>
         <div class="content">
-            <div class="video-control">
+            <div class="video-control" th:each="video: *{videoList}">
                 <span class="play"></span>
-                <video></video>
-            </div>
-            <div class="video-control">
-                <span class="play"></span>
-                <video></video>
-            </div>
-            <div class="video-control">
-                <span class="play"></span>
-                <video></video>
-            </div>
-            <div class="video-control">
-                <span class="play"></span>
-                <video></video>
-            </div>
-            <div class="video-control">
-                <span class="play"></span>
-                <video></video>
-            </div>
-            <div class="video-control">
-                <span class="play"></span>
-                <video></video>
+                <div class="video-title" th:text="${video.fileTitle}"></div>
+                <video th:src="${video.fileUrl}"></video>
             </div>
         </div>
     </section>
@@ -119,7 +100,7 @@
             <h2>产品档案</h2>
             <div class="line"></div>
         </div>
-        <table class="content">
+        <table class="content pc">
             <tr>
                 <td class="th">品牌</td>
                 <td class="td" th:text="*{brand}"></td>
@@ -141,8 +122,7 @@
                 </td>
             </tr>
         </table>
-        <!--
-        <div class="content">
+        <div class="content mobile">
             <div class="tr">
                 <div class="group">
                     <div class="th">品牌</div>
@@ -174,7 +154,6 @@
                 </div>
             </div>
         </div>
-        -->
     </section>
     <!-- 产品认证 -->
     <section class="section approve" th:unless="*{#arrays.isEmpty(authImageList)}">
@@ -246,7 +225,7 @@
 
     <!-- 日期 && 浏览量 -->
     <section class="section publish-info">
-        <div class="publish-date"><span>日   期:</span><span>2021-10-27 17:23</span></div>
+        <div class="publish-date"><span>日   期:</span><span th:text="*{#dates.format(publishTime, 'yyyy-MM-dd HH:mm')}"></span></div>
         <div class="review"><span>浏览量:</span><span th:text="*{pv}"></span></div>
     </section>
 
@@ -269,98 +248,21 @@
     </section>
 
     <!--  猜你喜欢 -->
-    <section class="section recommend">
+    <section class="section recommend" th:if="*{(null != recommendList) && (recommendList.size() > 0)}">
         <div class="title">
             <h2>猜你喜欢</h2>
             <!-- <div class="line"></div> -->
         </div>
         <div class="content">
-            <a class="recommend-section">
-                <div class="cover">
-                    <img src="https://picsum.photos/400/400"/>
-                </div>
-                <div class="content">
-                    <h3>MD果酸</h3>
-                    <div class="title">MD果酸(ceuticals™ )源自英国,由多位科学家及皮肤科医生共同研创;通过使用不同比</div>
-                    <div class="question">
-                        <p>MD果酸为什么不会刺激皮肤?</p>
-                        <p>MD果酸对于暗疮性皮肤能达到什么效果?</p>
-                        <p>MD果酸是否具有腐蚀性?</p>
-                    </div>
-                    <div class="tag-list">
-                        <span class="tag">常见问题</span>
-                        <span class="tag">效果展示</span>
-                        <span class="tag">技术原理</span>
-                        <span class="tag">术前术后</span>
-                    </div>
-                    <div class="dashed-line"></div>
-                    <footer class="footer">
-                        <time>日期:<span>2022-06-01   16:52</span></time>
-                        <span>浏览量:<span>999</span></span>
-                    </footer>
-                </div>
-            </a>
-            <a class="recommend-section">
-                <div class="cover">
-                    <img src="https://picsum.photos/400/400"/>
-                </div>
-                <div class="content">
-                    <h3>MD果酸</h3>
-                    <div class="title">MD果酸(ceuticals™ )源自英国,由多位科学家及皮肤科医生共同研创;通过使用不同比</div>
-                    <div class="question">
-                        <p>MD果酸为什么不会刺激皮肤?</p>
-                        <p>MD果酸对于暗疮性皮肤能达到什么效果?</p>
-                        <p>MD果酸是否具有腐蚀性?</p>
-                    </div>
-                    <div class="tag-list">
-                        <span class="tag">常见问题</span>
-                        <span class="tag">效果展示</span>
-                        <span class="tag">技术原理</span>
-                        <span class="tag">术前术后</span>
-                    </div>
-                    <div class="dashed-line"></div>
-                    <footer class="footer">
-                        <time>日期:<span>2022-06-01   16:52</span></time>
-                        <span>浏览量:<span>999</span></span>
-                    </footer>
-                </div>
-            </a>
-            <a class="recommend-section">
-                <div class="cover">
-                    <img src="https://picsum.photos/400/400"/>
-                </div>
-                <div class="content">
-                    <h3>MD果酸</h3>
-                    <div class="title">MD果酸(ceuticals™ )源自英国,由多位科学家及皮肤科医生共同研创;通过使用不同比</div>
-                    <div class="question">
-                        <p>MD果酸为什么不会刺激皮肤?</p>
-                        <p>MD果酸对于暗疮性皮肤能达到什么效果?</p>
-                        <p>MD果酸是否具有腐蚀性?</p>
-                    </div>
-                    <div class="tag-list">
-                        <span class="tag">常见问题</span>
-                        <span class="tag">效果展示</span>
-                        <span class="tag">技术原理</span>
-                        <span class="tag">术前术后</span>
-                    </div>
-                    <div class="dashed-line"></div>
-                    <footer class="footer">
-                        <time>日期:<span>2022-06-01   16:52</span></time>
-                        <span>浏览量:<span>999</span></span>
-                    </footer>
-                </div>
-            </a>
-            <a class="recommend-section">
+            <a class="recommend-section" th:href="${ 'product-' +  recommend.productId + '.html'} " th:each="recommend,stat : *{recommendList}">
                 <div class="cover">
-                    <img src="https://picsum.photos/400/400"/>
+                    <img th:src="${recommend.image}" th:alt="${recommend.name}"/>
                 </div>
                 <div class="content">
-                    <h3>MD果酸</h3>
-                    <div class="title">MD果酸(ceuticals™ )源自英国,由多位科学家及皮肤科医生共同研创;通过使用不同比</div>
+                    <h3 th:text="${recommend.name}"></h3>
+                    <div class="title" th:text="${recommend.discription}"></div>
                     <div class="question">
-                        <p>MD果酸为什么不会刺激皮肤?</p>
-                        <p>MD果酸对于暗疮性皮肤能达到什么效果?</p>
-                        <p>MD果酸是否具有腐蚀性?</p>
+                        <p th:each="question : ${recommend.questionList}" th:text="${question}"></p>
                     </div>
                     <div class="tag-list">
                         <span class="tag">常见问题</span>
@@ -370,8 +272,8 @@
                     </div>
                     <div class="dashed-line"></div>
                     <footer class="footer">
-                        <time>日期:<span>2022-06-01   16:52</span></time>
-                        <span>浏览量:<span>999</span></span>
+                        <time>日期:<span th:text="${#dates.format(recommend.publishTime, 'yyyy-MM-dd HH:mm:ss') }"></span></time>
+                        <span>浏览量:<span th:text="${recommend.pv lt 10000 ? recommend.pv : '9999+'}"></span></span>
                     </footer>
                 </div>
             </a>
@@ -399,7 +301,7 @@
     <div class="mask"></div>
     <span class="close"></span>
     <div class="content">
-        <video src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4" controls></video>
+        <video controls></video>
     </div>
 </div>
 

+ 8 - 7
src/main/resources/templates/supplier-center/article/article-edit.html

@@ -13,7 +13,7 @@
     <template th:replace="components/analysis"></template>
 </head>
 <body>
-<input type="hidden" th:value="${coreServer}" id="coreServer">
+<input type="hidden" th:value="${ossBucket}" id="ossBucket">
 <!-- 引用头部 -->
 <template th:replace="components/header"></template>
 
@@ -39,12 +39,12 @@
                             <!-- 标题 -->
                             <el-form-item label="标题" prop="title" class="mint-filter">
                                 <el-input v-model="formData.title" placeholder="请输入文章标题"></el-input>
-                                <div class="mint-message">123</div>
+                                <div class="mint-message" v-if="formValidate.title">{{formValidate.title}}</div>
                             </el-form-item>
                             <!-- 文章标签 -->
                             <el-form-item label="文章标签" prop="label" class="mint-filter">
                                 <el-input v-model="formData.label" placeholder="请输入文章标签"></el-input>
-                                <div class="mint-message">123</div>
+                                <div class="mint-message" v-if="formValidate.label">{{formValidate.label}}</div>
                             </el-form-item>
                             <el-form-item  label="">
                                 <!-- 预选标签列表 -->
@@ -65,12 +65,12 @@
                             <!-- SEO关键词 -->
                             <el-form-item label="SEO关键词" prop="keyword" class="mint-filter">
                                 <el-input v-model="formData.keyword" placeholder="请输入SEO关键词"></el-input>
-                                <div class="mint-message">123</div>
+                                <div class="mint-message" v-if="formValidate.keyword">{{formValidate.keyword}}</div>
                             </el-form-item>
                             <!-- 发布人 -->
                             <el-form-item label="发布人" prop="publisher" class="mint-filter">
                                 <el-input v-model="formData.publisher" placeholder="请填写发布人姓名"></el-input>
-                                <div class="mint-message">123</div>
+                                <div class="mint-message" v-if="formValidate.publisher">{{formValidate.publisher}}</div>
                             </el-form-item>
                             <!-- 来源 -->
                             <el-form-item label="来源" prop="source">
@@ -79,13 +79,13 @@
                             <!-- 推荐语(描述) -->
                             <el-form-item label="推荐语(描述)" prop="recommendContent" class="mint-filter">
                                 <el-input type="textarea" :rows="3" v-model="formData.recommendContent" placeholder="请填写推荐语(描述)"></el-input>
-                                <div class="mint-message">123</div>
+                                <div class="mint-message" v-if="formValidate.recommendContent">{{formValidate.recommendContent}}</div>
                             </el-form-item>
                             <!-- 文章内容 -->
                             <el-form-item label="文章内容" prop="articleContent" class="mint-filter">
                                 <el-input v-model="formData.articleContent" v-show="false"></el-input>
                                 <editor v-model="formData.articleContent" placeholder="请输入文章内容"></editor>
-                                <div class="mint-message">123</div>
+                                <div class="mint-message" v-if="formValidate.articleContent">{{formValidate.articleContent}}</div>
                             </el-form-item>
                             <!-- 文章分类 -->
                             <el-form-item label="文章分类" prop="typeId">
@@ -134,6 +134,7 @@
 <!-- 引入底部 -->
 <template th:replace="components/footer"></template>
 <template th:replace="components/foot-link"></template>
+
 <script charset="utf-8" type="text/javascript" th:src="@{/lib/wangEditor.min.js}"></script>
 <script charset="utf-8" type="text/javascript" th:src="@{/lib/element-ui/element-ui.min.js}"></script>
 <script charset="utf-8" type="text/javascript" th:src="@{/js/center.js(v=${version})}"></script>

+ 29 - 27
src/main/resources/templates/supplier-center/encyclopedia/instrument-edit.html

@@ -13,7 +13,7 @@
     <template th:replace="components/analysis"></template>
 </head>
 <body>
-<input type="hidden" th:value="${coreServer}" id="coreServer">
+<input type="hidden" th:value="${ossBucket}" id="ossBucket">
 <!-- 引用头部 -->
 <template th:replace="components/header"></template>
 
@@ -40,16 +40,16 @@
                             <div class="cm-big-label"><span class="cm-label">仪器简述</span></div>
                             <el-form-item label="仪器名称(必填)" prop="name" class="mint-filter">
                                 <el-input v-model="formData.name" placeholder="请输入仪器名称"></el-input>
-                                <div class="mint-message">123</div>
+                                <div class="mint-message" v-if="formValidate.name">{{formValidate.name}}</div>
                             </el-form-item>
                             <el-form-item label="仪器别名(必填)" prop="alias" class="mint-filter">
                                 <el-input v-model="formData.alias" placeholder="请输入英文名或其他名称"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.alias">{{formValidate.alias}}</div>
                             </el-form-item>
                             <el-form-item label="仪器概述(必填)" prop="discription" class="mint-filter">
                                 <el-input v-model="formData.discription" type="textarea" placeholder="请输入仪器概述"
                                           :rows="5"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.discription">{{formValidate.discription}}</div>
                             </el-form-item>
                             <el-form-item label="仪器链接(必填)" prop="productLink">
                                 <el-input v-model="formData.productLink" placeholder="输入您采美商城的商品链接详情,便于用户精准找到"></el-input>
@@ -72,9 +72,10 @@
                             </el-form-item>
 
                             <!-- 相关视频 -->
-                            <div class="form-video-group-list">
+                            <div class="form-video-group-list mint-filter">
                                 <div class="cm-big-label"><span class="cm-label">相关视频(各视频大小不超过50M,最多上传6个)</span></div>
-                                <form-video-group :action="action" ></form-video-group>
+                                <form-video-group ref="formVideoGroup" @change="onVideoGroupChange"></form-video-group>
+                                <div class="mint-message" v-if="formValidate.videoList">{{formValidate.videoList}}</div>
                             </div>
 
                             <!-- 仪器参数 -->
@@ -95,7 +96,7 @@
                                         >&times;</span>
                                     </el-row>
                                 </div>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.paramList">{{formValidate.paramList}}</div>
                             </el-form-item>
 
                             <!-- 正品识别 -->
@@ -124,38 +125,38 @@
                             <el-form-item label="仪器优点" prop="advantage" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.advantage" v-show="false"></el-input>
                                 <editor v-model="formData.advantage" placeholder="请输入仪器优点"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.advantage">{{formValidate.advantage}}</div>
                             </el-form-item>
 
                             <!-- 仪器缺点 -->
                             <el-form-item label="仪器缺点" prop="disadvantage" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.disadvantage" v-show="false"></el-input>
                                 <editor v-model="formData.disadvantage" placeholder="请输入仪器缺点"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.disadvantage">{{formValidate.disadvantage}}</div>
                             </el-form-item>
 
                             <!-- 仪器原理 -->
                             <el-form-item label="仪器原理" prop="principle" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.principle" v-show="false"></el-input>
                                 <editor v-model="formData.principle" placeholder="请输入仪器原理"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.principle">{{formValidate.principle}}</div>
                             </el-form-item>
 
                             <!-- SEO关键词 -->
-                            <el-form-item label="SEO关键字" prop="seo" class="mint-filter">
+                            <el-form-item label="SEO关键字" prop="seoKeyword" class="mint-filter">
                                 <el-input v-model="formData.seoKeyword" placeholder="例如:名称,名称"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.seoKeyword">{{formValidate.seoKeyword}}</div>
                             </el-form-item>
 
                             <!-- 仪器档案 -->
                             <div class="cm-big-label"><span class="cm-label">仪器档案</span></div>
                             <el-form-item label="品牌" prop="brand" class="mint-filter">
                                 <el-input v-model="formData.brand" placeholder="请输入仪器品牌"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.brand">{{formValidate.brand}}</div>
                             </el-form-item>
                             <el-form-item label="产地" prop="producePlace" class="mint-filter">
                                 <el-input v-model="formData.producePlace" placeholder="请输入仪器产地"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.producePlace">{{formValidate.producePlace}}</div>
                             </el-form-item>
                             <el-form-item label="上市时间" prop="marketTime">
                                 <el-date-picker class="max-width" v-model="formData.marketTime" :type="marketTimeType"
@@ -169,7 +170,7 @@
                             </el-form-item>
                             <el-form-item label="供应商" prop="company" class="mint-filter">
                                 <el-input v-model="formData.company" placeholder="请输入供应商"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.company">{{formValidate.company}}</div>
                             </el-form-item>
                             <el-form-item label="NMPA认证时间" prop="nmpaTime">
                                 <el-date-picker class="max-width" v-model="formData.nmpaTime" :type="nmpaTimeType"
@@ -202,21 +203,21 @@
                             <el-form-item label="适应症" prop="adaptiveMan" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.adaptiveMan" v-show="false"></el-input>
                                 <editor v-model="formData.adaptiveMan" placeholder="请输入适应症"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.adaptiveMan">{{formValidate.adaptiveMan}}</div>
                             </el-form-item>
 
                             <!-- 不适应人群 -->
                             <el-form-item label="不适应人群" prop="unAdaptiveMan" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.unAdaptiveMan" v-show="false"></el-input>
                                 <editor v-model="formData.unAdaptiveMan" placeholder="请输入不适应人群"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.unAdaptiveMan">{{formValidate.unAdaptiveMan}}</div>
                             </el-form-item>
 
                             <!-- 注意事项 -->
                             <el-form-item label="注意事项" prop="aroundOperation" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.aroundOperation" v-show="false"></el-input>
                                 <editor v-model="formData.aroundOperation" placeholder="请输入注意事项"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.aroundOperation">{{formValidate.aroundOperation}}</div>
                             </el-form-item>
 
                             <!-- 效果展示
@@ -254,7 +255,7 @@
                                         >
                                     </div>
                                 </div>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.questionList">{{formValidate.questionList}}</div>
                             </el-form-item>
 
                             <el-form-item label="仪器类别" prop="typeId" class="cm-big-label-el">
@@ -299,20 +300,17 @@
                     <el-form-item label="标题" prop="videoTitle">
                         <el-input v-model="formData.videoTitle" placeholder="请输入视频标题"></el-input>
                     </el-form-item>
-                    <el-form-item label="" prop="videoUrl">
+                    <el-form-item label="" prop="videoUrl" class="mint-filter">
                         <el-row :gutter="12">
                             <el-col :span="18">
-                                <el-input v-model="formData.videoUrl" disabled placeholder="请选择视频路径"></el-input>
+                                <el-input v-model="formData.videoUrl" disabled placeholder="请选择视频路径" ></el-input>
                             </el-col>
                             <el-col :span="6">
-                                <el-upload :action="action" :auto-upload="true" :show-file-list="false" :multiple="false" :limit="2"
-                                           accept=".mp4" :on-success="onUploadSuccess" :on-error="onUploadError" :file-list="fileList">
-                                    <template #trigger>
-                                        <button class="form-upload-button" @click.prevent>选择文件</button>
-                                    </template>
-                                </el-upload>
+                                <input type="file" v-show="false" ref="ossFile" @change="onFileChange(formData,$event)" accept="video/*">
+                                <button class="form-upload-button" @click.prevent="onChooseFile(index)">选择文件</button>
                             </el-col>
                         </el-row>
+                        <el-progress :percentage="formData.percentage" :show-text="false" v-if="formData.status === 2" style="margin-top: 16px;"></el-progress>
                     </el-form-item>
                 </el-form>
             </div>
@@ -324,10 +322,14 @@
 <template th:replace="components/footer"></template>
 <template th:replace="components/foot-link"></template>
 
+<script charset="utf-8" type="text/javascript" src="/lib/aliyun-oss-sdk-6.17.1.min.js"></script>
 <script charset="utf-8" type="text/javascript" th:src="@{/lib/wangEditor.min.js}"></script>
 <script charset="utf-8" type="text/javascript" th:src="@{/lib/element-ui/element-ui.min.js}"></script>
+<script charset="utf-8" type="text/javascript" th:src="@{/lib/uuidv4.min.js}"></script>
 <script charset="utf-8" type="text/javascript" th:src="@{/js/center.js(v=${version})}"></script>
+<script charset="utf-8" type="text/javascript" th:src="@{/js/common/serviceapi/utils.service.js(v=${version})}"></script>
 <script charset="utf-8" type="text/javascript" th:src="@{/js/common/serviceapi/supplier.service.js(v=${version})}"></script>
+<script charset="utf-8" type="text/javascript" th:src="@{/js/oss-upload.js(v=${version})}"></script>
 <script charset="UTF-8" type="text/javascript" th:src="@{/js/supplier-center/encyclopedia/editor-component.js(v=${version})}"></script>
 <script charset="UTF-8" type="text/javascript" th:src="@{/js/supplier-center/encyclopedia/components.js(v=${version})}"></script>
 <script charset="UTF-8" type="text/javascript" th:src="@{/js/supplier-center/encyclopedia/instrument-edit.js(v=${version})}"></script>

+ 50 - 48
src/main/resources/templates/supplier-center/encyclopedia/product-edit.html

@@ -13,7 +13,7 @@
     <template th:replace="components/analysis"></template>
 </head>
 <body>
-<input type="hidden" th:value="${coreServer}" id="coreServer">
+<input type="hidden" th:value="${ossBucket}" id="ossBucket">
 <!-- 引用头部 -->
 <template th:replace="components/header"></template>
 
@@ -40,16 +40,16 @@
                             <div class="cm-big-label"><span class="cm-label">产品简述</span></div>
                             <el-form-item label="产品名称(必填)" prop="name" class="mint-filter">
                                 <el-input v-model="formData.name" placeholder="请输入产品名称"></el-input>
-                                <div class="mint-message">123</div>
+                                <div class="mint-message" v-if="formValidate.name">{{formValidate.name}}</div>
                             </el-form-item>
                             <el-form-item label="产品别名(必填)" prop="alias" class="mint-filter">
                                 <el-input v-model="formData.alias" placeholder="请输入英文名或其他名称"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.alias">{{formValidate.alias}}</div>
                             </el-form-item>
                             <el-form-item label="产品概述(必填)" prop="discription" class="mint-filter">
                                 <el-input v-model="formData.discription" type="textarea" placeholder="请输入产品概述"
                                           :rows="5"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.discription">{{formValidate.discription}}</div>
                             </el-form-item>
                             <el-form-item label="产品链接(必填)" prop="productLink">
                                 <el-input v-model="formData.productLink" placeholder="输入您采美商城的商品链接详情,便于用户精准找到"></el-input>
@@ -72,9 +72,10 @@
                             </el-form-item>
 
                             <!-- 相关视频 -->
-                            <div class="form-video-group-list">
+                            <div class="form-video-group-list mint-filter">
                                 <div class="cm-big-label"><span class="cm-label">相关视频(各视频大小不超过50M,最多上传6个)</span></div>
-                                <form-video-group :action="action" ></form-video-group>
+                                <form-video-group ref="formVideoGroup" @change="onVideoGroupChange"></form-video-group>
+                                <div class="mint-message" v-if="formValidate.videoList">{{formValidate.videoList}}</div>
                             </div>
 
                             <!-- 产品参数 -->
@@ -96,45 +97,45 @@
                                         >
                                     </el-row>
                                 </div>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.paramList">{{formValidate.paramList}}</div>
                             </el-form-item>
 
                             <!-- 产品优点 -->
                             <el-form-item label="产品优点" prop="advantage" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.advantage" v-show="false"></el-input>
                                 <editor v-model="formData.advantage" placeholder="请输入产品优点"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.advantage">{{formValidate.advantage}}</div>
                             </el-form-item>
 
                             <!-- 产品缺点 -->
                             <el-form-item label="产品缺点" prop="disadvantage" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.disadvantage" v-show="false"></el-input>
                                 <editor v-model="formData.disadvantage" placeholder="请输入产品缺点"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.disadvantage">{{formValidate.disadvantage}}</div>
                             </el-form-item>
 
                             <!-- 产品原理 -->
                             <el-form-item label="产品原理" prop="principle" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.principle" v-show="false"></el-input>
                                 <editor v-model="formData.principle" placeholder="请输入产品原理"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.principle">{{formValidate.principle}}</div>
                             </el-form-item>
 
                             <!-- SEO关键词 -->
-                            <el-form-item label="SEO关键字" prop="seo" class="mint-filter">
+                            <el-form-item label="SEO关键字" prop="seoKeyword" class="mint-filter">
                                 <el-input v-model="formData.seoKeyword" placeholder="例如:名称,名称"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.seoKeyword">{{formValidate.seoKeyword}}</div>
                             </el-form-item>
 
                             <!-- 产品档案 -->
                             <div class="cm-big-label"><span class="cm-label">产品档案</span></div>
                             <el-form-item label="品牌" prop="brand" class="mint-filter">
                                 <el-input v-model="formData.brand" placeholder="请输入产品品牌"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.brand">{{formValidate.brand}}</div>
                             </el-form-item>
                             <el-form-item label="产地" prop="producePlace" class="mint-filter">
                                 <el-input v-model="formData.producePlace" placeholder="请输入产品产地"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.producePlace">{{formValidate.producePlace}}</div>
                             </el-form-item>
                             <el-form-item label="上市时间" prop="marketTime">
                                 <el-date-picker class="max-width" v-model="formData.marketTime" :type="marketTimeType"
@@ -148,7 +149,7 @@
                             </el-form-item>
                             <el-form-item label="供应商" prop="company" class="mint-filter">
                                 <el-input v-model="formData.company" placeholder="请输入供应商"></el-input>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.company">{{formValidate.company}}</div>
                             </el-form-item>
                             <el-form-item label="NMPA认证时间" prop="nmpaTime">
                                 <el-date-picker class="max-width" v-model="formData.nmpaTime" :type="nmpaTimeType"
@@ -181,21 +182,21 @@
                             <el-form-item label="适应证" prop="adaptiveMan" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.adaptiveMan" v-show="false"></el-input>
                                 <editor v-model="formData.adaptiveMan" placeholder="请输入适应证"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.adaptiveMan">{{formValidate.adaptiveMan}}</div>
                             </el-form-item>
 
                             <!-- 不适应人群 -->
                             <el-form-item label="不适应人群" prop="unAdaptiveMan" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.unAdaptiveMan" v-show="false"></el-input>
                                 <editor v-model="formData.unAdaptiveMan" placeholder="请输入不适应人群"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.unAdaptiveMan">{{formValidate.unAdaptiveMan}}</div>
                             </el-form-item>
 
                             <!-- 注意事项 -->
                             <el-form-item label="注意事项" prop="aroundOperation" class="cm-big-label-el mint-filter">
                                 <el-input v-model="formData.aroundOperation" v-show="false"></el-input>
                                 <editor v-model="formData.aroundOperation" placeholder="请输入注意事项"></editor>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.aroundOperation">{{formValidate.aroundOperation}}</div>
                             </el-form-item>
 
                             <!-- 常见问题 -->
@@ -214,7 +215,7 @@
                                         >&times;</span>
                                     </div>
                                 </div>
-                                <div class="mint-message"></div>
+                                <div class="mint-message" v-if="formValidate.questionList">{{formValidate.questionList}}</div>
                             </el-form-item>
 
                             <el-form-item label="产品类别" prop="typeId" class="cm-big-label-el" props="typeId">
@@ -249,35 +250,32 @@
 </div>
 
 <script id="form-video-group" type="text/html">
-<div class="form-video-group" :style="{ paddingBottom:formList.length === 0 ? '35px' : 0  }">
-    <span class="add-one" @click="insertOne" v-if="addFlag">添加视频</span>
-    <template v-for="(formData,index) in formList">
-        <div class="form-video-section">
-            <span class="remove-btn" @click="removeOne(formData)">删除</span>
-            <div class="form-video-number">视频{{++index}}</div>
-            <el-form :model="formData" :rules="rules" :ref="formData.ref">
-                <el-form-item label="标题" prop="videoTitle">
-                    <el-input v-model="formData.videoTitle" placeholder="请输入视频标题"></el-input>
-                </el-form-item>
-                <el-form-item label="" prop="videoUrl">
-                    <el-row :gutter="12">
-                        <el-col :span="18">
-                            <el-input v-model="formData.videoUrl" disabled placeholder="请选择视频路径"></el-input>
-                        </el-col>
-                        <el-col :span="6">
-                            <el-upload :action="action" :auto-upload="true" :show-file-list="false" :multiple="false" :limit="2"
-                                       accept=".mp4" :on-success="onUploadSuccess" :on-error="onUploadError" :file-list="fileList">
-                                <template #trigger>
-                                    <button class="form-upload-button" @click.prevent>选择文件</button>
-                                </template>
-                            </el-upload>
-                        </el-col>
-                    </el-row>
-                </el-form-item>
-            </el-form>
-        </div>
-    </template>
-</div>
+    <div class="form-video-group" :style="{ paddingBottom:formList.length === 0 ? '35px' : 0  }">
+        <span class="add-one" @click="insertOne" v-if="addFlag">添加视频</span>
+        <template v-for="(formData,index) in formList">
+            <div class="form-video-section">
+                <span class="remove-btn" @click="removeOne(formData)">删除</span>
+                <div class="form-video-number">视频{{++index}}</div>
+                <el-form :model="formData" :rules="rules" :ref="formData.ref">
+                    <el-form-item label="标题" prop="videoTitle">
+                        <el-input v-model="formData.videoTitle" placeholder="请输入视频标题"></el-input>
+                    </el-form-item>
+                    <el-form-item label="" prop="videoUrl" class="mint-filter">
+                        <el-row :gutter="12">
+                            <el-col :span="18">
+                                <el-input v-model="formData.videoUrl" disabled placeholder="请选择视频路径" ></el-input>
+                            </el-col>
+                            <el-col :span="6">
+                                <input type="file" v-show="false" ref="ossFile" @change="onFileChange(formData,$event)" accept="video/*">
+                                <button class="form-upload-button" @click.prevent="onChooseFile(index)">选择文件</button>
+                            </el-col>
+                        </el-row>
+                        <el-progress :percentage="formData.percentage" :show-text="false" v-if="formData.status === 2" style="margin-top: 16px;"></el-progress>
+                    </el-form-item>
+                </el-form>
+            </div>
+        </template>
+    </div>
 </script>
 
 
@@ -287,10 +285,14 @@
 <template th:replace="components/footer"></template>
 <template th:replace="components/foot-link"></template>
 
+<script charset="utf-8" type="text/javascript" src="/lib/aliyun-oss-sdk-6.17.1.min.js"></script>
 <script charset="utf-8" type="text/javascript" th:src="@{/lib/wangEditor.min.js}"></script>
 <script charset="utf-8" type="text/javascript" th:src="@{/lib/element-ui/element-ui.min.js}"></script>
+<script charset="utf-8" type="text/javascript" th:src="@{/lib/uuidv4.min.js}"></script>
 <script charset="utf-8" type="text/javascript" th:src="@{/js/center.js(v=${version})}"></script>
+<script charset="utf-8" type="text/javascript" th:src="@{/js/common/serviceapi/utils.service.js(v=${version})}"></script>
 <script charset="utf-8" type="text/javascript" th:src="@{/js/common/serviceapi/supplier.service.js(v=${version})}"></script>
+<script charset="utf-8" type="text/javascript" th:src="@{/js/oss-upload.js(v=${version})}"></script>
 <script charset="UTF-8" type="text/javascript" th:src="@{/js/supplier-center/encyclopedia/editor-component.js(v=${version})}"></script>
 <script charset="UTF-8" type="text/javascript" th:src="@{/js/supplier-center/encyclopedia/components.js(v=${version})}"></script>
 <script charset="UTF-8" type="text/javascript" th:src="@{/js/supplier-center/encyclopedia/product-edit.js(v=${version})}"></script>

+ 65 - 26
src/main/resources/templates/supplier-center/encyclopedia/recommend-edit.html

@@ -24,7 +24,7 @@
         <span>&gt;</span>
         <span>采美百科</span>
         <span>&gt;</span>
-        <span>产品</span>
+        <span>{{pageName}}</span>
         <span>&gt;</span>
         <span>相关推荐</span>
       </div>
@@ -33,46 +33,84 @@
       <!--左侧导航-->
       <template th:replace="supplier-center/components/tableft"></template>
       <div class="right">
-        <div class="recommend-title">产品名称:无纺布脱毛纸</div>
+        <div class="recommend-title">{{pageName}}名称:{{query.name}}</div>
         <div class="recommend-list">
-          <div class="recommend-list-label">推荐列表<span>(当前列表推荐数为1,最多可推荐15条)</span></div>
+          <div class="recommend-list-label">推荐列表<span>(当前列表推荐数为{{recommendList.length}},最多可推荐15条)</span></div>
           <el-table :data="recommendList" style="width: 100%" :border="true">
-            <el-table-column prop="date" label="ID" align="center"></el-table-column>
-            <el-table-column prop="name" label="产品名称" align="center"></el-table-column>
-            <el-table-column prop="address" label="排序" align="center"></el-table-column>
-            <el-table-column prop="address" label="操作" align="center"></el-table-column>
+            <el-table-column prop="recommendProductId" label="ID" align="center" width="80"></el-table-column>
+            <el-table-column prop="recommendProductName" :label="pageName + '名称'" align="center"></el-table-column>
+            <template v-if="recommendType === 1">
+              <el-table-column label="排序" align="center" width="120">
+                <template slot-scope="{ row }">
+                  <el-input v-model.lazy="row.sort" class="center" size="mini" @change="onSortChange(row)"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作" align="center" width="120">
+                <template slot-scope="{ row }">
+                  <el-button type="danger" @click="onRemove(row)" size="mini">删除</el-button>
+                </template>
+              </el-table-column>
+            </template>
           </el-table>
         </div>
         <!-- 推荐配置 -->
         <div class="recommend-control">
           <div class="recommend-control-item">
             <span class="recommend-control-label">设置类型:</span>
-            <el-radio-group v-model="recommendType">
+            <el-radio-group v-model="recommendType" @change="onRecommendTypeChange">
               <el-radio :label="1">手动选择</el-radio>
-              <el-radio :label="0">自动选择<span class="tip">(设置自动选择后,系统将自动推荐相同分类中的前15个)</span></el-radio>
+              <el-radio :label="2">自动选择<span class="tip">(设置自动选择后,系统将自动推荐相同分类中的前15个)</span></el-radio>
             </el-radio-group>
           </div>
         </div>
         <!-- 产品 仪器筛选 -->
-        <div class="recommend-filter">
-          <div class="recommend-filter-item">
-            <span class="recommend-filter-label">产品分类:</span>
-            <el-select  placeholder="请选择产品类别" class="max-width">
-              <el-option v-for="item in typeList" :key="item.typeId" :label="item.typeName" :value="item.typeId"></el-option>
-            </el-select>
+        <template v-if="recommendType === 1">
+          <div class="recommend-filter">
+            <div class="recommend-filter-item">
+              <span class="recommend-filter-label">{{pageName}}分类:</span>
+              <el-select :placeholder="'请选择' + pageName + '类别'" class="max-width" v-model="listQuery.typeId" clearable>
+                <el-option v-for="item in typeList" :key="item.typeId" :label="item.typeName"
+                           :value="item.typeId"></el-option>
+              </el-select>
+            </div>
+            <div class="recommend-filter-item">
+              <span class="recommend-filter-label">{{pageName}}名称:</span>
+              <el-input class="serach-input" clearable v-model="listQuery.name"></el-input>
+              <button class="button" @click="fetchList">查询</button>
+            </div>
           </div>
-          <div class="recommend-filter-item">
-            <span class="recommend-filter-label">产品名称:</span>
-            <el-input class="serach-input"></el-input>
-            <button class="button">查询</button>
+          <el-table :data="list" style="width: 100%" :border="true">
+            <el-table-column prop="productId" label="ID" align="center" width="80"></el-table-column>
+            <el-table-column prop="name" :label="pageName + '名称'" align="center"></el-table-column>
+            <el-table-column label="排序" align="center" width="100">
+              <template slot-scope="{ row }">
+                <el-input v-model="row.sort" class="center" size="mini"></el-input>
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" align="center" width="100">
+              <template slot-scope="{ row }">
+                <el-checkbox v-model="row.checked" @change="onCheckboxChange(row)" size="mini"></el-checkbox>
+              </template>
+            </el-table-column>
+          </el-table>
+
+          <div class="pageWrap clear" v-if="pageTotal>1">
+            <a v-if="listQuery.pageNum>1" class="prev" @click="toPagination(listQuery.pageNum*1-1)"
+               href="javascript:void(0);"></a>
+            <template v-for="n in showPageBtn">
+              <a v-if="n" :class="{'on':(n==listQuery.pageNum)}" @click="toPagination(n)"
+                 href="javascript:void(0);" v-text="n"></a>
+              <span v-else>···</span>
+            </template>
+            <a v-if="listQuery.pageNum<pageTotal" class="next" @click="toPagination(listQuery.pageNum*1+1)"
+               href="javascript:void(0);"></a>
+            <span>共<b v-text="pageTotal>1?pageTotal:1"></b>页</span>
+            <span>跳至</span>
+            <input v-model="pageInput" @blur="checkNum()"/>
+            <span>页</span>&nbsp;
+            <a class="btn" href="javascript:void(0);" @click="toPagination(pageInput)">点击跳转</a>
           </div>
-        </div>
-        <el-table :data="recommendList" style="width: 100%" :border="true">
-          <el-table-column prop="date" label="ID" align="center"></el-table-column>
-          <el-table-column prop="name" label="产品名称" align="center"></el-table-column>
-          <el-table-column prop="address" label="排序" align="center"></el-table-column>
-          <el-table-column prop="address" label="操作" align="center"></el-table-column>
-        </el-table>
+        </template>
 
         <div class="btns">
           <button class="btn break" @click.prevent="handleBack">返回</button>
@@ -87,6 +125,7 @@
 <template th:replace="components/foot-link"></template>
 <script charset="utf-8" type="text/javascript" th:src="@{/lib/element-ui/element-ui.min.js}"></script>
 <script charset="utf-8" type="text/javascript" th:src="@{/js/center.js(v=${version})}"></script>
+<script charset="utf-8" type="text/javascript" th:src="@{/js/common/serviceapi/supplier.service.js(v=${version})}"></script>
 <script charset="UTF-8" type="text/javascript" th:src="@{/js/supplier-center/encyclopedia/recommend-edit.js(v=${version})}"></script>
 </body>
 </html>

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.