Browse Source

feat:对接支付 对接签到功能 新手任务

dxj
杜贤金 1 year ago
parent
commit
d6026d28ed
  1. 32
      src/api/recharge/index.ts
  2. 29
      src/api/task/index.ts
  3. 15
      src/components/AppRecharge/index.d.ts
  4. 152
      src/components/AppRecharge/index.vue
  5. 2
      src/components/AppUserInfo/index.vue
  6. 7
      src/enums/recharge.ts
  7. 3
      src/utils/axios/index.ts
  8. 6
      src/views/task/components/BasicTask/index.d.ts
  9. 102
      src/views/task/components/BasicTask/index.vue
  10. 217
      src/views/task/components/HisTory/index.vue
  11. 5
      src/views/task/components/NoviceTask/index.d.ts
  12. 46
      src/views/task/components/NoviceTask/index.vue
  13. 5
      src/views/task/components/TaskList/index.d.ts
  14. 34
      src/views/task/components/TaskList/index.vue

32
src/api/recharge/index.ts

@ -1,7 +1,39 @@
import { defHttp } from '@/utils/axios/index'
// 商品分类tabs
export async function goodsType() {
return defHttp.get({
url: `/open-chat/sys/sys/dict/getDictItems/goods_type`,
})
}
// 商品分类列表
export async function goodsList(params: {
type: string
}) {
return defHttp.get({
url: `/open-chat/chatGoods/list`,
params,
})
}
// pc支付
export async function payment(data: {
goodsId: string
type: string
}) {
return defHttp.post({
url: `/open-chat/chat/pay/order`,
data,
})
}
// 是否支付成功
export async function getIsPay(params: {
orderCode: string
}) {
return defHttp.get({
url: `/open-chat/chat/pay/getIsPay`,
params,
})
}

29
src/api/task/index.ts

@ -0,0 +1,29 @@
import { defHttp } from '@/utils/axios/index'
// 签到详情
export async function current() {
return defHttp.get({
url: `/open-chat/logUserSign/current`,
})
}
// 签到
export async function logUserSign() {
return defHttp.post({
url: `/open-chat/logUserSign/sign`,
})
}
// 历史记录
export async function history(params: {
yearAndMonth: string
}) {
return defHttp.get({
url: `/open-chat/logUserSign/history`,
params,
})
}
// 新手任务
export async function noviceTask() {
return defHttp.get({
url: `/open-chat/taskCenter/noviceTask/list`,
})
}

15
src/components/AppRecharge/index.d.ts vendored

@ -0,0 +1,15 @@
interface Racharge {
title: string
key: string
}
interface GoodsData {
giveNum: number
title: string
gpt35Num: number
gpt40Num: number
pictureNum: number
price: number
costPrice: number
id: string
}
export { Racharge, GoodsData }

152
src/components/AppRecharge/index.vue

@ -1,42 +1,99 @@
<script lang="ts" setup>
import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue'
import { Button, Carousel } from 'ant-design-vue'
import { ref } from 'vue'
import { goodsType } from '@/api/recharge/index'
import { Button, Carousel, Modal, QRCode, message } from 'ant-design-vue'
import { onMounted, ref } from 'vue'
import type { GoodsData, Racharge } from './index.d'
import { getIsPay, goodsList, goodsType, payment } from '@/api/recharge/index'
import trumpet from '@/assets/images/recharge/lb.png'
import { useUserStore } from '@/store/moules/userStore/index'
import vip from '@/assets/images/recharge/vip.png'
import logo from '@/assets/images/conversation/logo.png'
import { useRechargeStore } from '@/store/moules/rechareStore'
import { RechargeEnum } from '@/enums/recharge'
const userStore = useUserStore()
const rechargeStore = useRechargeStore()
// ref
const carouselRef = ref()
//
const activeTab = ref(0)
//
const qrshow = ref<boolean>(false)
//
const tabs = ref<Racharge[]>([])
//
const goodslists = ref<GoodsData[]>([])
//
const payData = ref<any>({
orderCode: '',
wx_result: '',
})
//
const intervalId = ref()
//
function getType() {
goodsType().then((res) => {
console.log(res)
tabs.value = res
})
}
getType()
//
function handleClose() {
rechargeStore.rechargeClose()
activeTab.value = 0
}
//
const tabs = ref([
{ title: '日套餐', active: true },
{ title: '七日套餐', active: false },
{ title: '月套餐', active: false },
{ title: '年套餐', active: false },
{ title: '至尊礼遇卡', active: false },
])
const items = ref(['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7', 'item8'])
//
function getgoodsList(type: string) {
goodsList({ type }).then((res) => {
goodslists.value = res
console.log(res, 7777)
})
}
//
function selectTab(index: number) {
//
function selectTab(index: number, key: string) {
activeTab.value = index
getgoodsList(key)
}
//
function handlePay(goodsId: string) {
payment({ goodsId, type: RechargeEnum.PAY_PC }).then((res) => {
payData.value = res
qrshow.value = true
startPolling()
})
}
//
function startPolling() {
// 3
intervalId.value = setInterval(() => {
getIsPay({ orderCode: payData.value.orderCode }).then((res) => {
if (res) {
qrclose()
message.success('支付成功')
}
})
}, 2000)
}
//
function stopPolling() {
//
if (intervalId.value) {
clearInterval(intervalId.value)
intervalId.value = null
qrshow.value = false
userStore.getChatInfoFun()
}
}
//
function qrclose() {
stopPolling()
}
onMounted(() => {
getType()
getgoodsList('1')
})
//
function handleClose() {
rechargeStore.rechargeClose()
}
//
function tabClass(index: number) {
@ -70,6 +127,7 @@ const extreme = ref([{
unit: '幅',
name: '可生成图片',
}])
//
function onPrevClick() {
carouselRef.value.prev()
@ -89,34 +147,34 @@ function onNextClick() {
<div class="top">
<div class="tabs">
<div
v-for="(tab, index) in tabs"
v-for="(item, index) in tabs"
:key="index"
:class="tabClass(index)"
@click="selectTab(index)"
@click="selectTab(index, item.key)"
>
<img v-if="index === tabs.length - 1" class="vipphoto" :src="vip" alt="">
{{ tab.title }}
{{ item.title }}
</div>
</div>
</div>
<div class="bottom">
<div v-if="activeTab !== 4" class="basic-meal">
<div v-if="activeTab !== tabs.length - 1" class="basic-meal">
<div class="prev-button prev" @click="onPrevClick">
<LeftOutlined />
</div>
<Carousel ref="carouselRef" arrows :dots="false" :autoplay="false" :slides-to-show="3" :slides-to-scroll="1">
<div v-for="(item, index) in items" :key="index" class="carouse-box">
<Carousel ref="carouselRef" arrows :dots="false" :autoplay="false" :slides-to-show="goodslists.length > 3 ? 3 : goodslists.length" :slides-to-scroll="1">
<div v-for="(item, index) in goodslists" :key="index" class="carouse-box">
<div class="carouse-item">
<div class="dotnumbox">
<div class="dotnum">
100点数
{{ item.giveNum }}点数
</div>
<div class="discount">
限时折扣
</div>
</div>
<div class="deadline">
使用期限1个月
{{ item.title }}
</div>
<div class="equitiesbox">
<div class="equities">
@ -124,7 +182,7 @@ function onNextClick() {
GPT3.5版本
</div>
<div class="right">
250
{{ item.gpt35Num }}
</div>
</div>
@ -133,7 +191,7 @@ function onNextClick() {
GPT4.0版本
</div>
<div class="right">
25
{{ item.gpt40Num }}
</div>
</div>
<div class="equities">
@ -141,20 +199,20 @@ function onNextClick() {
AI绘图
</div>
<div class="right">
50
{{ item.pictureNum }}
</div>
</div>
</div>
<div class="price">
<div class="nowprice">
¥39.9
¥{{ item.price }}
</div>
<div class="oldprice">
¥50.00
¥{{ item.costPrice }}
</div>
</div>
<div class="payBtn">
<Button class="customBtn">
<Button class="customBtn" @click="handlePay(item.id)">
立即充值
</Button>
</div>
@ -219,7 +277,7 @@ function onNextClick() {
<div class="extremeopen">
<div class="openleft">
<span class="freeprice">畅享价</span>
<span class="newprice">¥2999</span>
<span class="newprice">¥{{ goodslists[0].price }}</span>
<span class="oldprice">¥4999</span>
</div>
<div class="openright">
@ -242,12 +300,27 @@ function onNextClick() {
</div>
</div>
</transition>
<Modal
v-model:open="qrshow"
:footer="null"
:width="210"
:body-style="{ zIndex: '2000' }"
:closable="false"
@cancel="qrclose"
>
<QRCode
style="text-align: center;"
error-level="H"
:value="payData.wx_result"
:icon="logo"
/>
</Modal>
</template>
<style scoped lang="scss">
<style scoped lang="scss">
.modal-mask {
position: fixed;
z-index: 9999;
z-index: 1000;
top: 0;
left: 0;
width: 100%;
@ -704,6 +777,7 @@ function onNextClick() {
height: 260px;
cursor: pointer;
align-items: center;
.dotnumbox {
display: flex;
justify-content: space-between;

2
src/components/AppUserInfo/index.vue

@ -25,7 +25,7 @@ function handleRecharge() {
</div>
</div>
<div class="num">
{{ info?.questionCount > 0 ? info?.questionCount : 0 }}
{{ (info?.questionCount ?? 0) > 0 ? info?.questionCount : 0 }}
</div>
<div class="pay" @click="handleRecharge">
充值

7
src/enums/recharge.ts

@ -0,0 +1,7 @@
/**
* @description:
*/
export enum RechargeEnum {
// PC支付
PAY_PC = 'qrcode',
}

3
src/utils/axios/index.ts

@ -22,7 +22,7 @@ const { createMessage, createErrorModal, createSuccessModal } = useMessage()
// 请求白名单,无须token的接口
const whiteList: string[] = ['/login', '/refresh-token']
// 不需要解密接口白名单
const notDecryptWhiteList = ['/hulk-auth/oauth/token', '/open-chat/chat/session', '/open-gpts/gpts/getDallEImages', 'open-chat/sys/sys/dict/getDictItems/goods_type']
const notDecryptWhiteList = ['/hulk-auth/oauth/token', '/open-chat/chat/session', '/open-gpts/gpts/getDallEImages']
/**
* @description: 便
@ -198,6 +198,7 @@ const transform: AxiosTransform = {
})
// 请求之前处理config
const token = userStore.getToken
if (token && !isToken) {
// jwt token
(config as Recordable).headers[import.meta.env.VITE_GLOB_APP_TOKEN_KEY] = options.tokenScheme

6
src/views/task/components/BasicTask/index.d.ts vendored

@ -0,0 +1,6 @@
export interface SignType {
continueDays: number
isSign: number
signDays: number
totalIntegral: number
}

102
src/views/task/components/BasicTask/index.vue

@ -1,54 +1,47 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { Popover } from 'ant-design-vue'
import { Popover, message } from 'ant-design-vue'
import { InviteForm } from '../InviteForm/index'
import { HisTory } from '../HisTory/index'
import type { SignType } from './index.d'
import { current, logUserSign } from '@/api/task/index'
import History from '@/assets/images/task/history.png'
import unclaimed from '@/assets/images/task/dlq.png'
import dot from '@/assets/images/task/dot.png'
import money from '@/assets/images/task/money.png'
import receive from '@/assets/images/task/ylq.png'
import lost from '@/assets/images/task/wlq.png'
import unclaimed from '@/assets/images/task/dlq.png'
//
const sign = ref([
{
day: '第1天',
img: receive,
status: 1,
//
const signData = ref<SignType>({
continueDays: 0,
isSign: 0,
signDays: 0,
totalIntegral: 0,
})
//
function getCurrent() {
current().then((res) => {
signData.value = res
})
}
getCurrent()
//
function handleSign() {
logUserSign().then(() => {
signData.value.isSign = 1
signData.value.continueDays += 1
if (signData.value.continueDays === 7) {
signData.value.totalIntegral += 10
}
else {
signData.value.totalIntegral += 1
}
message.success('签到成功!')
})
}
},
{
day: '第2天',
img: receive,
status: 1,
},
{
day: '第3天',
img: lost,
status: 1,
},
{
day: '第4天',
img: unclaimed,
status: 2,
},
{
day: '第5天',
img: unclaimed,
status: 3,
},
{
day: '第6天',
img: unclaimed,
status: 3,
},
{
day: '第7天',
img: unclaimed,
status: 3,
},
])
//
const earn = ref([
{
@ -83,7 +76,7 @@ function handleInvite() {
<div class="left basis-3/5">
<div class="top flex flex-row">
<div class="login-day">
您已连续登录 <span>3</span>
您已连续登录 <span>{{ signData.continueDays }}</span>
</div>
<div class="historyBox">
<img class="historyImg" :src="History" alt="">
@ -101,25 +94,36 @@ function handleInvite() {
<div class="bottom">
<div class="accumulate">
<div class="three">
累计领取3点数
累计领取{{ signData.totalIntegral }}点数
</div>
<div class="ten">
连续签到7天可得 10 点数
</div>
</div>
<div class="sign-box">
<div v-for="(item, index) in sign" :key="index" class="sign">
<div class="sign-photo" :style="{ backgroundImage: `url(${item.img})`, backgroundSize: '100% 100%', backgroundRepeat: 'no-repeat' }">
<div v-for="(item, index) in 7" :key="index" class="sign">
<div v-if="item === signData.signDays" class="sign-photo" :style="{ backgroundImage: `url(${!signData.isSign ? unclaimed : receive})`, backgroundSize: '100% 100%', backgroundRepeat: 'no-repeat' }">
<div class="count">
{{ index === 6 ? "10点数" : "1点数" }}
{{ item === 7 ? "10点数" : "1点数" }}
</div>
</div>
<div v-else class="sign-photo" :style="{ backgroundImage: `url(${item > signData.signDays ? unclaimed : receive})`, backgroundSize: '100% 100%', backgroundRepeat: 'no-repeat' }">
<div class="count">
{{ item === 7 ? "10点数" : "1点数" }}
</div>
<div v-show="item.status !== 2" class="sign-day">
{{ item.day }}
</div>
<div v-show="item.status === 2" class="sign-get">
<div v-if="item <= signData.signDays">
<div v-if="item === signData.signDays && !signData.isSign" class="sign-get" @click="handleSign">
领取
</div>
<div v-else class="sign-day">
{{ item }}
</div>
</div>
<div v-else class="sign-day">
{{ item }}
</div>
</div>
</div>
</div>

217
src/views/task/components/HisTory/index.vue

@ -6,8 +6,10 @@ import {
LeftOutlined,
RightOutlined,
} from '@ant-design/icons-vue'
import { history } from '@/api/task/index'
const data = ref(dayjs())
const yam = ref(dayjs(data.value).format('YYYY-MM'))
const datesData = ref<any>([])
const month = ref(data.value.month() + 1)
const formerly = ref(false)
@ -21,108 +23,16 @@ function changeMonth(type: number) {
}
if (tempDate.isSame(dayjs(), 'month')) {
formerly.value = false
datesData.value = [
{
num: 10,
type: 1,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 10,
type: 2,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 2,
},
{
num: 10,
type: 1,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 10,
type: 2,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 2,
},
{
num: 10,
type: 3,
},
]
}
data.value = tempDate
}
else {
datesData.value = []
formerly.value = true
data.value = dayjs(data.value).subtract(1, 'month')
}
month.value = data.value.month() + 1
yam.value = data.value.format('YYYY-MM')
getHistory(yam.value)
}
//
function format(val: string | number | dayjs.Dayjs | Date | null | undefined) {
@ -130,120 +40,32 @@ function format(val: string | number | dayjs.Dayjs | Date | null | undefined) {
return newVal
}
// 1 2 3 return
// 0 1
const getCellTypeClass = function (value: { $D: string | number }) {
const getData = datesData.value[value.$D]
const getData: any = datesData.value[value.$D - 1]
if (!getData && !formerly.value) {
return 'future-date'
}
if (formerly.value) {
return 'past-date'
}
switch (getData.type) {
switch (getData.signType) {
case 0:
return 'past-date'
case 1:
return 'current-date'
case 2:
return 'past-date'
case 3:
return 'future-date'
}
}
function getHistory(time: string) {
history({ yearAndMonth: time }).then((res) => {
console.log(res, 666)
onMounted(async () => {
// fetchDatesData()
datesData.value = [
{
num: 10,
type: 1,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 10,
type: 2,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 2,
},
{
num: 10,
type: 1,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 10,
type: 2,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 1,
},
{
num: 1,
type: 2,
},
{
num: 1,
type: 2,
},
{
num: 10,
type: 3,
},
datesData.value = res
})
}
]
onMounted(async () => {
getHistory(yam.value)
})
</script>
@ -264,16 +86,15 @@ onMounted(async () => {
</Button>
</div>
</template>
<!-- :class="getDateClass(value)" -->
<template #dateFullCellRender="{ current: value }">
<div v-if="value.month() === data.month() && value.year() === data.year()">
<div class="cellBox">
<div class="cellItem" :class="getCellTypeClass(value)">
<div class="count">
{{ datesData[value.$D]?.num ? datesData[value.$D]?.num : "1" }}点数
{{ datesData[value.$D - 1]?.awardIntegral ? datesData[value.$D - 1]?.awardIntegral : "1" }}点数
</div>
</div>
<div v-if="dayjs(value).isSame(dayjs(), 'day') && datesData[value.$D]?.type === 3 " class="getbtn">
<div v-if="dayjs(value).isSame(dayjs(), 'day') && datesData[value.$D - 1]?.signType === 0 " class="getbtn">
领取
</div>
<div v-else class="monthday">

5
src/views/task/components/NoviceTask/index.d.ts vendored

@ -0,0 +1,5 @@
export interface NoviceType {
name: string
score: number
status: number
}

46
src/views/task/components/NoviceTask/index.vue

@ -1,39 +1,25 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { onMounted, ref } from 'vue'
import { TaskList } from '../TaskList/index.ts'
import type { NoviceType } from './index.d'
import { noviceTask } from '@/api/task/index'
const total = ref(0)
const earn = ref<NoviceType[]>([])
const earn = ref([
{
title: '完成一次同聪3.5对话',
dot: 1,
status: 1,
},
{
title: '完成一次同聪4.0对话',
dot: 1,
status: 0,
},
{
title: '完成一次角色对话',
dot: 1,
status: 1,
},
{
title: '完成一次充值',
dot: 200,
status: 0,
},
{
title: '完成一次xxxxxxxxx',
dot: 2,
status: 1,
},
])
function getnoviceTaskList() {
noviceTask().then((res) => {
earn.value = res.taskCenterList
total.value = res.totalScore
})
}
onMounted(() => {
getnoviceTaskList()
})
</script>
<template>
<TaskList title="新手任务" :list="earn" />
<TaskList title="新手任务" :list="earn" :total="total" />
</template>
<style lang="scss" scoped>

5
src/views/task/components/TaskList/index.d.ts vendored

@ -0,0 +1,5 @@
export interface NoviceType {
name: string
score: number
status: number
}

34
src/views/task/components/TaskList/index.vue

@ -4,14 +4,18 @@ import {
DownOutlined,
UpOutlined,
} from '@ant-design/icons-vue'
import type { NoviceType } from './index.d'
import money from '@/assets/images/task/money.png'
const { title, list } = defineProps({
const { title, list, total } = defineProps({
title: {
type: String,
},
total: {
type: Number,
},
list: {
type: Array,
type: Array as PropType<NoviceType[]>,
default: () => [],
},
})
@ -43,7 +47,7 @@ const dynamicStyle = computed(() => {
<div class="nvoice-contant">
<div class="top flex justify-between">
<div class="left">
全部完成可获得 72 点数
全部完成可获得 {{ total }} 点数
</div>
<div class="right">
已完成 1/12
@ -55,13 +59,13 @@ const dynamicStyle = computed(() => {
<div v-for="(item, index) in list" :key="index" class="earn-list">
<div class="earn-list-left">
<div class="name">
{{ item.title }}
{{ item.name }}
</div>
<div v-show="item.status === 1" class="accomplish">
已获得{{ item.dot }}点数
已获得{{ item.score }}点数
</div>
<div v-show="item.status === 0" class="unfinished">
0/{{ item.dot }}
0/{{ item.score }}
</div>
</div>
<div class="earn-list-right">
@ -69,15 +73,19 @@ const dynamicStyle = computed(() => {
<div class="flex items-center">
<img class="add-dot-photo" :src="money" alt="">
<div class="add-dot-num">
+{{ item.dot }}
+{{ item.score }}
</div>
</div>
<div v-show="item.status === 1" class="add-dot-btn accomplish">
<div v-show="item.status === 2" class="add-dot-btn accomplish">
已完成
</div>
<div v-if="item.status === 0 " class="add-dot-btn unfinished">
<div v-show="item.status === 1" class="add-dot-btn finished">
领取
</div>
<div v-if="item.status === 0" class="add-dot-btn unfinished">
未完成
</div>
</div>
@ -213,8 +221,7 @@ const dynamicStyle = computed(() => {
padding-left: 3px;
}
.add-dot-btn {
cursor: pointer;
width: 50px;
border-radius: 16px;
font-size: 11px;
font-family:
@ -229,6 +236,11 @@ const dynamicStyle = computed(() => {
background: linear-gradient(90deg, #b5ceff 0%, #a6baff 100%);
color: #ffffff;
}
&.finished {
background: linear-gradient(90deg, #6fa3ff 0%, #486dff 100%);
color: #ffffff;
cursor: pointer;
}
&.unfinished {
background: #ffffff;
border: 1px solid #cdcdcd;

Loading…
Cancel
Save