Commit 2699e4ab by haojie

1

parent a06a490c
......@@ -9,22 +9,25 @@
"preview": "vite preview"
},
"dependencies": {
"@metamask/detect-provider": "^1.2.0",
"pinia": "^2.0.32",
"tdesign-vue-next": "^1.0.9",
"vue": "^3.2.45",
"vue-i18n": "^9.2.2",
"@metamask/detect-provider": "^1.2.0",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@types/node": "^18.14.0",
"@vitejs/plugin-vue": "^4.0.0",
"@vitejs/plugin-vue-jsx": "^3.0.0",
"axios": "^1.3.4",
"less": "^4.1.3",
"terser": "^5.16.5",
"typescript": "^4.9.3",
"vite": "^4.1.0",
"vite-plugin-compression": "^0.5.1",
"vite-svg-loader": "^4.0.0",
"vue-clipboard3": "^2.0.0",
"vue-tsc": "^1.0.24"
}
}
......@@ -4,6 +4,12 @@
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: rgba(240, 240, 240, 0.2);
.loader {
border-top: 0.3em solid rgba(0, 0, 0, 0.1);
border-right: 0.3em solid rgba(0, 0, 0, 0.1);
......
......@@ -6,14 +6,17 @@ export default defineComponent({
type: Array as PropType<any[]>,
default: [],
},
modelValue: String,
modelValue: {
type: Number || String,
},
},
emits: ['update:modelValue'],
emits: ['update:modelValue', 'change'],
setup(props, { emit }) {
const curbtn = ref(props.modelValue);
const CurrentChange = (value: string) => {
const curbtn = ref<string | number | undefined>(props.modelValue);
const CurrentChange = (value: string | number) => {
curbtn.value = value;
emit('update:modelValue', value);
emit('change', value);
// emit('update:modelValue', value);
};
return () => (
<div class="custom-radio-group">
......
import { useI18n } from 'vue-i18n';
import useClipboard from 'vue-clipboard3';
import { MessagePlugin } from 'tdesign-vue-next';
export default function () {
const { t } = useI18n();
const doCopy = (keyword: string) => {
if (!keyword) {
return;
}
const { toClipboard } = useClipboard();
toClipboard(keyword)
.then(() => {
MessagePlugin.closeAll();
MessagePlugin.success('复制成功');
})
.catch((e) => {
MessagePlugin.closeAll();
MessagePlugin.error('复制失败');
});
};
return {
doCopy,
};
}
......@@ -12,7 +12,8 @@ const cn = {
MyBlindBox: '我的盲盒',
BlindBoxactivity: '盲盒活动',
inprogress: '进行中',
unfinish: '未完成',
unfinish: '未开始',
Unfinished: '未完成',
losingLottery: '未中奖',
Ballot: '中签',
Finished: '已完成',
......@@ -23,6 +24,8 @@ const cn = {
second: '秒',
price: '价格',
NumberBuyers: '购买人数',
success: '成功',
Failed: '失败',
},
button: {
back: '返回',
......
......@@ -12,7 +12,8 @@ const en = {
MyBlindBox: 'My Blind Box',
BlindBoxactivity: 'Blind box activity',
inprogress: 'underway',
unfinish: 'Unfinish',
unfinish: 'Not started',
Unfinished: 'Unfinished',
losingLottery: 'losing lottery',
Ballot: 'Winning Lottery',
Finished: 'Finished',
......@@ -23,6 +24,8 @@ const en = {
second: 'Second',
price: 'Price',
NumberBuyers: 'Number of buyers',
success: 'Success',
Failed: 'Failed',
},
button: {
back: 'Back',
......
......@@ -19,6 +19,9 @@ export default defineComponent({
};
const { t } = useI18n();
const openDialog = () => {
if (userAddress.address) {
return;
}
foxDialog.dialog = !foxDialog.dialog;
};
const getText = () => {
......
......@@ -82,6 +82,13 @@ export default defineComponent({
navStatus.value = 'close';
navTextShow.value = false;
};
// 判断当前路由
const getCurRoute = (value: string) => {
if (value == '/raffle' && currentRoute.value.indexOf('/raffle') != -1) {
return 'nav-icon-active';
}
return currentRoute.value == value ? 'nav-icon-active' : '';
};
const NavChange = (item: any) => {
if (item.disable) {
return;
......@@ -135,12 +142,7 @@ export default defineComponent({
'default-line',
]}
></div>
<div
class={[
'nav-icon',
currentRoute.value == item.value ? 'nav-icon-active' : '',
]}
>
<div class={['nav-icon', getCurRoute(item.value)]}>
{item.icon}
<span
class="nav-text"
......
......@@ -5,6 +5,8 @@ import router from './router';
import i18n from './language';
import { createPinia } from 'pinia';
import Tdesign from './utils/Tdesign';
// 引入组件库全局样式资源
import 'tdesign-vue-next/es/style/index.css';
let app = createApp(App);
app.use(router);
app.use(i18n);
......
.custom-activity-box {
position: relative;
min-height: 600px;
.header {
border-bottom: 2px solid #e9e9e9;
transform: rotate(0.06deg);
......
import { computed, defineComponent, reactive, ref } from 'vue';
import {
computed,
defineComponent,
onMounted,
reactive,
ref,
watch,
} from 'vue';
import './index.less';
import { useI18n } from 'vue-i18n';
import CustomButton from '@/components/button';
import Card from '../card';
import ActivityDetail from '../ActivityDetail';
import { getCurrentDevice } from '@/utils/tool';
import { getBlindBoxList } from '@/utils/api/BlindBox';
import Loading from '@/components/Loading';
export default defineComponent({
props: {
modelValue: String,
......@@ -12,51 +21,15 @@ export default defineComponent({
emits: ['update:modelValue'],
setup(props, { emit }) {
const { t } = useI18n();
const loading = ref(false);
// 详情内容
const detailObj = ref({});
// 当前是否打开详情页面
const isDetailPage = ref(false);
const activityList = reactive({
list: [
{
img: new URL('@/assets/img/test.png', import.meta.url).href,
introduction: '5人抽奖一人必中',
status: '1',
price: '300',
buyersNum: '3/5',
canObtain: '1000',
countDown: 60000000,
index: 0,
},
{
img: new URL('@/assets/img/test.png', import.meta.url).href,
introduction: '5人抽奖一人必中',
status: '2',
price: '300',
buyersNum: '3/5',
canObtain: '1000',
countDown: 60000000,
index: 1,
},
{
img: new URL('@/assets/img/test.png', import.meta.url).href,
introduction: '5人抽奖一人必中',
status: '3',
price: '300',
buyersNum: '3/5',
canObtain: '1000',
countDown: 60000000,
index: 2,
},
{
img: new URL('@/assets/img/test.png', import.meta.url).href,
introduction: '5人抽奖一人必中',
status: '4',
price: '300',
buyersNum: '3/5',
canObtain: '1000',
countDown: 60000000,
index: 3,
},
],
const activityList = reactive<{
list: any[];
}>({
list: [],
});
const onBack = () => {
emit('update:modelValue', 'mybox');
......@@ -64,6 +37,8 @@ export default defineComponent({
const onCardClick = (value: number) => {
// 打开详情
isDetailPage.value = true;
// 详情内容
detailObj.value = activityList.list[value];
};
const backList = () => {
isDetailPage.value = !isDetailPage.value;
......@@ -75,6 +50,30 @@ export default defineComponent({
}
return true;
});
const getList = async () => {
try {
loading.value = true;
let res: any = await getBlindBoxList();
res.data.forEach((item: any, index: number) => {
item.index = index;
});
activityList.list = res.data;
loading.value = false;
} catch (e) {
console.log(e);
loading.value = false;
}
};
onMounted(() => {
getList();
});
const onUpdateTime = (item: any) => {
/**
* value--倒计时
* index--数组下标
*/
activityList.list[item.index].countdown = item.value;
};
return () => (
<div class="custom-activity-box">
<div class="header">
......@@ -100,14 +99,16 @@ export default defineComponent({
<Card
info={item}
Countdown={true}
onUpdateTime={onUpdateTime}
onCardClick={onCardClick}
></Card>
))}
</div>
<div v-show={isDetailPage.value}>
<ActivityDetail></ActivityDetail>
<ActivityDetail info={detailObj.value}></ActivityDetail>
</div>
</div>
<Loading v-show={loading.value}></Loading>
</div>
);
},
......
......@@ -105,6 +105,7 @@
font-weight: 500;
font-size: 18px;
color: #000000;
margin-bottom: 12px;
}
}
}
import { defineComponent } from 'vue';
import { defineComponent, PropType, ref } from 'vue';
import './index.less';
import CustomCountDown from '../CountDown';
import OpenSvg from '@/assets/svg/raffle/open.svg';
import { useFoxDialog } from '@/store/dialog';
import { useFoxWallet } from '@/store/FoxWallet';
import { MessagePlugin } from 'tdesign-vue-next';
import InviteDialog from '../InviteDialog';
import {
buy_blindBox,
IntervalCheckOrder,
useCanNotBuy,
} from '@/utils/api/BlindBox';
import { useRoute } from 'vue-router';
export default defineComponent({
props: {
info: {
type: Object as PropType<any>,
default: {},
},
},
setup(props) {
// 收款地址--先写死,之后从接口获取
let MyAddress = '0x51eF357cf7204DB2a6e31750817F709a10c86f37';
const route = useRoute();
const RouteParams = route.params;
const testImg = new URL('@/assets/img/test.png', import.meta.url).href;
const testTime = 60000000;
// 邀请弹窗状态
const InviteVisible = ref(false);
let InterVal: any = null;
// 用户hash
const UserHash = ref('');
const { $state: foxDialog } = useFoxDialog();
const { $state: FoxWallet } = useFoxWallet();
// 打开连接弹窗
const openDialog = () => {
foxDialog.dialog = true;
};
// 检测充值到账
const Check = async () => {
try {
let res: any = await IntervalCheckOrder({
hash: UserHash.value,
price: props.info.price,
form: FoxWallet.address,
});
if (res.data == true) {
closeInterval();
MessagePlugin.success('购买成功');
}
} catch (e) {
console.log(e);
}
};
// 开启轮询
const openCheckInterval = () => {
InterVal = window.setInterval(() => {
Check();
}, 3000);
};
// 关闭轮询
const closeInterval = () => {
if (InterVal) {
window.clearInterval(InterVal);
}
};
// 发起交易
const recharge = async (hash: string = '', price: number = 0) => {
let params: any = {
address: FoxWallet.address,
hash: hash,
id: props.info.id,
};
// 判断是否为分享的链接
if (
RouteParams.code &&
RouteParams.id &&
typeof RouteParams.id == 'string'
) {
params.invite_code = RouteParams.code;
params.Invitees_box_id = parseFloat(RouteParams.id);
}
let res: any = await buy_blindBox(params);
if (res.code == 0) {
// 开启轮询
MessagePlugin.success('充值记录检测中...');
if (UserHash.value) {
openCheckInterval();
}
}
};
// 支付
const Payment = () => {
let eth: any = window;
// 转换后的收款地址
let MyNewAddress = MyAddress.substring(2).padStart(64, '0');
// 实际价格--props.price
// 测试价格
// let price = 0.01;
// 转换后的数量
let newPrice = (parseInt(props.info.price) * Math.pow(10, 18))
.toString(16)
.padStart(64, '0');
// 小狐狸支付
eth.ethereum
.request({
method: 'eth_sendTransaction',
params: [
{
// 用户钱包地址
from: FoxWallet.address,
// 支付代币类型 BUSD
to: '0xCC88e86AA0E589e18ff03fB9b0449468A1EF1f4B',
// gas价格
// gasPrice: "0x12a05f200",
// gas: "0x16e360",
// 交易数据十六进制 交易0xa9059cbb 目标钱包地址(我们的钱包地址) 交易金额(十六转换)
data: '0xa9059cbb' + MyNewAddress + newPrice,
},
],
})
.then((hash: string) => {
// 交易hash
if (hash) {
UserHash.value = hash;
// 发送充值信息
recharge(hash, parseInt(props.info.price));
}
})
.catch((e: any) => {
console.log(e);
});
//
};
const getUserBuyStatus = async () => {
try {
let res: any = await useCanNotBuy({
address: FoxWallet.address,
id: props.info.id,
});
if (res.data === true) {
return true;
}
return false;
} catch (e) {
console.log(e);
return false;
}
};
// 打开邀请弹窗
const openInviteDialog = () => {
InviteVisible.value = true;
};
// 立即购买
const Buy_now = async () => {
const { info } = props;
// 地址
if (!FoxWallet.address) {
MessagePlugin.warning('请先连接钱包');
return;
}
if (info.countdown <= 0) {
MessagePlugin.warning('活动已结束');
return;
}
// 能否购买
let cannot = await getUserBuyStatus();
if (cannot) {
Payment();
} else {
MessagePlugin.closeAll();
MessagePlugin.warning('无法再购买');
}
};
return () => (
<div class="custom-card-detail">
<div class="detail-head">
<div class="detail-head-img">
<img src={testImg} alt="" />
<img src={props.info.project_icon} alt="" />
</div>
<div class="detail-head-content">
<div class="title">5人抽奖一人必中</div>
<div class="title">{props.info.projectName}</div>
<div class="count-down-box">
<div class="label">购买倒计时</div>
<div class="value">
<CustomCountDown time={testTime}></CustomCountDown>
<CustomCountDown time={props.info.countdown}></CustomCountDown>
</div>
</div>
<div class="price-and-buynum">
<div class="price-box">
<div class="label">价格</div>
<div class="value">200 RESDAO</div>
<div class="value">{props.info.price} RESDAO</div>
</div>
<div class="price-box margin">
<div class="label">当前购买人数</div>
<div class="value">0/5</div>
<div class="value">
{props.info.buy_num}/{props.info.max_participants_num}
</div>
</div>
</div>
<div class="line"></div>
<div class="price-and-prize">
<div>
<span>你将花费:</span>
200 RESDAO
{props.info.price} RESDAO
</div>
<div>
<span class="text2">中奖可获得:</span>
600 RESDAO
{props.info.price * 3} RESDAO
</div>
</div>
<div class="content-wallet-tooltip">
<button class="connect-wallet">连接钱包</button>
{FoxWallet.address ? (
<button class="connect-wallet" onClick={Buy_now}>
立即购买
</button>
) : (
<button class="connect-wallet" onClick={openDialog}>
连接钱包
</button>
)}
<span class="text">提示:一次只能购买一次</span>
<div class="open-link">
<OpenSvg></OpenSvg>
<span onClick={openInviteDialog}>
<OpenSvg></OpenSvg>
</span>
</div>
</div>
</div>
......@@ -55,8 +231,12 @@ export default defineComponent({
<div class="content-line"></div>
<div class="custom-Rules">
<div class="title">规则</div>
<div class="value"></div>
<div class="value" innerHTML={props.info.rules}></div>
</div>
<InviteDialog
v-model={InviteVisible.value}
id={props.info.id}
></InviteDialog>
</div>
);
},
......
import { defineComponent, computed } from 'vue';
import {
defineComponent,
computed,
watch,
reactive,
onUnmounted,
ref,
} from 'vue';
import { useI18n } from 'vue-i18n';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import './index.less';
dayjs.extend(duration);
export default defineComponent({
props: {
time: {
......@@ -8,11 +18,13 @@ export default defineComponent({
default: 0,
},
},
setup(props) {
emits: ['updateTime'],
setup(props, { emit }) {
let Interval: any = null;
const { t } = useI18n();
// 获取倒计时
const getCountDown = computed(() => {
let list: any = [
const RemainingTime = ref(0);
const countDownList = reactive({
list: [
{
label: t('raffle.day'),
value: '00',
......@@ -29,8 +41,76 @@ export default defineComponent({
label: t('raffle.second'),
value: '00',
},
];
return list;
],
});
// 获取倒计时
const getCountDown = computed(() => {
return countDownList.list;
});
const isMaxLength = (value: any) => {
return (value + '').length < 2;
};
// 获取当前币倒计时
const CountdownFun = (newTime: any) => {
let diffTime: any = dayjs.duration(newTime);
let day: any = diffTime.days(); //天
let hours: any = diffTime.hours(); //小时
let minutes: any = diffTime.minutes(); //分钟
let seconds: any = diffTime.seconds(); //秒
diffTime = null;
if (day < 10 && isMaxLength(day)) {
day = '0' + day + '';
}
if (hours < 10 && isMaxLength(hours)) {
hours = '0' + hours + '';
}
if (minutes < 10 && isMaxLength(minutes)) {
minutes = '0' + minutes + '';
}
if (seconds < 10 && isMaxLength(seconds)) {
seconds = '0' + seconds + '';
}
countDownList.list[0].value = day;
countDownList.list[1].value = hours;
countDownList.list[2].value = minutes;
countDownList.list[3].value = seconds;
};
// 关闭定时器
const closeInterval = () => {
if (Interval) {
window.clearInterval(Interval);
}
};
watch(
() => props.time,
(v) => {
if (v) {
// 开始倒计时
closeInterval();
RemainingTime.value = v;
CountdownFun(RemainingTime.value);
Interval = window.setInterval(() => {
if (RemainingTime.value <= 0) {
closeInterval();
return;
}
CountdownFun(RemainingTime.value);
RemainingTime.value -= 1000;
// 提交最新的倒计时
if (RemainingTime.value > 0) {
emit('updateTime', RemainingTime.value);
} else {
emit('updateTime', 0);
}
}, 1000);
}
},
{
immediate: true,
}
);
onUnmounted(() => {
closeInterval();
});
return () => (
<div class="custom-countdown-box">
......
.custom-invite-dialog {
.t-dialog__close {
display: flex;
align-items: center;
justify-content: center;
}
.t-dialog__body {
padding: 0;
overflow-y: hidden;
}
.invite-dialog-body {
.title {
font-weight: 400;
font-size: 18px;
color: #000000;
}
.invite-box {
width: 100%;
height: 39px;
border: 1px solid #544fa1;
border-radius: 10px;
box-sizing: border-box;
margin: 12px 0;
display: flex;
justify-content: space-between;
& > * {
height: 100%;
display: flex;
align-items: center;
padding: 0 12px;
}
.url {
font-weight: 500;
font-size: 16px;
color: #8c8c8c;
}
.copy-button {
cursor: pointer;
font-weight: 500;
font-size: 16px;
color: #000000;
border-left: 1px solid #544fa1;
user-select: none;
}
}
.current-invite-address {
font-weight: 400;
font-size: 18px;
display: flex;
flex-wrap: wrap;
.label {
color: #444444;
}
.value {
color: #000000;
}
}
.margin {
margin-top: 12px;
}
}
}
import { defineComponent, ref, watch } from 'vue';
import CloseSvg from '@/assets/svg/dialog/close.svg';
import './index.less';
import { getUserInfo } from '@/utils/api/BlindBox';
import { useFoxWallet } from '@/store/FoxWallet';
import useCopy from '@/hook/useCopy';
export default defineComponent({
props: {
modelValue: Boolean,
id: Number,
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const { doCopy } = useCopy();
const visible = ref(props.modelValue);
const resObj = ref<any>({});
const { $state: FoxWallet } = useFoxWallet();
const closeIcon = () => {
return <CloseSvg></CloseSvg>;
};
// 获取用户邀请码
const getUser = async () => {
try {
let res: any = await getUserInfo({
address: FoxWallet.address,
id: props.id,
});
resObj.value = res.data;
} catch (e) {
console.log(e);
}
};
watch(
() => props.modelValue,
(v) => {
visible.value = v;
if (v) {
getUser();
}
}
);
watch(
() => visible.value,
(v) => {
emit('update:modelValue', v);
}
);
return () => (
<t-dialog
v-model:visible={visible.value}
placement="center"
attach="body"
class="custom-invite-dialog"
footer={false}
header={false}
closeBtn={closeIcon}
>
<div class="invite-dialog-body">
<div class="title">你的邀请码</div>
<div class="invite-box">
<div class="url">{resObj.value.invitation_link}</div>
<div
class="copy-button"
onClick={doCopy.bind(this, resObj.value.invitation_link ?? '')}
>
复制
</div>
</div>
<div class="current-invite-address">
<div class="label">当前盲盒邀请:</div>
<div class="value">
{resObj.value.invite_list
? resObj.value.invite_list.map((item: any) => (
<span>{item}</span>
))
: ''}
</div>
</div>
<div class="current-invite-address margin">
<div class="label">提高成功概率:</div>
<div class="value">{resObj.value.rate}%</div>
</div>
</div>
</t-dialog>
);
},
});
......@@ -13,8 +13,7 @@
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
margin: 6px 0;
margin-top: 6px;
white-space: nowrap;
flex-wrap: wrap;
row-gap: 6px;
......@@ -33,7 +32,7 @@
align-items: center;
white-space: nowrap;
}
.status1 {
.success {
background: rgba(18, 185, 129, 0.1);
color: #12b981;
}
......@@ -41,11 +40,16 @@
background: rgba(126, 126, 126, 0.1);
color: #7e7e7e;
}
.status3 {
.failed {
background: rgba(240, 84, 81, 0.08);
color: #f05451;
}
}
.box-id {
font-weight: 400;
font-size: 16px;
color: #747474;
}
.custom-card-countDown {
display: flex;
justify-content: center;
......@@ -62,7 +66,7 @@
.card-line-two {
display: flex;
justify-content: space-between;
margin-bottom: 6px;
margin: 6px 0;
white-space: nowrap;
flex-wrap: wrap;
row-gap: 6px;
......
import { computed, defineComponent, PropType } from 'vue';
import { defineComponent, PropType } from 'vue';
import './index.less';
import CustomCountDown from '../CountDown';
import { useI18n } from 'vue-i18n';
......@@ -16,38 +16,65 @@ export default defineComponent({
type: Boolean,
default: false,
},
customStatus: {
type: Object as any,
default: {},
},
},
emits: ['CardClick'],
/**
* 三个状态
* 未开始1
* 进行中2
* 已结束3
*/
emits: ['CardClick', 'updateTime'],
setup(props, { emit }) {
const { t } = useI18n();
const curStauts = () => {
const { info } = props;
const { info, customStatus } = props;
if (customStatus.title === 'true') {
if (info.is_sucess) {
// 中奖了
return t('raffle.success');
} else {
return t('raffle.Failed');
}
} else if (customStatus.title) {
// 存在自定义状态
return customStatus.title;
}
if (Object.keys(info).length) {
switch (info.status) {
case '1':
return t('raffle.inprogress');
case '2':
return t('raffle.unfinish');
case '3':
return t('raffle.losingLottery');
case '4':
return t('raffle.Ballot');
if (info.status == 1) {
return t('raffle.unfinish');
} else if (info.status == 2) {
return t('raffle.inprogress');
} else if (info.status == 3) {
return t('raffle.Finished');
}
}
};
const getClass = () => {
const { info } = props;
const { info, customStatus } = props;
if (customStatus.title === 'true') {
if (info.is_sucess) {
// 中奖了
return 'success';
} else {
return 'failed';
}
} else if (customStatus.type) {
// 存在自定义状态
return customStatus.type;
}
if (Object.keys(info).length) {
switch (info.status) {
case '1':
return 'status1';
case '2':
return 'status2';
case '3':
return 'status3';
case '4':
return 'status1';
if (info.status == 1) {
return 'failed';
} else if (info.status == 2) {
// 进行中
return 'success';
} else if (info.status == 3) {
return 'status2';
}
}
};
......@@ -59,22 +86,36 @@ export default defineComponent({
}
emit('CardClick', info.index);
};
// 更新倒计时
const onUpdateTime = (value: number) => {
emit('updateTime', {
value: value,
index: props.info.index,
});
};
return () => (
<div
class={['custom-raffle-card', props.Countdown ? 'pointer' : '']}
class={[
'custom-raffle-card',
props.Countdown && props.info.status == 2 ? 'pointer' : '',
]}
onClick={CardClick}
>
<img class="img" src={props.info.img} alt="" />
<img class="img" src={props.info.project_icon} alt="" />
<div class="card-line-parent">
<div class="card-line-one">
<span class="left-introduction">{props.info.introduction}</span>
<span class="left-introduction">{props.info.projectName}</span>
<div class={['card-right-status', getClass()]}>{curStauts()}</div>
</div>
<div class="box-id">#{props.info.id}</div>
{props.Countdown ? (
<div class="custom-card-countDown">
<div class="label">{t('raffle.PurchaseCountdown')}</div>
<div class="value">
<CustomCountDown time={props.info.countDown}></CustomCountDown>
<CustomCountDown
time={props.info.countdown}
onUpdateTime={onUpdateTime}
></CustomCountDown>
</div>
</div>
) : (
......@@ -87,7 +128,9 @@ export default defineComponent({
</div>
<div class="price center">
<div class="label">{t('raffle.NumberBuyers')}</div>
<div class="value">{props.info.buyersNum}</div>
<div class="value">
{props.info.buy_num}/{props.info.max_participants_num}
</div>
</div>
<div class="price end" v-show={props.showEnd}>
<div class="label">中奖可得</div>
......
.custom-my-blind-box {
min-height: 600px;
position: relative;
width: 100%;
.header {
.blind-box-head {
font-weight: 500;
......@@ -9,7 +12,6 @@
}
}
.my-blind-box-content {
position: relative;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
......
import { computed, defineComponent, reactive, ref } from 'vue';
import {
computed,
defineComponent,
onMounted,
reactive,
ref,
watch,
} from 'vue';
import './index.less';
import { useI18n } from 'vue-i18n';
import CustomButton from '@/components/button';
import RadioGroup from '@/components/RadioGroup';
import Loading from '@/components/Loading';
import Card from '../card';
import { UserOrder } from '@/utils/api/BlindBox';
import { useFoxWallet } from '@/store/FoxWallet';
export default defineComponent({
props: {
modelValue: String,
......@@ -13,7 +22,8 @@ export default defineComponent({
setup(props, { emit }) {
const { t } = useI18n();
// 当前展示的模块
const myboxType = ref('0');
const myboxType = ref(2);
const { $state: FoxWallet } = useFoxWallet();
//
const loading = ref(false);
/**
......@@ -23,60 +33,91 @@ export default defineComponent({
* 中签4
*/
const myblindBoxList = reactive({
list: [
{
img: new URL('@/assets/img/test.png', import.meta.url).href,
introduction: '5人抽奖一人必中',
status: '1',
price: '300',
buyersNum: '3/5',
canObtain: '1000',
},
{
img: new URL('@/assets/img/test.png', import.meta.url).href,
introduction: '5人抽奖一人必中',
status: '2',
price: '300',
buyersNum: '3/5',
canObtain: '1000',
},
{
img: new URL('@/assets/img/test.png', import.meta.url).href,
introduction: '5人抽奖一人必中',
status: '3',
price: '300',
buyersNum: '3/5',
canObtain: '1000',
},
{
img: new URL('@/assets/img/test.png', import.meta.url).href,
introduction: '5人抽奖一人必中',
status: '4',
price: '300',
buyersNum: '3/5',
canObtain: '1000',
},
],
list: [],
});
// 是否显示可中签价值
const isShowEnd = ref(false);
const pageNum = ref(1);
const pageSize = ref(10);
const btnOptions = computed(() => [
{
label: t('raffle.inprogress'),
value: '0',
value: 2,
other: {
title: t('raffle.inprogress'),
type: 'success',
},
},
{
label: t('raffle.unfinish'),
value: '1',
label: t('raffle.Unfinished'),
value: 1,
other: {
title: t('raffle.Unfinished'),
type: 'failed',
},
},
{
label: t('raffle.Finished'),
value: '2',
value: 3,
other: {
title: 'true',
type: 'failed',
},
},
]);
// 当前显示的标签内容
const CustomStatus = ref(btnOptions.value[0].other);
const onBack = () => {
emit('update:modelValue', 'activity');
};
const getList = async () => {
try {
loading.value = true;
let res: any = await UserOrder({
page: pageNum.value,
limit: pageSize.value,
status: myboxType.value,
address: FoxWallet.address,
});
myblindBoxList.list = res.data.data;
loading.value = false;
} catch (e) {
console.log(e);
loading.value = false;
}
};
watch(
() => loading.value,
(v) => {
if (!v) {
// 判断当前模块
if (myboxType.value == 1) {
CustomStatus.value = btnOptions.value[1].other;
} else if (myboxType.value == 2) {
CustomStatus.value = btnOptions.value[0].other;
} else {
CustomStatus.value = btnOptions.value[2].other;
}
if (myboxType.value == 3) {
isShowEnd.value = true;
} else {
isShowEnd.value = false;
}
}
}
);
// 条件改变
const onChange = (value: any) => {
if (myboxType.value == value) {
return;
}
myboxType.value = value;
getList();
};
onMounted(() => {
getList();
});
return () => (
<div class="custom-my-blind-box">
<div class="header">
......@@ -90,14 +131,19 @@ export default defineComponent({
<RadioGroup
v-model={myboxType.value}
options={btnOptions.value}
onChange={onChange}
></RadioGroup>
</div>
<div class="my-blind-box-content">
{myblindBoxList.list.map((item: any) => (
<Card info={item} showEnd={isShowEnd.value}></Card>
<Card
info={item}
showEnd={isShowEnd.value}
customStatus={CustomStatus.value}
></Card>
))}
<Loading v-show={loading.value}></Loading>
</div>
<Loading v-show={loading.value}></Loading>
</div>
);
},
......
......@@ -3,17 +3,29 @@
<div v-show="current == 'activity'">
<Activity v-model="current"></Activity>
</div>
<div v-show="current == 'mybox'">
<Myraffle v-model="current"></Myraffle>
</div>
<template v-if="isLoadMyBox">
<div v-show="current == 'mybox'">
<Myraffle v-model="current"></Myraffle>
</div>
</template>
</div>
</template>
<script lang="ts" setup>
import Myraffle from './components/myraffle';
import Activity from './components/Activity';
import { ref } from 'vue';
import { ref, watch } from 'vue';
const current = ref('activity');
// 是否加载我的盲盒
const isLoadMyBox = ref(false);
watch(
() => current.value,
(v) => {
if (v == 'mybox' && !isLoadMyBox.value) {
isLoadMyBox.value = true;
}
}
);
</script>
<style lang="less">
......
......@@ -16,7 +16,7 @@ const defaultRouterList: Array<RouteRecordRaw> = [
},
// raffle - 抽奖
{
path: '/raffle',
path: '/raffle/:code?/:id?',
name: 'raffle',
component: () => import('@/pages/raffle/index.vue'),
},
......
import request from '@/utils/request';
// 获取用户邀请码
export const getUserInfo = (data: any) => {
return request.get('/api/user/info', {
params: data,
});
};
// 获取进行中的列表
export const getBlindBoxList = (address: string = '') => {
return request.get('/api/BlindBoxList', {
params: {
address: address ? address : undefined,
},
});
};
// 购买前判断用户能否购买盲盒
export const useCanNotBuy = (data: any) => {
return request.get('/api/CanNotBuy', {
params: data,
});
};
// 购买盲盒
export const buy_blindBox = (data: any) => {
return request.post('/api/BlindBox/buy', {
...data,
});
};
// 轮询检测是否购买成功
export const IntervalCheckOrder = (data: any) => {
return request.post('/api/BlindBox/buy/check', {
...data,
});
};
// 用户订单
export const UserOrder = (data: any) => {
return request.get('/api/order', {
params: data,
});
};
import axios from 'axios';
import { MessagePlugin } from 'tdesign-vue-next';
const instance = axios.create({
timeout: 1000,
withCredentials: true,
});
instance.interceptors.request.use((config) => {
return config;
});
instance.defaults.timeout = 60000;
instance.interceptors.response.use(
(response) => {
const { data, headers, status } = response;
if (data.code === 200) {
return data.data;
} else if (data.code === 0) {
return data;
} else if (data.code === 2) {
MessagePlugin.closeAll();
MessagePlugin.warning('登录信息已过期');
return;
} else {
MessagePlugin.closeAll();
MessagePlugin.error(data.msg || '请求错误,请稍后重试');
return Promise.reject(data.msg);
}
},
(err) => {
const { config } = err;
if ('response' in err) {
const { message: msg } = err.response.data;
MessagePlugin.error(msg || '请求错误,请稍后重试');
}
if (!config || !config.retry) return Promise.reject(err);
}
);
export default instance;
......@@ -12,7 +12,7 @@ export default defineConfig(({ command, mode }) => {
let newDate = `${date.getFullYear()}-${
date.getMonth() + 1
}-${date.getDate()}--${date.getHours()}.${date.getMinutes()}`;
let api = 1 ? 'http://mxcus.net' : 'http://snow.test';
let api = 0 ? 'http://mxcus.net' : 'http://127.0.0.1:8000';
return {
base: '/',
resolve: {
......@@ -42,14 +42,15 @@ export default defineConfig(({ command, mode }) => {
},
output: {
// 去掉注释内容
comments: true,
comments: false,
},
},
rollupOptions: {
output: {
manualChunks: {
// 拆分代码,这个就是分包,配置完后自动按需加载,现在还比不上webpack的splitchunk,不过也能用了。
vue: ['vue', 'vue-router', 'vuex'],
vue: ['vue', 'vue-router', 'pinia'],
Tdesign: ['tdesign-vue-next'],
// echarts: ['echarts'],
},
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment