Explorar o código

Merge branch 'master' of http://git.izouma.com/xiongzhu/raex_back into dev-meta

sunkean %!s(int64=3) %!d(string=hai) anos
pai
achega
a98f36dbd5
Modificáronse 28 ficheiros con 864 adicións e 215 borrados
  1. 28 0
      src/main/java/com/izouma/nineth/converter/IntArrayConverter.java
  2. 2 2
      src/main/java/com/izouma/nineth/domain/Asset.java
  3. 3 0
      src/main/java/com/izouma/nineth/domain/Collection.java
  4. 26 0
      src/main/java/com/izouma/nineth/domain/NumberSeq.java
  5. 2 2
      src/main/java/com/izouma/nineth/domain/User.java
  6. 9 0
      src/main/java/com/izouma/nineth/repo/NumberSeqRepo.java
  7. 52 11
      src/main/java/com/izouma/nineth/service/AssetService.java
  8. 6 0
      src/main/java/com/izouma/nineth/service/GiftOrderService.java
  9. 1 0
      src/main/vue/package.json
  10. BIN=BIN
      src/main/vue/src/assets/png-diwen-bai.png
  11. 128 81
      src/main/vue/src/components/phone/Home.vue
  12. 175 0
      src/main/vue/src/components/phone/NewsLarge.vue
  13. 7 1
      src/main/vue/src/components/phone/module.vue
  14. 53 19
      src/main/vue/src/components/phone/productLarge.vue
  15. 9 1
      src/main/vue/src/main.js
  16. 3 1
      src/main/vue/src/store.js
  17. 145 0
      src/main/vue/src/styles/font.less
  18. 75 13
      src/main/vue/src/styles/theme.less
  19. 20 20
      src/main/vue/src/views/Admin.vue
  20. 1 7
      src/main/vue/src/views/BlindBoxEdit.vue
  21. 24 27
      src/main/vue/src/views/CollectionEdit.vue
  22. 1 7
      src/main/vue/src/views/CompanyBlindBoxEdit.vue
  23. 7 8
      src/main/vue/src/views/CompanyCollectionEdit.vue
  24. 5 1
      src/main/vue/src/views/CompanyList.vue
  25. 4 4
      src/main/vue/src/views/UserBalance.vue
  26. 15 4
      src/main/vue/src/views/company/CompanyTheme.vue
  27. 25 0
      src/main/vue/yarn.lock
  28. 38 6
      src/test/java/com/izouma/nineth/CommonTest.java

+ 28 - 0
src/main/java/com/izouma/nineth/converter/IntArrayConverter.java

@@ -0,0 +1,28 @@
+package com.izouma.nineth.converter;
+
+import org.apache.commons.lang3.StringUtils;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Converter
+public class IntArrayConverter implements AttributeConverter<List<Integer>, String> {
+    @Override
+    public String convertToDatabaseColumn(List<Integer> integers) {
+        if (integers != null && !integers.isEmpty())
+            return StringUtils.join(integers, ",");
+        return null;
+    }
+
+    @Override
+    public List<Integer> convertToEntityAttribute(String s) {
+        if (StringUtils.isNotEmpty(s)) {
+            return Arrays.stream(s.split(",")).map(Integer::parseInt).collect(Collectors.toList());
+        }
+        return new ArrayList<>();
+    }
+}

+ 2 - 2
src/main/java/com/izouma/nineth/domain/Asset.java

@@ -331,8 +331,8 @@ public class Asset extends CollectionBaseEntity {
                 .ownerId(user.getId())
                 .ownerAvatar(user.getAvatar())
                 .type(CollectionType.PICTURE)
-                .holdDays(1)
-                .oldHoldDays(1)
+                .holdDays(null)
+                .oldHoldDays(null)
                 .source(AssetSource.OFFICIAL)
                 .companyId(1L)
                 .chainFlag(2)

+ 3 - 0
src/main/java/com/izouma/nineth/domain/Collection.java

@@ -287,6 +287,7 @@ public class Collection extends CollectionBaseEntity {
     @Column(columnDefinition = "bigint default 1 not null")
     private Long companyId = 1L;
 
+
     @ApiModelProperty("bit 0: 蚂蚁链, bit 1: 华储链")
     @Column(columnDefinition = "int default 3 not null")
     private int chainFlag;
@@ -301,4 +302,6 @@ public class Collection extends CollectionBaseEntity {
     private String newsPic;
 
     private LocalDateTime newsCreatedTime;
+
+    private boolean messNumber;
 }

+ 26 - 0
src/main/java/com/izouma/nineth/domain/NumberSeq.java

@@ -0,0 +1,26 @@
+package com.izouma.nineth.domain;
+
+
+import com.izouma.nineth.converter.IntArrayConverter;
+import lombok.Data;
+
+import javax.persistence.Column;
+import javax.persistence.Convert;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.util.List;
+
+@Data
+@Entity
+public class NumberSeq {
+
+    @Id
+    private Long id;
+
+    private int total;
+
+    @Convert(converter = IntArrayConverter.class)
+    @Column(columnDefinition = "text(65535)")
+    private List<Integer> numbers;
+
+}

+ 2 - 2
src/main/java/com/izouma/nineth/domain/User.java

@@ -57,10 +57,10 @@ public class User extends UserBaseEntity implements Serializable {
     //    @Pattern(regexp = Constants.Regex.USERNAME)
     @Size(min = 1, max = 50)
     @Column(nullable = false, unique = true)
-    @Searchable
+    @Searchable(mode = SearchMode.EXACT)
     private String username;
 
-    @Searchable
+    @Searchable(mode = SearchMode.EXACT)
     private String nickname;
 
     private String avatar;

+ 9 - 0
src/main/java/com/izouma/nineth/repo/NumberSeqRepo.java

@@ -0,0 +1,9 @@
+package com.izouma.nineth.repo;
+
+
+import com.izouma.nineth.domain.NumberSeq;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface NumberSeqRepo extends JpaRepository<NumberSeq, Long> {
+
+}

+ 52 - 11
src/main/java/com/izouma/nineth/service/AssetService.java

@@ -1,6 +1,7 @@
 package com.izouma.nineth.service;
 
 import com.google.common.collect.Lists;
+import com.google.common.hash.Hashing;
 import com.izouma.nineth.TokenHistory;
 import com.izouma.nineth.config.Constants;
 import com.izouma.nineth.config.GeneralProperties;
@@ -35,8 +36,10 @@ import org.springframework.stereotype.Service;
 
 import javax.persistence.criteria.Predicate;
 import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
 import java.util.concurrent.ExecutionException;
@@ -70,6 +73,7 @@ public class AssetService {
     private AssetLockRepo           assetLockRepo;
     private UserBalanceService      userBalanceService;
     private PhotoAssetRepo          photoAssetRepo;
+    private NumberSeqRepo           numberSeqRepo;
 
     public Page<Asset> all(PageQuery pageQuery) {
 
@@ -149,6 +153,9 @@ public class AssetService {
 
     public Asset createAsset(Collection collection, User user, Long orderId, BigDecimal price, String type,
                              Integer number, boolean safeFlag) {
+        if (collection.isMessNumber() && number != null) {
+            number = getMessedNumber(collection.getId(), number, collection.getTotal());
+        }
         Asset asset = Asset.create(collection, user);
         asset.setTokenId(TokenUtils.genTokenId());
         asset.setNumber(number);
@@ -247,12 +254,34 @@ public class AssetService {
         return asset;
     }
 
+    public synchronized int getMessedNumber(Long collectionId, int number, int total) {
+        NumberSeq numberSeq = numberSeqRepo.findById(collectionId).orElse(null);
+        if (numberSeq == null || numberSeq.getTotal() != total) {
+            numberSeq = new NumberSeq();
+            numberSeq.setId(collectionId);
+            numberSeq.setTotal(total);
+            Map<Integer, Integer> map = new HashMap<>();
+            for (int i = 0; i < total; i++) {
+                map.put(i, Hashing.md5().hashString(collectionId + ":" + i, StandardCharsets.UTF_8).asInt());
+            }
+            numberSeq.setNumbers(map.entrySet().stream()
+                    .sorted(Map.Entry.comparingByValue())
+                    .map(Map.Entry::getKey)
+                    .collect(Collectors.toList()));
+            numberSeqRepo.save(numberSeq);
+        }
+        return numberSeq.getNumbers().get(number);
+    }
+
     public Asset createAsset(BlindBoxItem winItem, User user, Long orderId, BigDecimal price, String type,
                              Integer number, Integer holdDays, boolean safeFlag) {
         Collection blindBox = collectionRepo.findDetailById(winItem.getBlindBoxId())
                 .orElseThrow(new BusinessException("盲盒不存在"));
         Collection collection = collectionRepo.findDetailById(winItem.getCollectionId())
                 .orElseThrow(new BusinessException("藏品不存在"));
+        if (collection.isMessNumber() && number != null) {
+            number = getMessedNumber(collection.getId(), number, collection.getTotal());
+        }
         winItem.setCollection(collection);
         Asset asset = Asset.create(winItem, user, holdDays);
         asset.setTokenId(TokenUtils.genTokenId());
@@ -372,7 +401,15 @@ public class AssetService {
 
         int holdDays;
         if (asset.getSource() == AssetSource.GIFT) {
-            holdDays = sysConfigService.getInt("gift_days");
+            LocalDateTime localDateTime = asset.getCreatedAt();
+            LocalDateTime gift_change_time = LocalDateTime
+                    .parse(sysConfigService.getString("gift_change_time"), DateTimeFormatter
+                            .ofPattern("yyyy-MM-dd HH:mm:ss"));
+            if (localDateTime.compareTo(gift_change_time) < 0) {
+                holdDays = 20;
+            } else {
+                holdDays = sysConfigService.getInt("gift_days");
+            }
         } else {
             if (ObjectUtils.isEmpty(asset.getHoldDays())) {
                 holdDays = sysConfigService.getInt("hold_days");
@@ -614,6 +651,10 @@ public class AssetService {
         newAsset.setTags(new HashSet<>(asset.getTags()));
         newAsset.setSafeFlag(safeFlag);
         newAsset.setHoldDays(asset.getOldHoldDays());
+        if (asset.getType().equals(CollectionType.PICTURE)) {
+            newAsset.setType(CollectionType.PICTURE);
+            newAsset.setHoldDays(0);
+        }
         Long savedId = assetRepo.saveAndFlush(newAsset).getId();
 
         if (asset.getType().equals(CollectionType.PICTURE)) {
@@ -878,16 +919,16 @@ public class AssetService {
             if (tags.isEmpty()) return new PageImpl<>(Collections.emptyList());
             return assetRepo.findAll((Specification<Asset>) (root, query, criteriaBuilder) ->
                     query.distinct(true).where(
-                            // where userId=some id
-                            criteriaBuilder.equal(root.get("userId"), userId),
-                            // and (lockTo is null or (lockTo is not null and lockTo < now))
-                            criteriaBuilder.or(criteriaBuilder.isNull(root.get("lockTo")),
-                                    criteriaBuilder.and(criteriaBuilder.isNotNull(root.get("lockTo")),
-                                            criteriaBuilder.lessThan(root.get("lockTo"), LocalDateTime.now()))),
-                            // and status = 'NORMAL'
-                            criteriaBuilder.equal(root.get("status"), AssetStatus.NORMAL),
-                            // and has some tagId
-                            root.join("tags").get("id").in(tags.stream().map(Tag::getId).toArray()))
+                                    // where userId=some id
+                                    criteriaBuilder.equal(root.get("userId"), userId),
+                                    // and (lockTo is null or (lockTo is not null and lockTo < now))
+                                    criteriaBuilder.or(criteriaBuilder.isNull(root.get("lockTo")),
+                                            criteriaBuilder.and(criteriaBuilder.isNotNull(root.get("lockTo")),
+                                                    criteriaBuilder.lessThan(root.get("lockTo"), LocalDateTime.now()))),
+                                    // and status = 'NORMAL'
+                                    criteriaBuilder.equal(root.get("status"), AssetStatus.NORMAL),
+                                    // and has some tagId
+                                    root.join("tags").get("id").in(tags.stream().map(Tag::getId).toArray()))
                             .getRestriction(), pageable);
         } else {
             return assetRepo.findByUserIdAndStatusAndNameLike(userId, AssetStatus.NORMAL,

+ 6 - 0
src/main/java/com/izouma/nineth/service/GiftOrderService.java

@@ -95,6 +95,9 @@ public class GiftOrderService {
 
         if (holdDays == 0 && AssetSource.OFFICIAL.equals(asset.getSource())) {
             BigDecimal officialConsignment = sysConfigService.getBigDecimal("OFFICIAL_CONSIGNMENT");
+            if (asset.getType().equals(CollectionType.PICTURE)) {
+                officialConsignment = sysConfigService.getBigDecimal("picture_hold_days");
+            }
             //天转小时
             int hour = officialConsignment.multiply(new BigDecimal("24")).intValue();
             if (ChronoUnit.HOURS.between(asset.getCreatedAt(), LocalDateTime.now()) < hour) {
@@ -167,6 +170,9 @@ public class GiftOrderService {
 
         if (holdDays == 0 && AssetSource.OFFICIAL.equals(asset.getSource())) {
             BigDecimal officialConsignment = sysConfigService.getBigDecimal("OFFICIAL_CONSIGNMENT");
+            if (asset.getType().equals(CollectionType.PICTURE)) {
+                officialConsignment = sysConfigService.getBigDecimal("picture_hold_days");
+            }
             //天转小时
             int hour = officialConsignment.multiply(new BigDecimal("24")).intValue();
             if (ChronoUnit.HOURS.between(asset.getCreatedAt(), LocalDateTime.now()) < hour) {

+ 1 - 0
src/main/vue/package.json

@@ -25,6 +25,7 @@
     "jdenticon": "^3.1.0",
     "normalize.css": "^8.0.1",
     "qs": "^6.9.0",
+    "query-string": "^7.1.1",
     "randomstring": "^1.2.1",
     "resolve-url": "^0.2.1",
     "swiper": "^6.8.1",

BIN=BIN
src/main/vue/src/assets/png-diwen-bai.png


+ 128 - 81
src/main/vue/src/components/phone/Home.vue

@@ -17,47 +17,57 @@
                 width="38"
                 height="38"
                 :radius="38"
-                :src="require('../../assets/img_default_photo.png')"
+                :src="companyInfo.logo || require('../../assets/img_default_photo.png')"
             ></van-image>
-            <div class="text1">探索者!</div>
+            <div class="text1 text3">{{ companyInfo.name }}</div>
         </div>
-        <div class="welcom" v-else>
+        <div class="welcom" v-else-if="theme === 'theme1' || theme === 'theme2'">
             <div class="left">
                 <div class="text1">WELCOME</div>
-                <div class="text2">探索者!</div>
+                <div class="text2 text3">{{ companyInfo.name }}</div>
             </div>
             <van-image
                 width="38"
                 height="38"
                 :radius="38"
-                :src="require('../../assets/img_default_photo.png')"
+                :src="companyInfo.logo || require('../../assets/img_default_photo.png')"
             ></van-image>
         </div>
-
-        <div class="mySwiper" :class="[`swiper-${theme}`]" ref="mySwiper">
-            <div class="swiper-wrapper">
-                <div class="swiper-slide" v-for="item in banners" :key="item.id">
-                    <product-small :info="item"></product-small>
+        <template v-if="theme === 'theme1' || theme === 'theme2' || theme === 'theme3'">
+            <div class="mySwiper" :class="[`swiper-${theme}`]" ref="mySwiper">
+                <div class="swiper-wrapper">
+                    <div class="swiper-slide" v-for="item in banners" :key="item.id">
+                        <product-small :info="item"></product-small>
+                    </div>
                 </div>
+                <!-- 如果需要分页器 -->
+                <div class="swiper-pagination"></div>
+            </div>
+        </template>
+        <div class="large-logo" v-else>
+            <van-image
+                width="100%"
+                height="100%"
+                radius="12"
+                fit="cover"
+                :src="companyInfo.logo || require('../../assets/img_default_photo.png')"
+            ></van-image>
+        </div>
+
+        <div class="home-title">
+            <span>新闻公告</span>
+
+            <div class="title-more">
+                <span>更多</span>
+                <van-icon name="icon-icon_inter" class-prefix="font_family" />
             </div>
-            <!-- 如果需要分页器 -->
-            <div class="swiper-pagination"></div>
         </div>
 
-        <!-- <swiper
-            pagination
-            @slideChange="changeMySwiper"
-            :space-between="theme === 'theme2' || theme === 'theme3' ? 8 : 16"
-            class="mySwiper"
-            :autoplay="{ delay: 5000, disableOnInteraction: false }"
-            v-if="banners.length > 0"
-            slidesPerView="auto"
-            centeredSlides
-        >
-            <swiper-slide v-for="item in banners" :key="item.id">
-                <product-small :info="item"></product-small>
-            </swiper-slide>
-        </swiper> -->
+        <div class="newsList">
+            <news-large v-for="(item, index) in news" :theme="theme" :key="index" :info="item.obj"></news-large>
+        </div>
+
+        <div class="home-title">数字艺术品</div>
 
         <!-- <div class="tabs" v-if="!empty">
             <div class="tab" :class="{ active: active === 0 }" @click="active = 0">数字藏品</div>
@@ -66,8 +76,9 @@
         <div class="listSwiper">
             <product-large
                 v-for="(item, index) in products"
+                :theme="theme"
                 :key="index"
-                :info="products[index].obj"
+                :info="item.obj"
                 :type="item.type"
             ></product-large>
         </div>
@@ -80,15 +91,11 @@ import Swiper from 'swiper/swiper-bundle';
 import 'swiper/swiper.min.css';
 import 'swiper/swiper-bundle.min.css';
 
-// import SwiperCore, { Pagination, Autoplay } from 'swiper';
-
-// // install Swiper modules
-// SwiperCore.use([Pagination, Autoplay]);
-
+import product from '../../mixins/product';
 import ProductLarge from './productLarge.vue';
+import NewsLarge from './NewsLarge.vue';
 import ProductSmall from './productSmall.vue';
 import { mapState } from 'vuex';
-import { Image as VanImage } from 'vant';
 
 export default {
     name: 'home',
@@ -96,12 +103,19 @@ export default {
         theme: {
             type: String,
             default: 'theme2'
+        },
+        companyInfo: {
+            type: Object,
+            default: () => {
+                return {};
+            }
         }
     },
+    mixins: [product],
     components: {
         ProductLarge,
         ProductSmall,
-        VanImage
+        NewsLarge
     },
     computed: {
         ...mapState(['userInfo']),
@@ -146,13 +160,35 @@ export default {
             riskShow: false,
             activeIndex: 0,
             isLoading: false,
-            mySwiper: null
+            mySwiper: null,
+            companyInfo: {}
         };
     },
     watch: {
         theme() {
             this.$nextTick(() => {
-                this.mySwiper.destroy();
+                if (this.mySwiper) {
+                    this.mySwiper.destroy();
+                }
+                if (this.$refs.mySwiper) {
+                    this.mySwiper = new Swiper(this.$refs.mySwiper, {
+                        pagination: {
+                            el: '.swiper-pagination'
+                        },
+                        autoplay: { delay: 5000, disableOnInteraction: false },
+                        spaceBetween: this.theme === 'theme2' || this.theme === 'theme3' ? 8 : 16,
+                        slidesPerView: 'auto',
+                        centeredSlides: true
+                    });
+                }
+            });
+        }
+    },
+    mounted() {
+        this.getInit();
+
+        this.$nextTick(() => {
+            if (this.$refs.mySwiper) {
                 this.mySwiper = new Swiper(this.$refs.mySwiper, {
                     pagination: {
                         el: '.swiper-pagination'
@@ -162,23 +198,11 @@ export default {
                     slidesPerView: 'auto',
                     centeredSlides: true
                 });
-            });
-        }
-    },
-    mounted() {
-        this.getInit();
-        console.log(new Swiper());
-        this.$nextTick(() => {
-            this.mySwiper = new Swiper(this.$refs.mySwiper, {
-                pagination: {
-                    el: '.swiper-pagination'
-                },
-                autoplay: { delay: 5000, disableOnInteraction: false },
-                spaceBetween: this.theme === 'theme2' || this.theme === 'theme3' ? 8 : 16,
-                slidesPerView: 'auto',
-                centeredSlides: true
-            });
+            }
         });
+        // this.$http.get('sysConfig/get/home_bg').then(res => {
+        //     this.bgImg = res.value || '';
+        // });
     },
     methods: {
         changeSwiper() {
@@ -199,9 +223,11 @@ export default {
                     this.products = res.filter(item => {
                         return item.type === 'collection';
                     });
-                    this.news = res.filter(item => {
-                        return item.type === 'news';
-                    });
+                    this.news = res
+                        .filter(item => {
+                            return item.type === 'news';
+                        })
+                        .slice(0, 3);
                 })
             ]).then(() => {
                 this.$toast.clear();
@@ -328,7 +354,7 @@ export default {
 
 <style lang="less" scoped>
 .darkBg {
-    background-color: #181818;
+    background-color: var(--bg);
 }
 .theme2-bg {
     width: 221px;
@@ -340,7 +366,7 @@ export default {
 }
 .theme3-bg {
     width: 100%;
-    position: absolute;
+    position: fixed;
     top: 0;
     left: 0;
     height: auto;
@@ -349,39 +375,21 @@ export default {
 .top {
     display: flex;
     padding: 9px 16px;
-    background-color: @bg;
+    background-color: var(--bg);
     display: flex;
     align-items: center;
     .logo {
         width: 74px;
         height: 26px;
     }
-
-    .search {
-        flex-grow: 1;
-        background-color: #fff;
-        display: flex;
-        align-items: center;
-        margin-left: 10px;
-        padding: 4px 20px;
-        border-radius: 21px;
-        img {
-            width: 16px;
-            height: 16px;
-            margin-right: 10px;
-        }
-        font-size: 14px;
-        color: @text3;
-        line-height: 24px;
-    }
 }
 
 .home {
     width: 100%;
-    // background-color: #fff;
+    // background-color: @bg3;
     position: relative;
     z-index: 1;
-    background-color: #272b2e;
+    background-color: var(--bg2);
     padding-top: var(--safe-top);
     overflow-y: auto;
     max-height: 720px;
@@ -396,7 +404,6 @@ export default {
     padding: 16px 0;
     // margin: 0 16px;
     box-sizing: border-box;
-    position: relative;
     // padding-bottom: 35px;
 
     .swiper-slide {
@@ -409,7 +416,7 @@ export default {
         bottom: 30px;
     }
 
-    /deep/.swiper-pagination-bullet {
+    .swiper-pagination-bullet {
         width: 5px;
         height: 5px;
         border-radius: 5px;
@@ -417,7 +424,8 @@ export default {
         margin: 0 4px;
         opacity: 1;
     }
-    /deep/.swiper-pagination-bullet-active {
+
+    .swiper-pagination-bullet-active {
         background: var(--prim);
     }
 }
@@ -441,6 +449,20 @@ export default {
         }
     }
 }
+
+.swiper-theme4,
+.swiper-theme5 {
+    padding: 20px 0 16px;
+    .swiper-slide {
+        height: 160px;
+    }
+}
+
+.large-logo {
+    padding: 20px 16px 16px;
+    height: 160px;
+    overflow: hidden;
+}
 // .swiper-slide {
 //     width: 255px;
 // }
@@ -623,4 +645,29 @@ export default {
         border-radius: 8px;
     }
 }
+
+.home-title {
+    font-size: 18px;
+    font-weight: bold;
+    color: var(--text1);
+    line-height: 24px;
+    padding: 10px 16px 8px;
+    .flex();
+
+    span {
+        flex-grow: 1;
+    }
+
+    .title-more {
+        .flex();
+        font-size: 12px;
+        color: #939599;
+        .font_family {
+            font-weight: normal;
+        }
+    }
+}
+.newsList {
+    padding-bottom: 8px;
+}
 </style>

+ 175 - 0
src/main/vue/src/components/phone/NewsLarge.vue

@@ -0,0 +1,175 @@
+<template>
+    <div class="news-small" :class="[`news-${theme}`, type === 'light' ? 'isLight' : '']">
+        <van-image :src="getImg(info.pic)" :radius="12" width="120" height="120" fit="cover" />
+
+        <div class="content">
+            <div class="name van-multi-ellipsis--l2">{{ info.title }}</div>
+            <div class="flex1"></div>
+            <van-button type="primary" round plain size="mini"> 查看</van-button>
+        </div>
+    </div>
+</template>
+
+<script>
+import product from '../../mixins/product';
+export default {
+    mixins: [product],
+    props: {
+        info: {
+            type: Object,
+            default: () => {
+                return {};
+            }
+        },
+        type: {
+            type: String,
+            default: 'dark'
+        },
+        theme: {
+            type: String,
+            default: 'theme2'
+        }
+    },
+    computed: {
+        time() {
+            if (this.info.startTime) {
+                if (this.dayjs().isSameOrBefore(this.info.startTime, 'YYYY-MM-DD HH:mm:ss')) {
+                    return this.dayjs(this.info.startTime).format('MM月DD日');
+                }
+            }
+
+            return '';
+        }
+    },
+    mounted() {
+        if (this.info.startTime) {
+            var x = this.dayjs(this.info.startTime, 'YYYY-MM-DD HH:mm:ss');
+            var y = this.dayjs();
+            let d = this.dayjs.duration(x.diff(y));
+            let day = parseInt(d.asDays());
+            if (day <= 0) {
+                this.getTime(this.info.startTime);
+            }
+        }
+    },
+    methods: {
+        likeProduct() {
+            if (!this.info.liked) {
+                this.$http.get(`/collection/${this.info.id}/like`).then(() => {
+                    this.$emit('update:info', {
+                        ...this.info,
+                        liked: true,
+                        likes: this.info.likes + 1
+                    });
+                    this.$toast.success('收藏成功');
+                });
+            } else {
+                this.$http.get(`/collection/${this.info.id}/unlike`).then(() => {
+                    this.$emit('update:info', {
+                        ...this.info,
+                        liked: false,
+                        likes: this.info.likes - 1
+                    });
+                    this.$toast.success('取消收藏');
+                });
+            }
+        }
+    }
+};
+</script>
+
+<style lang="less" scoped>
+.news-small {
+    width: calc(354px - 32px);
+    margin: 8px 16px;
+    position: relative;
+    background-color: var(--bglight);
+    display: inline-flex;
+    border-radius: 12px;
+    overflow: hidden;
+
+    .bg {
+        position: absolute;
+        width: 92px;
+        height: 116px;
+        right: 0;
+        bottom: 0;
+        z-index: 0;
+    }
+
+    .van-image {
+        display: block;
+        position: relative;
+        z-index: 2;
+        flex-shrink: 0;
+    }
+    .content {
+        padding: 16px 15px 16px 12px;
+        .flex-col();
+        flex-grow: 1;
+        .name {
+            font-size: 16px;
+            font-weight: bold;
+            color: var(--text1);
+            line-height: 22px;
+        }
+
+        .van-button {
+            width: 68px;
+            background-color: var(--fadePrim);
+            color: var(--prim);
+            font-size: 14px;
+            border-width: 0;
+        }
+
+        .flex1 {
+            flex-grow: 1;
+        }
+
+        .sales-list {
+            margin-top: 8px;
+            .flex();
+        }
+    }
+
+    .text {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+    }
+
+    .top-bg {
+        width: 100%;
+        display: block;
+        position: absolute;
+        z-index: 2;
+        top: 0;
+        left: 0;
+    }
+
+    &.isLight {
+        background-color: @bg;
+        .content {
+            .name {
+                color: #000000;
+            }
+            .bottom {
+                .miner {
+                    color: #939599;
+                }
+            }
+        }
+    }
+
+    &.news-theme4 {
+        .content {
+            .van-button {
+                width: 68px;
+                --van-button-plain-background-color: var(--fadePrimlight);
+                --van-button-primary-background-color: var(--primlight);
+                --van-button-mini-font-size: 14px;
+            }
+        }
+    }
+}
+</style>

+ 7 - 1
src/main/vue/src/components/phone/module.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="module">
         <div class="phone" :class="[theme]">
-            <phone-home :theme="theme"></phone-home>
+            <phone-home :theme="theme" :companyInfo="companyInfo"></phone-home>
         </div>
         <div class="phone-bg">
             <div class="bg" :style="{ backgroundImage: `url(${require('../../assets/phone.png')})` }"></div>
@@ -16,6 +16,12 @@ export default {
         theme: {
             type: String,
             default: 'theme2'
+        },
+        companyInfo: {
+            type: Object,
+            default: () => {
+                return {};
+            }
         }
     },
     components: { phoneHome }

+ 53 - 19
src/main/vue/src/components/phone/productLarge.vue

@@ -1,5 +1,5 @@
 <template>
-    <div class="product">
+    <div class="product" :class="[`product-${theme}`]">
         <van-image
             :radius="30"
             width="100%"
@@ -30,7 +30,7 @@
                     </div>
                     <div class="flex1"></div>
                     <div class="price" v-if="info.salable">
-                        <img src="../../assets/icon_jiage.png" alt="" />
+                        <van-icon name="icon-icon_jiage" class-prefix="font_family" />
                         <span> {{ info.price }}</span>
                     </div>
                     <div v-else class="status">仅展示</div>
@@ -38,12 +38,17 @@
             </div>
 
             <div class="sold xianliang" v-if="time && info.scheduleSale">
-                <img src="../../assets/shizhong.png" alt="" />
+                <van-icon name="icon-clock" class-prefix="font_family" />
                 <span>即将开售:{{ startTime || time }}</span>
             </div>
-
-            <div class="sold" v-if="isSolded">已售罄</div>
-            <div class="sold" v-else-if="isSold" style="color: #ff4f50">即将售罄</div>
+            <div class="sold" v-if="isSolded">
+                <van-icon name="icon-jinzhi" class-prefix="font_family" />
+                <span>已售罄</span>
+            </div>
+            <div class="sold" v-else-if="isSold">
+                <van-icon name="icon-info_icon_qianggouzhong" class-prefix="font_family" />
+                <span>即将售罄</span>
+            </div>
         </template>
 
         <div class="content" v-else>
@@ -60,8 +65,8 @@
             </div>
         </div>
 
-        <img class="bg" src="../../assets/png-diwen2.png" alt="" />
-        <!-- <img class="top-bg" v-if="time" src="@assets/collecbg.png" alt="" /> -->
+        <img class="bg" v-if="theme === 'theme4'" src="../../assets/png-diwen-bai.png" alt="" />
+        <img class="bg" v-else src="../../assets/png-diwen2.png" alt="" />
     </div>
 </template>
 
@@ -79,6 +84,10 @@ export default {
         type: {
             type: String,
             default: 'collection'
+        },
+        theme: {
+            type: String,
+            default: 'theme2'
         }
     },
     computed: {
@@ -177,7 +186,7 @@ export default {
         .name {
             font-size: 14px;
             font-weight: bold;
-            color: #fff;
+            color: var(--text1);
             line-height: 24px;
         }
 
@@ -198,7 +207,7 @@ export default {
                 display: inline-block;
                 &.sales-fir {
                     background: var(--prim);
-                    color: #000000;
+                    color: var(--text2);
                 }
                 background-color: var(--fadePrim);
                 color: var(--prim);
@@ -226,12 +235,13 @@ export default {
 
             .price {
                 font-size: 20px;
-                color: #fff;
                 line-height: 20px;
-                font-family: OSP;
-                img {
-                    width: 8px;
-                    display: inline-block;
+                color: var(--text1);
+                .flex();
+                .font_family {
+                    font-size: 8px;
+                    line-height: 16px;
+                    margin-top: 4px;
                 }
             }
         }
@@ -251,22 +261,46 @@ export default {
         top: 0;
         left: 0;
     }
+
+    &.product-theme4 {
+        .content {
+            .sales {
+                span {
+                    background: var(--fadePrimlight);
+                    &.sales-fir {
+                        background: var(--primlight);
+                    }
+                }
+            }
+        }
+        .xianliang {
+            color: var(--primlight);
+        }
+    }
 }
 
 .status {
     font-size: 14px;
-    color: #fff;
+    color: var(--text1);
 }
 .sold {
-    background-color: #ffffff;
+    background-color: var(--bglight);
     font-size: 12px;
-    color: #939599;
-    padding: 0 17px;
+    color: var(--info);
+    padding: 0 17px 0 10px;
     border-radius: 13px;
     line-height: 24px;
     position: absolute;
     top: 16px;
     left: 16px;
     z-index: 3;
+    .flex();
+    .font_family {
+        margin-right: 4px;
+    }
+
+    &.preSold {
+        color: var(--danger);
+    }
 }
 </style>

+ 9 - 1
src/main/vue/src/main.js

@@ -23,11 +23,14 @@ import CollectionCoupon from '@/components/CollectionCoupon';
 import CreatedAtPicker from '@/components/CreatedAtPicker';
 import { mapGetters } from 'vuex';
 import Vant from 'vant';
+import queryString from 'query-string';
+
 import 'vant/lib/index.css';
 
 import 'normalize.css/normalize.css';
 import './styles/element_theme/index.css';
 import './styles/theme.less';
+import './styles/font.less';
 import theme from '!less-vars-loader!./styles/common/theme.less';
 // import VueAMap from "vue-amap";
 // Vue.use(VueAMap);
@@ -81,8 +84,13 @@ Vue.mixin({
 });
 Vue.prototype.$theme = theme;
 console.log(theme);
-new Vue({
+const app = new Vue({
     router,
     store,
     render: h => h(App)
 }).$mount('#app');
+
+let query = queryString.parse(location.search);
+if (query.companyId) {
+    sessionStorage.setItem('companyId', query.companyId);
+}

+ 3 - 1
src/main/vue/src/store.js

@@ -23,7 +23,9 @@ export default new Vuex.Store({
     actions: {},
     getters: {
         companyId(state) {
-            if (state.userInfo) {
+            if (sessionStorage.getItem('companyId')) {
+                return sessionStorage.getItem('companyId');
+            } else if (state.userInfo) {
                 return state.userInfo.companyId || 1;
             }
             return 1;

+ 145 - 0
src/main/vue/src/styles/font.less

@@ -0,0 +1,145 @@
+@font-face {
+    font-family: 'OSP';
+    src: url(https://cdn.raex.vip/font/OSP-DIN.ttf);
+}
+
+@font-face {
+    font-family: 'DIN';
+    src: url(https://cdn.raex.vip/font/OSP-DIN.ttf);
+}
+@font-face {
+    font-family: 'SourceHanSans-Medium';
+    src: url(https://cdn.raex.vip/font/SourceHanSans-Medium.otf);
+}
+@font-face {
+    font-family: 'font_family'; /* Project id 2852142 */
+    src: url('//at.alicdn.com/t/c/font_2852142_o0irw07uokf.woff2?t=1665558717758') format('woff2'),
+        url('//at.alicdn.com/t/c/font_2852142_o0irw07uokf.woff?t=1665558717758') format('woff'),
+        url('//at.alicdn.com/t/c/font_2852142_o0irw07uokf.ttf?t=1665558717758') format('truetype');
+}
+.font_family {
+    font-family: 'font_family' !important;
+    font-size: 16px;
+    font-style: normal;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+}
+.flex1{
+    flex-grow: 1;
+}
+.font_family-icon-icon_inter1:before {
+    content: '\e64e';
+    font-size: 24px;
+}
+.font_family-icon-icon_inter:before {
+    content: '\e631';
+    font-size: 24px;
+}
+.font_family-icon-phone:before {
+    content: '\e64b';
+    font-size: 20px;
+}
+
+.font_family-icon-weibiaoti--:before {
+    content: '\e616';
+    font-size: 20px;
+}
+.font_family-icon-jinzhi:before {
+    content: '\e610';
+    font-size: 18px;
+}
+.font_family-icon-clock:before {
+    content: '\e617';
+    font-size: 18px;
+}
+.font_family-icon-info_icon_qianggouzhong:before {
+    content: '\e64d';
+    font-size: 18px;
+}
+.font_family-icon-yanzhengma:before {
+    content: '\e624';
+    font-size: 20px;
+}
+.font_family-icon-icon_gouxuan_pre:before {
+    content: '\e64a';
+    font-size: 24px;
+}
+
+.font_family-icon-tabbar_icon_02:before {
+    content: '\e646';
+    font-size: 28px;
+}
+
+.font_family-icon-tabbar_icon_04:before {
+    content: '\e647';
+    font-size: 28px;
+}
+
+.font_family-icon-tabbar_icon_03:before {
+    content: '\e648';
+    font-size: 28px;
+}
+
+.font_family-icon-tabbar_icon_01:before {
+    content: '\e649';
+    font-size: 28px;
+}
+
+.icon-png-u-weixuanzhong:before {
+    content: '\e640';
+}
+
+.icon-png-sr-weixuanzhong:before {
+    content: '\e641';
+}
+
+.icon-png-ssr-xuanzhong:before {
+    content: '\e642';
+}
+
+.icon-png-right-bg:before {
+    content: '\e643';
+}
+
+.icon-png-middle-bg:before {
+    content: '\e644';
+}
+
+.icon-png-left-bg:before {
+    content: '\e645';
+}
+
+.icon-help::before {
+    content: '\e752';
+}
+
+.icon-sanjiao::before {
+    content: '\e63f';
+    display: block;
+}
+
+.icon-a-icon-dianzan5:before {
+    content: '\e634';
+}
+
+.icon-icon_inter:before {
+    content: '\e631';
+}
+.font_family-icon-icon_jiage {
+    display: inline-block;
+    font-size: 8px;
+    line-height: 8px;
+}
+.font_family-icon-icon_jiage:before {
+    content: '\e632';
+    font-size: 8px;
+    line-height: 8px;
+}
+.icon-icon_jiage:before {
+    content: '\e632';
+    font-size: 8px;
+    line-height: 8px;
+}
+.icon-a-icon-dianzan2:before {
+    content: '\e633';
+}

+ 75 - 13
src/main/vue/src/styles/theme.less

@@ -5,17 +5,13 @@
     --primlight: #3ab200;
     --btnText: #fff;
     --bglight:#373b3e;
-    --swiper-theme-color:var(--prim);
-    --van-primary-color: var(--prim);
-    --van-tabbar-item-active-color: var(--prim);
-    --van-tabs-default-color: var(--prim);
-    --van-tabs-bottom-bar-color: var(--prim);
-    --van-tab-active-text-color: var(--prim);
-    --van-field-error-message-color: var(--prim);
-    --van-button-primary-background-color: var(--prim);
-    --van-button-primary-border-color: var(--prim);
-    --van-number-keyboard-button-background-color: var(--prim);
-    --van-button-primary-background-color:var(--prim);
+    --danger:#FF4F50;
+    --info:#939599;
+    --bg:#181818;
+    --bg2:#272b2e;
+    --bg3:#272b2e;
+    --text1:#fff;
+    --text2:#000;
 }
 
 .theme2 {
@@ -25,7 +21,13 @@
     --primlight: #f5ab23;
     --btnText: #000;
     --bglight:#2F2519;
-    --swiper-theme-color:var(--prim);
+    --danger:#FF4F50;
+    --info:#939599;
+    --bg:#181818;
+    --bg2:#272b2e;
+    --bg3:#272b2e;
+    --text1:#fff;
+    --text2:#000;
     --van-primary-color: var(--prim);
     --van-tabbar-item-active-color: var(--prim);
     --van-tabs-default-color: var(--prim);
@@ -45,7 +47,13 @@
     --primlight: #979DFF;
     --btnText: #000;
     --bglight:#3C3766;
-    --swiper-theme-color:var(--prim);
+    --danger:#FF4F50;
+    --info:#939599;
+    --bg:#181818;
+    --bg2:#272b2e;
+    --bg3:#272b2e;
+    --text1:#fff;
+    --text2:#000;
     --van-primary-color: var(--prim);
     --van-tabbar-item-active-color: var(--prim);
     --van-tabs-default-color: var(--prim);
@@ -57,3 +65,57 @@
     --van-number-keyboard-button-background-color: var(--prim);
     --van-button-primary-background-color:var(--prim);
 }
+
+.theme4 {
+    --prim: #51B8FF;
+    --fadePrim: #51B8FF20;
+    --darkPrim: #367bd7;
+    --primlight: #4092FF;
+    --fadePrimlight: #4092FF20;
+    --btnText: #000;
+    --bglight:#fff;
+    --danger:#FF4F50;
+    --info:#939599;
+    --bg:#fff;
+    --bg2:#F5F7FA;
+    --bg3:#fff;
+    --text1:#000;
+    --text2:#fff;
+    --van-primary-color: var(--prim);
+    --van-tabbar-item-active-color: var(--prim);
+    --van-tabs-default-color: var(--prim);
+    --van-tabs-bottom-bar-color: var(--prim);
+    --van-tab-active-text-color: var(--prim);
+    --van-field-error-message-color: var(--prim);
+    --van-button-primary-background-color: var(--prim);
+    --van-button-primary-border-color: var(--prim);
+    --van-number-keyboard-button-background-color: var(--prim);
+    --van-button-primary-background-color:var(--prim);
+}
+
+.theme5 {
+    --prim: #51B8FF;
+    --fadePrim: #51B8FF20;
+    --darkPrim: #367bd7;
+    --primlight: #4092FF;
+    --fadePrimlight: #4092FF20;
+    --btnText: #000;
+    --bglight:#1C1C1C;
+    --danger:#FF4F50;
+    --info:#939599;
+    --bg:#1C1C1C;
+    --bg2:#000000;
+    --bg3:#1C1C1C;
+    --text1:#fff;
+    --text2:#000;
+    --van-primary-color: var(--prim);
+    --van-tabbar-item-active-color: var(--prim);
+    --van-tabs-default-color: var(--prim);
+    --van-tabs-bottom-bar-color: var(--prim);
+    --van-tab-active-text-color: var(--prim);
+    --van-field-error-message-color: var(--prim);
+    --van-button-primary-background-color: var(--prim);
+    --van-button-primary-border-color: var(--prim);
+    --van-number-keyboard-button-background-color: var(--prim);
+    --van-button-primary-background-color:var(--prim);
+}

+ 20 - 20
src/main/vue/src/views/Admin.vue

@@ -176,7 +176,7 @@ export default {
             findActiveMenu([], this.rawMenus);
         },
         getMenus() {
-            if (this.userInfo.authorities.find(i => i.name === 'ROLE_SAAS')) {
+            if (this.isCompany) {
                 let menus = [
                     {
                         id: '3410316',
@@ -280,36 +280,36 @@ export default {
                         active: true,
                         category: '',
                         children: [
+                            // {
+                            //     id: '3252238',
+                            //     name: '轮播图',
+                            //     path: '/bannerList',
+                            //     icon: '',
+                            //     sort: 34,
+                            //     parent: '3252226',
+                            //     root: false,
+                            //     active: true
+                            // },
                             {
-                                id: '3252238',
-                                name: '轮播图',
-                                path: '/bannerList',
+                                id: '3252239',
+                                name: '首页推荐',
+                                path: '/recommendList',
                                 icon: '',
-                                sort: 34,
+                                sort: 35,
                                 parent: '3252226',
                                 root: false,
                                 active: true
                             },
                             {
-                                id: '3252239',
-                                name: '首页推荐',
-                                path: '/recommendList',
+                                id: '3252230',
+                                name: '新闻管理',
+                                path: '/newsList',
                                 icon: '',
-                                sort: 35,
+                                sort: 36,
                                 parent: '3252226',
                                 root: false,
                                 active: true
-                            },
-                            // {
-                            //     id: '3252230',
-                            //     name: '新闻管理',
-                            //     path: '/newsList',
-                            //     icon: '',
-                            //     sort: 36,
-                            //     parent: '3252226',
-                            //     root: false,
-                            //     active: true
-                            // }
+                            }
                         ]
                     },
                     {

+ 1 - 7
src/main/vue/src/views/BlindBoxEdit.vue

@@ -186,13 +186,7 @@
                             ></el-date-picker>
                         </div>
                     </el-form-item>
-                    <el-form-item
-                        prop="salable"
-                        label="可售"
-                        v-if="
-                            formData.onShelf === true || (formData.scanCode === true && formData.scheduleSale === false)
-                        "
-                    >
+                    <el-form-item prop="salable" label="可售">
                         <el-radio v-model="formData.salable" :label="true">是</el-radio>
                         <el-radio v-model="formData.salable" :label="false">仅展示</el-radio>
                     </el-form-item>

+ 24 - 27
src/main/vue/src/views/CollectionEdit.vue

@@ -188,6 +188,10 @@
                             <el-option label="蚂蚁链" :value="1" :key="1"></el-option>
                         </el-select>
                     </el-form-item>
+                    <el-form-item prop="messNumber" label="编号乱序">
+                        <el-radio v-model="formData.messNumber" :label="true">是</el-radio>
+                        <el-radio v-model="formData.messNumber" :label="false">否</el-radio>
+                    </el-form-item>
                     <!-- <el-form-item prop="likes" label="点赞">
                         <el-input-number v-model="formData.likes"></el-input-number>
                     </el-form-item> -->
@@ -220,13 +224,7 @@
                         </el-form-item>
                     </div>
 
-                    <el-form-item
-                        prop="salable"
-                        label="可售"
-                        v-if="
-                            formData.onShelf === true || (formData.scanCode === true && formData.scheduleSale === false)
-                        "
-                    >
+                    <el-form-item prop="salable" label="可售">
                         <el-radio v-model="formData.salable" :label="true">是</el-radio>
                         <el-radio v-model="formData.salable" :label="false">仅展示</el-radio>
                     </el-form-item>
@@ -338,19 +336,19 @@
                     </el-form-item>
                     <el-form-item prop="newsId" label="新闻">
                         <el-select v-model="formData.newsId" placeholder="请选择">
-                        <el-option
-                            v-for="item in newsOptions"
-                            :key="item.id"
-                            :label="item.title"
-                            :value="item.id"
-                            class="minter-item"
-                        >
-<!--                            <el-image :src="item.pic" fit="cover" class="avatar"></el-image>-->
-                            <div class="content">
-                                <div class="name">{{ item.title }}</div>
-                                <div class="id">#{{ item.subTitle }}</div>
-                            </div>
-                        </el-option>
+                            <el-option
+                                v-for="item in newsOptions"
+                                :key="item.id"
+                                :label="item.title"
+                                :value="item.id"
+                                class="minter-item"
+                            >
+                                <!--                            <el-image :src="item.pic" fit="cover" class="avatar"></el-image>-->
+                                <div class="content">
+                                    <div class="name">{{ item.title }}</div>
+                                    <div class="id">#{{ item.subTitle }}</div>
+                                </div>
+                            </el-option>
                         </el-select>
                     </el-form-item>
                     <el-form-item class="form-submit">
@@ -435,12 +433,10 @@ export default {
                 }
                 return resolve();
             }),
-            this.$http
-                .post('/news/all', { size: 10000, query: { del: false } }, { body: 'json' })
-                .then(res => {
-                    this.newsOptions = res.content;
-                    return Promise.resolve();
-                }),
+            this.$http.post('/news/all', { size: 10000, query: { del: false } }, { body: 'json' }).then(res => {
+                this.newsOptions = res.content;
+                return Promise.resolve();
+            }),
             this.$http
                 .post('/privilegeOption/all', { size: 10000, query: { del: false } }, { body: 'json' })
                 .then(res => {
@@ -484,7 +480,8 @@ export default {
                 assignment: 0,
                 couponPayment: false,
                 chainFlag: 3,
-                vip: false
+                vip: false,
+                messNumber: false
             },
             rules: {
                 name: [

+ 1 - 7
src/main/vue/src/views/CompanyBlindBoxEdit.vue

@@ -186,13 +186,7 @@
                             ></el-date-picker>
                         </div>
                     </el-form-item>
-                    <el-form-item
-                        prop="salable"
-                        label="可售"
-                        v-if="
-                            formData.onShelf === true || (formData.scanCode === true && formData.scheduleSale === false)
-                        "
-                    >
+                    <el-form-item prop="salable" label="可售">
                         <el-radio v-model="formData.salable" :label="true">是</el-radio>
                         <el-radio v-model="formData.salable" :label="false">仅展示</el-radio>
                     </el-form-item>

+ 7 - 8
src/main/vue/src/views/CompanyCollectionEdit.vue

@@ -188,6 +188,10 @@
                             <el-option label="蚂蚁链" :value="1" :key="1"></el-option>
                         </el-select>
                     </el-form-item>
+                    <el-form-item prop="messNumber" label="编号乱序">
+                        <el-radio v-model="formData.messNumber" :label="true">是</el-radio>
+                        <el-radio v-model="formData.messNumber" :label="false">否</el-radio>
+                    </el-form-item>
                     <!-- <el-form-item prop="likes" label="点赞">
                         <el-input-number v-model="formData.likes"></el-input-number>
                     </el-form-item> -->
@@ -220,13 +224,7 @@
                         </el-form-item>
                     </div>
 
-                    <el-form-item
-                        prop="salable"
-                        label="可售"
-                        v-if="
-                            formData.onShelf === true || (formData.scanCode === true && formData.scheduleSale === false)
-                        "
-                    >
+                    <el-form-item prop="salable" label="可售">
                         <el-radio v-model="formData.salable" :label="true">是</el-radio>
                         <el-radio v-model="formData.salable" :label="false">仅展示</el-radio>
                     </el-form-item>
@@ -459,7 +457,8 @@ export default {
                 scanCode: false,
                 noSoldOut: true,
                 assignment: 0,
-                couponPayment: false
+                couponPayment: false,
+                messNumber: false
             },
             rules: {
                 name: [

+ 5 - 1
src/main/vue/src/views/CompanyList.vue

@@ -62,11 +62,12 @@
                     <el-tag :type="row.disabled ? '' : 'info'">{{ row.disabled }}</el-tag>
                 </template>
             </el-table-column>
-            <el-table-column label="操作" align="center" fixed="right" width="220">
+            <el-table-column label="操作" align="center" fixed="right" width="300">
                 <template slot-scope="{ row }">
                     <el-button @click="adminMgmt(row)" size="mini">管理员</el-button>
                     <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
                     <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                    <el-button @click="admin(row)" size="mini" type="">后台</el-button>
                 </template>
             </el-table-column>
         </el-table>
@@ -195,6 +196,9 @@ export default {
                     id: row.id
                 }
             });
+        },
+        admin(row) {
+            window.open(`/admin?companyId=${row.id}&from=super`, '_blank');
         }
     }
 };

+ 4 - 4
src/main/vue/src/views/UserBalance.vue

@@ -12,7 +12,7 @@
                 <el-button @click="getData" slot="append" icon="el-icon-search"> </el-button>
             </el-input>
             <template v-if="$store.state.userInfo && $store.state.userInfo.username === 'xiong'">
-                <el-popover class="filter-item" placement="bottom" width="350" trigger="click" v-model="showSettlePop">
+                <!-- <el-popover class="filter-item" placement="bottom" width="350" trigger="click" v-model="showSettlePop">
                     <el-date-picker
                         type="daterange"
                         value-format="yyyy-MM-dd"
@@ -63,7 +63,7 @@
                 </el-button>
                 <el-button class="filter-item" type="primary" @click="showImportDialog = true" size="mini">
                     导入结果
-                </el-button>
+                </el-button> -->
             </template>
         </div>
         <el-table
@@ -81,10 +81,10 @@
             <el-table-column prop="userId" label="用户ID"></el-table-column>
             <el-table-column prop="balance" label="余额"></el-table-column>
             <el-table-column prop="locked" label="锁定" width="100" align="center">
-                <template slot="header" slot-scope="{ column }">
+                <template v-slot:header="{ column }">
                     <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
                 </template>
-                <template v-slot="{ row }">
+                <template v-slot:default="{ row }">
                     <el-tag :type="row.locked ? 'danger' : 'info'">{{ row.locked ? '是' : '否' }}</el-tag>
                 </template>
             </el-table-column>

+ 15 - 4
src/main/vue/src/views/company/CompanyTheme.vue

@@ -16,6 +16,9 @@
                     size="small"
                     style="max-width: 750px"
                 >
+                    <el-form-item prop="logo" label="LOGO">
+                        <single-upload v-model="formData.logo"></single-upload>
+                    </el-form-item>
                     <el-form-item label="选择主题">
                         <el-radio-group v-model="formData.theme">
                             <el-radio :label="item.value" v-for="(item, index) in themeOptions" :key="index">
@@ -34,7 +37,7 @@
                 </el-form>
 
                 <div class="phone">
-                    <phone-module :theme="formData.theme"></phone-module>
+                    <phone-module :theme="formData.theme" :companyInfo="formData"></phone-module>
                 </div>
             </div>
             <el-dialog title="添加藏品" :visible.sync="showCollectionDialog" width="500px">
@@ -84,6 +87,7 @@ export default {
         this.$http.get(`/company/get/${this.companyId}`).then(res => {
             if (res.theme) {
                 this.formData.theme = res.theme;
+                this.formData.logo = res.logo;
             }
         });
     },
@@ -91,13 +95,16 @@ export default {
         return {
             saving: false,
             formData: {
-                theme: 'theme1'
+                theme: 'theme1',
+                logo: ''
             },
             rules: {},
             themeOptions: [
                 { label: '主题1', value: 'theme1' },
                 { label: '主题2', value: 'theme2' },
-                { label: '主题3', value: 'theme3' }
+                { label: '主题3', value: 'theme3' },
+                { label: '主题4', value: 'theme4' },
+                { label: '主题5', value: 'theme5' }
             ],
             settings: [],
             collections: [],
@@ -114,7 +121,11 @@ export default {
     methods: {
         onSave() {
             this.$http
-                .post('/company/save', { id: this.companyId, theme: this.formData.theme }, { body: 'json' })
+                .post(
+                    '/company/save',
+                    { id: this.companyId, theme: this.formData.theme, logo: this.formData.logo },
+                    { body: 'json' }
+                )
                 .then(res => {
                     this.$message.success('保存成功');
                 });

+ 25 - 0
src/main/vue/yarn.lock

@@ -4154,6 +4154,11 @@ fill-range@^7.0.1:
   dependencies:
     to-regex-range "^5.0.1"
 
+filter-obj@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
+  integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==
+
 finalhandler@1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
@@ -6987,6 +6992,16 @@ query-string@^4.1.0:
     object-assign "^4.1.0"
     strict-uri-encode "^1.0.0"
 
+query-string@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.npmmirror.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1"
+  integrity sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w==
+  dependencies:
+    decode-uri-component "^0.2.0"
+    filter-obj "^1.1.0"
+    split-on-first "^1.0.0"
+    strict-uri-encode "^2.0.0"
+
 querystring-es3@^0.2.0:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@@ -7695,6 +7710,11 @@ spdy@^4.0.2:
     select-hose "^2.0.0"
     spdy-transport "^3.0.0"
 
+split-on-first@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
+  integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
+
 split-string@^3.0.1, split-string@^3.0.2:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@@ -7813,6 +7833,11 @@ strict-uri-encode@^1.0.0:
   resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
   integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==
 
+strict-uri-encode@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
+  integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==
+
 string-width@^3.0.0, string-width@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"

+ 38 - 6
src/test/java/com/izouma/nineth/CommonTest.java

@@ -1,23 +1,22 @@
+
 package com.izouma.nineth;
 
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.serializer.SerializerFeature;
-import com.alipay.api.AlipayApiException;
-import com.alipay.api.internal.util.AlipaySignature;
 import com.fasterxml.jackson.annotation.JsonView;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.github.kevinsawicki.http.HttpRequest;
 import com.google.common.base.Splitter;
+import com.google.common.hash.Hashing;
 import com.izouma.nineth.config.Constants;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.dto.SandPaySettle;
 import com.izouma.nineth.dto.UserWithdraw;
 import com.izouma.nineth.service.IdentityAuthService;
-import com.izouma.nineth.service.UserService;
 import com.izouma.nineth.utils.*;
 import com.izouma.nineth.web.BaseController;
 import io.ipfs.api.IPFS;
@@ -76,7 +75,6 @@ import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.math.RoundingMode;
-import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -90,10 +88,8 @@ import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.X509EncodedKeySpec;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
-import java.text.NumberFormat;
 import java.time.Duration;
 import java.time.LocalDate;
-import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.List;
@@ -101,8 +97,10 @@ import java.util.*;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
 import static java.nio.file.StandardOpenOption.CREATE;
@@ -753,4 +751,38 @@ public class CommonTest {
     public void resolve() {
         System.out.println(UrlUtils.resolve("http://www.baidu.com", "123", "456"));
     }
+
+    @Test
+    public void hash() {
+        long t = System.currentTimeMillis();
+        Map<Integer, Integer> map = new HashMap<>();
+        for (int i = 0; i < 5000; i++) {
+            map.put(i, Hashing.md5().hashString(i + "abcd", StandardCharsets.UTF_8).asInt());
+        }
+        AtomicInteger i = new AtomicInteger();
+        map.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEach(e -> {
+            System.out.println(i.incrementAndGet() + ":" + e.getKey() + " : " + e.getValue());
+        });
+        System.out.println(System.currentTimeMillis() - t);
+    }
+
+
+    public int randomize(int number, int total) {
+        Map<Integer, Integer> map = new HashMap<>();
+        for (int i = 0; i < total; i++) {
+            map.put(i, Hashing.md5().hashString(i + "abcd", StandardCharsets.UTF_8).asInt());
+        }
+        int i = 0;
+        Map<Integer, Integer> sorted = new HashMap<>();
+        for (Integer integer : map.entrySet().stream().sorted(Map.Entry.comparingByValue()).map(Map.Entry::getKey).collect(Collectors.toList())) {
+            sorted.put(integer, i++);
+        }
+        return sorted.get(number);
+    }
+
+
+    @Test
+    public void testrandomize() {
+        System.out.println(randomize(6, 1000));
+    }
 }