소스 검색

上传图片

nieyuge 2 년 전
부모
커밋
bd20dd16dd

+ 1 - 0
package.json

@@ -26,6 +26,7 @@
     "qrcode": "^1.5.0",
     "sass-loader": "^12.6.0",
     "vue": "^3.2.13",
+    "vue-cropper": "^1.0.3",
     "vue-router": "^4.0.14"
   },
   "devDependencies": {

BIN
src/assets/img/icon-header-cover.png


+ 8 - 0
src/assets/svg/icon-last-time.svg

@@ -0,0 +1,8 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<mask id="path-1-outside-1_21276_235944" maskUnits="userSpaceOnUse" x="0" y="0" width="14" height="14" fill="black">
+<rect fill="white" width="14" height="14"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1 7C1 3.69143 3.69143 1 7 1C10.3086 1 13 3.69143 13 7C13 10.3086 10.3086 13 7 13C3.69143 13 1 10.3086 1 7ZM1.861 7C1.861 9.83329 4.16629 12.139 7 12.139C9.83329 12.139 12.139 9.83329 12.139 7C12.139 4.16629 9.83329 1.861 7 1.861C4.16629 1.861 1.861 4.16629 1.861 7ZM7.13644 7.36158H9.01036C9.21841 7.36158 9.38672 7.54844 9.38672 7.7815C9.38672 8.01455 9.21841 8.20141 9.01036 8.20141H6.76158C6.55391 8.20141 6.38672 8.01455 6.38672 7.7815V4.42301C6.38672 4.19038 6.55353 4.00141 6.76158 4.00141C6.96962 4.00141 7.13644 4.18995 7.13644 4.42301V7.36158Z"/>
+</mask>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1 7C1 3.69143 3.69143 1 7 1C10.3086 1 13 3.69143 13 7C13 10.3086 10.3086 13 7 13C3.69143 13 1 10.3086 1 7ZM1.861 7C1.861 9.83329 4.16629 12.139 7 12.139C9.83329 12.139 12.139 9.83329 12.139 7C12.139 4.16629 9.83329 1.861 7 1.861C4.16629 1.861 1.861 4.16629 1.861 7ZM7.13644 7.36158H9.01036C9.21841 7.36158 9.38672 7.54844 9.38672 7.7815C9.38672 8.01455 9.21841 8.20141 9.01036 8.20141H6.76158C6.55391 8.20141 6.38672 8.01455 6.38672 7.7815V4.42301C6.38672 4.19038 6.55353 4.00141 6.76158 4.00141C6.96962 4.00141 7.13644 4.18995 7.13644 4.42301V7.36158Z" fill="#91989C"/>
+<path d="M7.13644 7.36158H7.03644V7.46158H7.13644V7.36158ZM7 0.9C3.6362 0.9 0.9 3.6362 0.9 7H1.1C1.1 3.74666 3.74666 1.1 7 1.1V0.9ZM13.1 7C13.1 3.6362 10.3638 0.9 7 0.9V1.1C10.2533 1.1 12.9 3.74666 12.9 7H13.1ZM7 13.1C10.3638 13.1 13.1 10.3638 13.1 7H12.9C12.9 10.2533 10.2533 12.9 7 12.9V13.1ZM0.9 7C0.9 10.3638 3.6362 13.1 7 13.1V12.9C3.74666 12.9 1.1 10.2533 1.1 7H0.9ZM7 12.039C4.22152 12.039 1.961 9.77806 1.961 7H1.761C1.761 9.88851 4.11105 12.239 7 12.239V12.039ZM12.039 7C12.039 9.77806 9.77806 12.039 7 12.039V12.239C9.88851 12.239 12.239 9.88851 12.239 7H12.039ZM7 1.961C9.77806 1.961 12.039 4.22152 12.039 7H12.239C12.239 4.11105 9.88851 1.761 7 1.761V1.961ZM1.961 7C1.961 4.22151 4.22151 1.961 7 1.961V1.761C4.11106 1.761 1.761 4.11106 1.761 7H1.961ZM9.01036 7.26158H7.13644V7.46158H9.01036V7.26158ZM9.48672 7.7815C9.48672 7.50365 9.28352 7.26158 9.01036 7.26158V7.46158C9.1533 7.46158 9.28672 7.59324 9.28672 7.7815H9.48672ZM9.01036 8.30141C9.28352 8.30141 9.48672 8.05935 9.48672 7.7815H9.28672C9.28672 7.96975 9.1533 8.10141 9.01036 8.10141V8.30141ZM6.76158 8.30141H9.01036V8.10141H6.76158V8.30141ZM6.28672 7.7815C6.28672 8.05887 6.48837 8.30141 6.76158 8.30141V8.10141C6.61944 8.10141 6.48672 7.97023 6.48672 7.7815H6.28672ZM6.28672 4.42301V7.7815H6.48672V4.42301H6.28672ZM6.76158 3.90141C6.48739 3.90141 6.28672 4.14674 6.28672 4.42301H6.48672C6.48672 4.23401 6.61968 4.10141 6.76158 4.10141V3.90141ZM7.23644 4.42301C7.23644 4.14625 7.03571 3.90141 6.76158 3.90141V4.10141C6.90354 4.10141 7.03644 4.23366 7.03644 4.42301H7.23644ZM7.23644 7.36158V4.42301H7.03644V7.36158H7.23644Z" fill="#91989C" mask="url(#path-1-outside-1_21276_235944)"/>
+</svg>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 46
src/assets/svg/img-A0.svg


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 46
src/assets/svg/img-A1.svg


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 30 - 30
src/assets/svg/img-B0.svg


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 30 - 30
src/assets/svg/img-B1.svg


+ 4 - 0
src/assets/svg/img-new.svg

@@ -0,0 +1,4 @@
+<svg width="40" height="18" viewBox="0 0 40 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="40" height="18" rx="9" fill="#F9B04E"/>
+<path d="M9.50029 13V7.92432H9.54863L13.1741 13H14.5169V5.24951H12.9646V10.2983H12.9217L9.30156 5.24951H7.94805V13H9.50029ZM21.2461 11.6572H17.7334V9.70215H21.0474V8.45605H17.7334V6.58691H21.2461V5.24951H16.1113V13H21.2461V11.6572ZM27.3576 7.7041H27.406L28.8776 13H30.4084L32.4655 5.24951H30.7844L29.5598 10.8623H29.5168L28.0666 5.24951H26.697L25.2468 10.8623H25.2038L23.9846 5.24951H22.3034L24.3552 13H25.8859L27.3576 7.7041Z" fill="white"/>
+</svg>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 3 - 0
src/assets/svg/poster-after.svg


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2 - 0
src/assets/svg/poster-befor.svg


+ 3 - 0
src/assets/svg/poster-loading.svg

@@ -0,0 +1,3 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path opacity="0.2" d="M20.0026 0V5C23.2952 5.00069 26.4961 6.08476 29.1116 8.08498C31.7271 10.0852 33.6117 12.8904 34.4748 16.0679C35.338 19.2454 35.1316 22.6187 33.8876 25.6673C32.6435 28.7159 30.431 31.2704 27.5912 32.9369C24.7514 34.6034 21.4422 35.2892 18.174 34.8885C14.9058 34.4878 11.8603 33.0229 9.5072 30.7198C7.15411 28.4166 5.6242 25.4032 5.15348 22.1444C4.68277 18.8855 5.2974 15.5624 6.90256 12.6875L2.53757 10.25C0.39731 14.0832 -0.422189 18.5142 0.205487 22.8594C0.833164 27.2045 2.87314 31.2225 6.01072 34.2933C9.1483 37.3641 13.2091 39.3173 17.5668 39.8514C21.9244 40.3855 26.3368 39.471 30.1231 37.2488C33.9095 35.0266 36.8595 31.6203 38.518 27.5554C40.1765 23.4905 40.4514 18.9927 39.3003 14.7561C38.1492 10.5194 35.636 6.77918 32.1485 4.11243C28.6609 1.44568 24.3928 0.000557603 20.0026 0Z" fill="black"/>
+</svg>

+ 19 - 0
src/http/media.js

@@ -0,0 +1,19 @@
+import axios from 'axios'
+import { service } from "./request";
+
+export function uploadSignature(params) {
+    return service({
+        url: `/media/uploadSignature`,
+        method: "post",
+        data: params,
+    });
+}
+
+export function uploadFile({url, data, headers}) {
+    return axios({
+        method: 'PUT',
+        url: url,
+        data: data,
+        headers: headers
+    })
+}

+ 23 - 0
src/types/global.js

@@ -0,0 +1,23 @@
+/**
+ * 全局通用字段定义
+ */
+
+/**
+ * 玩法类型
+ * 普通任务:common=1;
+ * 抽奖:lottery=2
+ */
+export const PlayType = {
+  common: 1,
+  lottery: 2,
+};
+
+/**
+ * 奖品类型
+ * 货币:money=1;
+ * 自定义奖品:custom=2
+ */
+export const RewardType = {
+  money: 1,
+  custom: 2,
+};

+ 1 - 0
src/types/index.js

@@ -0,0 +1 @@
+export * from "@/types/global";

+ 143 - 6
src/view/components/custom-card-cover.vue

@@ -1,6 +1,24 @@
 <!-- 自定义卡片红包封面 -->
 <template>
-    <div class="not-open">
+    <div class="custom-card" v-if="data.customPosterUrl">
+        <img class="customImg" :src="data.customPosterUrl" />
+        <div class="common-bottom">
+            <div class="theme">
+                <img v-if="isLottaryCpd" class="icon" :src="require('@/assets/svg/icon-last-time.svg')"/>
+                <span v-if="isLottaryCpd" class="time" >{{formatSecondsAsDaysOrTime(data.validityDuration * 3600)}}</span>
+                <span class="info">{{isLottaryCpd ? 'Left' : 'Instant Giveaway'}}</span>
+            </div>
+            <div class="winner-info">
+                <span class="count">{{data.totalCount}}Winners</span>
+                <span>to Share </span>
+                <span class="prize-name">{{isMoneyRewardCpd ? data.amountValue + ' ' + data.tokenSymbol : data.customizedReward}}</span>
+            </div>
+            <div class="open" @click="open">
+                {{isLottaryCpd ? 'Participate Now' : 'Open Now'}}
+            </div>
+        </div>
+    </div>
+    <div class="not-open" v-else>
         <img class="cover" v-if="data.type == 2" :src="require('@/assets/svg/img-preview-draw-bg.svg')"  />
         <img class="cover" v-else :src="require('@/assets/subject/001-card.png')"  />
 
@@ -29,7 +47,7 @@
         <div class="money-area">
             <div class="txt">{{data.currencyCode == 'USD' ? 'USD' : data.tokenSymbol}} GIVEAWAY</div>
             <div class="coin">
-                <img :src="data.currencyIconUrl" />
+                <img :src="data.currencyIconUrl || imgHeaderCover" />
                 <span id="preview-after-amount"
                     :style="{
                         fontSize: amountFontSize + 'px'
@@ -47,8 +65,11 @@
 </template>
 
 <script setup>
-import { defineProps, defineEmits, watch, ref } from "vue";
+import { defineProps, defineEmits, watch, ref, computed } from "vue";
 import { formatSecondsAsDaysOrTime } from "@/uilts/help";
+import { RewardType, PlayType } from "@/types";
+
+const imgHeaderCover = require('@/assets/img/icon-header-cover.png');
 
 const props = defineProps({
     data: {
@@ -60,6 +81,7 @@ const props = defineProps({
                 tokenSymbol: "",
                 type: 1,
                 validityDuration: '',
+                customPosterUrl: '',
                 userInfo: {
                     avatarUrl: "",
                     nickName: "",
@@ -71,10 +93,21 @@ const props = defineProps({
 
 let amountFontSize = ref(60);
 
+let isMoneyRewardCpd =computed(() => {
+    return props.data.rewardType === RewardType.money
+});
+
+let isLottaryCpd = computed(() => props.data.type === PlayType.lottery);
+
 watch(() => props.data, () => {
-    let lenstr = document.querySelector('#preview-after-amount').innerHTML.length;
-    let num = parseInt(360/lenstr);
-    amountFontSize.value = num < 56 ? num : 56;
+    let id = isMoneyRewardCpd.value ? 'preview-after-amount' : 'custom-name';
+    let baseWidth = isMoneyRewardCpd.value ? 56 : 22;
+    let lendom = document.querySelector(`#${id}`)
+    if(lendom) {
+        let lenstr = lendom.innerHTML.length;
+        let num = parseInt(360/lenstr);
+        amountFontSize.value = num < baseWidth ? num : baseWidth;
+    }
 })
 
 
@@ -242,4 +275,108 @@ const open = () => {
         z-index: 3;
     }
 }
+
+.custom-card {
+    position: relative;
+    width: 100%;
+    overflow: hidden;
+    position: relative;
+    border-radius: 20px;
+    .customImg {
+        width: 100%;
+        min-height: 200px;
+    }
+    .cover {
+        width: 100%;
+        min-height: 350px;
+        border-radius: 20px 20px 0 0;
+    }
+    .gift {
+        width: 210px;
+        position: absolute;
+        left: 50%;
+        top: 83px;
+        transform: translateX(-50%);
+    }
+    .prize {
+        width: 100%;
+        position: absolute;
+        top: 284px;
+        left: 0;
+        height: 47px;
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        font-style: normal;
+        font-weight: 800;
+        font-size: 22px;
+        line-height: 47px;
+        letter-spacing: 0.3px;
+
+        .icon {
+            width: 24px;
+        }
+        .name {
+            padding: 0 7px;
+            color: #fff;
+        }
+        .total {
+            color: #F5C03F;
+        }
+    }
+    .common-bottom {
+        width: 100%;
+        height: 125px;
+        background:#111214;
+        border-radius: 0 0 20px 20px;
+        padding: 10px 16px;
+        font-weight: 500;
+        font-size: 12px;
+        line-height: 14px;
+        letter-spacing: 0.3px;
+        color: #838383;
+        line-height: 20px;
+        .theme {
+            display: flex;
+            height: 20px;
+            align-items: center;
+            justify-content: flex-start;
+            .icon {
+                width: 12px;
+            }
+            .time {
+                margin: 0 4px;
+                color: #1D9BF0;
+            }
+        }
+        .winner-info {
+            display: flex;
+            height: 20px;
+            align-items: center;
+            justify-content: flex-start;
+            margin-bottom: 13px;
+            .count{
+                color: #1D9BF0;
+                margin-right: 4px;
+            }
+            .prize-name {
+                color: #1D9BF0;
+                margin-left: 4px;
+            }
+        }
+        .open {
+            width: 100%;
+            height: 45px;
+            background: linear-gradient(180deg, #4AB6FF 0%, #1D9BF0 100%, #1D9BF0 100%);
+            border: 1.5px solid rgba(255, 255, 255, 0.15);
+            border-radius: 52px;
+            line-height: 45px;
+            text-align: center;
+            cursor: pointer;
+            font-weight: 800;
+            font-size: 16px;
+            color: #FFFFFF;
+        }
+    }
+}
 </style>

+ 199 - 0
src/view/components/custom-card-horizontal-cover.vue

@@ -0,0 +1,199 @@
+<!-- 自定义卡片红包封面 -->
+<template>
+    <div class="card-wrapper" v-if="data.customPosterUrl">
+        <img class="customImg" :src="data.customPosterUrl" />
+    </div>
+    <div class="card-wrapper" v-else>
+        <img :src="require('@/assets/img/img-preview-draw-after-bg.png')"
+            v-if="data.type == 2"
+            class="card-cover">
+        <img :src="require('@/assets/subject/img-card-cover-blue.png')"
+            v-else
+            class="card-cover"/>
+        <div class="bottom-bar" v-if="showBottom">
+            <div class="title">
+                DeNet.me
+            </div>
+            <div class="desc">
+                🎁 <template v-if="data.tokenSymbol=='USD'">$</template>{{data.amountValue}} GIVEAWAY
+            </div>
+        </div>
+        <div class="user-info">
+            <img :src="data.userInfo.avatarUrl" 
+            class="avatar"/> {{data.userInfo.name}}
+        </div>
+        <div class="content-text">
+            <div class="title">
+                {{data.tokenSymbol}} GIVEAWAY
+            </div>
+            <div class="center"
+                :style="{
+                    fontSize: amountFontSize + 'px'
+                }">
+                <img :src="data.currencyIconUrl || imgHeaderCover" class="icon">
+                <span id="preview-before-amount">
+                    {{data.amountValue}}
+                </span>
+            </div>
+            <div class="desc">
+                <template  v-if="data.type == 2">
+                    <img class="icon-clock" 
+                    :src="require('@/assets/svg/icon-preview-clock.svg')" />  {{data.validityDuration}} H
+                    <img class="icon-trophy" 
+                    :src="require('@/assets/svg/icon-preview-trophy.svg')" /> <span class="trophy-count">{{data.totalCount}} WINNERS</span>
+                </template>
+                <template v-else>
+                    {{data.totalCount}} WINNERS TO SHARE
+                </template>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { defineProps, defineEmits, watch, ref } from "vue";
+
+const imgHeaderCover = require('@/assets/img/icon-header-cover.png');
+
+const props = defineProps({
+    data: {
+        type: Object,
+        default: () => {
+            return {
+                totalCount: 0,
+                amountValue: 0,
+                tokenSymbol: "",
+                type: 1,
+                validityDuration: '',
+                customPosterUrl: '',
+                userInfo: {
+                    avatarUrl: "",
+                    nickName: "",
+                },
+            };
+        },
+    },
+    showBottom: {
+        type: Boolean,
+        default: true
+    }
+});
+
+let amountFontSize = ref(60);
+
+watch(() => props.data, () => {
+    let dom = document.querySelector('#preview-after-amount');
+    if (dom) {
+        let lenstr = dom && dom.innerHTML.length;
+        let num = parseInt(360/lenstr);
+        amountFontSize.value = num < 56 ? num : 56;
+    }
+})
+</script>
+
+<style scoped lang="scss">
+.card-wrapper {
+    width: 491px;
+    border: 1px solid #D1D9DD;
+    background: #ffffff;
+    box-sizing: border-box;
+    overflow: hidden;
+    position: relative;
+    box-sizing: border-box;
+    border-radius: 16px;
+    left: 73px;
+    top: 238px;
+
+    .customImg {
+        width: 100%;
+        min-height: 200px;
+    }
+
+    .user-info {
+        position: absolute;
+        left: 8px;
+        top: 8px;
+        z-index: 100;
+        display: flex;
+        align-items: center;
+        font-size: 16px;
+        color: #FFF;
+        width: max-content;
+
+        img {
+            width: 24px;
+            height: 24px;
+            border: 2px solid #FFF4DB;
+            box-sizing: border-box;
+            border-radius: 50%;
+            margin-right: 10px;
+        }
+    }
+    .content-text {
+        position: absolute;
+        top: 53px;
+        left: 35px;
+        .title {
+            font-weight: 800;
+            font-size: 16px;
+            color: #ffffff;
+        }
+        .center {
+            padding: 12px 0;
+            box-sizing: border-box;
+            font-weight: 800;
+            font-size: 56px;
+            color: #fff;
+            display: flex;
+            align-items: center;
+            .icon {
+                width: 46px;
+                height: 46px;
+                margin-right: 10px;
+                border: 3px solid #fff;
+                border-radius: 50%;
+            }
+        }
+        .desc {
+            font-weight: 800;
+            font-size: 13px;
+            color: #ffffff;
+            display: flex;
+            align-items: center;
+
+            .icon-clock {
+                margin-right: 4px;
+            }
+
+            .icon-trophy {
+                margin-left: 8px;
+                margin-right: 4px;
+            }
+
+            .trophy-count {
+                color: #FFCC4D;
+            }
+        }
+    }
+    .card-cover {
+        width: 100%;
+        object-fit: contain;
+    }
+
+    .bottom-bar {
+        padding: 12px;
+        box-sizing: border-box;
+        .title {
+            color: #566370;
+            font-weight: 400;
+            font-size: 14px;
+            margin-bottom: 6px;
+        }
+        .desc {
+            font-weight: 500;
+            font-size: 15px;
+            color: #101419;
+        }
+    }
+}
+</style>

+ 353 - 0
src/view/iframe/publish/components/giveaway-poster.vue

@@ -0,0 +1,353 @@
+<template>
+    <div class="show">
+        <div class="row">
+            <img class="tagImg" :src=" require('@/assets/svg/poster-befor.svg') " />
+            <div class="l">
+                <div :style="{'zoom': reviewCanvasParams.zoomL}">
+                    <custom-card-horizontal-cover
+                        :data="{
+                            totalCount: baseFormData.totalCount,
+                            amountValue: baseFormData.amountValue,
+                            tokenSymbol: currentCurrencyInfo.tokenSymbol,
+                            currencyIconUrl: currentCurrencyInfo.iconPath,
+                            type: baseFormData.type,
+                            validityDuration: baseFormData.validityDuration,
+                            customPosterUrl: customPosterInfo && customPosterInfo.before && customPosterInfo.before.imagePath || '',
+                            userInfo: {
+                                nickName: userInfo.name,
+                                avatarUrl: userInfo.avatarUrl
+                            },
+                        }"
+                        :showBottom="false">
+                    </custom-card-horizontal-cover>
+                </div>
+            </div>
+            <div class="r">
+                <div class="desc"><span>1080*567</span> JPG, PNG</div>
+                <div
+                    class="box"
+                    data-type="1"
+                    @drop="dragImg"
+                    @dragenter="dragEnter"
+                    @dragover="dragEnter"
+                    @dragleave="dragLeave">
+                    <span class="drag">Drag Image Here</span>
+                    <div class="upload">
+                        <input
+                            class="file"
+                            type="file"
+                            data-type="1"
+                            @change="fileChange"
+                            accept="image/png,image/jpg,image/jpeg" />
+                        <span>Browse</span>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="row">
+            <img class="tagImg" :src=" require('@/assets/svg/poster-after.svg') " />
+            <div class="t">
+                <div
+                    :style="{
+                        'zoom': reviewCanvasParams.zoomT,
+                        'position': 'absolute',
+                        'width': '350px',
+                    }">
+                    <custom-card-cover
+                        :data="{
+                            totalCount: baseFormData.totalCount,
+                            amountValue: baseFormData.amountValue,
+                            tokenSymbol: currentCurrencyInfo.tokenSymbol,
+                            currencyIconUrl: currentCurrencyInfo.iconPath,
+                            type: baseFormData.type,
+                            validityDuration: baseFormData.validityDuration,
+                            customPosterUrl: customPosterInfo && customPosterInfo.after && customPosterInfo.after.imagePath || '',
+                            userInfo: {
+                                nickName: userInfo.name,
+                                avatarUrl: userInfo.avatarUrl
+                            }
+                        }">
+                    </custom-card-cover>
+                </div>
+            </div>
+            <div class="r">
+                <div class="desc"><span>1080*1080</span> JPG, PNG</div>
+                <div
+                    class="box"
+                    data-type="2"
+                    @drop="dragImg"
+                    @dragenter="dragEnter"
+                    @dragover="dragEnter"
+                    @dragleave="dragLeave">
+                    <span class="drag">Drag Image Here</span>
+                    <div class="upload">
+                        <input
+                            class="file"
+                            type="file"
+                            data-type="2"
+                            @change="fileChange"
+                            accept="image/png,image/jpg,image/jpeg" />
+                        <span>Browse</span>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="footer">
+            <button class="confirm" @click="confirmAction">Confirm</button>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, reactive, nextTick, defineEmits, defineProps, onMounted } from 'vue';
+import { message } from 'ant-design-vue';
+import customCardCover from '@/view/components/custom-card-cover.vue'
+import customCardHorizontalCover from '@/view/components/custom-card-horizontal-cover.vue'
+import { getChromeStorage } from "@/uilts/chromeExtension"
+import { getUser } from "@/http/publishApi"
+
+const userInfo = ref({});
+const emits = defineEmits(['selectImage', 'confirmData']);
+const reviewCanvasParams = reactive({
+    zoomL: 1,
+    zoomT: 1
+});
+
+defineProps({
+    baseFormData: {
+        type: Object,
+        default: () => {
+            return {
+            }
+        }
+    },
+    currentCurrencyInfo: {
+        type: Object,
+        default: () => {
+            return {
+            }
+        }
+    },
+    customPosterInfo: {
+        type: Object,
+        default: () => {
+            return {
+            }
+        }
+    }
+})
+
+const getUserInfo = (cb) => {
+    getChromeStorage('userInfo', (res) => {
+        if(res) {
+            userInfo.value = res;
+        }
+        cb && cb(res);
+    })
+}
+
+const getUserName = (screenName) => {
+    getUser({
+        params:{
+            screenName
+        }
+    }).then(res => {
+        console.log(res);
+        if(res.code == 0) {
+            userInfo.value.name = res.data.name || ''
+        }
+    });
+}
+
+const fileChange = (e) => {
+    if (e && e.target) {
+        let file = e.target.files[0]
+        let type = e.target.dataset && e.target.dataset.type || '';
+        cropImage(file, type)
+        e.target.value = '';
+    }
+}
+
+const dragImg = (e) => {
+    e.preventDefault();
+    e.target.classList.remove('light');
+
+    // upload
+    if (e && e.dataTransfer) {
+        let file = e.dataTransfer.files[0];
+        let type = e.target.dataset && e.target.dataset.type || '';
+        // 格式判断
+        if (['image/png', 'image/jpg', 'image/jpeg'].indexOf(String(file.type).toLowerCase()) == -1) {
+            message.warning(`This format is not currently supported`);
+        } else {
+            cropImage(file, type)
+        }
+    }
+}
+const dragEnter = (e) => {
+    e.preventDefault();
+    e.target.classList.add('light')
+}
+const dragLeave = (e) => {
+    e.preventDefault();
+    e.target.classList.remove('light')
+}
+
+const cropImage = (file, type) => {
+    emits('selectImage', {
+        file: URL.createObjectURL(file),
+        type,
+    })
+}
+
+const calcPreviewCanvasParams = () => {
+    nextTick(() => {
+        let lH = document.querySelector('div.row').offsetHeight;
+        reviewCanvasParams.zoomL = (lH - 100) / 266;
+        reviewCanvasParams.zoomT = (lH - 20) / 500;
+    });
+}
+
+const confirmAction = () => {
+    emits('confirmData')
+}
+
+onMounted(() => {
+    calcPreviewCanvasParams();
+    getUserInfo((res) => {
+        if(res) {
+            getUserName(res.nickName);
+        }
+    });
+    window.addEventListener('resize',function () {
+        calcPreviewCanvasParams();
+    })
+})
+</script>
+
+<style lang="scss" scoped>
+.show {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    height: 100%;
+    .row {
+        position: relative;
+        height: calc(50% - 40px);
+        padding: 0 75px;
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        justify-content: center;
+
+        &:nth-child(2) {
+            border-top: 1px solid #ececec;
+        }
+        .tagImg {
+            position: absolute;
+            top: 0;
+            left: 0;
+            width: 177px;
+            height: 32px;
+        }
+        .l {
+            display: flex;
+            width: 50%;
+            align-items: center;
+            justify-content: center;
+            height: calc(100% - 100px);
+        }
+        .t {
+            display: flex;
+            width: 50%;
+            align-items: center;
+            justify-content: center;
+            height: calc(100% - 20px);
+        }
+        .r {
+            width: 50%;
+            margin-left: 63px;
+            height: calc(100% - 100px);
+            .desc {
+                height: 33px;
+                font-size: 16px;
+                font-weight: 600;
+                span {
+                    color: #1D9BF0;
+                }
+            }
+            .box {
+                height: calc(100% - 33px);
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                justify-content: center;
+                border-radius: 10px;
+                border: 1px dashed #B4B4B4;
+
+                &.light {
+                    border: 1px dashed #ff0000;
+                }
+                .drag {
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    color: #AEAEAE;
+                    font-size: 15px;
+                    font-weight: 500;
+                    height: calc(100% - 76px);
+                }
+                .upload {
+                    user-select: none;
+                    position: relative;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    width: calc(100% - 32px);
+                    height: 44px;
+                    margin: 0 16px;
+                    font-size: 15px;
+                    color: #1D9BF0;
+                    border-radius: 22px;
+                    background-color: rgba($color: #1D9BF0, $alpha: .1);
+                    .file {
+                        position: absolute;
+                        z-index: 2;
+                        opacity: 0;
+                        cursor: pointer;
+                        top: 0;
+                        left: 0;
+                        width: 100%;
+                        height: 100%;
+                    }
+                }
+            }
+        }
+    }
+    .footer {
+        display: flex;
+        align-items: center;
+        justify-content: right;
+        height: 80px;
+        text-align: right;
+        padding-right: 30px;
+        border-top: 1px solid #ececec;
+        .confirm {
+            cursor: pointer;
+            border: 0;
+            width: 200px;
+            height: 50px;
+            color: #ffffff;
+            font-size: 18px;
+            font-weight: 700;
+            border-radius: 25px;
+            background: #1D9BF0;
+        }
+    }
+}
+
+:deep() .card-wrapper {
+    top: unset;
+    left: unset;
+}
+</style>

+ 26 - 147
src/view/iframe/publish/components/preview-card.vue

@@ -25,6 +25,7 @@
                             currencyIconUrl: currentCurrencyInfo.iconPath,
                             type: baseFormData.type,
                             validityDuration: baseFormData.validityDuration,
+                            customPosterUrl: customPosterInfo && customPosterInfo.after && customPosterInfo.after.imagePath || '',
                             userInfo: {
                                 nickName: userInfo.name,
                                 avatarUrl: userInfo.avatarUrl
@@ -51,51 +52,22 @@
                         </div>
                     </div>
                 </div>
-                <div class="card-wrapper" 
-                    :style="{'zoom': reviewCanvasParams.zoom}">
-                    <img :src="require('@/assets/img/img-preview-draw-after-bg.png')"
-                        v-if="baseFormData.type == 2"
-                        class="card-cover">
-                    <img :src="require('@/assets/subject/img-card-cover-blue.png')"
-                        v-else
-                        class="card-cover"/>
-                    <div class="bottom-bar">
-                        <div class="title">
-                            DeNet.me
-                        </div>
-                        <div class="desc">
-                            🎁 <template v-if="currentCurrencyInfo.tokenSymbol=='USD'">$</template>{{baseFormData.amountValue}} GIVEAWAY
-                        </div>
-                    </div>
-                    <div class="user-info">
-                        <img :src="userInfo.avatarUrl" 
-                        class="avatar"/> {{userInfo.name}}
-                    </div>
-                    <div class="content-text">
-                        <div class="title">
-                            {{currentCurrencyInfo.tokenSymbol}} GIVEAWAY
-                        </div>
-                        <div class="center"
-                            :style="{
-                                fontSize: amountFontSize + 'px'
-                            }">
-                            <img :src="currentCurrencyInfo.iconPath" class="icon">
-                            <span id="preview-before-amount">
-                                {{baseFormData.amountValue}}
-                            </span>
-                        </div>
-                        <div class="desc">
-                            <template  v-if="baseFormData.type == 2">
-                                <img class="icon-clock" 
-                                :src="require('@/assets/svg/icon-preview-clock.svg')" />  {{baseFormData.validityDuration}} H
-                                <img class="icon-trophy" 
-                                :src="require('@/assets/svg/icon-preview-trophy.svg')" /> <span class="trophy-count">{{baseFormData.totalCount}} WINNERS</span>
-                            </template>
-                            <template v-else>
-                                {{baseFormData.totalCount}} WINNERS TO SHARE
-                            </template>
-                        </div>
-                    </div>
+                <div :style="{'zoom': reviewCanvasParams.zoom}">
+                    <custom-card-horizontal-cover
+                        :data="{
+                            totalCount: baseFormData.totalCount,
+                            amountValue: baseFormData.amountValue,
+                            tokenSymbol: currentCurrencyInfo.tokenSymbol,
+                            currencyIconUrl: currentCurrencyInfo.iconPath,
+                            type: baseFormData.type,
+                            validityDuration: baseFormData.validityDuration,
+                            customPosterUrl: customPosterInfo && customPosterInfo.before && customPosterInfo.before.imagePath || '',
+                            userInfo: {
+                                nickName: userInfo.name,
+                                avatarUrl: userInfo.avatarUrl
+                            }
+                        }">
+                    </custom-card-horizontal-cover>
                 </div>
             </div>
         </div>
@@ -104,9 +76,8 @@
 
 <script setup>
 import { ref, defineProps, onMounted, nextTick, watch, reactive, inject, onUnmounted } from "vue";
-
 import customCardCover from '@/view/components/custom-card-cover.vue'
-
+import customCardHorizontalCover from '@/view/components/custom-card-horizontal-cover.vue'
 import {getChromeStorage} from "@/uilts/chromeExtension"
 import {getUser} from "@/http/publishApi"
 
@@ -146,7 +117,14 @@ defineProps({
     amountFontSize: {
         type: Number,
         default: '56'
-    }
+    },
+    customPosterInfo: {
+        type: Object,
+        default: () => {
+            return {
+            }
+        }
+    },
 })
 
 const getUserInfo = (cb) => {
@@ -327,105 +305,6 @@ onUnmounted(() => {
             background-size: contain;
             background-repeat: no-repeat;
             height: 100%;
-            .card-wrapper {
-                width: 491px;
-                border: 1px solid #D1D9DD;
-                background: #ffffff;
-                box-sizing: border-box;
-                overflow: hidden;
-                position: relative;
-                box-sizing: border-box;
-                border-radius: 16px;
-                left: 73px;
-                top: 238px;
-
-                .user-info {
-                    position: absolute;
-                    left: 8px;
-                    top: 8px;
-                    z-index: 100;
-                    display: flex;
-                    align-items: center;
-                    font-size: 16px;
-                    color: #FFF;
-                    width: max-content;
-
-                    img {
-                        width: 24px;
-                        height: 24px;
-                        border: 2px solid #FFF4DB;
-                        box-sizing: border-box;
-                        border-radius: 50%;
-                        margin-right: 10px;
-                    }
-                }
-                .content-text {
-                    position: absolute;
-                    top: 53px;
-                    left: 35px;
-                    .title {
-                        font-weight: 800;
-                        font-size: 16px;
-                        color: #ffffff;
-                    }
-                    .center {
-                        padding: 12px 0;
-                        box-sizing: border-box;
-                        font-weight: 800;
-                        font-size: 56px;
-                        color: #fff;
-                        display: flex;
-                        align-items: center;
-                        .icon {
-                            width: 46px;
-                            height: 46px;
-                            margin-right: 10px;
-                            border: 3px solid #fff;
-                            border-radius: 50%;
-                        }
-                    }
-                    .desc {
-                        font-weight: 800;
-                        font-size: 13px;
-                        color: #ffffff;
-                        display: flex;
-                        align-items: center;
-
-                        .icon-clock {
-                            margin-right: 4px;
-                        }
-
-                        .icon-trophy {
-                            margin-left: 8px;
-                            margin-right: 4px;
-                        }
-
-                        .trophy-count {
-                            color: #FFCC4D;
-                        }
-                    }
-                }
-                .card-cover {
-                    width: 100%;
-                    object-fit: contain;
-                }
-
-                .bottom-bar {
-                    padding: 12px;
-                    box-sizing: border-box;
-                    .title {
-                        color: #566370;
-                        font-weight: 400;
-                        font-size: 14px;
-                        margin-bottom: 6px;
-                    }
-                    .desc {
-                        font-weight: 500;
-                        font-size: 15px;
-                        color: #101419;
-                    }
-                }
-            }
         }
 
         .left, .content-before {

+ 320 - 11
src/view/iframe/publish/give-dialog.vue

@@ -48,13 +48,25 @@
             <!-- 内容 -->
             <div class="body">
                 <!-- 充值组件 -->
-                <top-up v-if="showComType == 'topUp'" 
+                <top-up
+                    v-if="showComType == 'topUp'" 
                     :asyncIng="asyncIng"
                     :currentCurrencyInfo="tempCurrentCurrencyInfo"
-                    @topUpDone="topUpDone"></top-up>
+                    @topUpDone="topUpDone">
+                </top-up>
+
+                <!-- 自定义红包封面 -->
+                <giveaway-poster
+                    v-else-if="showComType == 'poster'"
+                    :baseFormData="baseFormData"
+                    :currentCurrencyInfo="currentCurrencyInfo"
+                    :customPosterInfo="customPosterInfo"
+                    @selectImage="selectImage"
+                    @confirmData="confirmData">
+                </giveaway-poster>
 
                 <!-- 表单填写容器 -->
-                <div class="body-content" v-if="showComType != 'topUp'">
+                <div class="body-content" v-else>
 
                     <!-- 货币列表  -->
                     <div class="currency-pop" v-show="showCurrencyPop">
@@ -165,9 +177,39 @@
                                             </div>
                                         </div>
                                     </div>
+                                    <div class="giveaway-poster" @click="customCoverImg">
+                                        <div class="show-img">
+                                            <div
+                                                :style="{
+                                                    'zoom': 55 / 500,
+                                                    'position': 'absolute',
+                                                    'width': '345px',
+                                                }">
+                                                <custom-card-cover
+                                                    :data="{
+                                                        totalCount: baseFormData.totalCount,
+                                                        amountValue: baseFormData.amountValue,
+                                                        tokenSymbol: currentCurrencyInfo.tokenSymbol,
+                                                        currencyIconUrl: currentCurrencyInfo.iconPath,
+                                                        type: baseFormData.type,
+                                                        validityDuration: baseFormData.validityDuration,
+                                                        customPosterUrl: customPosterData && customPosterData.after && customPosterData.after.imagePath || '',
+                                                    }">
+                                                </custom-card-cover>
+                                            </div>
+                                        </div>
+                                        <div class="show-font">
+                                            <span>Giveaway Poster</span>
+                                            <img class="new" :src=" require('@/assets/svg/img-new.svg') " />
+                                        </div>
+                                        <div class="show-placeholder">Replace</div>
+                                        <div class="arrow">
+                                            <img :src=" require('@/assets/svg/icon-cell-arrow-right.svg') " />
+                                        </div>
+                                    </div>
                                     <!--  提示 -->
                                     <ul class="tips-wrapper">
-                                        <li class="row">
+                                        <li class="row" style="white-space:nowrap;">
                                             Rewards can only be claimed after the target user completes all tasks you set. 
                                         </li>
                                         <li class="row">
@@ -264,6 +306,7 @@
                                             :postData="publishRes"
                                             :baseFormData="baseFormData"
                                             :amountFontSize="previewFontSize"
+                                            :customPosterInfo="customPosterData"
                                         ></preview-card>
                                     </div>
                                 </div>
@@ -366,24 +409,58 @@
             @cancel="messageBoxCancel"
             @confirm="messageBoxConfirm"
         ></message-box>
+
+        <!-- 裁剪 -->
+        <div class="dialog" v-if="cropperDialog">
+            <div class="corp-title">
+                <img class="back" :src="require('@/assets/svg/icon-back.svg')" @click="hiddenDialog" />
+                <span>Crop</span>
+            </div>
+            <div class="corp-content">
+                <vue-cropper
+                    ref="refCropper"
+                    :img="cropperOption.img"
+                    :output-type="cropperOption.outputType"
+                    :infoTrue="cropperOption.infoTrue"
+                    :full="cropperOption.full"
+                    :fixed="cropperOption.fixed"
+                    :fixed-number="cropperOption.fixedNumber"
+                    :auto-crop="cropperOption.autoCrop"
+                    :auto-crop-width="cropperOption.autoCropWidth"
+                    :auto-crop-height="cropperOption.autoCropHeight"
+                    :center-box="cropperOption.centerBox"
+                    :high="cropperOption.high"
+                    :max-img-size="cropperOption.max">
+                </vue-cropper>
+            </div>
+            <div class="corp-footer">
+                <button v-if="cropperLoading" class="confirm disable">
+                    <img :src=" require('@/assets/svg/icon-btn-loading.svg') " />
+                    <span>Confirm</span>
+                </button>
+                <button v-else class="confirm" @click="confirmImage">Confirm</button>
+            </div>
+        </div>
+        <div class="dialog-mask" v-if="cropperDialog"></div>
     </div>
 </template>
 
 <script setup>
-import { ref, watch, reactive, defineProps, defineEmits, onMounted, nextTick, provide } from "vue";
+import { ref, watch, reactive, defineProps, defineEmits, onMounted, nextTick, provide, getCurrentInstance } from "vue";
 import { postPublish, verifyPaypalResult, syncChainTokenRechargeRecord, getCurrencyInfoByCode } from "@/http/publishApi";
 import { getInviteGuildInfo, getInviteGuildInfoByOpenApi, saveInviteGuildInfo } from "@/http/discordApi";
 import { payCalcFee, getPayConfig } from "@/http/pay";
 import { getFrontConfig } from "@/http/account";
+import { uploadSignature, uploadFile } from '@/http/media';
 import {setChromeStorage, getChromeStorage} from "@/uilts/chromeExtension"
 import { debounce, getBit } from "@/uilts/help"
 import Report from "@/log-center/log"
 import { ElMessage, ElLoading } from "element-plus";
 import "element-plus/es/components/message/style/css";
 import "element-plus/es/components/loading/style/css";
-
+import 'vue-cropper/dist/index.css'
+import { VueCropper }  from "vue-cropper";
 import {create, all} from "mathjs";
-
 import messageBox from "@/view/components/message-box.vue";
 import currencyList from "@/view/components/currency-list.vue";
 import currencySelect from "@/view/components/currency-select.vue";
@@ -392,7 +469,11 @@ import followInput from "@/view/iframe/publish/components/follow-input";
 import paypalButton from "@/view/iframe/publish/components/paypal-button";
 import topUp from "@/view/iframe/publish/components/top-up.vue";
 import topUp2 from "@/view/iframe/publish/components/top-up2.vue";
-import  GlobalTip  from '@/view/components/global-tip.vue'
+import giveawayPoster from '@/view/iframe/publish/components/giveaway-poster.vue';
+import GlobalTip from '@/view/components/global-tip.vue'
+import customCardCover from '@/view/components/custom-card-cover.vue'
+
+const currentInstance = getCurrentInstance();
 
 const config = {
     number: 'BigNumber',
@@ -422,6 +503,26 @@ let dialogStyle = reactive({
     dialogContentWidth: 1100
 })
 
+let cropperOption = ref({
+    img: '',
+    full: true,
+    infoTrue: true,
+    fixed: true,
+    fixedNumber: [16, 8.396],
+    outputType: 'jpeg',
+    autoCrop: true,
+    autoCropWidth: 99999,
+    autoCropHeight: 99999,
+    centerBox: true,
+    high: true,
+    max: 99999,
+})
+let cropperDialog = ref(false)
+let cropperLoading = ref(false)
+let cropperType = ref('before')
+let customPosterInfo = ref({})
+let customPosterData = ref({})
+
 // 当前展示组件内容 default(表单)  preview(预览)  topUp(充值)
 let showComType = ref("default"); 
 let currentComData = {
@@ -434,6 +535,9 @@ let currentComData = {
     topUp: {
         title: "Deposit",
     },
+    poster: {
+        title: "Giveaway Poster",
+    }
 };
 
 // 机器人开关
@@ -837,6 +941,14 @@ const goTopUp = () => {
     showComType.value = 'topUp';
 }
 
+/*
+ * 自定义封面
+ */
+const customCoverImg = () => {
+    customPosterInfo.value = {}
+    showComType.value = 'poster';
+}
+
 /**
  * 充值done事件
  */
@@ -942,10 +1054,22 @@ const submitRequest = async () => {
         receiveConditions,
         payAmountValue: amountValue,
         type: baseFormData.type,
+        posterType: 1,
         validityDuration
     };
     submitIng.value = true;
 
+    // 自定义封面
+    if (Object.keys(customPosterData.value).length > 0) {
+        formData['posterType'] = 2;
+        if (customPosterData.value && customPosterData.value.after) {
+            formData['customPosterInstalled'] = customPosterData.value.after.objectKey || ''
+        }
+        if (customPosterData.value && customPosterData.value.before) {
+            formData['customPosterUninstalled'] = customPosterData.value.before.objectKey || ''
+        }
+    }
+
     // 法币支付需要计算费率
     if(formData.amountCurrencyCode == "USD") { 
         let payAmountRes = await getPayAmount(amountValue);
@@ -1589,6 +1713,77 @@ const selectPublishMode = (params, index) => {
     setInputErrorMsg();
 }
 
+// 截图相关
+const showDialog = () => {
+    cropperDialog.value = true;
+}
+const hiddenDialog = () => {
+    cropperDialog.value = false;
+}
+const selectImage = (option) => {
+    // 设置图片
+    cropperOption.value.img = option.file
+    // 选取比例
+    if (option && option.type && option.type == 2) {
+        cropperType.value = 'after';
+        cropperOption.value.fixedNumber = [1, 1];
+    } else {
+        cropperType.value = 'before';
+        cropperOption.value.fixedNumber = [16, 8.396];
+    }
+    nextTick(() => {
+        showDialog()
+    })
+}
+const confirmImage = () => {
+    let contentType = 'image/jpeg';
+    cropperLoading.value = true;
+    currentInstance.ctx.$refs.refCropper.getCropBlob(imgData => {
+        uploadSignature({
+            params: {
+                bizType: 1,
+                fileType: 1,
+                contentType: contentType,
+                fileSuffix: 'jpg',
+            }
+        }).then(res => {
+            let { code, data } = res;
+            if (code === 0) {
+                let reader = new FileReader()
+                    reader.readAsArrayBuffer(imgData)
+                    reader.onload = function(e) {
+                        let execFile = e.target.result;
+                        uploadFile({
+                            url: data.url,
+                            data: new Blob([execFile]),
+                            headers: {
+                                'Authorization': data.authorization,
+                                'x-amz-date': data.date,
+                                'Content-Type': contentType
+                            }
+                        }).then(res => {
+                            let { status } = res
+                            if (status == 200) {
+                                successImage(data)
+                            }
+                        }).finally(() => {
+                            cropperLoading.value = false;
+                        })
+                    }
+            }
+        })
+    })
+}
+const successImage = (data) => {
+    hiddenDialog()
+    // setPosterInfo
+    customPosterInfo.value[cropperType.value] = data;
+}
+const confirmData = (data) => {
+    close()
+    customPosterData.value = customPosterInfo.value;
+}
+
 onMounted(() => {
     setFrontConfig();
     setPayConfig();
@@ -1859,11 +2054,11 @@ onMounted(() => {
                                 img {
                                     -webkit-user-drag: none;
                                     width: 220px;
-                                    height: 160px;
+                                    height: 90px;
                                 }
                             }
                             .form-base {
-                                margin-top: 14px;
+                                margin-top: 20px;
                                 border: 1px solid #D1D9DD;
                                 border-radius: 14px;
                                 box-sizing: border-box;
@@ -2104,7 +2299,48 @@ onMounted(() => {
                     }
                 }
 
-
+                .giveaway-poster {
+                    display: flex;
+                    align-items: center;
+                    flex-direction: row;
+                    cursor: pointer;
+                    height: 84px;
+                    margin-top: 20px;
+                    border-radius: 14px;
+                    border: 1px solid #D1D9DD;
+                    .show-img {
+                        display: flex;
+                        align-items: center;
+                        justify-content: center;
+                        width: 45px;
+                        height: 60px;
+                        margin-left: 14px;
+                        margin-right: 14px;
+                    }
+                    .show-font {
+                        position: relative;
+                        font-size: 15px;
+                        font-weight: 500;
+                        .new {
+                            width: 40px;
+                            height: 18px;
+                            margin-left: 10px;
+                        }
+                    }
+                    .show-placeholder {
+                        flex: 1;
+                        color: #1D9BF0;
+                        font-size: 15px;
+                        font-weight: 500;
+                        text-align: right;
+                    }
+                    .arrow {
+                        width: 18px;
+                        height: 24px;
+                        margin-left: 2px;
+                        margin-right: 12px;
+                    }
+                }
                 .tips-wrapper {
                     margin: 16px 0 0 12px !important;
                     padding: 0px !important;
@@ -2286,4 +2522,77 @@ onMounted(() => {
         }
     }
 }
+
+.dialog {
+    position: absolute;
+    z-index: 2002;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    width: 800px;
+    height: calc(100% - 100px);
+    border-radius: 20px;
+    background-color: #ffffff;
+    .corp-title {
+        display: flex;
+        height: 48px;
+        align-items: center;
+        .back {
+            cursor: pointer;
+            width: 24px;
+            height: 24px;
+            margin: 0 12px;
+        }
+        span {
+            font-size: 16px;
+            font-weight: 500;
+        }
+    }
+    .corp-content {
+        width: 472px;
+        margin: auto;
+        height: calc(100% - 130px);
+    }
+    .corp-footer {
+        display: flex;
+        align-items: center;
+        justify-content: right;
+        height: 80px;
+        text-align: right;
+        padding-right: 30px;
+        .confirm {
+            cursor: pointer;
+            border: 0;
+            width: 200px;
+            height: 50px;
+            color: #ffffff;
+            font-size: 18px;
+            font-weight: 700;
+            border-radius: 25px;
+            background: #1D9BF0;
+            &.disable {
+                background: #D9D9D9;
+                img {
+                    width: 20px;
+                    margin-right: 10px;
+                }
+            }
+        }
+    }
+}
+.dialog-mask {
+    position: absolute;
+    z-index: 2001;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.5);
+}
+:deep() .vue-cropper {
+    background-image: none;
+}
+:deep() .cropper-modal {
+    background: rgba(0, 0, 0, .05);
+}
 </style>

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