|
@@ -0,0 +1,929 @@
|
|
|
+<template>
|
|
|
+ <div style="position: relative;">
|
|
|
+ <div style="width: 100px; height:100px;position: absolute;right: 0;z-index: 10;" @click="showClick()"></div>
|
|
|
+ <el-form ref="ruleFormRef" :model="crtCouponForm" :rules="rules" label-width="120px">
|
|
|
+ <el-form-item label="發券位置" prop="srvAdr">
|
|
|
+ <el-radio-group v-model="crtCouponForm.srvAdr">
|
|
|
+ <el-radio label="Global" size="large" border>國際站</el-radio>
|
|
|
+ <el-radio label="China" size="large" border>大陸站</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="活動票券" prop="eventType">
|
|
|
+ <el-radio-group v-model="eventType" @change="setEventType">
|
|
|
+ <el-radio label="hiteach50-3" size="large" border>Hiteach 50 第三階段</el-radio>
|
|
|
+ <el-radio label="hiteach333-1" size="large" border>HiTeach 333 第一階段</el-radio>
|
|
|
+ <el-radio label="hiteach333-3" size="large" border>HiTeach 333 第三階段</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="detailSettingFlag" label="票券類型" prop="couponType" >
|
|
|
+ <el-select v-model="crtCouponForm.couponType" placeholder="請選擇" clearable style="width: 500px;">
|
|
|
+ <el-option label="公開型" value="Public" />
|
|
|
+ <el-option label="事件型" value="Event" />
|
|
|
+ <el-option label="大量生成" value="General" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="detailSettingFlag && crtCouponForm.couponType != 'General'" label="票券名稱" prop="couponName" >
|
|
|
+ <el-input v-model="crtCouponForm.couponName" placeholder="範例: TEAM10T2G(不填會自動生成)" style="width: 500px;"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="detailSettingFlag" label="活動名稱" prop="eventName">
|
|
|
+ <el-input v-model="crtCouponForm.eventName" placeholder="範例: hiteach333-1" style="width: 500px;"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="到期時間" prop="expire">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="crtCouponForm.expire"
|
|
|
+ type="datetime"
|
|
|
+ style="width: 500px;"
|
|
|
+ :disabled-date="disabledDate"
|
|
|
+ :shortcuts="shortcuts"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="crtCouponForm.couponType == 'Public'" label="兌換上限" prop="maxTaker">
|
|
|
+ <el-input-number v-model="crtCouponForm.maxTaker" :min="0" :disabled="!maxTakerFlag" style="margin-right: 6px;"/>
|
|
|
+ <el-checkbox v-model="maxTakerFlag" label="啟用" size="large" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="crtCouponForm.couponType == 'General'" label="生成券數" prop="quantity">
|
|
|
+ <el-input-number v-model="crtCouponForm.quantity" :min="0" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="detailSettingFlag" label="規則" prop="rule">
|
|
|
+ <div style="max-height: 330px;overflow: scroll;">
|
|
|
+ <div v-for="(rule, rIndex) in crtCouponForm.rule" style="display: flex;flex-direction: row;align-items: center;margin-bottom: 5px;">
|
|
|
+ <el-button :icon="Plus" circle @click="addRule(rIndex)" style="margin-right: 5px;"/>
|
|
|
+ <div style="text-align: left;border: 1px solid #dcdfe6;padding: 6px 11px;border-radius: 5px;margin-right: 5px;">
|
|
|
+ 條件:
|
|
|
+ <div style="min-height: 36px;max-width: 550px;border: 1px solid #e4e7ed;padding: 1px 11px;border-radius: 5px;background-color: #f5f7fa;">{{ translateRule(rule.q) }}</div>
|
|
|
+ <div v-for="(item, index) in rule.q" style="text-align:left;">
|
|
|
+ <el-button-group style="margin-right: 5px;">
|
|
|
+ <el-button :icon="Plus" @click="addCondition(rIndex, index)" />
|
|
|
+ <el-button :icon="CloseBold" @click="delCondition(rIndex, index)" :disabled="index == 0" />
|
|
|
+ </el-button-group>
|
|
|
+ <el-select v-model="item.operator" :disabled="index == 0" clearable :placeholder="index == 0 ? ' ' : '請選擇'" style="width: 100px;">
|
|
|
+ <el-option v-for="(o, i) in operatorList" :label="o.label" :value="o.val" :key="i" />
|
|
|
+ </el-select>
|
|
|
+ <el-select v-model="item.type" placeholder="檢核屬性" clearable style="width: 250px;">
|
|
|
+ <el-option v-for="(t, i) in typeList" :label="t.label" :value="t.val" :key="i"/>
|
|
|
+ </el-select>
|
|
|
+ <el-select v-model="item.how" placeholder="判斷方式" style="width: 120px;" clearable :change="(item.how == 'is null' || item.how == 'isnot null') ? item.val = '' : item.val = item.val">
|
|
|
+ <el-option v-for="(h, i) in howList" :label="h.label" :value="h.val" :key="i"/>
|
|
|
+ </el-select>
|
|
|
+ <el-input v-model="item.val" placeholder="數值" style="width: 100px;" :disabled="(item.how == 'is null' || item.how == 'isnot null')"/>
|
|
|
+ </div>
|
|
|
+ 獲得增益:
|
|
|
+ <div style="text-align:left;">
|
|
|
+ <el-select v-model="rule.b" placeholder="請選擇" clearable style="width: 500px;">
|
|
|
+ <el-option v-for="(b, i) in ruleBList" :label="b.label" :value="b.val" :key="i"/>
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ 獲得積分:
|
|
|
+ <div style="text-align:left;">
|
|
|
+ <el-input-number v-model="rule.p" :min="0" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-button v-if="rIndex != 0" :icon="CloseBold" circle @click="delRule(rIndex)" style="margin-right: 5px;"/>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="票券資訊" prop="info">
|
|
|
+ <div style="display: flex;">
|
|
|
+ <el-card v-for="info in crtCouponForm.info" style="width: 400px;margin-right:7px;">
|
|
|
+ <template #header>
|
|
|
+ <div style="text-align: left;font-weight: bold;font-size: 17px;">
|
|
|
+ {{langToName(info.l)}}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div style="text-align: left;">
|
|
|
+ 票券標題:<el-input v-model="info.n" :readonly="!detailSettingFlag"/>
|
|
|
+ 連結:<el-input v-model="info.u" :readonly="!detailSettingFlag"/>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <div style="display: flex;justify-content: center;">
|
|
|
+ <div class="coupons">
|
|
|
+ <div class="coupons-cont">
|
|
|
+ <div class="hole"/>
|
|
|
+ <div class="coupons-cont-box">
|
|
|
+ <div class="coupons-cont-box-title">{{ info.n }}</div>
|
|
|
+ <div class="coupons-cont-box-exp" >
|
|
|
+ {{ '有效期限: ' + convertDate(crtCouponForm.expire) }}
|
|
|
+ </div>
|
|
|
+ <div class="coupons-cont-box-link">
|
|
|
+ <el-button round size="small" @click="linkToPage(info.u)">了解更多</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="coupons-btn">
|
|
|
+ <div class="coupons-btn-hole-positon">
|
|
|
+ <div class="hole"/>
|
|
|
+ </div>
|
|
|
+ <div class="icon-box">
|
|
|
+ <el-icon :size="23" style="transform:rotate(-50deg);">
|
|
|
+ <Ticket />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ {{ '兌換' }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="crtCouponForm.couponType == 'Event'" label="指定醍摩豆ID" prop="targets">
|
|
|
+ <div style="display: flex;align-items: flex-end;">
|
|
|
+ <el-input style="width: 300px;" v-model="crtCouponForm.targets" :rows="6" type="textarea" placeholder="請填入教師ID, 並用換行分隔" />
|
|
|
+ <div style="margin-left: 5px;">總共 {{ targetsCount }} 位</div>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="crtCouponForm.couponType == 'Event'">
|
|
|
+ <el-checkbox v-model="consolidationFlag" label="直接歸戶給以上老師" size="large" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="submitForm(ruleFormRef)" :loading="loading">建立優惠券</el-button>
|
|
|
+ <el-button @click="resetForm(ruleFormRef)" :loading="loading">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="優惠券" style="margin-top: 50px;max-width: 600px;">
|
|
|
+ <el-input :rows="6" type="textarea" v-model="couponResult" :readonly="true" style="margin-bottom: 5px;"/>
|
|
|
+ <el-button type="primary" :icon="CopyDocument" @click="copyDocument(couponResult)">複製優惠券</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { reactive, ref, computed, getCurrentInstance, markRaw } from 'vue'
|
|
|
+import { Ticket, Plus, CloseBold, Warning, CopyDocument, SuccessFilled } from '@element-plus/icons'
|
|
|
+import { ElMessageBox } from 'element-plus'
|
|
|
+let { proxy } = getCurrentInstance()
|
|
|
+
|
|
|
+const clickCount = ref(0)
|
|
|
+const detailSettingFlag = ref(false)
|
|
|
+
|
|
|
+const showClick = ()=>{
|
|
|
+ clickCount.value++
|
|
|
+ if(clickCount.value == 10){
|
|
|
+ detailSettingFlag.value = !detailSettingFlag.value
|
|
|
+ clickCount.value = 0
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const targetsCount = ref(0)
|
|
|
+
|
|
|
+const maxTakerFlag = ref(false)
|
|
|
+const consolidationFlag = ref(false)
|
|
|
+const loading = ref(false)
|
|
|
+const couponResult = ref('')
|
|
|
+const defaultTime = new Date().setTime(new Date().getTime() + (60*60*1000))
|
|
|
+const shortcuts = [
|
|
|
+ {
|
|
|
+ text: '一個月後',
|
|
|
+ value: () => {
|
|
|
+ const date = new Date()
|
|
|
+ date.setTime(date.getTime() + 30 * 3600 * 1000 * 24)
|
|
|
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59)
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: '二個月後',
|
|
|
+ value: () => {
|
|
|
+ const date = new Date()
|
|
|
+ date.setTime(date.getTime() + 60 * 3600 * 1000 * 24)
|
|
|
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59)
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: '三個月後',
|
|
|
+ value: () => {
|
|
|
+ const date = new Date()
|
|
|
+ date.setTime(date.getTime() + 90 * 3600 * 1000 * 24)
|
|
|
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59)
|
|
|
+ },
|
|
|
+ },
|
|
|
+]
|
|
|
+const eventType= ref('')
|
|
|
+
|
|
|
+const setEventType = (type)=>{
|
|
|
+ switch(type){
|
|
|
+ case 'hiteach50-3':
|
|
|
+ crtCouponForm.couponType = 'Event'
|
|
|
+ crtCouponForm.eventName = 'hiteach50-3'
|
|
|
+ crtCouponForm.rule[0].q = [
|
|
|
+ {
|
|
|
+ operator: '',
|
|
|
+ type: 'GetWebIRS50EventType',
|
|
|
+ how: '==',
|
|
|
+ val: 'hiteach50'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ operator: '&&',
|
|
|
+ type: 'GetWebIRS50TimeStep2',
|
|
|
+ how: '!=',
|
|
|
+ val:'-1'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ operator: '&&',
|
|
|
+ type: 'GetWebIRS50TimeStep2',
|
|
|
+ how: 'isnot null',
|
|
|
+ val:''
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ crtCouponForm.rule[0].b = '901003'
|
|
|
+ crtCouponForm.info[0].l = 'zh-tw'
|
|
|
+ crtCouponForm.info[0].n = '教師自主增能三'
|
|
|
+ crtCouponForm.info[0].u = 'https://www.habook.com/zh-tw/event.php?act=view&id=170'
|
|
|
+ crtCouponForm.info[1].l = 'zh-cn'
|
|
|
+ crtCouponForm.info[1].n = '教师自主增能阶段三'
|
|
|
+ crtCouponForm.info[1].u = 'https://www.habook.com.cn/event.php?act=view&id=50'
|
|
|
+ crtCouponForm.info[2].l = 'en-us'
|
|
|
+ crtCouponForm.info[2].n = 'Self-enhancement Plan Stage 3'
|
|
|
+ crtCouponForm.info[2].u = 'https://www.habook.com/en/event.php?act=view&id=170'
|
|
|
+ break
|
|
|
+ case 'hiteach333-1':
|
|
|
+ crtCouponForm.couponType = 'Event'
|
|
|
+ crtCouponForm.eventName = 'hiteach333-1'
|
|
|
+ crtCouponForm.rule[0].q = [
|
|
|
+ {
|
|
|
+ operator: '',
|
|
|
+ type: 'GetWebIRS50TimeStep1',
|
|
|
+ how: 'isnot null',
|
|
|
+ val:''
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ crtCouponForm.rule[0].b = '901003'
|
|
|
+ crtCouponForm.info[0].l = 'zh-tw'
|
|
|
+ crtCouponForm.info[0].n = 'HiTeach 333 階段一'
|
|
|
+ crtCouponForm.info[0].u = 'https://www.habook.com/zh-tw/event.php?act=view&id=162'
|
|
|
+ crtCouponForm.info[1].l = 'zh-cn'
|
|
|
+ crtCouponForm.info[1].n = 'HiTeach 333 阶段一'
|
|
|
+ crtCouponForm.info[1].u = 'https://www.habook.com.cn/event.php?act=view&id=47'
|
|
|
+ crtCouponForm.info[2].l = 'en-us'
|
|
|
+ crtCouponForm.info[2].n = 'HiTeach 333 Stage 1'
|
|
|
+ crtCouponForm.info[2].u = 'https://www.habook.com/en/event.php?act=view&id=162'
|
|
|
+ break
|
|
|
+ case 'hiteach333-3':
|
|
|
+ crtCouponForm.couponType = 'Event'
|
|
|
+ crtCouponForm.eventName = 'hiteach333-3'
|
|
|
+ crtCouponForm.rule[0].q = [
|
|
|
+ {
|
|
|
+ operator: '',
|
|
|
+ type: 'GetWebIRS50EventType',
|
|
|
+ how: '==',
|
|
|
+ val: 'hiteach333'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ operator: '&&',
|
|
|
+ type: 'GetWebIRS50TimeStep2',
|
|
|
+ how: '!=',
|
|
|
+ val:'-1'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ operator: '&&',
|
|
|
+ type: 'GetWebIRS50TimeStep2',
|
|
|
+ how: 'isnot null',
|
|
|
+ val:''
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ crtCouponForm.rule[0].b = '901003'
|
|
|
+ crtCouponForm.info[0].l = 'zh-tw'
|
|
|
+ crtCouponForm.info[0].n = 'HiTeach 333 階段三'
|
|
|
+ crtCouponForm.info[0].u = 'https://www.habook.com/zh-tw/event.php?act=view&id=162'
|
|
|
+ crtCouponForm.info[1].l = 'zh-cn'
|
|
|
+ crtCouponForm.info[1].n = 'HiTeach 333 阶段三'
|
|
|
+ crtCouponForm.info[1].u = 'https://www.habook.com.cn/event.php?act=view&id=47'
|
|
|
+ crtCouponForm.info[2].l = 'en-us'
|
|
|
+ crtCouponForm.info[2].n = 'HiTeach 333 Stage 3'
|
|
|
+ crtCouponForm.info[2].u = 'https://www.habook.com/en/event.php?act=view&id=162'
|
|
|
+ break
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const operatorList = [
|
|
|
+ { label: "或", val: "||" },
|
|
|
+ { label: "而且", val: "&&"}
|
|
|
+]
|
|
|
+const typeList = [
|
|
|
+ { label: "年資", val: "TenureID"},
|
|
|
+ { label: "T綠燈(Total)", val: "TGreen"},
|
|
|
+ { label: "T數據(Total)", val: "TData"},
|
|
|
+ { label: "T綠燈(月)", val: "TGreenMonth"},
|
|
|
+ { label: "T數據(月)", val: "TDataMonth"},
|
|
|
+ { label: "綁定手機號", val: "Mobile"},
|
|
|
+ { label: "綁定Mail", val: "Mail"},
|
|
|
+ { label: "取得WebIRS50的時間", val: "TimeForWebIRS50Get"},
|
|
|
+ { label: "取得智慧評分模組的時間", val: "TimeForSmartRatingGet"},
|
|
|
+ { label: "取得AI文字分析的時間", val: "TimeForAitextGet"},
|
|
|
+ { label: "取得蘇格拉底小數據的時間", val: "TimeForSokliteGet"},
|
|
|
+ { label: "333和50 第一階段取得的時間", val: "GetWebIRS50TimeStep1"},
|
|
|
+ { label: "333和50 第二階段取得的時間", val: "GetWebIRS50TimeStep2"},
|
|
|
+ { label: "333和50 第三階段取得的時間", val: "GetWebIRS50TimeStep3"},
|
|
|
+ { label: "取得參加333還是50", val: "GetWebIRS50EventType"},
|
|
|
+ { label: "是否完善進階資訊", val: "FillBaseEx" }
|
|
|
+]
|
|
|
+
|
|
|
+const howList = [
|
|
|
+ { label: "大於", val: ">"},
|
|
|
+ { label: "小於", val: "<"},
|
|
|
+ { label: "等於", val: "=="},
|
|
|
+ { label: "不等於", val: "!="},
|
|
|
+ { label: "大於等於", val: ">="},
|
|
|
+ { label: "小於等於", val: "<="},
|
|
|
+ { label: "是", val: "is"},
|
|
|
+ { label: "不是", val: "isnot"},
|
|
|
+ { label: "是空的", val: "is null"},
|
|
|
+ { label: "不是空的", val: "isnot null"}
|
|
|
+]
|
|
|
+
|
|
|
+const ruleBList = [
|
|
|
+ { label: "AI文字分析模組(展延)", val: "901001"},
|
|
|
+ { label: "AI蘇格拉底小數據(展延)", val: "901002"},
|
|
|
+ { label: "Web IRS 50人 (展延3個月)", val: "901003"},
|
|
|
+ { label: "Web IRS 50人 (延長1個月)", val: "901004"},
|
|
|
+ { label: "智慧評分 (一年)", val: "901005"},
|
|
|
+ { label: "HiTeachCC-6任務數(展延)", val: "903001"},
|
|
|
+ { label: "HiTeachCC-10任務數(展延)", val: "903002"},
|
|
|
+ { label: "HiTeachCC-100連線數(展延)", val: "903003"},
|
|
|
+ { label: "HiTeachCC-200連線數(展延)", val: "903004"},
|
|
|
+ { label: "小組協作(一年)", val: "901006"},
|
|
|
+ { label: "AI GPT(一年)", val: "901007"}
|
|
|
+]
|
|
|
+
|
|
|
+const crtCouponForm = reactive({
|
|
|
+ srvAdr: '',
|
|
|
+ couponType: "",
|
|
|
+ couponName: "",
|
|
|
+ expire: "",
|
|
|
+ eventName: "",
|
|
|
+ maxTaker: -1,
|
|
|
+ quantity: 0,
|
|
|
+ rule:[
|
|
|
+ {
|
|
|
+ // 條件
|
|
|
+ "q": [
|
|
|
+ {
|
|
|
+ operator: '',
|
|
|
+ type: '',
|
|
|
+ how: '',
|
|
|
+ val:''
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ "b": "", // 給的權益
|
|
|
+ "p": "0", // 給的積分
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ info: [
|
|
|
+ {
|
|
|
+ "l": "zh-tw",
|
|
|
+ "n": "",
|
|
|
+ "u": ""
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "l": "zh-cn",
|
|
|
+ "n": "",
|
|
|
+ "u": ""
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "l": "en-us",
|
|
|
+ "n": "",
|
|
|
+ "u": ""
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ targets: ""
|
|
|
+})
|
|
|
+
|
|
|
+// 翻譯條件字串
|
|
|
+const translateRule = (q)=>{
|
|
|
+ let str = ''
|
|
|
+ q.forEach(e => {
|
|
|
+
|
|
|
+ let o = operatorList.find(i=> i.val == e.operator)
|
|
|
+ if(o) str += o.label +' '
|
|
|
+
|
|
|
+ let t = typeList.find(i=> i.val == e.type)
|
|
|
+ if(t) str += t.label +' '
|
|
|
+
|
|
|
+ let h = howList.find(i=> i.val == e.how)
|
|
|
+ if(h) str += h.label +' '
|
|
|
+
|
|
|
+ if(e.how != 'is null' && e.how != 'isnot null') str += e.val +' '
|
|
|
+ });
|
|
|
+ return str
|
|
|
+}
|
|
|
+
|
|
|
+// 增加規則
|
|
|
+const addRule = (rIndex) => {
|
|
|
+ crtCouponForm.rule.splice(rIndex+1, 0, {
|
|
|
+ "q": [
|
|
|
+ {
|
|
|
+ operator: '',
|
|
|
+ type: '',
|
|
|
+ how: '',
|
|
|
+ val:''
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "b": "",
|
|
|
+ "p": "0"
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 刪除規則
|
|
|
+const delRule = (rIndex) => {
|
|
|
+ crtCouponForm.rule.splice(rIndex, 1)
|
|
|
+}
|
|
|
+
|
|
|
+// 增加條件
|
|
|
+const addCondition = (rIndex, index) => {
|
|
|
+ crtCouponForm.rule[rIndex].q.splice(index+1, 0, {
|
|
|
+ operator: '',
|
|
|
+ type: '',
|
|
|
+ how: '',
|
|
|
+ val:''
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 刪除條件
|
|
|
+const delCondition = (rIndex, index) => {
|
|
|
+ crtCouponForm.rule[rIndex].q.splice(index, 1)
|
|
|
+}
|
|
|
+
|
|
|
+// 語系轉字串
|
|
|
+const langToName = (lang) => {
|
|
|
+ switch(lang){
|
|
|
+ case 'zh-tw':
|
|
|
+ return '繁體中文'
|
|
|
+ case 'zh-cn':
|
|
|
+ return '簡體中文'
|
|
|
+ case 'en-us':
|
|
|
+ return '英文'
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 跳轉url測試
|
|
|
+const linkToPage = (url) => {
|
|
|
+ try {
|
|
|
+ if(url != ''){
|
|
|
+ var temp = new URL(url);
|
|
|
+ window.open(url, '_blank')
|
|
|
+ } else {
|
|
|
+ ElMessageBox.alert('請確認網址格式', '了解更多',
|
|
|
+ {
|
|
|
+ type: 'warning',
|
|
|
+ icon: markRaw(Warning),
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ ElMessageBox.alert('請確認網址格式', '了解更多',
|
|
|
+ {
|
|
|
+ type: 'warning',
|
|
|
+ icon: markRaw(Warning),
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 日曆元件限制設定
|
|
|
+const disabledDate = time => {
|
|
|
+ if(time.getTime() > (Date.now() + 7776000000)){ // 不能超過3個月
|
|
|
+ return true
|
|
|
+ } else if(time.getTime() < Date.now()- 3600 * 1000 * 24){
|
|
|
+ return true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 時間轉型
|
|
|
+const convertDate = computed(()=>{
|
|
|
+ return function(val){
|
|
|
+ let date = new Date(val).getTime()
|
|
|
+ if(date.toString().substring(0,1) !== '9'){
|
|
|
+ if(date.toString().length < 13) date = date * 1000
|
|
|
+ return date ? new Date(parseInt(date)).toLocaleDateString() : ''
|
|
|
+ } else {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const ruleFormRef = ref()
|
|
|
+
|
|
|
+const checkRule = (rule, value, callback) => {
|
|
|
+ let isCheck = false
|
|
|
+ value.forEach(e=>{
|
|
|
+ if(e.b == ""){
|
|
|
+ isCheck = true
|
|
|
+ }
|
|
|
+
|
|
|
+ e.q.forEach((q) => {
|
|
|
+ if(q.operator != ""){
|
|
|
+ isCheck = true
|
|
|
+ }
|
|
|
+ if(q.type != ""){
|
|
|
+ isCheck = true
|
|
|
+ }
|
|
|
+ if(q.how != ""){
|
|
|
+ isCheck = true
|
|
|
+ }
|
|
|
+ if(q.val != ""){
|
|
|
+ isCheck = true
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ if(isCheck){
|
|
|
+ let errC = 0
|
|
|
+ value.forEach(e=>{
|
|
|
+ if(e.b == ""){
|
|
|
+ errC++
|
|
|
+ }
|
|
|
+
|
|
|
+ e.q.forEach((q, i) => {
|
|
|
+ if(i != 0 && q.operator == ""){
|
|
|
+ errC++
|
|
|
+ }
|
|
|
+ if(q.type == ""){
|
|
|
+ errC++
|
|
|
+ }
|
|
|
+ if(q.how == ""){
|
|
|
+ errC++
|
|
|
+ }
|
|
|
+ if(q.how != 'is null' && q.how != 'isnot null' && q.val == ""){
|
|
|
+ errC++
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ if(errC > 0){
|
|
|
+ callback(new Error("請完成票券規則設定, 至少需要給予權益"))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback()
|
|
|
+}
|
|
|
+
|
|
|
+const checkQuantity = (rule, value, callback) => {
|
|
|
+ if(!value && value < 1) {
|
|
|
+ callback(new Error("生成券數最少為1"))
|
|
|
+ }
|
|
|
+ callback()
|
|
|
+}
|
|
|
+
|
|
|
+const checkMaxTaker = (rule, value, callback) => {
|
|
|
+ if(maxTakerFlag.value){
|
|
|
+ if(value < 1){
|
|
|
+ callback(new Error("兌換上限不能小於1"))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback()
|
|
|
+}
|
|
|
+
|
|
|
+const checkTargets = (rule, value, callback) => {
|
|
|
+ let isErr = false
|
|
|
+ if(value && !/^[0-9\n]+$/.test(value)){
|
|
|
+ isErr = true
|
|
|
+ callback(new Error("只能輸入醍摩豆ID與換行"))
|
|
|
+ } else if(consolidationFlag.value && !value) {
|
|
|
+ isErr = true
|
|
|
+ callback(new Error("請填入要歸戶的醍摩豆ID"))
|
|
|
+ } else if(value && isArrayRep(value.split('\n'))){
|
|
|
+ isErr = true
|
|
|
+ callback(new Error("醍摩豆ID有重複"))
|
|
|
+ } else if(value){
|
|
|
+ let errIds = []
|
|
|
+ let tmp = value.split('\n')
|
|
|
+ tmp.forEach(e=> {
|
|
|
+ if(e.length != 10) {
|
|
|
+ errIds.push(e)
|
|
|
+ } else {
|
|
|
+ let now = Math.floor(new Date().getTime() / 1000)
|
|
|
+ let orgId = parseInt(e)
|
|
|
+ if(parseInt(e.substring(0, 1)) >= 6){
|
|
|
+ orgId -= 5000000000
|
|
|
+ }
|
|
|
+
|
|
|
+ if(orgId > now){
|
|
|
+ errIds.push(e)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ if(errIds.length > 0){
|
|
|
+ // console.log(errIds, 'errIds')
|
|
|
+ isErr = true
|
|
|
+ callback(new Error("請檢查醍摩豆ID是否符合格式或有空格"))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!isErr && value != ''){
|
|
|
+ let tArray = value.split('\n')
|
|
|
+ targetsCount.value = tArray.length
|
|
|
+ } else {
|
|
|
+ targetsCount.value = 0
|
|
|
+ }
|
|
|
+
|
|
|
+ callback()
|
|
|
+}
|
|
|
+
|
|
|
+function isArrayRep(array){
|
|
|
+ var result = new Set();
|
|
|
+ var repeat = new Set();
|
|
|
+ array.forEach(item => {
|
|
|
+ result.has(item) ? repeat.add(item) : result.add(item);
|
|
|
+ })
|
|
|
+ // console.log(result); // {1, 2, "a", 3, "b"}
|
|
|
+ // console.log(repeat); // {1, "a"}
|
|
|
+ // console.log(repeat.size)
|
|
|
+ if(repeat.size != 0){
|
|
|
+ return true
|
|
|
+ } else {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const checkCupoInfo = (rule, value, callback) => {
|
|
|
+ let errC = 0
|
|
|
+ value.forEach(e => {
|
|
|
+ if(!e.n) errC ++
|
|
|
+ if(!e.u) errC ++
|
|
|
+ });
|
|
|
+ if(errC > 0){
|
|
|
+ callback(new Error("請完成票券資訊設定"))
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const checkEventType = (rule, value, callback) => {
|
|
|
+ if(eventType.value == '') callback(new Error("請選擇一個優惠券活動"))
|
|
|
+ callback()
|
|
|
+}
|
|
|
+
|
|
|
+const checkExpire = (rule, value, callback) => {
|
|
|
+ if(!value){
|
|
|
+ callback(new Error("請輸入到期時間"))
|
|
|
+ } else {
|
|
|
+ let timestemp = new Date(value).getTime()
|
|
|
+ console.log(timestemp)
|
|
|
+ console.log(Date.now())
|
|
|
+ console.log(timestemp - Date.now())
|
|
|
+ if(Math.floor((timestemp - Date.now())/1000) < 60 *60){
|
|
|
+ callback(new Error("限制時間至少要大於一小時"))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback()
|
|
|
+}
|
|
|
+
|
|
|
+const rules = reactive({
|
|
|
+ couponType: [{required: true, trigger: "blur", message: '請選擇一個發券類型' }],
|
|
|
+ eventName: [{required: true, trigger: "blur", message: '請輸入活動名稱'}],
|
|
|
+ expire: [{required: true, validator: checkExpire, trigger: "blur"}],
|
|
|
+ maxTaker: [{validator: checkMaxTaker, trigger: "blur"}],
|
|
|
+ quantity: [{required: true,validator: checkQuantity, trigger: "blur"}],
|
|
|
+ rule: [{required: true, validator: checkRule, trigger: "blur"}],
|
|
|
+ info: [{required: true, validator: checkCupoInfo, trigger: "blur"}],
|
|
|
+ targets: [{required: true, validator: checkTargets, trigger: "blur"}],
|
|
|
+ srvAdr: [{required: true, trigger: "blur", message: '請選擇一個站別' }],
|
|
|
+ eventType: [{required: true, trigger: "blur", validator: checkEventType}],
|
|
|
+})
|
|
|
+
|
|
|
+const submitForm = formEl => {
|
|
|
+ if (!formEl) return
|
|
|
+ formEl.validate(async (valid, ddd) => {
|
|
|
+ if (valid) {
|
|
|
+ console.log("submit!")
|
|
|
+
|
|
|
+ ElMessageBox.confirm(
|
|
|
+ '確認建立此優惠券嗎?',
|
|
|
+ '',
|
|
|
+ {
|
|
|
+ type: 'info',
|
|
|
+ confirmButtonText: '我確定'
|
|
|
+ }
|
|
|
+ ).then(async ()=>{
|
|
|
+ loading.value = true
|
|
|
+
|
|
|
+ let success = false
|
|
|
+ let data = JSON.parse(JSON.stringify(crtCouponForm))
|
|
|
+ data.expire = Math.floor( new Date(crtCouponForm.expire).getTime() / 1000)
|
|
|
+ crtCouponForm.rule.forEach((e, i)=>{
|
|
|
+ let qStr = ""
|
|
|
+ e.q.forEach(qe => {
|
|
|
+ let values = Object.values(qe)
|
|
|
+ values.forEach(v => {
|
|
|
+ if(v != '') qStr += v + ' '
|
|
|
+ })
|
|
|
+ })
|
|
|
+ data.rule[i].q = qStr
|
|
|
+ })
|
|
|
+ data.maxTaker = crtCouponForm.maxTaker == 0 ? -1 : crtCouponForm.maxTaker
|
|
|
+ data.targets = []
|
|
|
+ if(crtCouponForm.targets != ''){
|
|
|
+ data.targets = crtCouponForm.targets.split('\n')
|
|
|
+ }
|
|
|
+
|
|
|
+ await proxy.$api.crtCoupon(data).then((res) => {
|
|
|
+ console.log(res)
|
|
|
+ couponResult.value = '' // 先清空
|
|
|
+ if(res.coupons){
|
|
|
+ res.coupons.forEach(e=>{
|
|
|
+ couponResult.value += (e+'\n')
|
|
|
+ })
|
|
|
+ } else if(res.coupon){
|
|
|
+ couponResult.value = res.coupon
|
|
|
+ }
|
|
|
+ success = true
|
|
|
+ }).catch(e=>{
|
|
|
+ ElMessageBox.alert('請確認優惠券設定是否合乎格式', '建立優惠券',
|
|
|
+ {
|
|
|
+ type: 'warning',
|
|
|
+ icon: markRaw(Warning),
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }).finally(() => {
|
|
|
+ loading.value = false
|
|
|
+ })
|
|
|
+
|
|
|
+ if(consolidationFlag.value == true && success) {
|
|
|
+ loading.value = true
|
|
|
+ let consolidationData = {
|
|
|
+ srvAdr: crtCouponForm.srvAdr,
|
|
|
+ ids: crtCouponForm.targets.split('\n'),
|
|
|
+ coupon: couponResult.value
|
|
|
+ }
|
|
|
+ await proxy.$api.consolidationCoupon(consolidationData).then((res) => {
|
|
|
+ consolidationFlag.value = false
|
|
|
+ console.log(res, '歸戶成功')
|
|
|
+ }).catch(e=>{
|
|
|
+ ElMessageBox.alert('歸戶失敗', '歸戶',
|
|
|
+ {
|
|
|
+ type: 'warning',
|
|
|
+ icon: markRaw(Warning),
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }).finally(() => {
|
|
|
+ loading.value = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ if(success){
|
|
|
+ ElMessageBox.alert('成功', '建立優惠券',
|
|
|
+ {
|
|
|
+ type: 'info',
|
|
|
+ icon: markRaw(SuccessFilled),
|
|
|
+ }
|
|
|
+ )
|
|
|
+ resetForm(formEl) // 欄位清除
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ console.log("error submit!")
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const resetForm = formEl => {
|
|
|
+ if (!formEl) return
|
|
|
+ formEl.resetFields()
|
|
|
+ crtCouponForm.info = [
|
|
|
+ {
|
|
|
+ "l": "zh-tw",
|
|
|
+ "n": "",
|
|
|
+ "u": ""
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "l": "zh-cn",
|
|
|
+ "n": "",
|
|
|
+ "u": ""
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "l": "en-us",
|
|
|
+ "n": "",
|
|
|
+ "u": ""
|
|
|
+ }
|
|
|
+ ]
|
|
|
+
|
|
|
+ crtCouponForm.rule = [
|
|
|
+ {
|
|
|
+ "q": [
|
|
|
+ {
|
|
|
+ operator: '',
|
|
|
+ type: '',
|
|
|
+ how: '',
|
|
|
+ val:''
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "b": "",
|
|
|
+ "p": "0"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+
|
|
|
+ crtCouponForm.targets = ''
|
|
|
+ eventType.value = ''
|
|
|
+ consolidationFlag.value= false
|
|
|
+ crtCouponForm.couponType = ''
|
|
|
+}
|
|
|
+
|
|
|
+const copyDocument = (data) => {
|
|
|
+ navigator.clipboard.writeText(data)
|
|
|
+ .then(() => {
|
|
|
+ ElMessageBox.alert('複製成功', '複製優惠券',
|
|
|
+ {
|
|
|
+ type: 'info',
|
|
|
+ icon: markRaw(CopyDocument),
|
|
|
+ }
|
|
|
+ )
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.coupons {
|
|
|
+ box-shadow: 0px 3px 2px 2px #E0E0E0;
|
|
|
+ border-radius: 10px;
|
|
|
+ background-color: #ffffff;
|
|
|
+ display: flex;
|
|
|
+ width: 100%;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ align-items: stretch;
|
|
|
+ font-family: "Noto Sans CJK TC", "serif";
|
|
|
+ max-width: 330px;
|
|
|
+ min-height: 100px;
|
|
|
+}
|
|
|
+.coupons-cont {
|
|
|
+ flex: 2;
|
|
|
+ padding: 8px 16px;
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.coupons-cont .hole {
|
|
|
+ position: absolute;
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background-color: #ffffff;
|
|
|
+ left: -9px;
|
|
|
+ box-shadow: #e0e0e0 3px 0px 2px 0px;
|
|
|
+}
|
|
|
+.coupons-cont-box {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: space-around;
|
|
|
+}
|
|
|
+.coupons-cont-box-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ white-space: pre-line;
|
|
|
+ text-align: left;
|
|
|
+ line-height: 1.3;
|
|
|
+}
|
|
|
+.coupons-cont-box-link {
|
|
|
+ text-align: left;
|
|
|
+ font-size: 13px;
|
|
|
+}
|
|
|
+.coupons-cont-box-exp {
|
|
|
+ display: flex;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+.coupons-cont-box-exp.isExpiringSoon {
|
|
|
+ color: #f57c00;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+.coupons-btn {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 15px;
|
|
|
+ border-left: 1px dashed #9e9e9e;
|
|
|
+ background-color: #03a9f4;
|
|
|
+ color: #f3f3f3;
|
|
|
+ border-top-right-radius: 10px;
|
|
|
+ border-bottom-right-radius: 10px;
|
|
|
+ flex-direction: column;
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+.coupons-btn.exchange {
|
|
|
+ background-color: #9e9e9e;
|
|
|
+}
|
|
|
+.coupons-btn-hole-positon {
|
|
|
+ position: absolute;
|
|
|
+ height: 100%;
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.coupons-btn-hole-positon .hole {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ border-radius: 50%;
|
|
|
+ position: absolute;
|
|
|
+ background-color: #ffffff;
|
|
|
+ right: -9px;
|
|
|
+}
|
|
|
+.coupons-btn .icon-box {
|
|
|
+ border: 1px solid #fff;
|
|
|
+ display: flex;
|
|
|
+ border-radius: 50%;
|
|
|
+ padding: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+</style>
|