update @c端接口联调,字段确认

This commit is contained in:
huangzhen 2024-09-02 18:28:27 +08:00
parent 699c7f4cb1
commit ebfe165175
15 changed files with 251 additions and 133 deletions

View File

@ -50,6 +50,7 @@ module.exports = {
plugins: [
new VueLoaderPlugin(), // vue-loader 插件
new MiniCssExtractPlugin({
ignoreOrder: true, // 解决css引入顺序不一导致冲突的警告问题
filename: 'css/[name].[contenthash].content.css',
}),
new HtmlWebpackPlugin({

View File

@ -114,3 +114,10 @@ html {
justify-content: center;
}
}
// 按钮置灰
.btnPlacingAshes {
background: #e8eaec !important;
}

View File

@ -1,6 +1,6 @@
import { createApp } from 'vue'
import './scripts/flexible.js'
import './global.css' // 添加全局样式
import './global.scss' // 添加全局样式
import App from './App.vue'
import router from './router'

View File

@ -5,14 +5,14 @@ export enum Api {
LOGIN = "/v1/user/login",
// 商品查询
QUERY = "/v1/goods/list/key",
// 商品详情
DETAIL = "/v1/goods/detail",
// 模板查询
GET_THEME = "/v1/theme",
// 兑换
EXCHANGE = "/v1/order/exchange",
// 商品详情查询
DETAIL_QUERY = "/v1/goods/detail",
// 订单状态查询
ORDER_QUERY = "/v1/order/query",
}
@ -20,10 +20,10 @@ export const LOGIN = (data?: any) => request.post(Api.LOGIN, data);
export const QUERY = (data?: any) => request.get(Api.QUERY, data);
export const DETAIL = (data?: any) => request.get(Api.DETAIL, data);
export const GET_THEME = (data?: any) => request.get(Api.GET_THEME, data);
export const EXCHANGE = (data?: any) => request.post(Api.EXCHANGE, data);
export const getGoodsDetail = (data?: any) => request.get(Api.DETAIL_QUERY, data);
export const DETAIL_QUERY = (data?: any) => request.get(Api.DETAIL_QUERY, data);
export const ORDER_QUERY = (data?: any) => request.post(Api.ORDER_QUERY, data);

View File

@ -3,21 +3,18 @@ import {
createWebHashHistory,
createWebHistory,
} from "vue-router";
import { LOGIN, QUERY } from "@/router/api";
// 公共路由
const routePublic = [
{
// 商品中转(空白页)
path: "/proTransfer",
name: "proTransfer",
component: () => ({}),
path: "/",
redirect: "/home"
},
{
// 中转失败页
path: "/transferError",
name: "transferError",
component: () => import("../views/transferError.vue"),
// 首页(中转处理逻辑)
path: "/home",
name: "home",
component: () => import("../views/home.vue"),
},
];
@ -67,64 +64,4 @@ const router = createRouter({
routes,
});
router.beforeEach(async (to, from, next) => {
if (to.path === "/proTransfer") {
// 商品中转
// console.log('---proTransfer-router.beforeEach---', to, from);
try {
const key =
to.query.key || "rPBVzOCs0FN4P7jNMqHo5IVfb0oaAyigbfNBmCKnavs=";
// const res = await LOGIN({ key: key }); // 请求 token
const res = { data: { Token: "" }, code: 200, message: "" }; // 请求 token
if (res && res.code === 200) {
res.data.Token =
"admingL_q2QjDlD_MdeU11q5ILoeLFEqhbGBtcEptfs-OHzI53vbrgX9GFCh-SB4fSJBGwgSTl.uDPUoyiPjJ2O6e7zUBzExUUXhsdnPxtyLwncYc8.vy9ikZRp-wUba2POZJlTERJxnLbK6DzpQFQTxuYfcNM_yl3Avo7hgCjkS-ot5QgzNiO5TE";
if (res.data.Token) {
localStorage.setItem("token", res.data.Token);
const resList = await QUERY({ key: key }); // 根据商品判断 跳转指定路由
if (resList && resList.code === 200) {
localStorage.setItem("key", String(key));
sessionStorage.removeItem("transferError_page_identify");
if (resList.data.length !== 1) {
sessionStorage.setItem(
"transferProListData",
JSON.stringify(resList.data)
);
return next({
path: "/cmsList",
query: { key: key },
}); // 商品列表
} else {
const pro = resList.data[0];
if (pro.goods.group_id) {
return next({
path: "/comProduct",
query: { group_id: pro.goods.group_id },
}); // 组合商品
} else {
// todo 3 种商品的详情页
}
}
}
} else {
showToast("登录失败");
}
} else {
showToast(res?.message || '接口请求失败');
}
next({
path: "/transferError",
query: { redirect: to.fullPath },
});
} catch (error) {
next({
path: "/transferError",
query: { redirect: to.fullPath },
});
}
} else {
next(); // 对于非匹配路由,直接放行
}
});
export default router;

110
src/views/home.vue Normal file
View File

@ -0,0 +1,110 @@
<template>
<van-loading class="pageNoneLoading_Vant" size="24px" color="#0094ff" vertical>{{ loadingText || '加载中'}}</van-loading>
</template>
<script lang="ts">
import { LOGIN, QUERY } from "@/router/api";
export default {
name: 'home',
data() {
return {
loadingText: '',
key: this.$route.query.key || localStorage.getItem("key"),
pageData: null,
}
},
created() {
//
this.listJudge();
},
methods: {
async listJudge() {
const key = this.$route.query.key || "rPBVzOCs0FN4P7jNMqHo5IVfb0oaAyigbfNBmCKnavs=";
// const res = await LOGIN({ key: key }); // token
// todo
const res = {data: { Token: "" }, code: 200, message: ""};
if (res && res.code === 200) {
// todo
res.data.Token = "admingL_q2QjDlD_MdeU11q5ILoeLFEqhbGBtcEptfs-OHzI53vbrgX9GFCh-SB4fSJBGwgSTl.uDPUoyiPjJ2O6e7zUBzExUUXhsdnPxtyLwncYc8.vy9ikZRp-wUba2POZJlTERJxnLbK6DzpQFQTxuYfcNM_yl3Avo7hgCjkS-ot5QgzNiO5TE";
if (res.data.Token) {
localStorage.setItem("token", res.data.Token);
const resList = await QUERY({ key: key }); //
if (resList && resList.code === 200) {
localStorage.setItem("key", key);
const changeData = this.changeData(resList.data);
const errCode = changeData[0].goods.err_code;
const reson = changeData[0].goods.reson;
if (this.startsWith(String(errCode), ['10', '11'])) { // 10XXX key 11XXX 12XXX
return showToast(reson || '活动异常');
}
this.pageData = [ ...changeData ];
if (resList.data.length !== 1) {
sessionStorage.setItem("transferProListData", JSON.stringify(resList.data));
this.$router.replace({
path: '/cmsList',
query: { key: this.key }
});
} else {
this.jumpDetail(resList.data[0])
}
} else {
this.loadingText = '请求失败,请刷新重试'
showToast(res?.message || '请求失败');
}
}
} else {
this.loadingText = '请求失败,请刷新重试'
showToast(res?.message || '登录失败');
}
},
changeData(data: Array<any>) {
let list: Array<any> = [], obj: any = {};
data.forEach(item => {
if (item.goods.group_id) {
if (!obj[item.goods.group_id]) { // group_id
obj[item.goods.group_id] = 1;
list.push({
...item,
goods: {
...item.goods,
name: JSON.parse(item.goods.group_info)?.name,
icon: JSON.parse(item.goods.group_info)?.icon,
}
});
}
} else {
list.push(item);
}
})
return list;
},
startsWith(str: string, prefixes: Array<string>) {
const regex = new RegExp('^(' + prefixes.join('|') + ')');
return regex.test(str);
},
jumpDetail(item: any) {
if (this.comType === 2) return;
const detailPage: any = {
'1': '/cmsCash',
'2': '/cmsDetail',
'3': '/cmsVoucher',
}
let params = {
path: detailPage[item.goods.type],
query: { key: this.key, id: item.goods.id }
}
if (item.goods.group_id) {
params.path = '/comProduct';
delete params.query.id;
params.query.group_id = item.goods.group_id;
}
this.$router.push(params);
},
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -14,27 +14,32 @@
<p @click.stop="receiveAll('top', pageData.get(item))" :style="{ color: listCss.get(item)?.btnTextColor, opacity: !detailType ? 1 : 0 }" class="topBtn">一键领取 ></p>
<ul class="listBox">
<li v-for="(i, j) in pageData.get(item)" :key="j" class="listContent">
<p class="tipText">支付宝账号达到可领取上限请换号重试</p>
<p class="tipText">{{ '暂无接口字段' }}</p>
<div v-if="listCss.get(item)?.text" class="leftInfo" :style="{ backgroundColor: listCss.get(item)?.infoBgc }">
<p class="infoIcon" :style="{ backgroundImage: `url(${listCss.get(item)?.textIcon})` }">{{ listCss.get(item)?.text }}</p>
<p class="amountInfo" :style="{ color: listCss.get(item)?.textColor }">
<span>10</span>
<span>x2</span>
<span v-if="(i.goods.type === 3) && i.info.send_num">{{ `${10}`}}</span>
<span v-else class="bigMount">{{ 10 }}</span>
<span v-if="(i.goods.type === 3) && i.info.send_num">x{{i.info.send_num}}</span>
</p>
<p class="amountTip">满10.01减10</p>
<p v-if="(i.goods.type === 3) && (i.info.full_amount && i.info.amount)" class="amountTip">{{ `${i.info.full_amount}${i.info.amount}` }}</p>
</div>
<!-- 数字权益 -->
<img v-else class="listProImg" src="" alt="">
<img v-else class="listProImg" :src="i.goods.icon" alt="">
<div class="contentInfo">
<p class="contentInfoTop">默认文案-云闪付5元立减金</p>
<p class="contentInfoBottom">默认文案-使用招商银行卡支付可立减金</p>
<p class="contentInfoTop">{{ i.goods.name }}</p>
<p v-if="(i.goods.type === 3) && i.info.instruction" class="contentInfoBottom">{{ i.info.instruction }}</p>
</div>
<p
:class="['contentBtn', 0 && 'contentBtnDisable']"
:class="['contentBtn', i.goods.err_code && 'contentBtnDisable']"
:style="{ background: `linear-gradient(270deg, ${listCss.get(item)?.btnColorList[0]} 0%, ${listCss.get(item)?.btnColorList[1]} 100%)` }"
@click.stop="receiveOne(i)"
>
{{ '立即领取' }}
{{
i.goods.err_code
? 'code 文案'
: (i.goods.type === 3) && i.info.send_num ? `立即领取 ${i.goods.exchange_times/i.info.send_num}` : '立即领取'
}}
</p>
</li>
</ul>
@ -49,6 +54,19 @@
</li>
</template>
</ul>
<van-popup
v-model:show="inputModalShow"
closeable
position="bottom"
:style="{ height: '30%' }"
class="inputModal"
>
<p class="inputModalTitle">标题</p>
<van-cell-group class="inputModalContent" inset>
<van-field v-model="inputVal" label="账号" placeholder="请输入手机号或QQ号" />
<van-field v-model="inputValAgain" label="再次输入" placeholder="请再次输入" />
</van-cell-group>
</van-popup>
</div>
</template>
@ -132,6 +150,9 @@ export default {
btnImg: require('./img/btnImg3_3.png'),
}],
]),
inputModalShow: false,
inputVal: '',
inputValAgain: '',
}
},
async created() {
@ -286,6 +307,10 @@ export default {
position: relative;
margin: 0 11px;
border-radius: 4px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.infoIcon {
position: absolute;
width: 35px;
@ -302,13 +327,16 @@ export default {
line-height: 16px;
}
.amountInfo {
height: 26px;
font-family: D-DIN, D-DIN;
font-weight: 400;
font-size: 14px;
line-height: 26px;
margin-top: 7px;
text-align: center;
span {
font-family: D-DIN, D-DIN;
font-weight: 400;
font-size: 14px;
width: 100%;
}
.bigMount {
font-size: 24px;
}
}
.amountTip {
height: 12px;
@ -317,7 +345,7 @@ export default {
font-size: 8px;
color: #6F727A;
line-height: 12px;
text-align: center;
margin-top: 2px;
}
}
.listProImg {
@ -351,7 +379,7 @@ export default {
}
}
.contentBtn {
width: 76px;
padding: 0 10px;
height: 29px;
border-radius: 15px;
font-family: Source Han Sans, Source Han Sans;
@ -363,7 +391,7 @@ export default {
margin-right: 8px;
}
.contentBtnDisable {
opacity: 0.8;
opacity: 0.5;
}
}
}
@ -436,4 +464,13 @@ export default {
}
}
}
.inputModal {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
.inputModalContent {
margin-bottom: 20px;
}
}
</style>

View File

@ -16,6 +16,8 @@
height: 100%;
}
.detailCarousel {
width: 100%;
height: 373px;
overflow: hidden;
img {
display: block;

View File

@ -1,5 +1,5 @@
// 仅在单独打包时候使用
import '../../../global.css'; // 添加全局样式
import '../../../global.scss'; // 添加全局样式
import cmsDetail from './index.vue';
export default cmsDetail;

View File

@ -18,7 +18,9 @@
</van-cell-group>
</div>
<p class="line"/>
<div @click="receive" class="detailBtn" :style="{ color: cssData.btnColor, backgroundColor: cssData.btnBgc }">立即兑换</div>
<div @click="receive" class="detailBtn" :style="{ color: cssData.btnColor, backgroundColor: cssData.btnBgc }">
{{ btnText || '立即兑换' }}
</div>
<img v-if="cssData.activityDescType === 1 && cssData.bottomImg.length" class="bottomImg" :src="cssData.bottomImg[0].url" alt="">
<img v-if="cssData.activityDescType === 2 && cssData.floatImg" @click="changePopComStatus" class="floatImg" :src="cssData.floatImg" alt="">
<div v-if="cssData.activityDescType === 3 && cssData.floatText" class="description-3" v-html="cssData.floatText" />
@ -40,7 +42,7 @@
<script lang="ts">
import "./index.scss";
import config from "./config";
import { DETAIL, GET_THEME, EXCHANGE } from "@/router/api";
import { DETAIL_QUERY, GET_THEME, EXCHANGE, ORDER_QUERY } from "@/router/api";
export default {
name: 'cmsDetail',
@ -78,7 +80,9 @@ export default {
imgList: [],
inputVal: '',
inputValAgain: '',
popComStatus: false
popComStatus: false,
btnText: '',
orderData: [], //
}
},
created() {
@ -109,7 +113,8 @@ export default {
this.loadingStatus = true;
const id = this.$route.query.id;
if (!id) return showToast('商品ID不能为空');
const res = await DETAIL({ key: this.key, id: this.$route.query.id });
this.orderData = [{ key: this.key, id: Number(this.$route.query.id) }];
const res = await DETAIL_QUERY({ key: this.key, id: this.$route.query.id });
if (res && res.code === 200) {
if (res.data) {
this.pageData = { ...res.data };
@ -149,16 +154,32 @@ export default {
this.overlayLoad = true;
const res = await EXCHANGE({
key: this.key,
id: [ Number(this.$route.query.id) ],
id: [Number(this.$route.query.id)],
account: 'account' || this.inputVal,
name: 'name',
});
this.overlayLoad = false;
if (res && res.code === 200) {
showToast('兑换成功');
this.btnText = '领取中';
this.overlayLoad = true;
await this.orderPolling();
} else {
showToast(res?.message || '接口请求失败');
}
},
async orderPolling () {
const that = this;
const res = await ORDER_QUERY(this.orderData[0]);
if (res && res.code === 200) {
if (res.data.status === 2) {
return await this.getData()
} else {
setTimeout(() => { that.orderPolling() }, 3000)
}
} else {
setTimeout(() => { that.orderPolling() }, 3000)
}
}
}
}

View File

@ -41,6 +41,15 @@
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
.proIcon {
display: block;
width: 56px;
height: 56px;
position: absolute;
top: 0;
right: 0;
}
.swiper-slide-image {
display: block;
width: 45px;

View File

@ -1,5 +1,5 @@
// 仅在单独打包时候使用
import '../../../global.css';// 添加全局样式
import '../../../global.scss';// 添加全局样式
import cmsList from './index.vue';
export default cmsList;

View File

@ -18,6 +18,7 @@
>
<swiper-slide class="" v-for="(item, index) in pageData" :key="index">
<div class="swiper-slide-box">
<img class="proIcon" v-if="iconMap.get(String(item.goods.err_code))" :src="iconMap.get(String(item.goods.err_code))" alt="">
<img :src="item.goods.icon" class="swiper-slide-image" alt=""/>
<p class="swiper-slide-name">{{ item.goods.name }}</p>
</div>
@ -49,7 +50,9 @@
</template>
</ul>
</div>
<div @click="jumpDetail(pageData[initialSlide])" v-if="cssData.contentType === 1" class="btn" :style="{ color: cssData.btnColor, backgroundColor: cssData.btnBgc }">立即兑换</div>
<div @click="jumpDetail(pageData[initialSlide])" v-if="cssData.contentType === 1" :class="['btn', pageData[initialSlide].goods.err_code && 'btnPlacingAshes']" :style="{ color: cssData.btnColor, backgroundColor: cssData.btnBgc }">
立即兑换
</div>
<img v-if="cssData.activityDescType === 1 && cssData.bottomImg.length" class="bottomImg" :src="cssData.bottomImg[0].url" alt="">
<img v-if="cssData.activityDescType === 2 && cssData.floatImg" @click="changePopComStatus" class="floatImg" :src="cssData.floatImg" alt="">
<div v-if="cssData.activityDescType === 3 && cssData.floatText" class="description-3" v-html="cssData.floatText" />
@ -112,6 +115,12 @@ export default {
defaultMatrix: [1, 2, 3, 4, 5, 6],
defaultMatrixImg: 'https://lsxdmgoss.oss-cn-chengdu.aliyuncs.com/MarketingSystem/image/uploads/2024-08-20/0338CC5C-D9FD-4f9d-B65C-DCAB50F2FAA7.png',
},
iconMap: new Map([ // code icon
['1', 'https://lsxdmgoss.oss-cn-chengdu.aliyuncs.com/MarketingSystem/image/front/jijiangkaishi.png'], //
['2', 'https://lsxdmgoss.oss-cn-chengdu.aliyuncs.com/MarketingSystem/image/front/yiduihuan.png'], //
['3', 'https://lsxdmgoss.oss-cn-chengdu.aliyuncs.com/MarketingSystem/image/front/yiduiwan.png'], //
['4', 'https://lsxdmgoss.oss-cn-chengdu.aliyuncs.com/MarketingSystem/image/front/yiguoqi.png'], //
]),
popComStatus: false
}
},
@ -146,17 +155,19 @@ export default {
sessionStorage.removeItem('transferProListData');
const changeData = this.changeData(data);
this.pageData = [ ...changeData ];
console.log('---缓存列表数据---', this.pageData);
await this.getCss(data[0].theme_list_id);
} else {
const res = await QUERY({ key: this.key });
if (res && res.code === 200) {
if (res.data.length) {
const changeData = this.changeData(res.data);
const errCode = changeData[0].goods.err_code;
const reson = changeData[0].goods.reson;
if (this.startsWith(String(errCode), ['10', '11'])) { // 10XXX key 11XXX 12XXX
return showToast(reson || '活动异常');
}
this.pageData = [ ...changeData ];
console.log('---接口列表数据---', this.pageData);
await this.getCss(res.data[0].theme_list_id);
await this.getCss(res.data[0].theme_list_id || '43');
} else {
showToast('商品数据为空');
}
@ -176,7 +187,7 @@ export default {
goods: {
...item.goods,
name: JSON.parse(item.goods.group_info)?.name,
icon: JSON.parse(item.goods.group_info)?.icon,
icon: item.goods.icon,
}
});
}
@ -186,10 +197,13 @@ export default {
})
return list;
},
startsWith(str: string, prefixes: Array<string>) {
const regex = new RegExp('^(' + prefixes.join('|') + ')');
return regex.test(str);
},
async getCss(id: number | string) {
const res = await GET_THEME({ id: id });
if (res && res.code === 200) {
console.log('---列表模版数据---', JSON.parse(res.data.config));
this.cssData = {
...this.cssData,
...JSON.parse(res.data.config)
@ -210,6 +224,10 @@ export default {
},
jumpDetail(item: any) {
if (this.comType === 2) return;
if (item.goods.err_code && item.goods.reson) {
// todo
// return showToast(item.goods.reson);
}
const detailPage: any = {
'1': '/cmsCash',
'2': '/cmsDetail',

View File

@ -161,7 +161,7 @@
</template>
<script lang="ts">
import "./index.scss";
import { getGoodsDetail, EXCHANGE } from "@/router/api";
import { DETAIL_QUERY, EXCHANGE } from "@/router/api";
let goodsDetail = 0,
goodsCount = 0;
@ -188,7 +188,7 @@ export default {
},
async created() {
this.goodsID = localStorage.getItem("id") || 810;
const { code, data } = await getGoodsDetail({ id: this.goodsID });
const { code, data } = await DETAIL_QUERY({ id: this.goodsID });
if (code === 200) {
Object.assign(this.info, data.info, {
time_limit: data.info.time_limit

View File

@ -1,24 +0,0 @@
<template>
<van-loading class="loading" size="24px" color="#0094ff" vertical>请求失败请刷新重试</van-loading>
</template>
<script lang="ts">
export default {
name: 'transferError',
created() {
const identify = sessionStorage.getItem('transferError_page_identify');
if (!identify) {
sessionStorage.setItem('transferError_page_identify', 'is');
} else {
const redirect = this.$route.query.redirect;
this.$router.replace(String(redirect));
}
}
}
</script>
<style lang="scss" scoped>
.loading {
margin-top: 200px;
}
</style>